X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farmv7%2Fmx5%2Fclock.c;h=827f8a6048996da0397064e61d81ea9fe66023c1;hp=1c9223fa0783072f77a4aabdb4db225c0ae49856;hb=f991f7c69dbeda644e465da2b83c507bd18853f2;hpb=01968b96a24414ff8f0735111907cbb750c7af43 diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 1c9223fa07..827f8a6048 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -4,23 +4,7 @@ * * (C) Copyright 2009 Freescale Semiconductor, Inc. * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA + * SPDX-License-Identifier: GPL-2.0+ */ #include @@ -62,15 +46,12 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { #define EMI_DIV_MAX 8 #define NFC_DIV_MAX 8 -#define MX5_CBCMR 0x00015154 -#define MX5_CBCDR 0x02888945 - struct fixed_pll_mfd { u32 ref_clk_hz; u32 mfd; }; -const struct fixed_pll_mfd fixed_mfd[] = { +static const struct fixed_pll_mfd fixed_mfd[] = { {MXC_HCLK, 24 * 16}, }; @@ -83,12 +64,102 @@ struct pll_param { #define PLL_FREQ_MAX(ref_clk) (4 * (ref_clk) * PLL_MFI_MAX) #define PLL_FREQ_MIN(ref_clk) \ - ((2 * (ref_clk) * (PLL_MFI_MIN - 1)) / PLL_PD_MAX) + ((4 * (ref_clk) * PLL_MFI_MIN) / PLL_PD_MAX) #define MAX_DDR_CLK 420000000 #define NFC_CLK_MAX 34000000 struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE; +int clk_enable(struct clk *clk) +{ + int ret = 0; + + if (!clk) + return 0; + + if (clk->usecount++ == 0) { + if (!clk->enable) + return 0; + ret = clk->enable(clk); + if (ret) + clk->usecount--; + } + return ret; +} + +void clk_disable(struct clk *clk) +{ + if (!clk) + return; + + if (!(--clk->usecount)) { + if (clk->disable) + clk->disable(clk); + } + if (clk->usecount < 0) { + printf("%s: clk %p (%s) underflow\n", __func__, clk, clk->name); + hang(); + } +} + +int clk_get_usecount(struct clk *clk) +{ + if (clk == NULL) + return 0; + + return clk->usecount; +} + +u32 clk_get_rate(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->rate; +} + +struct clk *clk_get_parent(struct clk *clk) +{ + if (!clk) + return 0; + + return clk->parent; +} + +int clk_set_rate(struct clk *clk, unsigned long rate) +{ + if (clk && clk->set_rate) + clk->set_rate(clk, rate); + return clk->rate; +} + +long clk_round_rate(struct clk *clk, unsigned long rate) +{ + if (clk == NULL || !clk->round_rate) + return 0; + + return clk->round_rate(clk, rate); +} + +int clk_set_parent(struct clk *clk, struct clk *parent) +{ + debug("Setting parent of clk %p to %p (%p)\n", clk, parent, + clk ? clk->parent : NULL); + + if (!clk || clk == parent) + return 0; + + if (clk->set_parent) { + int ret; + + ret = clk->set_parent(clk, parent); + if (ret) + return ret; + } + clk->parent = parent; + return 0; +} + void set_usboh3_clk(void) { clrsetbits_le32(&mxc_ccm->cscmr1, @@ -110,6 +181,100 @@ void enable_usboh3_clk(unsigned char enable) MXC_CCM_CCGR2_USBOH3_60M(cg)); } +void ipu_clk_enable(void) +{ + /* IPU root clock derived from AXI B */ + clrsetbits_le32(&mxc_ccm->cbcmr, MXC_CCM_CBCMR_IPU_HSP_CLK_SEL_MASK, + MXC_CCM_CBCMR_IPU_HSP_CLK_SEL(1)); + + setbits_le32(&mxc_ccm->CCGR5, + MXC_CCM_CCGR5_IPU(MXC_CCM_CCGR_CG_MASK)); + + /* Handshake with IPU when certain clock rates are changed. */ + clrbits_le32(&mxc_ccm->ccdr, MXC_CCM_CCDR_IPU_HS_MASK); + + /* Handshake with IPU when LPM is entered as its enabled. */ + clrbits_le32(&mxc_ccm->clpcr, MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS); +} + +void ipu_clk_disable(void) +{ + clrbits_le32(&mxc_ccm->CCGR5, + MXC_CCM_CCGR5_IPU(MXC_CCM_CCGR_CG_MASK)); + + /* Handshake with IPU when certain clock rates are changed. */ + setbits_le32(&mxc_ccm->ccdr, MXC_CCM_CCDR_IPU_HS_MASK); + + /* Handshake with IPU when LPM is entered as its enabled. */ + setbits_le32(&mxc_ccm->clpcr, MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS); +} + +void ipu_di_clk_enable(int di) +{ + switch (di) { + case 0: + setbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_IPU_DI0(MXC_CCM_CCGR_CG_MASK)); + break; + case 1: + setbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_IPU_DI1(MXC_CCM_CCGR_CG_MASK)); + break; + default: + printf("%s: Invalid DI index %d\n", __func__, di); + } +} + +void ipu_di_clk_disable(int di) +{ + switch (di) { + case 0: + clrbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_IPU_DI0(MXC_CCM_CCGR_CG_MASK)); + break; + case 1: + clrbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_IPU_DI1(MXC_CCM_CCGR_CG_MASK)); + break; + default: + printf("%s: Invalid DI index %d\n", __func__, di); + } +} + +#ifdef CONFIG_MX53 +void ldb_clk_enable(int ldb) +{ + switch (ldb) { + case 0: + setbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_LDB_DI0(MXC_CCM_CCGR_CG_MASK)); + break; + case 1: + setbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_LDB_DI1(MXC_CCM_CCGR_CG_MASK)); + break; + default: + printf("%s: Invalid LDB index %d\n", __func__, ldb); + } +} + +void ldb_clk_disable(int ldb) +{ + switch (ldb) { + case 0: + clrbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_LDB_DI0(MXC_CCM_CCGR_CG_MASK)); + break; + case 1: + clrbits_le32(&mxc_ccm->CCGR6, + MXC_CCM_CCGR6_LDB_DI1(MXC_CCM_CCGR_CG_MASK)); + break; + default: + printf("%s: Invalid LDB index %d\n", __func__, ldb); + } +} +#endif + #ifdef CONFIG_I2C_MXC /* i2c_num can be from 0, to 1 for i.MX51 and 2 for i.MX53 */ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) @@ -176,9 +341,10 @@ void enable_usb_phy2_clk(unsigned char enable) */ static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq) { - uint32_t ctrl, op, mfd, mfn, mfi, pdf, ret; + uint32_t ctrl, op; + int mfd, mfn, mfi, pdf, ret; uint64_t refclk, temp; - int32_t mfn_abs; + uint32_t mfn_abs; ctrl = readl(&pll->ctrl); @@ -205,23 +371,23 @@ static uint32_t decode_pll(struct mxc_pll_reg *pll, uint32_t infreq) if (mfn >= 0x04000000) { mfn |= 0xfc000000; mfn_abs = -mfn; - } else + } else { mfn_abs = mfn; - + } refclk = infreq * 2; if (ctrl & MXC_DPLLC_CTL_DPDCK0_2_EN) refclk *= 2; - do_div(refclk, pdf + 1); temp = refclk * mfn_abs; do_div(temp, mfd + 1); ret = refclk * mfi; - if ((int)mfn < 0) + if (mfn < 0) ret -= temp; else ret += temp; + ret /= pdf + 1; return ret; } @@ -567,18 +733,17 @@ static int gcd(int m, int n) */ static int calc_pll_params(u32 ref, u32 target, struct pll_param *pll) { - u64 pd, mfi = 1, mfn, mfd, t1; - u32 n_target = target; - u32 n_ref = ref, i; + int pd, mfi = 1, mfn, mfd; + u64 t1; + size_t i; /* * Make sure targeted freq is in the valid range. * Otherwise the following calculation might be wrong!!! */ - if (n_target < PLL_FREQ_MIN(ref) || - n_target > PLL_FREQ_MAX(ref)) { - printf("Targeted peripheral clock should be" - "within [%d - %d]\n", + if (target < PLL_FREQ_MIN(ref) || + target > PLL_FREQ_MAX(ref)) { + printf("Targeted pll clock should be within [%d - %d]\n", PLL_FREQ_MIN(ref) / SZ_DEC_1M, PLL_FREQ_MAX(ref) / SZ_DEC_1M); return -EINVAL; @@ -594,10 +759,9 @@ static int calc_pll_params(u32 ref, u32 target, struct pll_param *pll) if (i == ARRAY_SIZE(fixed_mfd)) return -EINVAL; - /* Use n_target and n_ref to avoid overflow */ for (pd = 1; pd <= PLL_PD_MAX; pd++) { - t1 = n_target * pd; - do_div(t1, (4 * n_ref)); + t1 = (u64)target * pd; + do_div(t1, (4 * ref)); mfi = t1; if (mfi > PLL_MFI_MAX) return -EINVAL; @@ -608,25 +772,26 @@ static int calc_pll_params(u32 ref, u32 target, struct pll_param *pll) /* * Now got pd and mfi already * - * mfn = (((n_target * pd) / 4 - n_ref * mfi) * mfd) / n_ref; + * mfn = (((target * pd) / 4 - ref * mfi) * mfd) / ref; */ - t1 = n_target * pd; + t1 = (u64)target * pd; do_div(t1, 4); - t1 -= n_ref * mfi; - t1 *= mfd; - do_div(t1, n_ref); + t1 = (t1 - ref * mfi) * mfd; + do_div(t1, ref); mfn = t1; - debug("ref=%d, target=%d, pd=%d," "mfi=%d,mfn=%d, mfd=%d\n", - ref, n_target, (u32)pd, (u32)mfi, (u32)mfn, (u32)mfd); - i = 1; - if (mfn != 0) + if (mfn != 0) { i = gcd(mfd, mfn); - pll->pd = (u32)pd; - pll->mfi = (u32)mfi; - do_div(mfn, i); - pll->mfn = (u32)mfn; - do_div(mfd, i); - pll->mfd = (u32)mfd; + mfn /= i; + mfd /= i; + } else { + mfd = 1; + } + debug("ref=%d, target=%d, pd=%d, mfi=%d, mfn=%d, mfd=%d\n", + ref, target, pd, mfi, mfn, mfd); + pll->pd = pd; + pll->mfi = mfi; + pll->mfn = mfn; + pll->mfd = mfd; return 0; } @@ -644,71 +809,63 @@ static int calc_pll_params(u32 ref, u32 target, struct pll_param *pll) #define CHANGE_PLL_SETTINGS(pll, pd, fi, fn, fd) \ { \ - writel(0x1232, &pll->ctrl); \ - writel(0x2, &pll->config); \ - writel((((pd) - 1) << 0) | ((fi) << 4), \ - &pll->op); \ - writel(fn, &(pll->mfn)); \ - writel((fd) - 1, &pll->mfd); \ - writel((((pd) - 1) << 0) | ((fi) << 4), \ - &pll->hfs_op); \ - writel(fn, &pll->hfs_mfn); \ - writel((fd) - 1, &pll->hfs_mfd); \ - writel(0x1232, &pll->ctrl); \ - while (!readl(&pll->ctrl) & 0x1) \ + __raw_writel(0x1232, &pll->ctrl); \ + __raw_writel(0x2, &pll->config); \ + __raw_writel((((pd) - 1) << 0) | ((fi) << 4), \ + &pll->op); \ + __raw_writel(fn, &(pll->mfn)); \ + __raw_writel((fd) - 1, &pll->mfd); \ + __raw_writel((((pd) - 1) << 0) | ((fi) << 4), \ + &pll->hfs_op); \ + __raw_writel(fn, &pll->hfs_mfn); \ + __raw_writel((fd) - 1, &pll->hfs_mfd); \ + __raw_writel(0x1232, &pll->ctrl); \ + while (!__raw_readl(&pll->ctrl) & 0x1) \ ;\ } static int config_pll_clk(enum pll_clocks index, struct pll_param *pll_param) { - u32 ccsr = readl(&mxc_ccm->ccsr); + u32 ccsr = __raw_readl(&mxc_ccm->ccsr); struct mxc_pll_reg *pll = mxc_plls[index]; switch (index) { case PLL1_CLOCK: /* Switch ARM to PLL2 clock */ - writel(ccsr | MXC_CCM_CCSR_PLL1_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr | 0x4, &mxc_ccm->ccsr); CHANGE_PLL_SETTINGS(pll, pll_param->pd, pll_param->mfi, pll_param->mfn, pll_param->mfd); /* Switch back */ - writel(ccsr & ~MXC_CCM_CCSR_PLL1_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr & ~0x4, &mxc_ccm->ccsr); break; case PLL2_CLOCK: /* Switch to pll2 bypass clock */ - writel(ccsr | MXC_CCM_CCSR_PLL2_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr | 0x2, &mxc_ccm->ccsr); CHANGE_PLL_SETTINGS(pll, pll_param->pd, pll_param->mfi, pll_param->mfn, pll_param->mfd); /* Switch back */ - writel(ccsr & ~MXC_CCM_CCSR_PLL2_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr & ~0x2, &mxc_ccm->ccsr); break; case PLL3_CLOCK: /* Switch to pll3 bypass clock */ - writel(ccsr | MXC_CCM_CCSR_PLL3_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr | 0x1, &mxc_ccm->ccsr); CHANGE_PLL_SETTINGS(pll, pll_param->pd, pll_param->mfi, pll_param->mfn, pll_param->mfd); /* Switch back */ - writel(ccsr & ~MXC_CCM_CCSR_PLL3_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr & ~0x1, &mxc_ccm->ccsr); break; #ifdef CONFIG_MX53 case PLL4_CLOCK: /* Switch to pll4 bypass clock */ - writel(ccsr | MXC_CCM_CCSR_PLL4_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr | 0x20, &mxc_ccm->ccsr); CHANGE_PLL_SETTINGS(pll, pll_param->pd, pll_param->mfi, pll_param->mfn, pll_param->mfd); /* Switch back */ - writel(ccsr & ~MXC_CCM_CCSR_PLL4_SW_CLK_SEL, - &mxc_ccm->ccsr); + __raw_writel(ccsr & ~0x20, &mxc_ccm->ccsr); break; #endif default: @@ -718,31 +875,70 @@ static int config_pll_clk(enum pll_clocks index, struct pll_param *pll_param) return 0; } +static int __adjust_core_voltage_stub(u32 freq) +{ + return 0; +} +int adjust_core_voltage(u32 freq) + __attribute__((weak, alias("__adjust_core_voltage_stub"))); + /* Config CPU clock */ static int config_core_clk(u32 ref, u32 freq) { int ret = 0; struct pll_param pll_param; + u32 cur_freq = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); + + if (freq == cur_freq) + return 0; memset(&pll_param, 0, sizeof(struct pll_param)); /* The case that periph uses PLL1 is not considered here */ ret = calc_pll_params(ref, freq, &pll_param); if (ret != 0) { - printf("Error:Can't find pll parameters: %d\n", ret); + printf("Error: Can't find pll parameters for %u.%03uMHz ref %u.%03uMHz\n", + freq / 1000000, freq / 1000 % 1000, + ref / 1000000, ref / 1000 % 1000); return ret; } - - return config_pll_clk(PLL1_CLOCK, &pll_param); + if (freq > cur_freq) { + ret = adjust_core_voltage(freq); + if (ret < 0) { + printf("Failed to adjust core voltage for changing ARM clk from %u.%03uMHz to %u.%03uMHz\n", + cur_freq / 1000000, cur_freq / 1000 % 1000, + freq / 1000000, freq / 1000 % 1000); + return ret; + } + ret = config_pll_clk(PLL1_CLOCK, &pll_param); + if (ret) { + adjust_core_voltage(cur_freq); + } + } else { + ret = config_pll_clk(PLL1_CLOCK, &pll_param); + if (ret) { + return ret; + } + ret = adjust_core_voltage(freq); + if (ret < 0) { + printf("Failed to adjust core voltage for changing ARM clk from %u.%03uMHz to %u.%03uMHz\n", + cur_freq / 1000000, cur_freq / 1000 % 1000, + freq / 1000000, freq / 1000 % 1000); + calc_pll_params(ref, cur_freq, &pll_param); + config_pll_clk(PLL1_CLOCK, &pll_param); + } + } + return ret; } static int config_nfc_clk(u32 nfc_clk) { u32 parent_rate = get_emi_slow_clk(); - u32 div = parent_rate / nfc_clk; + u32 div; - if (nfc_clk <= 0) + if (nfc_clk == 0) return -EINVAL; + div = parent_rate / nfc_clk; if (div == 0) div++; if (parent_rate / div > NFC_CLK_MAX) @@ -755,6 +951,15 @@ static int config_nfc_clk(u32 nfc_clk) return 0; } +void enable_nfc_clk(unsigned char enable) +{ + unsigned int cg = enable ? MXC_CCM_CCGR_CG_ON : MXC_CCM_CCGR_CG_OFF; + + clrsetbits_le32(&mxc_ccm->CCGR5, + MXC_CCM_CCGR5_EMI_ENFC(MXC_CCM_CCGR_CG_MASK), + MXC_CCM_CCGR5_EMI_ENFC(cg)); +} + /* Config main_bus_clock for periphs */ static int config_periph_clk(u32 ref, u32 freq) { @@ -908,27 +1113,39 @@ void mxc_set_sata_internal_clock(void) /* * Dump some core clockes. */ +#define pr_clk_val(c, v) { \ + printf("%-11s %3lu.%03lu MHz\n", #c, \ + (v) / 1000000, (v) / 1000 % 1000); \ +} + +#define pr_clk(c) { \ + unsigned long __clk = mxc_get_clock(MXC_##c##_CLK); \ + pr_clk_val(c, __clk); \ +} + int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { - u32 freq; + unsigned long freq; freq = decode_pll(mxc_plls[PLL1_CLOCK], MXC_HCLK); - printf("PLL1 %8d MHz\n", freq / 1000000); + pr_clk_val(PLL1, freq); freq = decode_pll(mxc_plls[PLL2_CLOCK], MXC_HCLK); - printf("PLL2 %8d MHz\n", freq / 1000000); + pr_clk_val(PLL2, freq); freq = decode_pll(mxc_plls[PLL3_CLOCK], MXC_HCLK); - printf("PLL3 %8d MHz\n", freq / 1000000); + pr_clk_val(PLL3, freq); #ifdef CONFIG_MX53 freq = decode_pll(mxc_plls[PLL4_CLOCK], MXC_HCLK); - printf("PLL4 %8d MHz\n", freq / 1000000); + pr_clk_val(PLL4, freq); #endif printf("\n"); - printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); - printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); - printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); - printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); - + pr_clk(AHB); + pr_clk(IPG); + pr_clk(IPG); + pr_clk(DDR); +#ifdef CONFIG_MXC_SPI + pr_clk(CSPI); +#endif return 0; }