/* * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. */ /* * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include #include "crm_regs.h" #define PRE_DIV_MIN_FREQ 10000000 /* Minimum Frequency after Predivider */ #define PROPAGATE_RATE_DIS 2 struct timer_list dptcen_timer; static int cpu_curr_wp; static struct cpu_wp *cpu_wp_tbl; static int cpu_wp_nr; static int cpu_wp_offset; static struct clk mcu_pll_clk; static struct clk peri_pll_clk; static struct clk ipg_clk; static struct clk ckih_clk; static struct clk ckie_clk; static struct clk ahb_clk; static struct clk cpu_clk; #define CLK_CODE(arm, ahb, sel) (((arm) << 16) + ((ahb) << 8) + (sel)) #define CLK_CODE_ARM(c) (((c) >> 16) & 0xFF) #define CLK_CODE_AHB(c) (((c) >> 8) & 0xFF) #define CLK_CODE_PATH(c) ((c) & 0xFF) static int __get_arm_div(unsigned long pdr0, int *fi, int *fd); static int g_clk_mux_auto[8] = { CLK_CODE(1, 3, 0), CLK_CODE(1, 2, 1), CLK_CODE(2, 1, 1), -1, CLK_CODE(1, 6, 0), CLK_CODE(1, 4, 1), CLK_CODE(2, 2, 1), -1, }; static int g_clk_mux_consumer[16] = { CLK_CODE(1, 4, 0), CLK_CODE(1, 3, 1), CLK_CODE(2, 2, 0), -1, -1, -1, CLK_CODE(4, 1, 0), CLK_CODE(1, 5, 0), CLK_CODE(1, 8, 0), CLK_CODE(1, 6, 1), CLK_CODE(2, 4, 0), -1, -1, -1, CLK_CODE(4, 2, 0), -1, }; static int g_hsp_div_table[3][16] = { {4, 3, 2, -1, -1, -1, 1, 5, 4, 3, 2, -1, -1, -1, 1, -1}, {-1, -1, -1, -1, -1, -1, -1, -1, 8, 6, 4, -1, -1, -1, 2, -1}, {3, -1, -1, -1, -1, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1}, }; static void __calc_dividers(u32 div, u32 *pre, u32 *post, u32 base) { u32 min_pre, temp_pre, old_err, err; min_pre = (div - 1) / base + 1; old_err = 8; for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) { if (div > (temp_pre * base)) break; if (div < (temp_pre * temp_pre)) continue; err = div % temp_pre; if (err == 0) { *pre = temp_pre; break; } err = temp_pre - err; if (err < old_err) { old_err = err; *pre = temp_pre; } } *post = (div + *pre - 1) / *pre; } static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post) { if (div >= 512) { *pre = 8; *post = 64; } else if (div >= 64) { __calc_dividers(div, pre, post, 64); } else if (div <= 8) { *pre = div; *post = 1; } else { *pre = 1; *post = div; } } static void __calc_two_dividers(u32 div, u32 *pre, u32 *post) { if (div >= 64) { *pre = *post = 8; } else if (div > 8) { __calc_dividers(div, pre, post, 8); } else { *pre = 1; *post = div; } } static unsigned long _clk_per_post_round_rate(struct clk *clk, unsigned long rate) { u32 pre, post; u32 div = clk->parent->rate / rate; if (clk->parent->rate % rate) div++; __calc_pre_post_dividers(div, &pre, &post); return clk->parent->rate / (pre * post); } static unsigned long _clk_round_rate(struct clk *clk, unsigned long rate) { u32 pre, post; u32 div = clk->parent->rate / rate; if (clk->parent->rate % rate) div++; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { __calc_two_dividers(div, &pre, &post); return clk->parent->rate / (pre * post); } else return clk->parent->rate / div; } static int __switch_cpu_wp(struct clk *clk, unsigned long rate) { int i; u32 reg_value; if (cpu_wp_tbl[cpu_curr_wp].cpu_rate < rate) { for (i = cpu_curr_wp + 2; i < cpu_wp_nr; i += 2) { if (rate == cpu_wp_tbl[i].cpu_rate) goto found; } return -EINVAL; } else { for (i = cpu_curr_wp - 2; i >= 0; i -= 2) { if (rate == cpu_wp_tbl[i].cpu_rate) goto found; } return -EINVAL; } found: reg_value = __raw_readl(MXC_CCM_PDR0); reg_value = (reg_value & ~(MXC_CCM_PDR0_CON_MUX_DIV_MASK | MXC_CCM_PDR0_AUTO_MUX_DIV_MASK)) | cpu_wp_tbl[i].pdr0_reg; __raw_writel(reg_value, MXC_CCM_PDR0); if (cpu_wp_tbl[i].pll_rate != cpu_wp_tbl[cpu_curr_wp].pll_rate) clk_set_rate(clk->parent, cpu_wp_tbl[i].pll_rate); cpu_curr_wp = i; clk->rate = rate; return 0; } static int __switch_cpu_rate(struct clk *clk, unsigned long rate) { int prev; unsigned long tmp; int arm_div, fi, fd, start, end; u32 reg_value; if (cpu_wp_tbl[cpu_curr_wp].cpu_rate < rate) { start = cpu_curr_wp + 2; end = cpu_wp_nr; prev = cpu_curr_wp; } else { start = cpu_wp_offset + 2; end = cpu_curr_wp; prev = cpu_wp_offset; } while (start < end) { arm_div = __get_arm_div(cpu_wp_tbl[start].pdr0_reg, &fi, &fd); tmp = (mcu_pll_clk.rate * fi) / (arm_div * fd); if (tmp == rate) { prev = start; break; } if (tmp < rate) { if (prev < start) prev = start; } else { break; } start += 2; } if (start >= end) return -EINVAL; if (prev == cpu_curr_wp) return 0; reg_value = __raw_readl(MXC_CCM_PDR0); reg_value = (reg_value & ~(MXC_CCM_PDR0_CON_MUX_DIV_MASK | MXC_CCM_PDR0_AUTO_MUX_DIV_MASK)) | cpu_wp_tbl[prev].pdr0_reg; __raw_writel(reg_value, MXC_CCM_PDR0); cpu_curr_wp = prev; clk->rate = rate; return 0; } static int __get_arm_div(unsigned long pdr0, int *fi, int *fd) { int *pclk_mux; if ((pdr0 & MXC_CCM_PDR0_AUTO_CON) || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) pclk_mux = g_clk_mux_consumer + ((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >> MXC_CCM_PDR0_CON_MUX_DIV_OFFSET); else { pclk_mux = g_clk_mux_auto + ((pdr0 & MXC_CCM_PDR0_AUTO_MUX_DIV_MASK) >> MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET); } if ((*pclk_mux) == -1) { BUG(); return -EINVAL; } if (fi && fd) { if (!CLK_CODE_PATH(*pclk_mux)) { *fi = *fd = 1; return CLK_CODE_ARM(*pclk_mux); } if ((pdr0 & MXC_CCM_PDR0_AUTO_CON) || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) { *fi = 3; *fd = 4; } else { *fi = 2; *fd = 3; } } return CLK_CODE_ARM(*pclk_mux); } static int __get_ahb_div(unsigned long pdr0) { int *pclk_mux; if ((pdr0 & MXC_CCM_PDR0_AUTO_CON) || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) { pclk_mux = g_clk_mux_consumer + ((pdr0 & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >> MXC_CCM_PDR0_CON_MUX_DIV_OFFSET); } else { pclk_mux = g_clk_mux_auto + ((pdr0 & MXC_CCM_PDR0_AUTO_MUX_DIV_MASK) >> MXC_CCM_PDR0_AUTO_MUX_DIV_OFFSET); } if ((*pclk_mux) == -1) { BUG(); return -EINVAL; } return CLK_CODE_AHB(*pclk_mux); } static void sync_cpu_wb(void) { int i; struct cpu_wp *p; unsigned long reg = __raw_readl(MXC_CCM_PDR0); if ((reg & MXC_CCM_PDR0_AUTO_CON) || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) { reg &= MXC_CCM_PDR0_CON_MUX_DIV_MASK; } else { reg &= MXC_CCM_PDR0_AUTO_MUX_DIV_MASK; } for (i = 0; i < cpu_wp_nr; i++) { p = cpu_wp_tbl + cpu_curr_wp; if (p->pdr0_reg == (reg & 0xF0E00)) break; cpu_curr_wp = (cpu_curr_wp + 1) % cpu_wp_nr; } cpu_wp_offset = cpu_curr_wp & 1; } static int _clk_enable(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg |= 3 << clk->enable_shift; __raw_writel(reg, clk->enable_reg); return 0; } static void _clk_disable(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg &= ~(3 << clk->enable_shift); __raw_writel(reg, clk->enable_reg); } static void _clk_emi_disable(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg &= ~(3 << clk->enable_shift); reg |= (1 << clk->enable_shift); __raw_writel(reg, clk->enable_reg); } static int _clk_asrc_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_COSR); __raw_writel(reg | MXC_CCM_COSR_ASRC_AUDIO_EN, MXC_CCM_COSR); return 0; } static void _clk_asrc_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_COSR); __raw_writel(reg & (~MXC_CCM_COSR_ASRC_AUDIO_EN), MXC_CCM_COSR); } static int _clk_pll_set_rate(struct clk *clk, unsigned long rate) { u32 reg; signed long pd = 1; /* Pre-divider */ signed long mfi; /* Multiplication Factor (Integer part) */ signed long mfn; /* Multiplication Factor (Integer part) */ signed long mfd; /* Multiplication Factor (Denominator Part) */ signed long tmp; u32 ref_freq = clk->parent->rate; if ((clk == &mcu_pll_clk) && (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate)) { __raw_writel(cpu_wp_tbl[cpu_curr_wp].pll_reg, MXC_CCM_MPCTL); clk->rate = rate; return 0; } while (((ref_freq / pd) * 10) > rate) pd++; if ((ref_freq / pd) < PRE_DIV_MIN_FREQ) return -EINVAL; /* the ref_freq/2 in the following is to round up */ mfi = (((rate / 2) * pd) + (ref_freq / 2)) / ref_freq; if (mfi < 5 || mfi > 15) return -EINVAL; /* pick a mfd value that will work * then solve for mfn */ mfd = ref_freq / 50000; /* * pll_freq * pd * mfd * mfn = -------------------- - (mfi * mfd) * 2 * ref_freq */ /* the tmp/2 is for rounding */ tmp = ref_freq / 10000; mfn = ((((((rate / 2) + (tmp / 2)) / tmp) * pd) * mfd) / 10000) - (mfi * mfd); mfn = mfn & 0x3ff; pd--; mfd--; /* Change the Pll value */ reg = (mfi << MXC_CCM_PCTL_MFI_OFFSET) | (mfn << MXC_CCM_PCTL_MFN_OFFSET) | (mfd << MXC_CCM_PCTL_MFD_OFFSET) | (pd << MXC_CCM_PCTL_PD_OFFSET); if (clk == &mcu_pll_clk) __raw_writel(reg, MXC_CCM_MPCTL); else if (clk == &peri_pll_clk) __raw_writel(reg, MXC_CCM_PPCTL); clk->rate = rate; return 0; } static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) { if ((rate < ahb_clk.rate) || (rate % ahb_clk.rate != 0)) { printk(KERN_ERR "Wrong rate %lu in _clk_cpu_set_rate\n", rate); return -EINVAL; } if (clk->rate == rate) return 0; if (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate) return __switch_cpu_wp(clk, rate); return __switch_cpu_rate(clk, rate); } static void _clk_pll_recalc(struct clk *clk) { long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; unsigned long reg = 0; s64 temp; ref_clk = ckih_clk.rate; if (clk == &mcu_pll_clk) reg = __raw_readl(MXC_CCM_MPCTL); else if (clk == &peri_pll_clk) reg = __raw_readl(MXC_CCM_PPCTL); else BUG(); pdf = (reg & MXC_CCM_PCTL_PD_MASK) >> MXC_CCM_PCTL_PD_OFFSET; mfd = (reg & MXC_CCM_PCTL_MFD_MASK) >> MXC_CCM_PCTL_MFD_OFFSET; mfi = (reg & MXC_CCM_PCTL_MFI_MASK) >> MXC_CCM_PCTL_MFI_OFFSET; mfi = (mfi <= 5) ? 5 : mfi; mfn = mfn_abs = reg & MXC_CCM_PCTL_MFN_MASK; if (mfn >= 0x200) { mfn |= 0xFFFFFE00; mfn_abs = -mfn; } ref_clk *= 2; ref_clk /= pdf + 1; temp = (u64) ref_clk * mfn_abs; do_div(temp, mfd + 1); if (mfn < 0) temp = -temp; temp = (ref_clk * mfi) + temp; clk->rate = temp; } static int _clk_peri_pll_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CCMR); reg |= MXC_CCM_CCMR_UPE; __raw_writel(reg, MXC_CCM_CCMR); /* No lock bit on MX31, so using max time from spec */ udelay(80); return 0; } static void _clk_peri_pll_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CCMR); reg &= ~MXC_CCM_CCMR_UPE; __raw_writel(reg, MXC_CCM_CCMR); } #define PDR0(mask, off) ((__raw_readl(MXC_CCM_PDR0) & mask) >> off) #define PDR1(mask, off) ((__raw_readl(MXC_CCM_PDR1) & mask) >> off) #define PDR2(mask, off) ((__raw_readl(MXC_CCM_PDR2) & mask) >> off) #define PDR3(mask, off) ((__raw_readl(MXC_CCM_PDR3) & mask) >> off) #define PDR4(mask, off) ((__raw_readl(MXC_CCM_PDR4) & mask) >> off) static void _clk_cpu_recalc(struct clk *clk) { unsigned long pdr0 = __raw_readl(MXC_CCM_PDR0); int arm_div, fi, fd; if (clk->parent->rate == cpu_wp_tbl[cpu_curr_wp].pll_rate) { clk->rate = cpu_wp_tbl[cpu_curr_wp].cpu_rate; } else { arm_div = __get_arm_div(pdr0, &fi, &fd); clk->rate = (clk->parent->rate * fi) / (arm_div * fd); } } static void _clk_hclk_recalc(struct clk *clk) { unsigned long ahb_div, pdr0 = __raw_readl(MXC_CCM_PDR0); ahb_div = __get_ahb_div(pdr0); clk->rate = clk->parent->rate / ahb_div; } static void _clk_ipg_recalc(struct clk *clk) { clk->rate = clk->parent->rate / 2; } static void _clk_nfc_recalc(struct clk *clk) { unsigned long nfc_pdf; nfc_pdf = PDR4(MXC_CCM_PDR4_NFC_PODF_MASK, MXC_CCM_PDR4_NFC_PODF_OFFSET); clk->rate = clk->parent->rate / (nfc_pdf + 1); } static void _clk_hsp_recalc(struct clk *clk) { int hsp_pdf; unsigned long reg; reg = __raw_readl(MXC_CCM_PDR0); if ((reg & MXC_CCM_PDR0_AUTO_CON) || (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) { hsp_pdf = (reg & MXC_CCM_PDR0_HSP_PODF_MASK) >> MXC_CCM_PDR0_HSP_PODF_OFFSET; reg = (reg & MXC_CCM_PDR0_CON_MUX_DIV_MASK) >> MXC_CCM_PDR0_CON_MUX_DIV_OFFSET; if (hsp_pdf < 3) { hsp_pdf = g_hsp_div_table[hsp_pdf][reg]; if (hsp_pdf > 0) clk->rate = clk->parent->rate / hsp_pdf; } } else { clk->rate = clk->parent->rate; } } static void _clk_mlb_recalc(struct clk *clk) { clk->rate = clk->parent->rate * 2; } static void _clk_usb_recalc(struct clk *clk) { unsigned long usb_podf, usb_prdf; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { usb_podf = PDR4(MXC_CCM_PDR4_USB_PODF_MASK, MXC_CCM_PDR4_USB_PODF_OFFSET); usb_prdf = PDR4(MXC_CCM_PDR4_USB_PRDF_MASK, MXC_CCM_PDR4_USB_PRDF_OFFSET); clk->rate = clk->parent->rate / ((usb_prdf + 1) * (usb_podf + 1)); } else { usb_podf = PDR4(MXC_CCM_PDR4_USB_PODF_MASK_V2, MXC_CCM_PDR4_USB_PODF_OFFSET); clk->rate = clk->parent->rate / (usb_podf + 1); } } static int _clk_usb_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 podf, prdf; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { __calc_two_dividers(div, &prdf, &podf); reg = __raw_readl(MXC_CCM_PDR4) & ~(MXC_CCM_PDR4_USB_PODF_MASK | MXC_CCM_PDR4_USB_PRDF_MASK); reg |= (podf - 1) << MXC_CCM_PDR4_USB_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR4_USB_PRDF_OFFSET; } else { podf = div - 1; reg = __raw_readl(MXC_CCM_PDR4) & ~MXC_CCM_PDR4_USB_PODF_MASK_V2; reg |= (podf - 1) << MXC_CCM_PDR4_USB_PODF_OFFSET; } __raw_writel(reg, MXC_CCM_PDR4); clk->rate = rate; return 0; } static void _clk_csi_recalc(struct clk *clk) { u32 podf, prdf; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = PDR2(MXC_CCM_PDR2_CSI_PRDF_MASK, MXC_CCM_PDR2_CSI_PRDF_OFFSET); podf = PDR2(MXC_CCM_PDR2_CSI_PODF_MASK, MXC_CCM_PDR2_CSI_PODF_OFFSET); clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1)); } else { podf = PDR2(MXC_CCM_PDR2_CSI_PODF_MASK_V2, MXC_CCM_PDR2_CSI_PODF_OFFSET); clk->rate = clk->parent->rate / (podf + 1); } } static int _clk_csi_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 prdf, podf; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { __calc_two_dividers(div, &prdf, &podf); reg = __raw_readl(MXC_CCM_PDR2) & ~(MXC_CCM_PDR2_CSI_PRDF_MASK | MXC_CCM_PDR2_CSI_PODF_MASK); reg |= (podf - 1) << MXC_CCM_PDR2_CSI_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR2_CSI_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR2) & ~MXC_CCM_PDR2_CSI_PODF_MASK_V2; reg |= (div - 1) << MXC_CCM_PDR2_CSI_PODF_OFFSET; } /* Set CSI clock divider */ __raw_writel(reg, MXC_CCM_PDR2); clk->rate = rate; return 0; } static int _clk_csi_set_parent(struct clk *clk, struct clk *parent) { u32 reg; if (parent == &cpu_clk) reg = __raw_readl(MXC_CCM_PDR2) | MXC_CCM_PDR2_CSI_M_U; else if (parent == &peri_pll_clk) reg = __raw_readl(MXC_CCM_PDR2) & (~MXC_CCM_PDR2_CSI_M_U); else return -EINVAL; __raw_writel(reg, MXC_CCM_PDR2); return 0; } static void _clk_per_recalc(struct clk *clk) { u32 podf = 0, prdf = 0; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { if (clk->parent == &cpu_clk) { prdf = PDR4(MXC_CCM_PDR4_PER0_PRDF_MASK, MXC_CCM_PDR4_PER0_PRDF_OFFSET); podf = PDR4(MXC_CCM_PDR4_PER0_PODF_MASK, MXC_CCM_PDR4_PER0_PODF_OFFSET); } else { podf = PDR0(MXC_CCM_PDR0_PER_PODF_MASK, MXC_CCM_PDR0_PER_PODF_OFFSET); } clk->rate = clk->parent->rate / ((podf + 1) * (prdf + 1)); } else { if (clk->parent == &ahb_clk) podf = PDR0(MXC_CCM_PDR0_PER_PODF_MASK, MXC_CCM_PDR0_PER_PODF_OFFSET); else if (clk->parent == &cpu_clk) { podf = PDR4(MXC_CCM_PDR4_PER0_PODF_MASK_V2, MXC_CCM_PDR4_PER0_PODF_OFFSET); } clk->rate = clk->parent->rate / (podf + 1); } } static void _clk_uart_per_recalc(struct clk *clk) { unsigned long podf, prdf; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = PDR4(MXC_CCM_PDR4_UART_PRDF_MASK, MXC_CCM_PDR4_UART_PRDF_OFFSET); podf = PDR4(MXC_CCM_PDR4_UART_PODF_MASK, MXC_CCM_PDR4_UART_PODF_OFFSET); clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1)); } else { podf = PDR4(MXC_CCM_PDR4_UART_PODF_MASK_V2, MXC_CCM_PDR4_UART_PODF_OFFSET); clk->rate = clk->parent->rate / (podf + 1); } } static int _clk_uart_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 prdf, podf; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; /* Set UART clock divider */ if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { __calc_two_dividers(div, &prdf, &podf); reg = __raw_readl(MXC_CCM_PDR4) & ~(MXC_CCM_PDR4_UART_PRDF_MASK | MXC_CCM_PDR4_UART_PODF_MASK); reg |= (podf - 1) << MXC_CCM_PDR4_UART_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR4_UART_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR4) & ~MXC_CCM_PDR4_UART_PODF_MASK_V2; reg |= (div - 1) << MXC_CCM_PDR4_UART_PODF_OFFSET; } __raw_writel(reg, MXC_CCM_PDR4); clk->rate = rate; return 0; } static void _clk_ssi_recalc(struct clk *clk) { unsigned long ssi_pdf, ssi_prepdf; if (clk->id == 1) { ssi_pdf = PDR2(MXC_CCM_PDR2_SSI2_PODF_MASK, MXC_CCM_PDR2_SSI2_PODF_OFFSET); ssi_prepdf = PDR2(MXC_CCM_PDR2_SSI2_PRDF_MASK, MXC_CCM_PDR2_SSI2_PRDF_OFFSET); } else { ssi_pdf = PDR2(MXC_CCM_PDR2_SSI1_PODF_MASK, MXC_CCM_PDR2_SSI1_PODF_OFFSET); ssi_prepdf = PDR2(MXC_CCM_PDR2_SSI1_PRDF_MASK, MXC_CCM_PDR2_SSI1_PRDF_OFFSET); } clk->rate = clk->parent->rate / ((ssi_prepdf + 1) * (ssi_pdf + 1)); } static int _clk_ssi_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 pre, post; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; __calc_pre_post_dividers(div, &pre, &post); if (clk->id == 1) { reg = __raw_readl(MXC_CCM_PDR2) & ~(MXC_CCM_PDR2_SSI2_PRDF_MASK | MXC_CCM_PDR2_SSI2_PODF_MASK); reg |= (post - 1) << MXC_CCM_PDR2_SSI2_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_PDR2_SSI2_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR2) & ~(MXC_CCM_PDR2_SSI1_PRDF_MASK | MXC_CCM_PDR2_SSI1_PODF_MASK); reg |= (post - 1) << MXC_CCM_PDR2_SSI1_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_PDR2_SSI1_PRDF_OFFSET; } __raw_writel(reg, MXC_CCM_PDR2); clk->rate = rate; return 0; } static void _clk_mstick1_recalc(struct clk *clk) { unsigned long prdf, podf; prdf = PDR1(MXC_CCM_PDR1_MSHC_PRDF_MASK, MXC_CCM_PDR1_MSHC_PRDF_OFFSET); podf = PDR1(MXC_CCM_PDR1_MSHC_PODF_MASK, MXC_CCM_PDR1_MSHC_PODF_OFFSET); clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1)); } static int _clk_mstick1_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 pre, post; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; __calc_pre_post_dividers(div, &pre, &post); reg = __raw_readl(MXC_CCM_PDR1) & ~(MXC_CCM_PDR1_MSHC_PRDF_MASK | MXC_CCM_PDR1_MSHC_PODF_MASK); reg |= (post - 1) << MXC_CCM_PDR1_MSHC_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_PDR1_MSHC_PRDF_OFFSET; __raw_writel(reg, MXC_CCM_PDR1); clk->rate = rate; return 0; } static int _clk_mstick1_set_parent(struct clk *clk, struct clk *parent) { u32 reg; if (parent == &cpu_clk) reg = __raw_readl(MXC_CCM_PDR1) | MXC_CCM_PDR1_MSHC_M_U; else if (parent == &peri_pll_clk) reg = __raw_readl(MXC_CCM_PDR1) & (~MXC_CCM_PDR1_MSHC_M_U); else return -EINVAL; __raw_writel(reg, MXC_CCM_PDR1); return 0; } static void _clk_spdif_recalc(struct clk *clk) { unsigned long prdf, podf; prdf = PDR3(MXC_CCM_PDR3_SPDIF_PRDF_MASK, MXC_CCM_PDR3_SPDIF_PRDF_OFFSET); podf = PDR3(MXC_CCM_PDR3_SPDIF_PODF_MASK, MXC_CCM_PDR3_SPDIF_PODF_OFFSET); clk->rate = clk->parent->rate / ((prdf + 1) * (podf + 1)); } static int _clk_spdif_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 pre, post; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; __calc_pre_post_dividers(div, &pre, &post); reg = __raw_readl(MXC_CCM_PDR3) & ~(MXC_CCM_PDR3_SPDIF_PRDF_MASK | MXC_CCM_PDR3_SPDIF_PODF_MASK); reg |= (post - 1) << MXC_CCM_PDR3_SPDIF_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_PDR3_SPDIF_PRDF_OFFSET; __raw_writel(reg, MXC_CCM_PDR3); clk->rate = rate; return 0; } static int _clk_spdif_set_parent(struct clk *clk, struct clk *parent) { u32 reg; if (parent == &cpu_clk) reg = __raw_readl(MXC_CCM_PDR3) | MXC_CCM_PDR3_SPDIF_M_U; else if (parent == &peri_pll_clk) reg = __raw_readl(MXC_CCM_PDR3) & (~MXC_CCM_PDR3_SPDIF_M_U); else return -EINVAL; __raw_writel(reg, MXC_CCM_PDR3); return 0; } static void _clk_asrc_recalc(struct clk *clk) { unsigned long div; div = __raw_readl(MXC_CCM_COSR) & MXC_CCM_COSR_ASRC_AUDIO_PODF_MASK; div = div >> MXC_CCM_COSR_ASRC_AUDIO_PODF_OFFSET; clk->rate = clk->parent->rate / (div + 1); } static int _clk_asrc_set_rate(struct clk *clk, unsigned long rate) { int div; unsigned long reg; if (clk->parent->rate % rate) return -EINVAL; div = clk->parent->rate / rate; reg = __raw_readl(MXC_CCM_COSR) & (~MXC_CCM_COSR_ASRC_AUDIO_PODF_MASK); reg |= (div - 1) << MXC_CCM_COSR_ASRC_AUDIO_PODF_OFFSET; __raw_writel(reg, MXC_CCM_COSR); clk->rate = rate; return 0; } static void _clk_sdhc_recalc(struct clk *clk) { u32 podf = 0, prdf = 0; switch (clk->id) { case 0: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = PDR3(MXC_CCM_PDR3_ESDHC1_PRDF_MASK, MXC_CCM_PDR3_ESDHC1_PRDF_OFFSET); podf = PDR3(MXC_CCM_PDR3_ESDHC1_PODF_MASK, MXC_CCM_PDR3_ESDHC1_PODF_OFFSET); } else podf = PDR3(MXC_CCM_PDR3_ESDHC1_PODF_MASK_V2, MXC_CCM_PDR3_ESDHC1_PODF_OFFSET); break; case 1: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = PDR3(MXC_CCM_PDR3_ESDHC2_PRDF_MASK, MXC_CCM_PDR3_ESDHC2_PRDF_OFFSET); podf = PDR3(MXC_CCM_PDR3_ESDHC2_PODF_MASK, MXC_CCM_PDR3_ESDHC2_PODF_OFFSET); } else podf = PDR3(MXC_CCM_PDR3_ESDHC2_PODF_MASK_V2, MXC_CCM_PDR3_ESDHC2_PODF_OFFSET); break; case 2: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = PDR3(MXC_CCM_PDR3_ESDHC3_PRDF_MASK, MXC_CCM_PDR3_ESDHC3_PRDF_OFFSET); podf = PDR3(MXC_CCM_PDR3_ESDHC3_PODF_MASK, MXC_CCM_PDR3_ESDHC3_PODF_OFFSET); } else podf = PDR3(MXC_CCM_PDR3_ESDHC3_PODF_MASK_V2, MXC_CCM_PDR3_ESDHC3_PODF_OFFSET); break; default: return; } clk->rate = clk->parent->rate / ((podf + 1) * (prdf + 1)); } static int _clk_sdhc_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div; u32 prdf, podf; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) __calc_pre_post_dividers(div, &prdf, &podf); switch (clk->id) { case 0: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { reg = __raw_readl(MXC_CCM_PDR3) & ~(MXC_CCM_PDR3_ESDHC1_PRDF_MASK | MXC_CCM_PDR3_ESDHC1_PODF_MASK); reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC1_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC1_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR3) & ~MXC_CCM_PDR3_ESDHC1_PODF_MASK_V2; reg |= (div - 1) << MXC_CCM_PDR3_ESDHC1_PODF_OFFSET; } break; case 1: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { reg = __raw_readl(MXC_CCM_PDR3) & ~(MXC_CCM_PDR3_ESDHC2_PRDF_MASK | MXC_CCM_PDR3_ESDHC2_PODF_MASK); reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC2_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC2_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR3) & ~MXC_CCM_PDR3_ESDHC2_PODF_MASK_V2; reg |= (div - 1) << MXC_CCM_PDR3_ESDHC2_PODF_OFFSET; } break; case 2: if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { reg = __raw_readl(MXC_CCM_PDR3) & ~(MXC_CCM_PDR3_ESDHC3_PRDF_MASK | MXC_CCM_PDR3_ESDHC3_PODF_MASK); reg |= (podf - 1) << MXC_CCM_PDR3_ESDHC3_PODF_OFFSET; reg |= (prdf - 1) << MXC_CCM_PDR3_ESDHC3_PRDF_OFFSET; } else { reg = __raw_readl(MXC_CCM_PDR3) & ~MXC_CCM_PDR3_ESDHC3_PODF_MASK_V2; reg |= (div - 1) << MXC_CCM_PDR3_ESDHC3_PODF_OFFSET; } break; default: return -EINVAL; } __raw_writel(reg, MXC_CCM_PDR3); clk->rate = rate; return 0; } static struct clk ckih_clk = { .name = "ckih", .rate = CKIH_CLK_FREQ, .flags = RATE_FIXED, }; static struct clk int_32k_clk = { .name = "int_32k", .rate = CKIL_CLK_FREQ, .flags = RATE_FIXED, }; static struct clk ext_32k_clk = { .name = "ext_32k", .rate = CKIL_EXT_FREQ, .flags = RATE_FIXED, }; static int _clk_ckil_set_parent(struct clk *clk, struct clk *parent) { u32 reg; if (parent == &int_32k_clk) { reg = __raw_readl(MXC_CCM_PDR0) & (~MXC_CCM_PDR0_CKIL_SEL); clk->rate = parent->rate; } else if (parent == &ext_32k_clk) { reg = __raw_readl(MXC_CCM_PDR0) | MXC_CCM_PDR0_CKIL_SEL; clk->rate = parent->rate; } else return -EINVAL; __raw_writel(reg, MXC_CCM_PDR0); return 0; } static int _clk_ckil_set_rate(struct clk *clk, unsigned long rate) { clk->rate = clk->parent->rate; return 0; } static struct clk ckil_clk = { .name = "ckil", .parent = &ext_32k_clk, .set_parent = _clk_ckil_set_parent, .set_rate = _clk_ckil_set_rate, }; static int _clk_ckie_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_PMCR2) & ~MXC_CCM_PMCR2_OSC_AUDIO_DOWN; __raw_writel(reg, MXC_CCM_PMCR2); return 0; } static void _clk_ckie_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_PMCR2) | MXC_CCM_PMCR2_OSC_AUDIO_DOWN; __raw_writel(reg, MXC_CCM_PMCR2); } static struct clk ckie_clk = { .name = "ckie", .rate = CKIE_CLK_FREQ, .flags = RATE_FIXED, .enable = _clk_ckie_enable, .disable = _clk_ckie_disable, }; static struct clk mcu_pll_clk = { .name = "mcu_pll", .parent = &ckih_clk, .set_rate = _clk_pll_set_rate, .recalc = _clk_pll_recalc, .flags = RATE_PROPAGATES, }; static struct clk peri_pll_clk = { .name = "peri_pll", .parent = &ckih_clk, .set_rate = _clk_pll_set_rate, .recalc = _clk_pll_recalc, .enable = _clk_peri_pll_enable, .disable = _clk_peri_pll_disable, .flags = RATE_PROPAGATES, }; static struct clk cpu_clk = { .name = "cpu_clk", .parent = &mcu_pll_clk, .recalc = _clk_cpu_recalc, .set_rate = _clk_cpu_set_rate, }; static struct clk ahb_clk = { .name = "ahb_clk", .parent = &cpu_clk, .recalc = _clk_hclk_recalc, .flags = RATE_PROPAGATES, }; static struct clk ipg_clk = { .name = "ipg_clk", .parent = &ahb_clk, .recalc = _clk_ipg_recalc, .flags = RATE_PROPAGATES, }; static struct clk perclk_clk = { .name = "perclk_clk", .parent = &ahb_clk, .recalc = _clk_per_recalc, .flags = RATE_PROPAGATES, }; static struct clk uart_per_clk = { .name = "uart_per_clk", .parent = &peri_pll_clk, .recalc = _clk_uart_per_recalc, .round_rate = _clk_round_rate, .set_rate = _clk_uart_set_rate, .flags = RATE_PROPAGATES, }; static struct clk asrc_clk[] = { { .name = "asrc_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ASRC_OFFSET, .disable = _clk_disable,}, { .name = "asrc_audio_clk", .parent = &ckie_clk, .recalc = _clk_asrc_recalc, .round_rate = _clk_round_rate, .set_rate = _clk_asrc_set_rate, .enable = _clk_asrc_enable, .disable = _clk_asrc_disable,}, }; static struct clk ata_clk = { .name = "ata_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ATA_OFFSET, .disable = _clk_disable, }; static struct clk can_clk[] = { { .name = "can_clk", .id = 0, .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_CAN1_OFFSET, .disable = _clk_disable,}, { .name = "can_clk", .id = 1, .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_CAN2_OFFSET, .disable = _clk_disable,}, }; static struct clk cspi_clk[] = { { .name = "cspi_clk", .id = 0, .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_CSPI1_OFFSET, .disable = _clk_disable,}, { .name = "cspi_clk", .id = 1, .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_CSPI2_OFFSET, .disable = _clk_disable,}, }; static struct clk ect_clk = { .name = "ect_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ECT_OFFSET, .disable = _clk_disable, }; static struct clk emi_clk = { .name = "emi_clk", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_EMI_OFFSET, .disable = _clk_emi_disable, }; static struct clk epit_clk[] = { { .name = "epit_clk", .id = 0, .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_EPIT1_OFFSET, .disable = _clk_disable,}, { .name = "epit_clk", .id = 1, .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_EPIT2_OFFSET, .disable = _clk_disable,}, }; static struct clk esai_clk = { .name = "esai_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ESAI_OFFSET, .disable = _clk_disable, }; static struct clk sdhc_clk[] = { { .name = "sdhc_clk", .id = 0, .parent = &peri_pll_clk, .recalc = _clk_sdhc_recalc, .set_rate = _clk_sdhc_set_rate, .round_rate = _clk_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ESDHC1_OFFSET, .disable = _clk_disable,}, { .name = "sdhc_clk", .id = 1, .parent = &peri_pll_clk, .recalc = _clk_sdhc_recalc, .set_rate = _clk_sdhc_set_rate, .round_rate = _clk_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ESDHC2_OFFSET, .disable = _clk_disable,}, { .name = "sdhc_clk", .id = 2, .parent = &peri_pll_clk, .recalc = _clk_sdhc_recalc, .set_rate = _clk_sdhc_set_rate, .round_rate = _clk_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR0, .enable_shift = MXC_CCM_CGR0_ESDHC3_OFFSET, .disable = _clk_disable,}, }; static struct clk fec_clk = { .name = "fec_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_FEC_OFFSET, .disable = _clk_disable, }; static struct clk gpt_clk = { .name = "gpt_clk", .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_GPT_OFFSET, .disable = _clk_disable, }; static struct clk i2c_clk[] = { { .name = "i2c_clk", .id = 0, .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_I2C1_OFFSET, .disable = _clk_disable,}, { .name = "i2c_clk", .id = 1, .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_I2C2_OFFSET, .disable = _clk_disable,}, { .name = "i2c_clk", .id = 2, .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_I2C3_OFFSET, .disable = _clk_disable,}, }; static struct clk ipu_clk = { .name = "ipu_clk", .parent = &cpu_clk, .recalc = _clk_hsp_recalc, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_IPU_OFFSET, .disable = _clk_disable, }; static struct clk kpp_clk = { .name = "kpp_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_KPP_OFFSET, .disable = _clk_disable, }; static struct clk mlb_clk = { .name = "mlb_clk", .parent = &ahb_clk, .recalc = _clk_mlb_recalc, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_MLB_OFFSET, .disable = _clk_disable, }; static struct clk mstick_clk = { .name = "mstick_clk", .id = 0, .parent = &peri_pll_clk, .recalc = _clk_mstick1_recalc, .set_rate = _clk_mstick1_set_rate, .round_rate = _clk_per_post_round_rate, .set_parent = _clk_mstick1_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_MSHC_OFFSET, .disable = _clk_disable, }; static struct clk owire_clk = { .name = "owire_clk", .parent = &perclk_clk, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_OWIRE_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static struct clk pwm_clk = { .name = "pwm_clk", .parent = &perclk_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_PWM_OFFSET, .disable = _clk_disable, }; static struct clk rng_clk = { .name = "rng_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR1, .enable_shift = MXC_CCM_CGR1_RNGC_OFFSET, .disable = _clk_disable, }; static struct clk rtc_clk = { .name = "rtc_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_RTC_OFFSET, .disable = _clk_disable, }; static struct clk rtic_clk = { .name = "rtic_clk", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_RTIC_OFFSET, .disable = _clk_disable, }; static struct clk scc_clk = { .name = "scc_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SCC_OFFSET, .disable = _clk_disable, }; static struct clk sdma_clk[] = { { .name = "sdma_ahb_clk", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SDMA_OFFSET, .disable = _clk_disable,}, { .name = "sdma_ipg_clk", .parent = &ipg_clk,} }; static struct clk spba_clk = { .name = "spba_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SPBA_OFFSET, .disable = _clk_disable, }; static struct clk spdif_clk[] = { { .name = "spdif_clk", .parent = &peri_pll_clk, .recalc = _clk_spdif_recalc, .set_rate = _clk_spdif_set_rate, .round_rate = _clk_per_post_round_rate, .set_parent = _clk_spdif_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SPDIF_OFFSET, .disable = _clk_disable,}, { .name = "spdif_audio_clk", .parent = &ckie_clk,}, { .name = "spdif_ipg_clk", .parent = &ipg_clk,}, }; static struct clk ssi_clk[] = { { .name = "ssi_clk", .parent = &peri_pll_clk, .recalc = _clk_ssi_recalc, .set_rate = _clk_ssi_set_rate, .round_rate = _clk_per_post_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SSI1_OFFSET, .disable = _clk_disable,}, { .name = "ssi_clk", .id = 1, .parent = &peri_pll_clk, .recalc = _clk_ssi_recalc, .set_rate = _clk_ssi_set_rate, .round_rate = _clk_per_post_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_SSI2_OFFSET, .disable = _clk_disable,}, }; static struct clk uart_clk[] = { { .name = "uart_clk", .id = 0, .parent = &uart_per_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_UART1_OFFSET, .disable = _clk_disable,}, { .name = "uart_clk", .id = 1, .parent = &uart_per_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_UART2_OFFSET, .disable = _clk_disable,}, { .name = "uart_clk", .id = 2, .parent = &uart_per_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_UART3_OFFSET, .disable = _clk_disable,}, }; static struct clk usb_clk[] = { { .name = "usb_clk", .parent = &peri_pll_clk, .recalc = _clk_usb_recalc, .round_rate = _clk_round_rate, .set_rate = _clk_usb_set_rate,}, { .name = "usb_ahb_clk", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_USBOTG_OFFSET, .disable = _clk_disable,}, }; static struct clk wdog_clk = { .name = "wdog_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR2, .enable_shift = MXC_CCM_CGR2_WDOG_OFFSET, .disable = _clk_disable, }; static struct clk csi_clk = { .name = "csi_clk", .parent = &peri_pll_clk, .recalc = _clk_csi_recalc, .round_rate = _clk_round_rate, .set_rate = _clk_csi_set_rate, .set_parent = _clk_csi_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR3, .enable_shift = MXC_CCM_CGR3_CSI_OFFSET, .disable = _clk_disable, }; static struct clk iim_clk = { .name = "iim_clk", .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR3, .enable_shift = MXC_CCM_CGR3_IIM_OFFSET, .disable = _clk_disable, }; static struct clk nfc_clk = { .name = "nfc_clk", .parent = &ahb_clk, .recalc = _clk_nfc_recalc, }; static unsigned long _clk_cko1_round_rate(struct clk *clk, unsigned long rate) { u32 div = 0, div1 = 1; div = clk->parent->rate / rate; if (clk->parent->rate % rate) div++; if (div > 64) { div = (div + 1) >> 1; div1++; } if (div > 128) div = 64; return clk->parent->rate / (div * div1); } static int _clk_cko1_set_rate(struct clk *clk, unsigned long rate) { u32 reg; u32 div, div1 = 0; u32 prdf, podf; div = clk->parent->rate / rate; if ((clk->parent->rate / div) != rate) return -EINVAL; if (div > 64) { div1 = MXC_CCM_COSR_CLKOUTDIV_1; div >>= 1; } else { div1 = 0; } if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { __calc_two_dividers(div, &prdf, &podf); reg = __raw_readl(MXC_CCM_COSR) & ~(MXC_CCM_COSR_CLKOUT_PREDIV_MASK | MXC_CCM_COSR_CLKOUT_PRODIV_MASK | MXC_CCM_COSR_CLKOUTDIV_1); reg |= ((prdf - 1) << MXC_CCM_COSR_CLKOUT_PREDIV_OFFSET) | ((podf - 1) << MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET) | div1; } else { reg = __raw_readl(MXC_CCM_COSR) & ~(MXC_CCM_COSR_CLKOUT_PRODIV_MASK_V2 | MXC_CCM_COSR_CLKOUTDIV_1); reg |= ((div - 1) << MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET) | div1; } __raw_writel(reg, MXC_CCM_COSR); return 0; } static void _clk_cko1_recalc(struct clk *clk) { u32 prdf = 1; u32 podf, div1; u32 reg = __raw_readl(MXC_CCM_COSR); div1 = 1 << ((reg & MXC_CCM_COSR_CLKOUTDIV_1) != 0); if (cpu_is_mx35_rev(CHIP_REV_2_0) < 1) { prdf = (reg & MXC_CCM_COSR_CLKOUT_PREDIV_MASK) >> MXC_CCM_COSR_CLKOUT_PREDIV_OFFSET; podf = (reg & MXC_CCM_COSR_CLKOUT_PRODIV_MASK) >> MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET; } else podf = (reg & MXC_CCM_COSR_CLKOUT_PRODIV_MASK_V2) >> MXC_CCM_COSR_CLKOUT_PRODIV_OFFSET; clk->rate = clk->parent->rate / (div1 * (podf + 1) * (prdf + 1)); } static int _clk_cko1_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_COSR) & ~MXC_CCM_COSR_CLKOSEL_MASK; if (parent == &ckil_clk) { reg &= ~MXC_CCM_COSR_CKIL_CKIH_MASK; reg |= 0 << MXC_CCM_COSR_CLKOSEL_OFFSET; } else if (parent == &ckih_clk) { reg |= 1 << MXC_CCM_COSR_CLKOSEL_OFFSET; } else if (parent == &ckie_clk) reg |= 2 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &peri_pll_clk) reg |= 6 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &cpu_clk) reg |= 7 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &ahb_clk) reg |= 8 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &ipg_clk) reg |= 9 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &usb_clk[1]) reg |= 0xB << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &sdhc_clk[1]) reg |= 0xC << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &ssi_clk[1]) reg |= 0xD << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &mlb_clk) reg |= 0xE << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &csi_clk) reg |= 0x11 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &spdif_clk[0]) reg |= 0x12 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &uart_clk[0]) reg |= 0x13 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if (parent == &asrc_clk[1]) reg |= 0x14 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if ((parent == &nfc_clk) && (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) reg |= 0x17 << MXC_CCM_COSR_CLKOSEL_OFFSET; else if ((parent == &ipu_clk) && (cpu_is_mx35_rev(CHIP_REV_2_0) >= 1)) reg |= 0x18 << MXC_CCM_COSR_CLKOSEL_OFFSET; else return -EINVAL; __raw_writel(reg, MXC_CCM_COSR); return 0; } static int _clk_cko1_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_COSR) | MXC_CCM_COSR_CLKOEN; __raw_writel(reg, MXC_CCM_COSR); return 0; } static void _clk_cko1_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_COSR) & ~MXC_CCM_COSR_CLKOEN; __raw_writel(reg, MXC_CCM_COSR); } static struct clk cko1_clk = { .name = "cko1_clk", .recalc = _clk_cko1_recalc, .set_rate = _clk_cko1_set_rate, .round_rate = _clk_cko1_round_rate, .set_parent = _clk_cko1_set_parent, .enable = _clk_cko1_enable, .disable = _clk_cko1_disable, }; static struct clk gpu2d_clk = { .name = "gpu2d_clk", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CGR3, .enable_shift = MXC_CCM_CGR3_GPU2D_OFFSET, .disable = _clk_disable, }; static struct clk *mxc_clks[] = { &int_32k_clk, &ext_32k_clk, &ckih_clk, &ckil_clk, &ckie_clk, &mcu_pll_clk, &peri_pll_clk, &cpu_clk, &ahb_clk, &ipg_clk, &perclk_clk, &uart_per_clk, &asrc_clk[0], &asrc_clk[1], &ata_clk, &can_clk[0], &can_clk[1], &cspi_clk[0], &cspi_clk[1], &ect_clk, &emi_clk, &epit_clk[0], &epit_clk[1], &esai_clk, &sdhc_clk[0], &sdhc_clk[1], &sdhc_clk[2], &fec_clk, &gpt_clk, &i2c_clk[0], &i2c_clk[1], &i2c_clk[2], &ipu_clk, &kpp_clk, &mlb_clk, &mstick_clk, &owire_clk, &rng_clk, &pwm_clk, &rtc_clk, &rtic_clk, &scc_clk, &sdma_clk[0], &sdma_clk[1], &spba_clk, &spdif_clk[0], &spdif_clk[1], &spdif_clk[2], &ssi_clk[0], &ssi_clk[1], &uart_clk[0], &uart_clk[1], &uart_clk[2], &usb_clk[0], &usb_clk[1], &wdog_clk, &csi_clk, &iim_clk, &nfc_clk, &cko1_clk, &gpu2d_clk, }; extern void propagate_rate(struct clk *tclk); static void mxc_clockout_scan(void) { u32 reg = __raw_readl(MXC_CCM_COSR) & MXC_CCM_COSR_CLKOSEL_MASK; reg >>= MXC_CCM_COSR_CLKOSEL_OFFSET; switch (reg) { case 0: cko1_clk.parent = &ckil_clk; break; case 1: cko1_clk.parent = &ckih_clk; break; case 2: cko1_clk.parent = &ckie_clk; break; case 6: cko1_clk.parent = &peri_pll_clk; break; case 7: cko1_clk.parent = &cpu_clk; break; case 8: cko1_clk.parent = &ahb_clk; break; case 9: cko1_clk.parent = &ipg_clk; break; case 0xB: cko1_clk.parent = &usb_clk[1]; break; case 0xC: cko1_clk.parent = &sdhc_clk[1]; break; case 0xD: cko1_clk.parent = &ssi_clk[1]; break; case 0xE: cko1_clk.parent = &mlb_clk; break; case 0x11: cko1_clk.parent = &csi_clk; break; case 0x12: cko1_clk.parent = &spdif_clk[0]; break; case 0x13: cko1_clk.parent = &uart_clk[0]; break; case 0x14: cko1_clk.parent = &asrc_clk[1]; break; case 0x17: cko1_clk.parent = &nfc_clk; break; case 0x18: cko1_clk.parent = &ipu_clk; break; } } static void mxc_update_clocks(void) { unsigned long reg; reg = __raw_readl(MXC_CCM_PDR0); if ((!(reg & MXC_CCM_PDR0_AUTO_CON)) && (cpu_is_mx35_rev(CHIP_REV_2_0) < 1)) ipu_clk.parent = &ahb_clk; if (reg & MXC_CCM_PDR0_PER_SEL) perclk_clk.parent = &cpu_clk; reg = __raw_readl(MXC_CCM_PDR1); if (reg & MXC_CCM_PDR1_MSHC_M_U) mstick_clk.parent = &cpu_clk; reg = __raw_readl(MXC_CCM_PDR2); if (reg & MXC_CCM_PDR2_CSI_M_U) csi_clk.parent = &cpu_clk; if (reg & MXC_CCM_PDR2_SSI_M_U) { ssi_clk[0].parent = &cpu_clk; ssi_clk[1].parent = &cpu_clk; } reg = __raw_readl(MXC_CCM_PDR3); if (reg & MXC_CCM_PDR3_SPDIF_M_U) spdif_clk[0].parent = &cpu_clk; if (reg & MXC_CCM_PDR3_UART_M_U) uart_per_clk.parent = &cpu_clk; if (reg & MXC_CCM_PDR3_ESDHC_M_U) { sdhc_clk[0].parent = &cpu_clk; sdhc_clk[1].parent = &cpu_clk; sdhc_clk[2].parent = &cpu_clk; } reg = __raw_readl(MXC_CCM_PDR4); if (reg & MXC_CCM_PDR4_USB_M_U) usb_clk[0].parent = &cpu_clk; mxc_clockout_scan(); } int __init mx35_clocks_init(void) { struct clk **clkp; for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++) clk_register(*clkp); /* Turn off all possible clocks */ __raw_writel(MXC_CCM_CGR0_ECT_MASK | MXC_CCM_CGR0_EMI_MASK | MXC_CCM_CGR0_ESDHC1_MASK | MXC_CCM_CGR0_ESDHC2_MASK | MXC_CCM_CGR0_ESDHC3_MASK, MXC_CCM_CGR0); __raw_writel(MXC_CCM_CGR1_GPIO1_MASK | MXC_CCM_CGR1_GPIO2_MASK | MXC_CCM_CGR1_GPIO3_MASK | MXC_CCM_CGR1_GPT_MASK | MXC_CCM_CGR1_IOMUXC_MASK, MXC_CCM_CGR1); __raw_writel(MXC_CCM_CGR2_MAX_MASK | MXC_CCM_CGR2_SPBA_MASK | MXC_CCM_CGR2_AUDMUX_MASK | MXC_CCM_CGR2_MAX_ENABLE, MXC_CCM_CGR2); __raw_writel(MXC_CCM_CGR3_IIM_MASK, MXC_CCM_CGR3); __raw_writel((__raw_readl(MXC_CCM_PMCR2) | MXC_CCM_PMCR2_OSC24M_DOWN | MXC_CCM_PMCR2_OSC_AUDIO_DOWN), MXC_CCM_PMCR2); mxc_update_clocks(); pr_info("Clock input source is %ld\n", ckih_clk.rate); /* Determine which high frequency clock source is coming in */ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); sync_cpu_wb(); /* This will propagate to all children and init all the clock rates */ propagate_rate(&ckih_clk); propagate_rate(&ext_32k_clk); propagate_rate(&ckie_clk); clk_enable(&mcu_pll_clk); clk_enable(&gpt_clk); clk_enable(&emi_clk); clk_enable(&iim_clk); clk_enable(&spba_clk); /* Init serial PLL according */ clk_set_rate(&peri_pll_clk, 300000000); clk_enable(&peri_pll_clk); mxc_timer_init(&gpt_clk, IO_ADDRESS(GPT1_BASE_ADDR), MXC_INT_GPT); return 0; }