/* * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "crm_regs.h" static struct clk pll1_main_clk; static struct clk pll1_sw_clk; static struct clk pll2_sw_clk; static struct clk pll3_sw_clk; static struct clk apbh_dma_clk; static struct clk apll_clk; static struct clk pfd0_clk; static struct clk pfd1_clk; static struct clk pfd2_clk; static struct clk pfd3_clk; static struct clk pfd4_clk; static struct clk pfd5_clk; static struct clk pfd6_clk; static struct clk pfd7_clk; static struct clk lp_apm_clk; static struct clk weim_clk[]; static struct clk ddr_clk; static struct clk axi_a_clk; static struct clk axi_b_clk; static struct clk gpu2d_clk; static int cpu_curr_wp; static struct cpu_wp *cpu_wp_tbl; static void __iomem *pll1_base; static void __iomem *pll2_base; static void __iomem *pll3_base; static void __iomem *apll_base; extern int cpu_wp_nr; extern int lp_high_freq; extern int lp_med_freq; void __iomem *databahn; #define DDR_SYNC_MODE 0x30000 #define SPIN_DELAY 1000000 /* in nanoseconds */ #define WAIT(exp, timeout) \ ({ \ struct timespec nstimeofday; \ struct timespec curtime; \ int result = 1; \ getnstimeofday(&nstimeofday); \ while (!(exp)) { \ getnstimeofday(&curtime); \ if ((curtime.tv_nsec - nstimeofday.tv_nsec) > (timeout)) { \ result = 0; \ break; \ } \ } \ result; \ }) extern int mxc_jtag_enabled; extern int uart_at_24; extern int cpufreq_trig_needed; extern int low_bus_freq_mode; static int cpu_clk_set_wp(int wp); extern void propagate_rate(struct clk *tclk); extern struct cpu_wp *(*get_cpu_wp)(int *wp); extern void (*set_num_cpu_wp)(int num); static struct clk esdhc3_clk[]; static void __calc_pre_post_dividers(u32 div, u32 *pre, u32 *post) { u32 min_pre, temp_pre, old_err, err; if (div >= 512) { *pre = 8; *post = 64; } else if (div >= 8) { min_pre = (div - 1) / 64 + 1; old_err = 8; for (temp_pre = 8; temp_pre >= min_pre; temp_pre--) { 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; } else if (div < 8) { *pre = div; *post = 1; } } static int _clk_enable(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg |= MXC_CCM_CCGR_CG_MASK << clk->enable_shift; __raw_writel(reg, clk->enable_reg); if (clk->flags & AHB_HIGH_SET_POINT) lp_high_freq++; else if (clk->flags & AHB_MED_SET_POINT) lp_med_freq++; return 0; } static int _clk_enable_inrun(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); reg |= 1 << 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 &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); __raw_writel(reg, clk->enable_reg); if (clk->flags & AHB_HIGH_SET_POINT) lp_high_freq--; else if (clk->flags & AHB_MED_SET_POINT) lp_med_freq--; } static void _clk_disable_inwait(struct clk *clk) { u32 reg; reg = __raw_readl(clk->enable_reg); reg &= ~(MXC_CCM_CCGR_CG_MASK << clk->enable_shift); reg |= 1 << clk->enable_shift; __raw_writel(reg, clk->enable_reg); } static unsigned long _clk_round_rate_div(struct clk *clk, unsigned long rate, u32 max_div, u32 *new_div) { u32 div; div = DIV_ROUND_UP(clk->parent->rate, rate); if (div > max_div) div = max_div; else if (div == 0) div++; if (new_div != NULL) *new_div = div; return clk->parent->rate / div; } /* * For the 4-to-1 muxed input clock */ static inline u32 _get_mux(struct clk *parent, struct clk *m0, struct clk *m1, struct clk *m2, struct clk *m3) { if (parent == m0) return 0; else if (parent == m1) return 1; else if (parent == m2) return 2; else if (parent == m3) return 3; else BUG(); return 0; } /* * For the 4-to-1 muxed input clock */ static inline u32 _get_mux8(struct clk *parent, struct clk *m0, struct clk *m1, struct clk *m2, struct clk *m3, struct clk *m4, struct clk *m5, struct clk *m6, struct clk *m7) { if (parent == m0) return 0; else if (parent == m1) return 1; else if (parent == m2) return 2; else if (parent == m3) return 3; else if (parent == m4) return 4; else if (parent == m5) return 5; else if (parent == m6) return 6; else if (parent == m7) return 7; else BUG(); return 0; } static inline void __iomem *_get_pll_base(struct clk *pll) { if (pll == &pll1_main_clk) return pll1_base; else if (pll == &pll2_sw_clk) return pll2_base; else if (pll == &pll3_sw_clk) return pll3_base; else BUG(); return NULL; } static struct clk ckih_clk = { .name = "ckih", .flags = RATE_PROPAGATES, }; static struct clk ckih2_clk = { .name = "ckih2", .flags = RATE_PROPAGATES, }; static struct clk osc_clk = { .name = "osc", .flags = RATE_PROPAGATES, }; static int apll_enable(struct clk *clk) { __raw_writel(1, apll_base + MXC_ANADIG_MISC_SET); return 0; } static void apll_disable(struct clk *clk) { __raw_writel(1, apll_base + MXC_ANADIG_MISC_CLR); } static struct clk apll_clk = { .name = "apll", .rate = 480000000, .enable = apll_enable, .disable = apll_disable, .flags = RATE_PROPAGATES, }; static void pfd_recalc(struct clk *clk) { u32 frac; u64 rate; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.enable(&apbh_dma_clk); frac = __raw_readl(apll_base + (int)clk->enable_reg) >> clk->enable_shift; frac &= MXC_ANADIG_PFD_FRAC_MASK; rate = (u64)clk->parent->rate * 18; do_div(rate, frac); clk->rate = rate; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.disable(&apbh_dma_clk); } static unsigned long pfd_round_rate(struct clk *clk, unsigned long rate) { u32 frac; u64 tmp; tmp = (u64)clk->parent->rate * 18; do_div(tmp, rate); frac = tmp; frac = frac < 18 ? 18 : frac; frac = frac > 35 ? 35 : frac; do_div(tmp, frac); return tmp; } static int pfd_set_rate(struct clk *clk, unsigned long rate) { u32 frac; u64 tmp; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.enable(&apbh_dma_clk); tmp = (u64)clk->parent->rate * 18; do_div(tmp, rate); frac = tmp; frac = frac < 18 ? 18 : frac; frac = frac > 35 ? 35 : frac; /* clear clk frac bits */ __raw_writel(MXC_ANADIG_PFD_FRAC_MASK << clk->enable_shift, apll_base + (int)clk->enable_reg + 8); /* set clk frac bits */ __raw_writel(frac << clk->enable_shift, apll_base + (int)clk->enable_reg + 4); tmp = (u64)clk->parent->rate * 18; do_div(tmp, frac); clk->rate = tmp; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.disable(&apbh_dma_clk); return 0; } static int pfd_enable(struct clk *clk) { int index; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.enable(&apbh_dma_clk); index = _get_mux8(clk, &pfd0_clk, &pfd1_clk, &pfd2_clk, &pfd3_clk, &pfd4_clk, &pfd5_clk, &pfd6_clk, &pfd7_clk); __raw_writel(1 << (index + MXC_ANADIG_PFD_DIS_OFFSET), apll_base + MXC_ANADIG_PLLCTRL_CLR); /* clear clk gate bit */ __raw_writel((1 << (clk->enable_shift + 7)), apll_base + (int)clk->enable_reg + 8); /* check lock bit */ if (!WAIT(__raw_readl(apll_base + MXC_ANADIG_PLLCTRL) & MXC_ANADIG_APLL_LOCK, 50000)) { __raw_writel(MXC_ANADIG_APLL_FORCE_LOCK, apll_base + MXC_ANADIG_PLLCTRL_CLR); __raw_writel(MXC_ANADIG_APLL_FORCE_LOCK, apll_base + MXC_ANADIG_PLLCTRL_SET); if (!WAIT(__raw_readl(apll_base + MXC_ANADIG_PLLCTRL) & MXC_ANADIG_APLL_LOCK, SPIN_DELAY)) panic("pfd_enable failed!\n"); } if (apbh_dma_clk.usecount == 0) apbh_dma_clk.disable(&apbh_dma_clk); return 0; } static void pfd_disable(struct clk *clk) { int index; if (apbh_dma_clk.usecount == 0) apbh_dma_clk.enable(&apbh_dma_clk); index = _get_mux8(clk, &pfd0_clk, &pfd1_clk, &pfd2_clk, &pfd3_clk, &pfd4_clk, &pfd5_clk, &pfd6_clk, &pfd7_clk); /* set clk gate bit */ __raw_writel((1 << (clk->enable_shift + 7)), apll_base + (int)clk->enable_reg + 4); __raw_writel(1 << (index + MXC_ANADIG_PFD_DIS_OFFSET), apll_base + MXC_ANADIG_PLLCTRL_SET); if (apbh_dma_clk.usecount == 0) apbh_dma_clk.disable(&apbh_dma_clk); } static struct clk pfd0_clk = { .name = "pfd0", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC0, .enable_shift = MXC_ANADIG_PFD0_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd1_clk = { .name = "pfd1", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC0, .enable_shift = MXC_ANADIG_PFD1_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd2_clk = { .name = "pfd2", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC0, .enable_shift = MXC_ANADIG_PFD2_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd3_clk = { .name = "pfd3", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC0, .enable_shift = MXC_ANADIG_PFD3_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd4_clk = { .name = "pfd4", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC1, .enable_shift = MXC_ANADIG_PFD4_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd5_clk = { .name = "pfd5", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC1, .enable_shift = MXC_ANADIG_PFD5_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd6_clk = { .name = "pfd6", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC1, .enable_shift = MXC_ANADIG_PFD6_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk pfd7_clk = { .name = "pfd7", .parent = &apll_clk, .enable_reg = (void *)MXC_ANADIG_FRAC1, .enable_shift = MXC_ANADIG_PFD7_FRAC_OFFSET, .recalc = pfd_recalc, .set_rate = pfd_set_rate, .round_rate = pfd_round_rate, .enable = pfd_enable, .disable = pfd_disable, .flags = RATE_PROPAGATES, }; static struct clk ckil_clk = { .name = "ckil", .flags = RATE_PROPAGATES, }; static void _clk_pll_recalc(struct clk *clk) { long mfi, mfn, mfd, pdf, ref_clk, mfn_abs; unsigned long dp_op, dp_mfd, dp_mfn, dp_ctl, pll_hfsm, dbl; void __iomem *pllbase; s64 temp; pllbase = _get_pll_base(clk); dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; dbl = dp_ctl & MXC_PLL_DP_CTL_DPDCK0_2_EN; if (pll_hfsm == 0) { dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP); dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD); dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN); } else { dp_op = __raw_readl(pllbase + MXC_PLL_DP_HFS_OP); dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFD); dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFN); } pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK; mfi = (dp_op & MXC_PLL_DP_OP_MFI_MASK) >> MXC_PLL_DP_OP_MFI_OFFSET; mfi = (mfi <= 5) ? 5 : mfi; mfd = dp_mfd & MXC_PLL_DP_MFD_MASK; mfn = mfn_abs = dp_mfn & MXC_PLL_DP_MFN_MASK; /* Sign extend to 32-bits */ if (mfn >= 0x04000000) { mfn |= 0xFC000000; mfn_abs = -mfn; } ref_clk = 2 * clk->parent->rate; if (dbl != 0) 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_pll_set_rate(struct clk *clk, unsigned long rate) { u32 reg, reg1; void __iomem *pllbase; long mfi, pdf, mfn, mfd = 999999; s64 temp64; unsigned long quad_parent_rate; unsigned long pll_hfsm, dp_ctl; pllbase = _get_pll_base(clk); quad_parent_rate = 4*clk->parent->rate; pdf = mfi = -1; while (++pdf < 16 && mfi < 5) mfi = rate * (pdf+1) / quad_parent_rate; if (mfi > 15) return -1; pdf--; temp64 = rate*(pdf+1) - quad_parent_rate*mfi; do_div(temp64, quad_parent_rate/1000000); mfn = (long)temp64; dp_ctl = __raw_readl(pllbase + MXC_PLL_DP_CTL); /* use dpdck0_2 */ __raw_writel(dp_ctl | 0x1000L, pllbase + MXC_PLL_DP_CTL); pll_hfsm = dp_ctl & MXC_PLL_DP_CTL_HFSM; if (pll_hfsm == 0) { reg = mfi<<4 | pdf; __raw_writel(reg, pllbase + MXC_PLL_DP_OP); __raw_writel(mfd, pllbase + MXC_PLL_DP_MFD); __raw_writel(mfn, pllbase + MXC_PLL_DP_MFN); } else { reg = mfi<<4 | pdf; __raw_writel(reg, pllbase + MXC_PLL_DP_HFS_OP); __raw_writel(mfd, pllbase + MXC_PLL_DP_HFS_MFD); __raw_writel(mfn, pllbase + MXC_PLL_DP_HFS_MFN); } /* If auto restart is disabled, restart the PLL and * wait for it to lock. */ reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); if (reg & MXC_PLL_DP_CTL_UPEN) { reg = __raw_readl(pllbase + MXC_PLL_DP_CONFIG); if (!(reg & MXC_PLL_DP_CONFIG_AREN)) { reg1 = __raw_readl(pllbase + MXC_PLL_DP_CTL); reg1 |= MXC_PLL_DP_CTL_RST; __raw_writel(reg1, pllbase + MXC_PLL_DP_CTL); } /* Wait for lock */ if (!WAIT(__raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_LRF, SPIN_DELAY)) panic("pll_set_rate: pll relock failed\n"); } clk->rate = rate; return 0; } static int _clk_pll_enable(struct clk *clk) { u32 reg; void __iomem *pllbase; pllbase = _get_pll_base(clk); reg = __raw_readl(pllbase + MXC_PLL_DP_CTL); if (reg & MXC_PLL_DP_CTL_UPEN) return 0; reg |= MXC_PLL_DP_CTL_UPEN; __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); /* Wait for lock */ if (!WAIT(__raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_LRF, SPIN_DELAY)) panic("pll relock failed\n"); return 0; } static void _clk_pll_disable(struct clk *clk) { u32 reg; void __iomem *pllbase; pllbase = _get_pll_base(clk); reg = __raw_readl(pllbase + MXC_PLL_DP_CTL) & ~MXC_PLL_DP_CTL_UPEN; __raw_writel(reg, pllbase + MXC_PLL_DP_CTL); } static struct clk pll1_main_clk = { .name = "pll1_main_clk", .parent = &osc_clk, .recalc = _clk_pll_recalc, .enable = _clk_pll_enable, .disable = _clk_pll_disable, .flags = RATE_PROPAGATES, }; static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CCSR); if (parent == &pll1_main_clk) { reg &= ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL; __raw_writel(reg, MXC_CCM_CCSR); /* Set the step_clk parent to be lp_apm, to save power. */ mux = _get_mux(&lp_apm_clk, &lp_apm_clk, NULL, &pll2_sw_clk, &pll3_sw_clk); reg = __raw_readl(MXC_CCM_CCSR); reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) | (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET); } else { if (parent == &lp_apm_clk) { reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL; reg = __raw_readl(MXC_CCM_CCSR); mux = _get_mux(parent, &lp_apm_clk, NULL, &pll2_sw_clk, &pll3_sw_clk); reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) | (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET); } else { mux = _get_mux(parent, &lp_apm_clk, NULL, &pll2_sw_clk, &pll3_sw_clk); reg = (reg & ~MXC_CCM_CCSR_STEP_SEL_MASK) | (mux << MXC_CCM_CCSR_STEP_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CCSR); reg = __raw_readl(MXC_CCM_CCSR); reg |= MXC_CCM_CCSR_PLL1_SW_CLK_SEL; } } __raw_writel(reg, MXC_CCM_CCSR); return 0; } static void _clk_pll1_sw_recalc(struct clk *clk) { u32 reg, div; div = 1; reg = __raw_readl(MXC_CCM_CCSR); if (clk->parent == &pll2_sw_clk) { div = ((reg & MXC_CCM_CCSR_PLL2_PODF_MASK) >> MXC_CCM_CCSR_PLL2_PODF_OFFSET) + 1; } else if (clk->parent == &pll3_sw_clk) { div = ((reg & MXC_CCM_CCSR_PLL3_PODF_MASK) >> MXC_CCM_CCSR_PLL3_PODF_OFFSET) + 1; } clk->rate = clk->parent->rate / div; } /* pll1 switch clock */ static struct clk pll1_sw_clk = { .name = "pll1_sw_clk", .parent = &pll1_main_clk, .set_parent = _clk_pll1_sw_set_parent, .recalc = _clk_pll1_sw_recalc, .flags = RATE_PROPAGATES, }; static int _clk_pll2_sw_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_CCSR); if (parent == &pll2_sw_clk) { reg &= ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL; } else { reg = (reg & ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL); reg |= MXC_CCM_CCSR_PLL2_SW_CLK_SEL; } __raw_writel(reg, MXC_CCM_CCSR); return 0; } /* same as pll2_main_clk. These two clocks should always be the same */ static struct clk pll2_sw_clk = { .name = "pll2", .parent = &osc_clk, .recalc = _clk_pll_recalc, .enable = _clk_pll_enable, .disable = _clk_pll_disable, .set_rate = _clk_pll_set_rate, .set_parent = _clk_pll2_sw_set_parent, .flags = RATE_PROPAGATES, }; /* same as pll3_main_clk. These two clocks should always be the same */ static struct clk pll3_sw_clk = { .name = "pll3", .parent = &osc_clk, .set_rate = _clk_pll_set_rate, .recalc = _clk_pll_recalc, .enable = _clk_pll_enable, .disable = _clk_pll_disable, .flags = RATE_PROPAGATES, }; static int _clk_lp_apm_set_parent(struct clk *clk, struct clk *parent) { u32 reg; if (parent == &osc_clk) reg = __raw_readl(MXC_CCM_CCSR) & ~MXC_CCM_CCSR_LP_APM_SEL; else if (parent == &apll_clk) reg = __raw_readl(MXC_CCM_CCSR) | MXC_CCM_CCSR_LP_APM_SEL; else return -EINVAL; __raw_writel(reg, MXC_CCM_CCSR); return 0; } static struct clk lp_apm_clk = { .name = "lp_apm", .parent = &osc_clk, .set_parent = _clk_lp_apm_set_parent, .flags = RATE_PROPAGATES, }; static void _clk_arm_recalc(struct clk *clk) { u32 cacrr, div; cacrr = __raw_readl(MXC_CCM_CACRR); div = (cacrr & MXC_CCM_CACRR_ARM_PODF_MASK) + 1; clk->rate = clk->parent->rate / div; } static int _clk_cpu_set_rate(struct clk *clk, unsigned long rate) { u32 i; for (i = 0; i < cpu_wp_nr; i++) { if (rate == cpu_wp_tbl[i].cpu_rate) break; } if (i >= cpu_wp_nr) return -EINVAL; cpu_clk_set_wp(i); return 0; } static unsigned long _clk_cpu_round_rate(struct clk *clk, unsigned long rate) { u32 i; u32 wp; for (i = 0; i < cpu_wp_nr; i++) { if (rate == cpu_wp_tbl[i].cpu_rate) break; } if (i > cpu_wp_nr) wp = 0; return cpu_wp_tbl[wp].cpu_rate; } static struct clk cpu_clk = { .name = "cpu_clk", .parent = &pll1_sw_clk, .recalc = _clk_arm_recalc, .set_rate = _clk_cpu_set_rate, .round_rate = _clk_cpu_round_rate, }; /* TODO: Need to sync with GPC to determine if DVFS is in place so that * the DVFS_PODF divider can be applied in CDCR register. */ static void _clk_main_bus_recalc(struct clk *clk) { u32 div = 0; if (dvfs_per_divider_active() || low_bus_freq_mode) div = (__raw_readl(MXC_CCM_CDCR) & 0x3); clk->rate = clk->parent->rate / (div + 1); } static int _clk_main_bus_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &lp_apm_clk); reg = __raw_readl(MXC_CCM_CBCDR) & ~MX50_CCM_CBCDR_PERIPH_CLK_SEL_MASK; reg |= (mux << MX50_CCM_CBCDR_PERIPH_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CBCDR); return 0; } static struct clk main_bus_clk = { .name = "main_bus_clk", .parent = &pll2_sw_clk, .set_parent = _clk_main_bus_set_parent, .recalc = _clk_main_bus_recalc, .flags = RATE_PROPAGATES, }; static void _clk_axi_a_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CBCDR); div = ((reg & MXC_CCM_CBCDR_AXI_A_PODF_MASK) >> MXC_CCM_CBCDR_AXI_A_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / div; } static int _clk_axi_a_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; div = clk->parent->rate / rate; if (div == 0) div++; if (((clk->parent->rate / div) != rate) || (div > 8)) return -EINVAL; reg = __raw_readl(MXC_CCM_CBCDR); reg &= ~MXC_CCM_CBCDR_AXI_A_PODF_MASK; reg |= (div - 1) << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET; __raw_writel(reg, MXC_CCM_CBCDR); if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AXI_A_PODF_BUSY), SPIN_DELAY)) panic("pll _clk_axi_a_set_rate failed\n"); clk->rate = rate; return 0; } static unsigned long _clk_axi_a_round_rate(struct clk *clk, unsigned long rate) { u32 div; div = clk->parent->rate / rate; if (div > 8) div = 8; else if (div == 0) div++; return clk->parent->rate / div; } static struct clk axi_a_clk = { .name = "axi_a_clk", .parent = &main_bus_clk, .recalc = _clk_axi_a_recalc, .set_rate = _clk_axi_a_set_rate, .round_rate = _clk_axi_a_round_rate, .flags = RATE_PROPAGATES, }; static void _clk_axi_b_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CBCDR); div = ((reg & MXC_CCM_CBCDR_AXI_B_PODF_MASK) >> MXC_CCM_CBCDR_AXI_B_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / div; } static int _clk_axi_b_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; div = clk->parent->rate / rate; if (div == 0) div++; if (((clk->parent->rate / div) != rate) || (div > 8)) return -EINVAL; reg = __raw_readl(MXC_CCM_CBCDR); reg &= ~MXC_CCM_CBCDR_AXI_B_PODF_MASK; reg |= (div - 1) << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET; __raw_writel(reg, MXC_CCM_CBCDR); if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AXI_B_PODF_BUSY), SPIN_DELAY)) panic("_clk_axi_b_set_rate failed\n"); clk->rate = rate; return 0; } static unsigned long _clk_axi_b_round_rate(struct clk *clk, unsigned long rate) { u32 div; div = clk->parent->rate / rate; if (div > 8) div = 8; else if (div == 0) div++; return clk->parent->rate / div; } static struct clk axi_b_clk = { .name = "axi_b_clk", .parent = &main_bus_clk, .recalc = _clk_axi_b_recalc, .set_rate = _clk_axi_b_set_rate, .round_rate = _clk_axi_b_round_rate, .flags = RATE_PROPAGATES, }; static void _clk_ahb_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CBCDR); div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / div; } static int _clk_ahb_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; div = clk->parent->rate / rate; if (div == 0) div++; if (((clk->parent->rate / div) != rate) || (div > 8)) return -EINVAL; reg = __raw_readl(MXC_CCM_CBCDR); reg &= ~MXC_CCM_CBCDR_AHB_PODF_MASK; reg |= (div - 1) << MXC_CCM_CBCDR_AHB_PODF_OFFSET; __raw_writel(reg, MXC_CCM_CBCDR); if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_AHB_PODF_BUSY), SPIN_DELAY)) panic("_clk_ahb_set_rate failed\n"); clk->rate = rate; return 0; } static unsigned long _clk_ahb_round_rate(struct clk *clk, unsigned long rate) { u32 div; div = clk->parent->rate / rate; if (div > 8) div = 8; else if (div == 0) div++; return clk->parent->rate / div; } static struct clk ahb_clk = { .name = "ahb_clk", .parent = &main_bus_clk, .recalc = _clk_ahb_recalc, .set_rate = _clk_ahb_set_rate, .round_rate = _clk_ahb_round_rate, .flags = RATE_PROPAGATES, }; static int _clk_max_enable(struct clk *clk) { u32 reg; _clk_enable(clk); /* Handshake with MAX when LPM is entered. */ reg = __raw_readl(MXC_CCM_CLPCR); reg &= ~MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS; __raw_writel(reg, MXC_CCM_CLPCR); return 0; } static void _clk_max_disable(struct clk *clk) { u32 reg; _clk_disable_inwait(clk); /* No Handshake with MAX when LPM is entered as its disabled. */ reg = __raw_readl(MXC_CCM_CLPCR); reg |= MXC_CCM_CLPCR_BYPASS_MAX_LPM_HS; __raw_writel(reg, MXC_CCM_CLPCR); } static struct clk ahb_max_clk = { .name = "max_clk", .parent = &ahb_clk, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGR0_CG14_OFFSET, .enable = _clk_max_enable, .disable = _clk_max_disable, }; static struct clk ahbmux1_clk = { .name = "ahbmux1_clk", .id = 0, .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGR0_CG8_OFFSET, .disable = _clk_disable_inwait, }; static void _clk_ipg_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CBCDR); div = ((reg & MXC_CCM_CBCDR_IPG_PODF_MASK) >> MXC_CCM_CBCDR_IPG_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / div; } static struct clk ipg_clk = { .name = "ipg_clk", .parent = &ahb_clk, .recalc = _clk_ipg_recalc, .flags = RATE_PROPAGATES, }; static void _clk_ipg_per_recalc(struct clk *clk) { u32 reg, prediv1, prediv2, podf; if (clk->parent == &main_bus_clk || clk->parent == &lp_apm_clk) { /* the main_bus_clk is the one before the DVFS engine */ reg = __raw_readl(MXC_CCM_CBCDR); prediv1 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED1_MASK) >> MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET) + 1; prediv2 = ((reg & MXC_CCM_CBCDR_PERCLK_PRED2_MASK) >> MXC_CCM_CBCDR_PERCLK_PRED2_OFFSET) + 1; podf = ((reg & MXC_CCM_CBCDR_PERCLK_PODF_MASK) >> MXC_CCM_CBCDR_PERCLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv1 * prediv2 * podf); } else if (clk->parent == &ipg_clk) { clk->rate = ipg_clk.rate; } else { BUG(); } } static int _clk_ipg_per_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CBCMR); mux = _get_mux(parent, &main_bus_clk, &lp_apm_clk, &ipg_clk, NULL); if (mux == 2) { reg |= MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; } else { reg &= ~MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL; if (mux == 0) reg &= ~MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; else reg |= MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL; } __raw_writel(reg, MXC_CCM_CBCMR); return 0; } static struct clk ipg_perclk = { .name = "ipg_perclk", .parent = &lp_apm_clk, .recalc = _clk_ipg_per_recalc, .set_parent = _clk_ipg_per_set_parent, .flags = RATE_PROPAGATES, }; static struct clk ipmux1_clk = { .name = "ipmux1", .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGR5_CG6_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static struct clk ipmux2_clk = { .name = "ipmux2", .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG0_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static int _clk_sys_clk_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CLK_SYS); reg &= ~(MXC_CCM_CLK_SYS_SYS_XTAL_CLKGATE_MASK | MXC_CCM_CLK_SYS_SYS_PLL_CLKGATE_MASK); if (__raw_readl(MXC_CCM_CLKSEQ_BYPASS) & 0x1) reg |= MXC_CCM_CLK_SYS_SYS_PLL_CLKGATE_MASK; else reg |= MXC_CCM_CLK_SYS_SYS_XTAL_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_CLK_SYS); return 0; } static void _clk_sys_clk_disable(struct clk *clk) { u32 reg, reg1; reg1 = (__raw_readl(databahn + DATABAHN_CTL_REG55)) & DDR_SYNC_MODE; reg = __raw_readl(MXC_CCM_CLK_SYS); reg &= ~(MXC_CCM_CLK_SYS_SYS_XTAL_CLKGATE_MASK | MXC_CCM_CLK_SYS_SYS_PLL_CLKGATE_MASK); if (__raw_readl(MXC_CCM_CLKSEQ_BYPASS) & 0x1) reg |= 1 << MXC_CCM_CLK_SYS_SYS_PLL_CLKGATE_OFFSET; else { /* If DDR is sourced from SYS_CLK (in Sync mode), we cannot * gate its clock when ARM is in wait if the DDR is not in * self refresh. */ if (reg1 == DDR_SYNC_MODE) reg |= 3 << MXC_CCM_CLK_SYS_SYS_XTAL_CLKGATE_OFFSET; else reg |= 1 << MXC_CCM_CLK_SYS_SYS_XTAL_CLKGATE_OFFSET; } __raw_writel(reg, MXC_CCM_CLK_SYS); } static struct clk sys_clk = { .name = "sys_clk", .enable = _clk_sys_clk_enable, .disable = _clk_sys_clk_disable, }; static int _clk_weim_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_CBCDR); if (parent == &ahb_clk) reg |= MX50_CCM_CBCDR_WEIM_CLK_SEL; else if (parent == &main_bus_clk) reg &= ~MX50_CCM_CBCDR_WEIM_CLK_SEL; else BUG(); __raw_writel(reg, MXC_CCM_CBCDR); return 0; } static void _clk_weim_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CBCDR); div = ((reg & MXC_CCM_CBCDR_EMI_PODF_MASK) >> MXC_CCM_CBCDR_EMI_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / div; } static int _clk_weim_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; div = clk->parent->rate / rate; if (div == 0) div++; if (((clk->parent->rate / div) != rate) || (div > 8)) return -EINVAL; reg = __raw_readl(MXC_CCM_CBCDR); reg &= ~MXC_CCM_CBCDR_EMI_PODF_MASK; reg |= (div - 1) << MXC_CCM_CBCDR_EMI_PODF_OFFSET; __raw_writel(reg, MXC_CCM_CBCDR); if (!WAIT(!(__raw_readl(MXC_CCM_CDHIPR) & MXC_CCM_CDHIPR_EMI_PODF_BUSY), SPIN_DELAY)) panic("_clk_emi_slow_set_rate failed\n"); clk->rate = rate; return 0; } static unsigned long _clk_weim_round_rate(struct clk *clk, unsigned long rate) { u32 div; div = clk->parent->rate / rate; if (div > 8) div = 8; else if (div == 0) div++; return clk->parent->rate / div; } static struct clk weim_clk[] = { { .name = "weim_clk", .parent = &main_bus_clk, .set_parent = _clk_weim_set_parent, .recalc = _clk_weim_recalc, .set_rate = _clk_weim_set_rate, .round_rate = _clk_weim_round_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGR5_CG8_OFFSET, .disable = _clk_disable_inwait, .flags = RATE_PROPAGATES, .secondary = &weim_clk[1], }, { .name = "weim_ipg_clk", .parent = &ipg_clk, .secondary = &sys_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGR5_CG9_OFFSET, .disable = _clk_disable_inwait, } }; static int _clk_ocram_enable(struct clk *clk) { return 0; } static void _clk_ocram_disable(struct clk *clk) { } static struct clk ocram_clk = { .name = "ocram_clk", .parent = &sys_clk, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG1_OFFSET, .enable = _clk_ocram_enable, .disable = _clk_ocram_disable, }; static struct clk aips_tz1_clk = { .name = "aips_tz1_clk", .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGR0_CG12_OFFSET, .enable = _clk_enable, .disable = _clk_disable_inwait, }; static struct clk aips_tz2_clk = { .name = "aips_tz2_clk", .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable_reg = MXC_CCM_CCGR0, .enable_shift = MXC_CCM_CCGR0_CG13_OFFSET, .enable = _clk_enable, .disable = _clk_disable_inwait, }; static struct clk gpc_dvfs_clk = { .name = "gpc_dvfs_clk", .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGR5_CG12_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static int _clk_sdma_enable(struct clk *clk) { u32 reg; _clk_enable(clk); /* Handshake with SDMA when LPM is entered. */ reg = __raw_readl(MXC_CCM_CLPCR); reg &= ~MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS; __raw_writel(reg, MXC_CCM_CLPCR); return 0; } static void _clk_sdma_disable(struct clk *clk) { u32 reg; _clk_disable(clk); /* No handshake with SDMA as its not enabled. */ reg = __raw_readl(MXC_CCM_CLPCR); reg |= MXC_CCM_CLPCR_BYPASS_SDMA_LPM_HS; __raw_writel(reg, MXC_CCM_CLPCR); } static struct clk sdma_clk[] = { { .name = "sdma_ahb_clk", .parent = &ahb_clk, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG15_OFFSET, .enable = _clk_sdma_enable, .disable = _clk_sdma_disable, }, { .name = "sdma_ipg_clk", .parent = &ipg_clk, .secondary = &ddr_clk, }, }; static struct clk spba_clk = { .name = "spba_clk", .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR5, .enable_shift = MXC_CCM_CCGR5_CG0_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static void _clk_uart_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CSCDR1); prediv = ((reg & MXC_CCM_CSCDR1_UART_CLK_PRED_MASK) >> MXC_CCM_CSCDR1_UART_CLK_PRED_OFFSET) + 1; podf = ((reg & MXC_CCM_CSCDR1_UART_CLK_PODF_MASK) >> MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_uart_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &lp_apm_clk); reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_UART_CLK_SEL_MASK; reg |= mux << MXC_CCM_CSCMR1_UART_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk uart_main_clk = { .name = "uart_main_clk", .parent = &pll2_sw_clk, .recalc = _clk_uart_recalc, .set_parent = _clk_uart_set_parent, .flags = RATE_PROPAGATES, }; static struct clk uart1_clk[] = { { .name = "uart_clk", .id = 0, .parent = &uart_main_clk, .secondary = &uart1_clk[1], .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG4_OFFSET, .enable = _clk_enable, .disable = _clk_disable, #ifdef UART1_DMA_ENABLE .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, #endif }, { .name = "uart_ipg_clk", .id = 0, .parent = &ipg_clk, #ifdef UART1_DMA_ENABLE .secondary = &aips_tz1_clk, #endif .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG3_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static struct clk uart2_clk[] = { { .name = "uart_clk", .id = 1, .parent = &uart_main_clk, .secondary = &uart2_clk[1], .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG6_OFFSET, .enable = _clk_enable, .disable = _clk_disable, #ifdef UART2_DMA_ENABLE .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, #endif }, { .name = "uart_ipg_clk", .id = 1, .parent = &ipg_clk, #ifdef UART2_DMA_ENABLE .secondary = &aips_tz1_clk, #endif .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG5_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static struct clk uart3_clk[] = { { .name = "uart_clk", .id = 2, .parent = &uart_main_clk, .secondary = &uart3_clk[1], .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG8_OFFSET, .enable = _clk_enable, .disable = _clk_disable, #ifdef UART3_DMA_ENABLE .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, #endif }, { .name = "uart_ipg_clk", .id = 2, .parent = &ipg_clk, .secondary = &spba_clk, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG7_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static struct clk uart4_clk[] = { { .name = "uart_clk", .id = 3, .parent = &uart_main_clk, .secondary = &uart4_clk[1], .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG5_OFFSET, .enable = _clk_enable, .disable = _clk_disable, #ifdef UART4_DMA_ENABLE .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, #endif }, { .name = "uart_ipg_clk", .id = 3, .parent = &ipg_clk, .secondary = &spba_clk, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG4_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static struct clk uart5_clk[] = { { .name = "uart_clk", .id = 4, .parent = &uart_main_clk, .secondary = &uart5_clk[1], .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG7_OFFSET, .enable = _clk_enable, .disable = _clk_disable, #ifdef UART5_DMA_ENABLE .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, #endif }, { .name = "uart_ipg_clk", .id = 4, .parent = &ipg_clk, .secondary = &spba_clk, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG6_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static struct clk gpt_clk[] = { { .name = "gpt_clk", .parent = &ipg_perclk, .id = 0, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG10_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "gpt_ipg_clk", .id = 0, .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG9_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "gpt_32k_clk", .id = 0, .parent = &ckil_clk, }, }; static struct clk pwm1_clk[] = { { .name = "pwm", .parent = &ipg_perclk, .id = 0, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG6_OFFSET, .enable = _clk_enable, .disable = _clk_disable, .secondary = &pwm1_clk[1], }, { .name = "pwm_ipg_clk", .id = 0, .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG5_OFFSET, .enable = _clk_enable_inrun, /*Active only when ARM is running. */ .disable = _clk_disable, }, { .name = "pwm_32k_clk", .id = 0, .parent = &ckil_clk, }, }; static struct clk pwm2_clk[] = { { .name = "pwm", .parent = &ipg_perclk, .id = 1, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG8_OFFSET, .enable = _clk_enable, .disable = _clk_disable, .secondary = &pwm2_clk[1], }, { .name = "pwm_ipg_clk", .id = 1, .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG7_OFFSET, .enable = _clk_enable_inrun, /*Active only when ARM is running. */ .disable = _clk_disable, }, { .name = "pwm_32k_clk", .id = 1, .parent = &ckil_clk, }, }; static struct clk i2c_clk[] = { { .name = "i2c_clk", .id = 0, .parent = &ipg_perclk, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG9_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "i2c_clk", .id = 1, .parent = &ipg_perclk, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG10_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "i2c_clk", .id = 2, .parent = &ipg_perclk, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG11_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, }; static void _clk_cspi_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CSCDR2); prediv = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PRED_MASK) >> MXC_CCM_CSCDR2_CSPI_CLK_PRED_OFFSET) + 1; if (prediv == 1) BUG(); podf = ((reg & MXC_CCM_CSCDR2_CSPI_CLK_PODF_MASK) >> MXC_CCM_CSCDR2_CSPI_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_cspi_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &lp_apm_clk); reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_CSPI_CLK_SEL_MASK; reg |= mux << MXC_CCM_CSCMR1_CSPI_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk cspi_main_clk = { .name = "cspi_main_clk", .parent = &pll3_sw_clk, .recalc = _clk_cspi_recalc, .set_parent = _clk_cspi_set_parent, .flags = RATE_PROPAGATES, }; static struct clk cspi1_clk[] = { { .name = "cspi_clk", .id = 0, .parent = &cspi_main_clk, .secondary = &cspi1_clk[1], .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG10_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "cspi_ipg_clk", .id = 0, .parent = &ipg_clk, .secondary = &spba_clk, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG9_OFFSET, .enable = _clk_enable_inrun, /*Active only when ARM is running. */ .disable = _clk_disable, }, }; static struct clk cspi2_clk[] = { { .name = "cspi_clk", .id = 1, .parent = &cspi_main_clk, .secondary = &cspi2_clk[1], .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG12_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "cspi_ipg_clk", .id = 1, .parent = &ipg_clk, .secondary = &aips_tz2_clk, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG11_OFFSET, .enable = _clk_enable_inrun, /*Active only when ARM is running. */ .disable = _clk_disable, }, }; static struct clk cspi3_clk = { .name = "cspi_clk", .id = 2, .parent = &ipg_clk, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG13_OFFSET, .enable = _clk_enable, .disable = _clk_disable, .secondary = &aips_tz2_clk, }; static int _clk_ssi_lp_apm_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &ckih_clk, &lp_apm_clk, &ckih2_clk, NULL); reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_MASK; reg |= mux << MXC_CCM_CSCMR1_SSI_APM_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk ssi_lp_apm_clk = { .name = "ssi_lp_apm_clk", .parent = &ckih_clk, .set_parent = _clk_ssi_lp_apm_set_parent, }; static void _clk_ssi1_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CS1CDR); prediv = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK) >> MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET) + 1; if (prediv == 1) BUG(); podf = ((reg & MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK) >> MXC_CCM_CS1CDR_SSI1_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_ssi1_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &ssi_lp_apm_clk); reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI1_CLK_SEL_MASK; reg |= mux << MXC_CCM_CSCMR1_SSI1_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk ssi1_clk[] = { { .name = "ssi_clk", .id = 0, .parent = &pll3_sw_clk, .set_parent = _clk_ssi1_set_parent, .secondary = &ssi1_clk[1], .recalc = _clk_ssi1_recalc, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG9_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "ssi_ipg_clk", .id = 0, .parent = &ipg_clk, .secondary = &ssi1_clk[2], .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG8_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "ssi_dep_clk", .id = 0, .parent = &aips_tz2_clk, }, }; static void _clk_ssi2_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CS2CDR); prediv = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK) >> MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET) + 1; if (prediv == 1) BUG(); podf = ((reg & MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK) >> MXC_CCM_CS2CDR_SSI2_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_ssi2_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &ssi_lp_apm_clk); reg = __raw_readl(MXC_CCM_CSCMR1) & ~MXC_CCM_CSCMR1_SSI2_CLK_SEL_MASK; reg |= mux << MXC_CCM_CSCMR1_SSI2_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk ssi2_clk[] = { { .name = "ssi_clk", .id = 1, .parent = &pll3_sw_clk, .set_parent = _clk_ssi2_set_parent, .secondary = &ssi2_clk[1], .recalc = _clk_ssi2_recalc, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG11_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "ssi_ipg_clk", .id = 1, .parent = &ipg_clk, .secondary = &ssi2_clk[2], .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG10_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }, { .name = "ssi_dep_clk", .id = 1, .parent = &spba_clk, }, }; static void _clk_ssi_ext1_recalc(struct clk *clk) { u32 reg, prediv, podf; clk->rate = clk->parent->rate; reg = __raw_readl(MXC_CCM_CSCMR1); if ((reg & MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL) == 0) { reg = __raw_readl(MXC_CCM_CS1CDR); prediv = ((reg & MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK) >> MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET) + 1; if (prediv == 1) BUG(); podf = ((reg & MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK) >> MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } } static int _clk_ssi_ext1_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div, pre, post; div = clk->parent->rate / rate; if (div == 0) div++; if (((clk->parent->rate / div) != rate) || div > 512) return -EINVAL; __calc_pre_post_dividers(div, &pre, &post); reg = __raw_readl(MXC_CCM_CS1CDR); reg &= ~(MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_MASK | MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_MASK); reg |= (post - 1) << MXC_CCM_CS1CDR_SSI_EXT1_CLK_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_CS1CDR_SSI_EXT1_CLK_PRED_OFFSET; __raw_writel(reg, MXC_CCM_CS1CDR); clk->rate = rate; return 0; } static int _clk_ssi_ext1_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CSCMR1); if (parent == &ssi1_clk[0]) { reg |= MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL; } else { reg &= ~MXC_CCM_CSCMR1_SSI_EXT1_COM_CLK_SEL; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &ssi_lp_apm_clk); reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_MASK) | (mux << MXC_CCM_CSCMR1_SSI_EXT1_CLK_SEL_OFFSET); } __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static unsigned long _clk_ssi_ext1_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 struct clk ssi_ext1_clk = { .name = "ssi_ext1_clk", .parent = &pll3_sw_clk, .set_parent = _clk_ssi_ext1_set_parent, .set_rate = _clk_ssi_ext1_set_rate, .round_rate = _clk_ssi_ext1_round_rate, .recalc = _clk_ssi_ext1_recalc, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG14_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static void _clk_ssi_ext2_recalc(struct clk *clk) { u32 reg, prediv, podf; clk->rate = clk->parent->rate; reg = __raw_readl(MXC_CCM_CSCMR1); if ((reg & MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL) == 0) { reg = __raw_readl(MXC_CCM_CS2CDR); prediv = ((reg & MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_MASK) >> MXC_CCM_CS2CDR_SSI_EXT2_CLK_PRED_OFFSET) + 1; if (prediv == 1) BUG(); podf = ((reg & MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_MASK) >> MXC_CCM_CS2CDR_SSI_EXT2_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } } static int _clk_ssi_ext2_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CSCMR1); if (parent == &ssi2_clk[0]) { reg |= MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL; } else { reg &= ~MXC_CCM_CSCMR1_SSI_EXT2_COM_CLK_SEL; mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &ssi_lp_apm_clk); reg = (reg & ~MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_MASK) | (mux << MXC_CCM_CSCMR1_SSI_EXT2_CLK_SEL_OFFSET); } __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk ssi_ext2_clk = { .name = "ssi_ext2_clk", .parent = &pll3_sw_clk, .set_parent = _clk_ssi_ext2_set_parent, .recalc = _clk_ssi_ext2_recalc, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG15_OFFSET, .enable = _clk_enable, .disable = _clk_disable, }; static struct clk tmax2_clk = { .name = "tmax2_clk", .id = 0, .parent = &ahb_clk, .secondary = &ahb_max_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR1, .enable_shift = MXC_CCM_CCGR1_CG1_OFFSET, .disable = _clk_disable, }; static struct clk usb_ahb_clk = { .name = "usb_ahb_clk", .parent = &ipg_clk, .secondary = &ddr_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG13_OFFSET, .disable = _clk_disable, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static struct clk usb_phy_clk[] = { { .name = "usb_phy1_clk", .id = 0, .parent = &osc_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG5_OFFSET, .disable = _clk_disable, }, { .name = "usb_phy2_clk", .id = 1, .parent = &osc_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG6_OFFSET, .disable = _clk_disable, } }; static struct clk esdhc_dep_clks = { .name = "sd_dep_clk", .parent = &spba_clk, .secondary = &ddr_clk, }; static void _clk_esdhc1_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CSCDR1); prediv = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PRED_MASK) >> MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PRED_OFFSET) + 1; podf = ((reg & MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PODF_MASK) >> MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_esdhc1_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CSCMR1); mux = _get_mux(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &lp_apm_clk); reg = reg & ~MX50_CCM_CSCMR1_ESDHC1_CLK_SEL_MASK; reg |= mux << MX50_CCM_CSCMR1_ESDHC1_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static int _clk_esdhc1_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); /* Set sdhc1 clock divider */ reg = __raw_readl(MXC_CCM_CSCDR1) & ~(MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PRED_MASK | MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PODF_MASK); reg |= (post - 1) << MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_CSCDR1_ESDHC1_MSHC2_CLK_PRED_OFFSET; __raw_writel(reg, MXC_CCM_CSCDR1); clk->rate = rate; return 0; } static struct clk esdhc1_clk[] = { { .name = "esdhc_clk", .id = 0, .parent = &pll2_sw_clk, .set_parent = _clk_esdhc1_set_parent, .recalc = _clk_esdhc1_recalc, .set_rate = _clk_esdhc1_set_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG1_OFFSET, .disable = _clk_disable, .secondary = &esdhc1_clk[1], .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .name = "esdhc_ipg_clk", .id = 0, .parent = &ipg_clk, .secondary = &esdhc1_clk[2], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG0_OFFSET, .disable = _clk_disable, }, { .name = "esdhc_sec_clk", .id = 0, .parent = &tmax2_clk, .secondary = &esdhc_dep_clks, }, }; static int _clk_esdhc2_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_CSCMR1); if (parent == &esdhc1_clk[0]) reg &= ~MX50_CCM_CSCMR1_ESDHC2_CLK_SEL; else if (parent == &esdhc3_clk[0]) reg |= MX50_CCM_CSCMR1_ESDHC2_CLK_SEL; else BUG(); __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk esdhc2_clk[] = { { .name = "esdhc_clk", .id = 1, .parent = &esdhc1_clk[0], .set_parent = _clk_esdhc2_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG3_OFFSET, .disable = _clk_disable, .secondary = &esdhc2_clk[1], .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .name = "esdhc_ipg_clk", .id = 1, .parent = &ipg_clk, .secondary = &esdhc2_clk[2], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG2_OFFSET, .disable = _clk_disable, }, { .name = "esdhc_sec_clk", .id = 0, .parent = &tmax2_clk, .secondary = &esdhc_dep_clks, }, }; static int _clk_esdhc3_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CSCMR1); mux = _get_mux8(parent, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &lp_apm_clk, &pfd0_clk, &pfd1_clk, &pfd4_clk, &osc_clk); reg = reg & ~MX50_CCM_CSCMR1_ESDHC3_CLK_SEL_MASK; reg |= mux << MX50_CCM_CSCMR1_ESDHC3_CLK_SEL_OFFSET; __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static void _clk_esdhc3_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_CSCDR1); prediv = ((reg & MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PRED_MASK) >> MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PRED_OFFSET) + 1; podf = ((reg & MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PODF_MASK) >> MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static int _clk_esdhc3_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); /* Set sdhc1 clock divider */ reg = __raw_readl(MXC_CCM_CSCDR1) & ~(MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PRED_MASK | MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PODF_MASK); reg |= (post - 1) << MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PODF_OFFSET; reg |= (pre - 1) << MXC_CCM_CSCDR1_ESDHC3_MSHC2_CLK_PRED_OFFSET; __raw_writel(reg, MXC_CCM_CSCDR1); clk->rate = rate; return 0; } static struct clk esdhc3_clk[] = { { .name = "esdhc_clk", .id = 2, .parent = &pll2_sw_clk, .set_parent = _clk_esdhc3_set_parent, .recalc = _clk_esdhc3_recalc, .set_rate = _clk_esdhc3_set_rate, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG5_OFFSET, .disable = _clk_disable, .secondary = &esdhc3_clk[1], .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .name = "esdhc_ipg_clk", .id = 2, .parent = &ipg_clk, .secondary = &esdhc3_clk[2], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG4_OFFSET, .disable = _clk_disable, }, { .name = "esdhc_sec_clk", .id = 0, .parent = &ahb_max_clk, .secondary = &esdhc_dep_clks, }, }; static int _clk_esdhc4_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_CSCMR1); if (parent == &esdhc1_clk[0]) reg &= ~MX50_CCM_CSCMR1_ESDHC4_CLK_SEL; else if (parent == &esdhc3_clk[0]) reg |= MX50_CCM_CSCMR1_ESDHC4_CLK_SEL; else BUG(); __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } static struct clk esdhc4_clk[] = { { .name = "esdhc_clk", .id = 3, .parent = &esdhc1_clk[0], .set_parent = _clk_esdhc4_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG7_OFFSET, .disable = _clk_disable, .secondary = &esdhc4_clk[1], .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .name = "esdhc_ipg_clk", .id = 3, .parent = &ipg_clk, .secondary = &esdhc4_clk[2], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR3, .enable_shift = MXC_CCM_CCGR3_CG6_OFFSET, .disable = _clk_disable, }, { .name = "esdhc_sec_clk", .id = 0, .parent = &tmax2_clk, .secondary = &esdhc_dep_clks, }, }; static int _clk_ddr_set_parent(struct clk *clk, struct clk *parent) { u32 reg; reg = __raw_readl(MXC_CCM_CLK_DDR); if (parent == &pfd0_clk) reg |= MXC_CCM_CLK_DDR_DDR_PFD_SEL; else if (parent == &pll1_sw_clk) reg &= ~MXC_CCM_CLK_DDR_DDR_PFD_SEL; else return -EINVAL; __raw_writel(reg, MXC_CCM_CLK_DDR); return 0; } static void _clk_ddr_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CLK_DDR); div = (reg & MXC_CCM_CLK_DDR_DDR_DIV_PLL_MASK) >> MXC_CCM_CLK_DDR_DDR_DIV_PLL_OFFSET; if (div) clk->rate = clk->parent->rate / div; else clk->rate = 0; } static int _clk_ddr_enable(struct clk *clk) { u32 reg; _clk_enable(clk); reg = (__raw_readl(databahn + DATABAHN_CTL_REG55)) & DDR_SYNC_MODE; if (reg != DDR_SYNC_MODE) { reg = __raw_readl(MXC_CCM_CLK_DDR); reg |= MXC_CCM_CLK_DDR_DDR_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_CLK_DDR); } return 0; } static void _clk_ddr_disable(struct clk *clk) { _clk_disable_inwait(clk); } static struct clk ddr_clk = { .name = "ddr_clk", .parent = &pll1_sw_clk, .secondary = &sys_clk, .set_parent = _clk_ddr_set_parent, .recalc = _clk_ddr_recalc, .enable = _clk_ddr_enable, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG15_OFFSET, .disable = _clk_ddr_disable, }; static void _clk_pgc_recalc(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CSCDR1); div = (reg & MXC_CCM_CSCDR1_PGC_CLK_PODF_MASK) >> MXC_CCM_CSCDR1_PGC_CLK_PODF_OFFSET; div = 1 >> div; clk->rate = clk->parent->rate / div; } static struct clk pgc_clk = { .name = "pgc_clk", .parent = &ipg_clk, .recalc = _clk_pgc_recalc, }; /*usb OTG clock */ static struct clk usb_clk = { .name = "usb_clk", .rate = 60000000, }; static struct clk rtc_clk = { .name = "rtc_clk", .parent = &ckil_clk, .secondary = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR4, .enable_shift = MXC_CCM_CCGR4_CG14_OFFSET, .disable = _clk_disable, }; struct clk rng_clk = { .name = "rng_clk", .id = 0, .parent = &ipg_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG1_OFFSET, .disable = _clk_disable, }; static struct clk owire_clk = { /* 1w driver come from upstream and use owire as clock name*/ .name = "owire", .parent = &ipg_perclk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG11_OFFSET, .disable = _clk_disable, }; static struct clk fec_clk[] = { { .name = "fec_clk", .parent = &ipg_clk, .secondary = &fec_clk[1], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR2, .enable_shift = MXC_CCM_CCGR2_CG12_OFFSET, .disable = _clk_disable, .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }, { .name = "fec_sec1_clk", .parent = &aips_tz2_clk, .secondary = &ddr_clk, }, }; static int gpmi_clk_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_GPMI); reg |= MXC_CCM_GPMI_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_GPMI); _clk_enable(clk); return 0; } static void gpmi_clk_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_GPMI); reg &= ~MXC_CCM_GPMI_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_GPMI); _clk_disable(clk); } static int bch_clk_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_BCH); reg |= MXC_CCM_BCH_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_BCH); _clk_enable(clk); return 0; } static void bch_clk_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_BCH); reg &= ~MXC_CCM_BCH_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_BCH); _clk_disable(clk); } static struct clk gpmi_nfc_clk[] = { { .name = "gpmi-nfc", .parent = &osc_clk, .secondary = &gpmi_nfc_clk[1], .enable = gpmi_clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG9_OFFSET, .disable = gpmi_clk_disable, }, { .name = "gpmi-apb", .parent = &ahb_clk, .secondary = &gpmi_nfc_clk[2], .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG8_OFFSET, .disable = _clk_disable, }, { .name = "bch", .parent = &osc_clk, .secondary = &gpmi_nfc_clk[3], .enable = bch_clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG0_OFFSET, .disable = bch_clk_disable, }, { .name = "bch-apb", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG12_OFFSET, .disable = _clk_disable, }, }; static struct clk ocotp_clk = { .name = "ocotp_ctrl_apb", .parent = &ahb_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG13_OFFSET, .disable = _clk_disable, }; static int _clk_gpu2d_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CBCMR); mux = _get_mux(parent, &axi_a_clk, &axi_b_clk, &weim_clk[0], &ahb_clk); reg = (reg & ~MXC_CCM_CBCMR_GPU2D_CLK_SEL_MASK) | (mux << MXC_CCM_CBCMR_GPU2D_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CBCMR); return 0; } static struct clk gpu2d_clk = { .name = "gpu2d_clk", .parent = &axi_a_clk, .secondary = &ddr_clk, .set_parent = _clk_gpu2d_set_parent, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG7_OFFSET, .disable = _clk_disable, .flags = AHB_HIGH_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static struct clk apbh_dma_clk = { .name = "apbh_dma_clk", .parent = &ahb_clk, .secondary = &ddr_clk, .enable = _clk_enable, .disable = _clk_disable_inwait, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG10_OFFSET, }; struct clk dcp_clk = { .name = "dcp_clk", .id = 0, .parent = &ahb_clk, .secondary = &apbh_dma_clk, .enable = _clk_enable, .enable_reg = MXC_CCM_CCGR7, .enable_shift = MXC_CCM_CCGR7_CG11_OFFSET, .disable = _clk_disable, }; static int _clk_display_axi_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CLKSEQ_BYPASS); mux = _get_mux(parent, &osc_clk, &pfd2_clk, &pll1_sw_clk, NULL); reg = (reg & ~MXC_CCM_CLKSEQ_BYPASS_BYPASS_DISPLAY_AXI_CLK_SEL_MASK) | (mux << MXC_CCM_CLKSEQ_BYPASS_BYPASS_DISPLAY_AXI_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CLKSEQ_BYPASS); return 0; } static void _clk_display_axi_recalc(struct clk *clk) { u32 div; div = __raw_readl(MXC_CCM_DISPLAY_AXI); div &= MXC_CCM_DISPLAY_AXI_DIV_MASK; if (div == 0) { /* gated off */ clk->rate = clk->parent->rate; } else { clk->rate = clk->parent->rate / div; } } static unsigned long _clk_display_axi_round_rate(struct clk *clk, unsigned long rate) { u32 max_div = (2 << 6) - 1; return _clk_round_rate_div(clk, rate, max_div, NULL); } static int _clk_display_axi_set_rate(struct clk *clk, unsigned long rate) { u32 new_div, max_div; u32 reg; max_div = (2 << 6) - 1; clk->rate = _clk_round_rate_div(clk, rate, max_div, &new_div); reg = __raw_readl(MXC_CCM_DISPLAY_AXI); reg &= ~MXC_CCM_DISPLAY_AXI_DIV_MASK; reg |= new_div << MXC_CCM_DISPLAY_AXI_DIV_OFFSET; __raw_writel(reg, MXC_CCM_DISPLAY_AXI); while (__raw_readl(MXC_CCM_CSR2) & MXC_CCM_CSR2_DISPLAY_AXI_BUSY) ; return 0; } static struct clk display_axi_clk = { .name = "display_axi", .parent = &osc_clk, .secondary = &apbh_dma_clk, .set_parent = _clk_display_axi_set_parent, .recalc = _clk_display_axi_recalc, .set_rate = _clk_display_axi_set_rate, .round_rate = _clk_display_axi_round_rate, .enable = _clk_enable, .disable = _clk_disable, .enable_reg = MXC_CCM_DISPLAY_AXI, .enable_shift = MXC_CCM_DISPLAY_AXI_CLKGATE_OFFSET, .flags = RATE_PROPAGATES, }; /* TODO: check Auto-Slow Mode */ static struct clk pxp_axi_clk = { .name = "pxp_axi", .parent = &display_axi_clk, .enable = _clk_enable, .disable = _clk_disable, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG9_OFFSET, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static struct clk elcdif_axi_clk = { .name = "elcdif_axi", .parent = &display_axi_clk, .enable = _clk_enable, .disable = _clk_disable, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG10_OFFSET, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_elcdif_pix_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CLKSEQ_BYPASS); mux = _get_mux(parent, &osc_clk, &pfd6_clk, &pll1_sw_clk, &ckih_clk); reg = (reg & ~MXC_CCM_CLKSEQ_BYPASS_BYPASS_ELCDIF_PIX_CLK_SEL_MASK) | (mux << MXC_CCM_CLKSEQ_BYPASS_BYPASS_ELCDIF_PIX_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CLKSEQ_BYPASS); return 0; } static void _clk_elcdif_pix_recalc(struct clk *clk) { u32 reg, prediv, podf; reg = __raw_readl(MXC_CCM_ELCDIFPIX); prediv = ((reg & MXC_CCM_ELCDIFPIX_CLK_PRED_MASK) >> MXC_CCM_ELCDIFPIX_CLK_PRED_OFFSET) + 1; podf = ((reg & MXC_CCM_ELCDIFPIX_CLK_PODF_MASK) >> MXC_CCM_ELCDIFPIX_CLK_PODF_OFFSET) + 1; clk->rate = clk->parent->rate / (prediv * podf); } static unsigned long _clk_elcdif_pix_round_rate(struct clk *clk, unsigned long rate) { u32 max_div = (2 << 12) - 1; return _clk_round_rate_div(clk, rate, max_div, NULL); } static int _clk_elcdif_pix_set_rate(struct clk *clk, unsigned long rate) { u32 new_div, max_div; u32 reg; max_div = (2 << 12) - 1; clk->rate = _clk_round_rate_div(clk, rate, max_div, &new_div); reg = __raw_readl(MXC_CCM_ELCDIFPIX); /* Pre-divider set to 1 - only use PODF for clk dividing */ reg &= ~MXC_CCM_ELCDIFPIX_CLK_PRED_MASK; reg |= 1 << MXC_CCM_ELCDIFPIX_CLK_PRED_OFFSET; reg &= ~MXC_CCM_ELCDIFPIX_CLK_PODF_MASK; reg |= new_div << MXC_CCM_ELCDIFPIX_CLK_PODF_OFFSET; __raw_writel(reg, MXC_CCM_ELCDIFPIX); return 0; } static int _clk_elcdif_pix_enable(struct clk *clk) { u32 reg; _clk_enable(clk); reg = __raw_readl(MXC_CCM_ELCDIFPIX); reg |= 0x3 << MXC_CCM_ELCDIFPIX_CLKGATE_OFFSET; __raw_writel(reg, MXC_CCM_ELCDIFPIX); return 0; } static void _clk_elcdif_pix_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_ELCDIFPIX); reg &= ~MXC_CCM_ELCDIFPIX_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_ELCDIFPIX); _clk_disable(clk); } static struct clk elcdif_pix_clk = { .name = "elcdif_pix", .parent = &osc_clk, .secondary = &ddr_clk, .enable = _clk_elcdif_pix_enable, .disable = _clk_elcdif_pix_disable, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG6_OFFSET, .set_parent = _clk_elcdif_pix_set_parent, .recalc = _clk_elcdif_pix_recalc, .round_rate = _clk_elcdif_pix_round_rate, .set_rate = _clk_elcdif_pix_set_rate, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_epdc_axi_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CLKSEQ_BYPASS); mux = _get_mux(parent, &osc_clk, &pfd3_clk, &pll1_sw_clk, NULL); reg = (reg & ~MXC_CCM_CLKSEQ_BYPASS_BYPASS_EPDC_AXI_CLK_SEL_MASK) | (mux << MXC_CCM_CLKSEQ_BYPASS_BYPASS_EPDC_AXI_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CLKSEQ_BYPASS); return 0; } static void _clk_epdc_axi_recalc(struct clk *clk) { u32 div; div = __raw_readl(MXC_CCM_EPDC_AXI); div &= MXC_CCM_EPDC_AXI_DIV_MASK; if (div == 0) { /* gated off */ clk->rate = clk->parent->rate; } else { clk->rate = clk->parent->rate / div; } } static unsigned long _clk_epdc_axi_round_rate_div(struct clk *clk, unsigned long rate, u32 *new_div) { u32 div, max_div; max_div = (2 << 6) - 1; div = DIV_ROUND_UP(clk->parent->rate, rate); if (div > max_div) div = max_div; else if (div == 0) div++; if (new_div != NULL) *new_div = div; return clk->parent->rate / div; } static unsigned long _clk_epdc_axi_round_rate(struct clk *clk, unsigned long rate) { return _clk_epdc_axi_round_rate_div(clk, rate, NULL); } static int _clk_epdc_axi_set_rate(struct clk *clk, unsigned long rate) { u32 new_div; u32 reg; clk->rate = _clk_epdc_axi_round_rate_div(clk, rate, &new_div); reg = __raw_readl(MXC_CCM_EPDC_AXI); reg &= ~MXC_CCM_EPDC_AXI_DIV_MASK; reg |= new_div << MXC_CCM_EPDC_AXI_DIV_OFFSET; __raw_writel(reg, MXC_CCM_EPDC_AXI); while (__raw_readl(MXC_CCM_CSR2) & MXC_CCM_CSR2_EPDC_AXI_BUSY) ; return 0; } static int _clk_epdc_axi_enable(struct clk *clk) { u32 reg; _clk_enable(clk); reg = __raw_readl(MXC_CCM_EPDC_AXI); reg |= MXC_CCM_EPDC_AXI_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_EPDC_AXI); return 0; } static void _clk_epdc_axi_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_EPDC_AXI); reg &= ~MXC_CCM_EPDC_AXI_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_EPDC_AXI); _clk_disable(clk); } /* TODO: check Auto-Slow Mode */ static struct clk epdc_axi_clk = { .name = "epdc_axi", .parent = &osc_clk, .secondary = &apbh_dma_clk, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG8_OFFSET, .set_parent = _clk_epdc_axi_set_parent, .recalc = _clk_epdc_axi_recalc, .set_rate = _clk_epdc_axi_set_rate, .round_rate = _clk_epdc_axi_round_rate, .enable = _clk_epdc_axi_enable, .disable = _clk_epdc_axi_disable, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static int _clk_epdc_pix_set_parent(struct clk *clk, struct clk *parent) { u32 reg, mux; reg = __raw_readl(MXC_CCM_CLKSEQ_BYPASS); mux = _get_mux(parent, &osc_clk, &pfd5_clk, &pll1_sw_clk, &ckih_clk); reg = (reg & ~MXC_CCM_CLKSEQ_BYPASS_BYPASS_EPDC_PIX_CLK_SEL_MASK) | (mux << MXC_CCM_CLKSEQ_BYPASS_BYPASS_EPDC_PIX_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CLKSEQ_BYPASS); return 0; } static void _clk_epdc_pix_recalc(struct clk *clk) { u32 div; div = __raw_readl(MXC_CCM_EPDCPIX); div &= MXC_CCM_EPDC_PIX_CLK_PODF_MASK; if (div == 0) { /* gated off */ clk->rate = clk->parent->rate; } else { clk->rate = clk->parent->rate / div; } } static unsigned long _clk_epdc_pix_round_rate(struct clk *clk, unsigned long rate) { u32 max_div = (2 << 12) - 1; return _clk_round_rate_div(clk, rate, max_div, NULL); } static int _clk_epdc_pix_set_rate(struct clk *clk, unsigned long rate) { u32 new_div, max_div; u32 reg; max_div = (2 << 12) - 1; clk->rate = _clk_round_rate_div(clk, rate, max_div, &new_div); reg = __raw_readl(MXC_CCM_EPDCPIX); /* Pre-divider set to 1 - only use PODF for clk dividing */ reg &= ~MXC_CCM_EPDC_PIX_CLK_PRED_MASK; reg |= 1 << MXC_CCM_EPDC_PIX_CLK_PRED_OFFSET; reg &= ~MXC_CCM_EPDC_PIX_CLK_PODF_MASK; reg |= new_div << MXC_CCM_EPDC_PIX_CLK_PODF_OFFSET; __raw_writel(reg, MXC_CCM_EPDCPIX); while (__raw_readl(MXC_CCM_CSR2) & MXC_CCM_CSR2_EPDC_PIX_BUSY) ; return 0; } static int _clk_epdc_pix_enable(struct clk *clk) { u32 reg; _clk_enable(clk); reg = __raw_readl(MXC_CCM_EPDCPIX); reg |= MXC_CCM_EPDC_PIX_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_EPDCPIX); return 0; } static void _clk_epdc_pix_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_EPDCPIX); reg &= ~MXC_CCM_EPDC_PIX_CLKGATE_MASK; __raw_writel(reg, MXC_CCM_EPDCPIX); _clk_disable(clk); } /* TODO: check Auto-Slow Mode */ static struct clk epdc_pix_clk = { .name = "epdc_pix", .parent = &osc_clk, .secondary = &apbh_dma_clk, .enable_reg = MXC_CCM_CCGR6, .enable_shift = MXC_CCM_CCGR6_CG5_OFFSET, .set_parent = _clk_epdc_pix_set_parent, .recalc = _clk_epdc_pix_recalc, .set_rate = _clk_epdc_pix_set_rate, .round_rate = _clk_epdc_pix_round_rate, .enable = _clk_epdc_pix_enable, .disable = _clk_epdc_pix_disable, .flags = AHB_MED_SET_POINT | CPU_FREQ_TRIG_UPDATE, }; static void cko1_recalc(struct clk *clk) { unsigned long rate; u32 reg; reg = __raw_readl(MXC_CCM_CCOSR); reg &= MX50_CCM_CCOSR_CKO1_DIV_MASK; reg = reg >> MX50_CCM_CCOSR_CKO1_DIV_OFFSET; rate = clk->parent->rate; clk->rate = rate / (reg + 1); } static int cko1_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CCOSR); reg |= MX50_CCM_CCOSR_CKO1_EN; __raw_writel(reg, MXC_CCM_CCOSR); return 0; } static void cko1_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CCOSR); reg &= ~MX50_CCM_CCOSR_CKO1_EN; __raw_writel(reg, MXC_CCM_CCOSR); } static int cko1_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; div = (clk->parent->rate/rate - 1) & 0x7; reg = __raw_readl(MXC_CCM_CCOSR); reg &= ~MX50_CCM_CCOSR_CKO1_DIV_MASK; reg |= div << MX50_CCM_CCOSR_CKO1_DIV_OFFSET; __raw_writel(reg, MXC_CCM_CCOSR); return 0; } static unsigned long cko1_round_rate(struct clk *clk, unsigned long rate) { u32 div; div = clk->parent->rate / rate; div = div < 1 ? 1 : div; div = div > 8 ? 8 : div; return clk->parent->rate / div; } static int cko1_set_parent(struct clk *clk, struct clk *parent) { u32 sel, reg, fast; if (parent == &cpu_clk) { sel = 0; fast = 1; } else if (parent == &pll1_sw_clk) { sel = 1; fast = 1; } else if (parent == &pll2_sw_clk) { sel = 2; fast = 1; } else if (parent == &pll3_sw_clk) { sel = 3; fast = 1; } else if (parent == &apll_clk) { sel = 0; fast = 0; } else if (parent == &pfd0_clk) { sel = 1; fast = 0; } else if (parent == &pfd1_clk) { sel = 2; fast = 0; } else if (parent == &pfd2_clk) { sel = 3; fast = 0; } else if (parent == &pfd3_clk) { sel = 4; fast = 0; } else if (parent == &pfd4_clk) { sel = 5; fast = 0; } else if (parent == &pfd5_clk) { sel = 6; fast = 0; } else if (parent == &pfd6_clk) { sel = 7; fast = 0; } else if (parent == &weim_clk[0]) { sel = 10; fast = 0; } else if (parent == &ahb_clk) { sel = 11; fast = 0; } else if (parent == &ipg_clk) { sel = 12; fast = 0; } else if (parent == &ipg_perclk) { sel = 13; fast = 0; } else if (parent == &pfd7_clk) { sel = 15; fast = 0; } else return -EINVAL; reg = __raw_readl(MXC_CCM_CCOSR); reg &= ~MX50_CCM_CCOSR_CKO1_SEL_MASK; reg |= sel << MX50_CCM_CCOSR_CKO1_SEL_OFFSET; if (fast) reg &= ~MX50_CCM_CCOSR_CKO1_SLOW_SEL; else reg |= MX50_CCM_CCOSR_CKO1_SLOW_SEL; __raw_writel(reg, MXC_CCM_CCOSR); return 0; } static struct clk cko1_clk = { .name = "cko1_clk", .parent = &pll1_sw_clk, .recalc = cko1_recalc, .enable = cko1_enable, .disable = cko1_disable, .set_rate = cko1_set_rate, .round_rate = cko1_round_rate, .set_parent = cko1_set_parent, }; static struct clk *mxc_clks[] = { &osc_clk, &ckih_clk, &ckih2_clk, &ckil_clk, &pll1_main_clk, &pll1_sw_clk, &pll2_sw_clk, &pll3_sw_clk, &apll_clk, &pfd0_clk, &pfd1_clk, &pfd2_clk, &pfd3_clk, &pfd4_clk, &pfd5_clk, &pfd6_clk, &pfd7_clk, &ipmux1_clk, &ipmux2_clk, &gpc_dvfs_clk, &lp_apm_clk, &cpu_clk, &main_bus_clk, &axi_a_clk, &axi_b_clk, &ahb_clk, &ahb_max_clk, &ipg_clk, &ipg_perclk, &ahbmux1_clk, &aips_tz1_clk, &aips_tz2_clk, &sdma_clk[0], &sdma_clk[1], &uart_main_clk, &uart1_clk[0], &uart1_clk[1], &uart2_clk[0], &uart2_clk[1], &uart3_clk[0], &uart3_clk[1], &spba_clk, &i2c_clk[0], &i2c_clk[1], &gpt_clk[0], &gpt_clk[1], &gpt_clk[2], &pwm1_clk[0], &pwm1_clk[1], &pwm1_clk[2], &pwm2_clk[0], &pwm2_clk[1], &pwm2_clk[2], &cspi_main_clk, &cspi1_clk[0], &cspi1_clk[1], &cspi2_clk[0], &cspi2_clk[1], &cspi3_clk, &ssi_lp_apm_clk, &ssi1_clk[0], &ssi1_clk[1], &ssi1_clk[2], &ssi2_clk[0], &ssi2_clk[1], &ssi2_clk[2], &ssi_ext1_clk, &ssi_ext2_clk, &tmax2_clk, &usb_ahb_clk, &usb_phy_clk[0], &usb_clk, &esdhc1_clk[0], &esdhc1_clk[1], &esdhc2_clk[0], &esdhc2_clk[1], &esdhc3_clk[0], &esdhc3_clk[1], &esdhc4_clk[0], &esdhc4_clk[1], &esdhc_dep_clks, &weim_clk[0], &weim_clk[1], &ddr_clk, &pgc_clk, &rtc_clk, &rng_clk, &dcp_clk, &owire_clk, &fec_clk[0], &fec_clk[1], &gpu2d_clk, &cko1_clk, &display_axi_clk, &pxp_axi_clk, &elcdif_axi_clk, &epdc_axi_clk, &epdc_pix_clk, &elcdif_pix_clk, &gpmi_nfc_clk[0], &gpmi_nfc_clk[1], &gpmi_nfc_clk[2], &gpmi_nfc_clk[3], &ocotp_clk, }; static void clk_tree_init(void) { u32 reg; ipg_perclk.set_parent(&ipg_perclk, &lp_apm_clk); /* *Initialise the IPG PER CLK dividers to 3. IPG_PER_CLK should be at * 8MHz, its derived from lp_apm. */ reg = __raw_readl(MXC_CCM_CBCDR); reg &= ~MXC_CCM_CBCDR_PERCLK_PRED1_MASK; reg &= ~MXC_CCM_CBCDR_PERCLK_PRED2_MASK; reg &= ~MXC_CCM_CBCDR_PERCLK_PODF_MASK; reg |= (2 << MXC_CCM_CBCDR_PERCLK_PRED1_OFFSET); __raw_writel(reg, MXC_CCM_CBCDR); /* set pll1_main_clk parent */ pll1_main_clk.parent = &osc_clk; /* set pll2_sw_clk parent */ pll2_sw_clk.parent = &osc_clk; /* set pll3_clk parent */ pll3_sw_clk.parent = &osc_clk; /* set weim_clk parent */ weim_clk[0].parent = &main_bus_clk; reg = __raw_readl(MXC_CCM_CBCDR); if ((reg & MX50_CCM_CBCDR_WEIM_CLK_SEL) != 0) weim_clk[0].parent = &ahb_clk; /* set ipg_perclk parent */ ipg_perclk.parent = &lp_apm_clk; reg = __raw_readl(MXC_CCM_CBCMR); if ((reg & MXC_CCM_CBCMR_PERCLK_IPG_CLK_SEL) != 0) { ipg_perclk.parent = &ipg_clk; } else { if ((reg & MXC_CCM_CBCMR_PERCLK_LP_APM_CLK_SEL) == 0) ipg_perclk.parent = &main_bus_clk; } } int __init mx50_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1) { __iomem void *base; struct clk **clkp; int i = 0, j = 0, reg; int wp_cnt = 0; pll1_base = ioremap(MX53_BASE_ADDR(PLL1_BASE_ADDR), SZ_4K); pll2_base = ioremap(MX53_BASE_ADDR(PLL2_BASE_ADDR), SZ_4K); pll3_base = ioremap(MX53_BASE_ADDR(PLL3_BASE_ADDR), SZ_4K); apll_base = ioremap(ANATOP_BASE_ADDR, SZ_4K); /* Turn off all possible clocks */ if (mxc_jtag_enabled) { __raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET | 3 << MXC_CCM_CCGR0_CG2_OFFSET | 3 << MXC_CCM_CCGR0_CG3_OFFSET | 3 << MXC_CCM_CCGR0_CG4_OFFSET | 3 << MXC_CCM_CCGR0_CG8_OFFSET | 1 << MXC_CCM_CCGR0_CG12_OFFSET | 1 << MXC_CCM_CCGR0_CG13_OFFSET | 1 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0); } else { __raw_writel(1 << MXC_CCM_CCGR0_CG0_OFFSET | 3 << MXC_CCM_CCGR0_CG3_OFFSET | 3 << MXC_CCM_CCGR0_CG8_OFFSET | 1 << MXC_CCM_CCGR0_CG12_OFFSET | 1 << MXC_CCM_CCGR0_CG13_OFFSET | 3 << MXC_CCM_CCGR0_CG14_OFFSET, MXC_CCM_CCGR0); } __raw_writel(0, MXC_CCM_CCGR1); __raw_writel(0, MXC_CCM_CCGR2); __raw_writel(0, MXC_CCM_CCGR3); __raw_writel(0, MXC_CCM_CCGR4); __raw_writel(3 << MXC_CCM_CCGR5_CG6_OFFSET | 1 << MXC_CCM_CCGR5_CG8_OFFSET | 3 << MXC_CCM_CCGR5_CG9_OFFSET, MXC_CCM_CCGR5); __raw_writel(3 << MXC_CCM_CCGR6_CG0_OFFSET | 3 << MXC_CCM_CCGR6_CG1_OFFSET | 2 << MXC_CCM_CCGR6_CG14_OFFSET | 3 << MXC_CCM_CCGR6_CG15_OFFSET, MXC_CCM_CCGR6); __raw_writel(0, MXC_CCM_CCGR7); ckil_clk.rate = ckil; osc_clk.rate = osc; ckih_clk.rate = ckih1; usb_phy_clk[0].enable_reg = MXC_CCM_CCGR4; usb_phy_clk[0].enable_shift = MXC_CCM_CCGR4_CG5_OFFSET; clk_tree_init(); for (clkp = mxc_clks; clkp < mxc_clks + ARRAY_SIZE(mxc_clks); clkp++) clk_register(*clkp); clk_register(&uart4_clk[0]); clk_register(&uart4_clk[1]); clk_register(&uart5_clk[0]); clk_register(&uart5_clk[1]); clk_register(&i2c_clk[2]); clk_register(&usb_phy_clk[1]); clk_register(&ocram_clk); clk_register(&apbh_dma_clk); clk_register(&sys_clk); /* set DDR clock parent */ reg = __raw_readl(MXC_CCM_CLK_DDR) & MXC_CCM_CLK_DDR_DDR_PFD_SEL; if (reg) clk_set_parent(&ddr_clk, &pfd0_clk); else clk_set_parent(&ddr_clk, &pll1_sw_clk); clk_set_parent(&esdhc1_clk[0], &pll2_sw_clk); clk_set_parent(&esdhc1_clk[2], &tmax2_clk); clk_set_parent(&esdhc2_clk[0], &esdhc1_clk[0]); clk_set_parent(&esdhc3_clk[0], &pll2_sw_clk); /* This will propagate to all children and init all the clock rates */ propagate_rate(&osc_clk); propagate_rate(&ckih_clk); propagate_rate(&ckil_clk); propagate_rate(&pll1_sw_clk); propagate_rate(&pll2_sw_clk); propagate_rate(&pll3_sw_clk); propagate_rate(&apll_clk); clk_enable(&cpu_clk); clk_enable(&main_bus_clk); clk_enable(&ocotp_clk); databahn = ioremap(MX50_DATABAHN_BASE_ADDR, SZ_16K); /* Initialise the parents to be axi_b, parents are set to * axi_a when the clocks are enabled. */ clk_set_parent(&gpu2d_clk, &axi_a_clk); /* move cspi to 24MHz */ clk_set_parent(&cspi_main_clk, &lp_apm_clk); clk_set_rate(&cspi_main_clk, 12000000); /* * Set DISPLAY_AXI to 200Mhz * For Display AXI, source clocks must be * enabled before dividers can be changed */ clk_enable(&display_axi_clk); clk_enable(&elcdif_axi_clk); clk_enable(&pxp_axi_clk); clk_set_parent(&display_axi_clk, &pfd2_clk); clk_set_rate(&display_axi_clk, 200000000); clk_disable(&display_axi_clk); clk_disable(&pxp_axi_clk); clk_disable(&elcdif_axi_clk); clk_enable(&elcdif_pix_clk); clk_set_parent(&elcdif_pix_clk, &pll1_sw_clk); clk_disable(&elcdif_pix_clk); /* * Enable and set EPDC AXI to 200MHz * For EPDC AXI, source clocks must be * enabled before dividers can be changed */ clk_enable(&epdc_axi_clk); clk_set_parent(&epdc_axi_clk, &pfd3_clk); clk_set_rate(&epdc_axi_clk, 200000000); clk_disable(&epdc_axi_clk); clk_set_parent(&epdc_pix_clk, &pfd5_clk); /* Move SSI clocks to SSI_LP_APM clock */ clk_set_parent(&ssi_lp_apm_clk, &lp_apm_clk); clk_set_parent(&ssi1_clk[0], &ssi_lp_apm_clk); /* set the SSI dividers to divide by 2 */ reg = __raw_readl(MXC_CCM_CS1CDR); reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PODF_MASK; reg &= ~MXC_CCM_CS1CDR_SSI1_CLK_PRED_MASK; reg |= 1 << MXC_CCM_CS1CDR_SSI1_CLK_PRED_OFFSET; __raw_writel(reg, MXC_CCM_CS1CDR); clk_set_parent(&ssi2_clk[0], &ssi_lp_apm_clk); reg = __raw_readl(MXC_CCM_CS2CDR); reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PODF_MASK; reg &= ~MXC_CCM_CS2CDR_SSI2_CLK_PRED_MASK; reg |= 1 << MXC_CCM_CS2CDR_SSI2_CLK_PRED_OFFSET; __raw_writel(reg, MXC_CCM_CS2CDR); /* Change the SSI_EXT1_CLK to be sourced from SSI1_CLK_ROOT */ clk_set_parent(&ssi_ext1_clk, &ssi1_clk[0]); clk_set_parent(&ssi_ext2_clk, &ssi2_clk[0]); /* move usb_phy_clk to 24MHz */ clk_set_parent(&usb_phy_clk[0], &osc_clk); clk_set_parent(&usb_phy_clk[1], &osc_clk); /* move gpmi-nfc to 24MHz */ clk_set_parent(&gpmi_nfc_clk[0], &osc_clk); /* set SDHC root clock as 200MHZ*/ clk_set_rate(&esdhc1_clk[0], 200000000); clk_set_rate(&esdhc3_clk[0], 200000000); /* Set the current working point. */ cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); /* Update the cpu working point table based on the PLL1 freq * at boot time */ if (pll1_main_clk.rate <= cpu_wp_tbl[cpu_wp_nr - 1].cpu_rate) wp_cnt = 1; else if (pll1_main_clk.rate <= cpu_wp_tbl[1].cpu_rate && pll1_main_clk.rate > cpu_wp_tbl[2].cpu_rate) wp_cnt = cpu_wp_nr - 1; else wp_cnt = cpu_wp_nr; cpu_wp_tbl[0].cpu_rate = pll1_main_clk.rate; if (wp_cnt == 1) { cpu_wp_tbl[0] = cpu_wp_tbl[cpu_wp_nr - 1]; memset(&cpu_wp_tbl[cpu_wp_nr - 1], 0, sizeof(struct cpu_wp)); memset(&cpu_wp_tbl[cpu_wp_nr - 2], 0, sizeof(struct cpu_wp)); } else if (wp_cnt < cpu_wp_nr) { for (i = 0; i < wp_cnt; i++) cpu_wp_tbl[i] = cpu_wp_tbl[i+1]; memset(&cpu_wp_tbl[i], 0, sizeof(struct cpu_wp)); } if (wp_cnt < cpu_wp_nr) { set_num_cpu_wp(wp_cnt); cpu_wp_tbl = get_cpu_wp(&cpu_wp_nr); } for (j = 0; j < cpu_wp_nr; j++) { /* Change the CPU podf divider based on the boot up * pll1 rate. */ cpu_wp_tbl[j].cpu_podf = max( (int)((pll1_main_clk.rate / cpu_wp_tbl[j].cpu_rate) - 1), 0); if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) > cpu_wp_tbl[j].cpu_rate) { cpu_wp_tbl[j].cpu_podf++; cpu_wp_tbl[j].cpu_rate = pll1_main_clk.rate/ (1000 * (cpu_wp_tbl[j].cpu_podf + 1)); cpu_wp_tbl[j].cpu_rate *= 1000; } if (pll1_main_clk.rate/(cpu_wp_tbl[j].cpu_podf + 1) < cpu_wp_tbl[j].cpu_rate) { cpu_wp_tbl[j].cpu_rate = pll1_main_clk.rate; } cpu_wp_tbl[j].pll_rate = pll1_main_clk.rate; } /* Set the current working point. */ for (i = 0; i < cpu_wp_nr; i++) { if (clk_get_rate(&cpu_clk) == cpu_wp_tbl[i].cpu_rate) { cpu_curr_wp = i; break; } } if (i > cpu_wp_nr) BUG(); propagate_rate(&osc_clk); propagate_rate(&pll1_sw_clk); propagate_rate(&pll2_sw_clk); propagate_rate(&pll3_sw_clk); clk_set_parent(&uart_main_clk, &lp_apm_clk); clk_set_parent(&gpu2d_clk, &axi_b_clk); clk_set_parent(&weim_clk[0], &ahb_clk); clk_set_rate(&weim_clk[0], clk_round_rate(&weim_clk[0], 130000000)); /* Do the following just to disable the PLL since its not used */ clk_enable(&pll3_sw_clk); clk_disable(&pll3_sw_clk); base = ioremap(MX53_BASE_ADDR(GPT1_BASE_ADDR), SZ_4K); mxc_timer_init(&gpt_clk[0], base, MXC_INT_GPT); return 0; } /*! * Setup cpu clock based on working point. * @param wp cpu freq working point * @return 0 on success or error code on failure. */ static int cpu_clk_set_wp(int wp) { struct cpu_wp *p; u32 reg; if (wp == cpu_curr_wp) return 0; p = &cpu_wp_tbl[wp]; /* * leave the PLL1 freq unchanged. */ reg = __raw_readl(MXC_CCM_CACRR); reg &= ~MXC_CCM_CACRR_ARM_PODF_MASK; reg |= cpu_wp_tbl[wp].cpu_podf << MXC_CCM_CACRR_ARM_PODF_OFFSET; __raw_writel(reg, MXC_CCM_CACRR); cpu_curr_wp = wp; cpu_clk.rate = cpu_wp_tbl[wp].cpu_rate; #if defined(CONFIG_CPU_FREQ) cpufreq_trig_needed = 1; #endif return 0; }