On 16/03/2012 21:21, Fabio Estevam wrote: > From: Jason Liu <jason....@linaro.org> > > mx5: Add clock config interface
Hi Fabio, > > Add clock config interface support, so that we > can configure CPU or DDR clock in the later init > > Signed-off-by: Jason Liu <jason....@linaro.org> > Signed-off-by: Eric Miao <eric.m...@linaro.org> > --- My personal quote: this seems to introduce several changes as the clocks are computed - my personal feeling is that this patch will go into the -next branch. > > diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c > index e92f106..c7613f0 100644 > --- a/arch/arm/cpu/armv7/mx5/clock.c > +++ b/arch/arm/cpu/armv7/mx5/clock.c > @@ -24,6 +24,7 @@ > */ > > #include <common.h> > +#include <div64.h> > #include <asm/io.h> > #include <asm/errno.h> > #include <asm/arch/imx-regs.h> > @@ -48,6 +49,39 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = { > #endif > }; > > +#define AHB_CLK_ROOT 133333333 > +#define SZ_DEC_1M 1000000 > +#define PLL_PD_MAX 16 /* Actual pd+1 */ > +#define PLL_MFI_MAX 15 > +#define PLL_MFI_MIN 5 > +#define ARM_DIV_MAX 8 > +#define IPG_DIV_MAX 4 > +#define AHB_DIV_MAX 8 > +#define EMI_DIV_MAX 8 > +#define NFC_DIV_MAX 8 > + > +struct fixed_pll_mfd { > + u32 ref_clk_hz; > + u32 mfd; > +}; > + > +const struct fixed_pll_mfd fixed_mfd[] = { > + {CONFIG_SYS_MX5_HCLK, 24 * 16}, > +}; > + > +struct pll_param { > + u32 pd; > + u32 mfi; > + u32 mfn; > + u32 mfd; > +}; > + > +#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) > +#define MAX_DDR_CLK 420000000 > +#define NFC_CLK_MAX 34000000 > + > struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE; > > void set_usboh3_clk(void) > @@ -212,20 +246,13 @@ static u32 get_periph_clk(void) > /* NOTREACHED */ > } > > -/* > - * Get the rate of ahb clock. > - */ > static u32 get_ahb_clk(void) > { > - uint32_t freq, div, reg; > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 pdf = (cbcdr & MXC_CCM_CBCDR_AHB_PODF_MASK) \ > + >> MXC_CCM_CBCDR_AHB_PODF_OFFSET; > > - freq = get_periph_clk(); > - > - reg = __raw_readl(&mxc_ccm->cbcdr); > - div = ((reg & MXC_CCM_CBCDR_AHB_PODF_MASK) >> > - MXC_CCM_CBCDR_AHB_PODF_OFFSET) + 1; > - > - return freq / div; > + return get_periph_clk() / (pdf + 1); A good occasion to factorize with mx6 code. What about to move the function into arch/arm/cpu/armv7/imx-common/ ? > } > > /* > @@ -306,7 +333,7 @@ static u32 get_uart_clk(void) > /* > * This function returns the low power audio clock. > */ > -u32 get_lp_apm(void) > +static u32 get_lp_apm(void) Right, all internal functions should be static - thanks for fixing it. > > +static u32 get_axi_a_clk(void) > +{ > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 pdf = (cbcdr & MXC_CCM_CBCDR_AXI_A_PODF_MASK) \ > + >> MXC_CCM_CBCDR_AXI_A_PODF_OFFSET; > + > + return get_periph_clk() / (pdf + 1); > +} > + > +static u32 get_axi_b_clk(void) > +{ > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 pdf = (cbcdr & MXC_CCM_CBCDR_AXI_B_PODF_MASK) \ > + >> MXC_CCM_CBCDR_AXI_B_PODF_OFFSET; > + > + return get_periph_clk() / (pdf + 1); > +} > + > +static u32 get_emi_slow_clk(void) > +{ > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 emi_clk_sel = cbcdr & MXC_CCM_CBCDR_EMI_CLK_SEL; > + u32 pdf = (cbcdr & MXC_CCM_CBCDR_EMI_PODF_MASK) \ > + >> MXC_CCM_CBCDR_EMI_PODF_OFFSET; > + > + if (emi_clk_sel) > + return get_ahb_clk() / (pdf + 1); > + > + return get_periph_clk() / (pdf + 1); > +} > + > +static u32 get_nfc_clk(void) > +{ > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 pdf = (cbcdr & MXC_CCM_CBCDR_NFC_PODF_MASK) \ > + >> MXC_CCM_CBCDR_NFC_PODF_OFFSET; > + > + return get_emi_slow_clk() / (pdf + 1); > +} > + > +static u32 get_ddr_clk(void) > +{ > + u32 ret_val = 0; > + u32 cbcmr = __raw_readl(&mxc_ccm->cbcmr); > + u32 ddr_clk_sel = (cbcmr & MXC_CCM_CBCMR_DDR_CLK_SEL_MASK) \ > + >> MXC_CCM_CBCMR_DDR_CLK_SEL_OFFSET; > +#ifdef CONFIG_MX51 > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + if (cbcdr & MXC_CCM_CBCDR_DDR_HIFREQ_SEL) { > + u32 ddr_clk_podf = (cbcdr & MXC_CCM_CBCDR_DDR_PODF_MASK) >> \ > + MXC_CCM_CBCDR_DDR_PODF_OFFSET; > + > + ret_val = decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK); > + ret_val /= ddr_clk_podf + 1; > + > + return ret_val; > + } > +#endif > + switch (ddr_clk_sel) { > + case 0: > + ret_val = get_axi_a_clk(); > + break; > + case 1: > + ret_val = get_axi_b_clk(); > + break; > + case 2: > + ret_val = get_emi_slow_clk(); > + break; > + case 3: > + ret_val = get_ahb_clk(); > + break; > + default: > + break; > + } > + > + return ret_val; > +} > + > + > /* > * The API of get mxc clockes. > */ > @@ -376,10 +482,12 @@ unsigned int mxc_get_clock(enum mxc_clock clk) > case MXC_UART_CLK: > return get_uart_clk(); > case MXC_CSPI_CLK: > - return imx_get_cspiclk(); > + return get_cspi_clk(); > case MXC_FEC_CLK: > return decode_pll(mxc_plls[PLL1_CLOCK], > CONFIG_SYS_MX5_HCLK); > + case MXC_DDR_CLK: > + return get_ddr_clk(); > default: > break; > } > @@ -398,6 +506,424 @@ u32 imx_get_fecclk(void) > } > > /* > + * Clock config code start here > + */ > + > +/* precondition: m>0 and n>0. Let g=gcd(m,n). */ > +static int gcd(int m, int n) > +{ > + int t; > + while (m > 0) { > + if (n > m) { > + t = m; > + m = n; > + n = t; > + } /* swap */ > + m -= n; > + } > + return n; > +} > + > +/* > + * This is to calculate various parameters based on reference clock and > + * targeted clock based on the equation: > + * t_clk = 2*ref_freq*(mfi + mfn/(mfd+1))/(pd+1) > + * This calculation is based on a fixed MFD value for simplicity. > + * > + * @param ref reference clock freq in Hz > + * @param target targeted clock in Hz > + * @param pll pll_param structure. We have not doxygen in all u-boot code and this function remains an exception. So you can drop all @statements. This should be fixed globally. > + } > + /* Now got pd and mfi already */ > + /* > + mfn = (((n_target * pd) / 4 - n_ref * mfi) * mfd) / n_ref; > + */ Dead code ? > + t1 = n_target * pd; > + do_div(t1, 4); > + t1 -= n_ref * mfi; > + t1 *= mfd; > + do_div(t1, n_ref); > + mfn = t1; > +#ifdef CMD_CLOCK_DEBUG > + printf("ref=%d, target=%d, pd=%d," "mfi=%d,mfn=%d, mfd=%d\n", > + ref, n_target, (u32)pd, (u32)mfi, (u32)mfn, (u32)mfd); What is CMD_CLOCK_DEBUG ? I think it is enough if you replace printf() with debug() > + > +static u32 calc_per_cbcdr_val(u32 per_clk) > +{ > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + u32 tmp_clk = 0, div = 0, clk_sel = 0; > + > + cbcdr &= ~MXC_CCM_CBCDR_PERIPH_CLK_SEL; > + > + /* emi_slow_podf divider */ > + tmp_clk = get_emi_slow_clk(); > + clk_sel = cbcdr & MXC_CCM_CBCDR_EMI_CLK_SEL; > + if (clk_sel) { > + div = calc_div(tmp_clk, per_clk, 8); > + cbcdr &= ~MXC_CCM_CBCDR_EMI_PODF_MASK; > + cbcdr |= (div << MXC_CCM_CBCDR_EMI_PODF_OFFSET); > + } > + > + /* axi_b_podf divider */ > + tmp_clk = get_axi_b_clk(); > + div = calc_div(tmp_clk, per_clk, 8); > + cbcdr &= ~MXC_CCM_CBCDR_AXI_B_PODF_MASK; > + cbcdr |= (div << MXC_CCM_CBCDR_AXI_B_PODF_OFFSET); > + > + /* axi_b_podf divider */ > + tmp_clk = get_axi_a_clk(); > + div = calc_div(tmp_clk, per_clk, 8); > + cbcdr &= ~MXC_CCM_CBCDR_AXI_A_PODF_MASK; > + cbcdr |= (div << MXC_CCM_CBCDR_AXI_A_PODF_OFFSET); > + > + /* ahb podf divider */ > + tmp_clk = AHB_CLK_ROOT; > + div = calc_div(tmp_clk, per_clk, 8); > + cbcdr &= ~MXC_CCM_CBCDR_AHB_PODF_MASK; > + cbcdr |= (div << MXC_CCM_CBCDR_AHB_PODF_OFFSET); > + > + return cbcdr; > +} > + > +#define CHANGE_PLL_SETTINGS(pll, pd, fi, fn, fd) \ > + { \ > + __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 = __raw_readl(&mxc_ccm->ccsr); > + struct mxc_pll_reg *pll = mxc_plls[index]; > + > + switch (index) { > + case PLL1_CLOCK: > + /* Switch ARM to PLL2 clock */ > + __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 */ > + __raw_writel(ccsr & ~0x4, &mxc_ccm->ccsr); > + break; > + case PLL2_CLOCK: > + /* Switch to pll2 bypass clock */ > + __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 */ > + __raw_writel(ccsr & ~0x2, &mxc_ccm->ccsr); > + break; > + case PLL3_CLOCK: > + /* Switch to pll3 bypass clock */ > + __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 */ > + __raw_writel(ccsr & ~0x1, &mxc_ccm->ccsr); > + break; > + case PLL4_CLOCK: > + /* Switch to pll4 bypass clock */ > + __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 */ > + __raw_writel(ccsr & ~0x20, &mxc_ccm->ccsr); > + break; > + default: > + return -1; > + } > + > + return 0; > +} > + > +/* Config CPU clock */ > +static int config_core_clk(u32 ref, u32 freq) > +{ > + int ret = 0; > + struct pll_param pll_param; > + > + 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); > + return ret; > + } > + > + return config_pll_clk(PLL1_CLOCK, &pll_param); > +} > + > +static int config_nfc_clk(u32 nfc_clk) > +{ > + u32 reg; > + u32 parent_rate = get_emi_slow_clk(); > + u32 div = parent_rate / nfc_clk; > + > + if (nfc_clk <= 0) > + return -1; > + if (div == 0) > + div++; > + if (parent_rate / div > NFC_CLK_MAX) > + div++; > + reg = __raw_readl(&mxc_ccm->cbcdr); > + reg &= ~MXC_CCM_CBCDR_NFC_PODF_MASK; > + reg |= (div - 1) << MXC_CCM_CBCDR_NFC_PODF_OFFSET; > + __raw_writel(reg, &mxc_ccm->cbcdr); > + while (__raw_readl(&mxc_ccm->cdhipr) != 0) > + ; > + return 0; > +} > + > +/* Config main_bus_clock for periphs */ > +static int config_periph_clk(u32 ref, u32 freq) > +{ > + int ret = 0; > + struct pll_param pll_param; > + > + memset(&pll_param, 0, sizeof(struct pll_param)); > + > + if (__raw_readl(&mxc_ccm->cbcdr) & MXC_CCM_CBCDR_PERIPH_CLK_SEL) { > + ret = calc_pll_params(ref, freq, &pll_param); > + if (ret != 0) { > + printf("Error:Can't find pll parameters: %d\n", > + ret); > + return ret; > + } > + switch ((__raw_readl(&mxc_ccm->cbcmr) & \ > + MXC_CCM_CBCMR_PERIPH_CLK_SEL_MASK) >> \ > + MXC_CCM_CBCMR_PERIPH_CLK_SEL_OFFSET) { > + case 0: > + return config_pll_clk(PLL1_CLOCK, &pll_param); > + break; > + case 1: > + return config_pll_clk(PLL3_CLOCK, &pll_param); > + break; > + default: > + return -1; > + } > + } else { > + u32 old_cbcmr = __raw_readl(&mxc_ccm->cbcmr); > + u32 new_cbcdr = calc_per_cbcdr_val(freq); > + u32 old_nfc = get_nfc_clk(); > + > + /* Switch peripheral to PLL3 */ > + __raw_writel(0x00015154, &mxc_ccm->cbcmr); > + __raw_writel(0x02888945, &mxc_ccm->cbcdr); Can we set constants for these ? > + > + /* Make sure change is effective */ > + while (__raw_readl(&mxc_ccm->cdhipr) != 0) > + ; > + > + /* Setup PLL2 */ > + ret = calc_pll_params(ref, freq, &pll_param); > + if (ret != 0) { > + printf("Error:Can't find pll parameters: %d\n", > + ret); > + return ret; > + } > + config_pll_clk(PLL2_CLOCK, &pll_param); > + > + /* Switch peripheral back */ > + __raw_writel(new_cbcdr, &mxc_ccm->cbcdr); > + __raw_writel(old_cbcmr, &mxc_ccm->cbcmr); > + > + /* Make sure change is effective */ > + while (__raw_readl(&mxc_ccm->cdhipr) != 0) > + ; > + /* restore to old NFC clock */ > + config_nfc_clk(old_nfc); > + } > + > + return 0; > +} > + > +static int config_ddr_clk(u32 emi_clk) > +{ > + u32 clk_src; > + s32 shift = 0, clk_sel, div = 1; > + u32 cbcmr = __raw_readl(&mxc_ccm->cbcmr); > + u32 cbcdr = __raw_readl(&mxc_ccm->cbcdr); > + > + if (emi_clk > MAX_DDR_CLK) { > + printf("Warning:DDR clock should not exceed %d MHz\n", > + MAX_DDR_CLK / SZ_DEC_1M); > + emi_clk = MAX_DDR_CLK; > + } > + > + clk_src = get_periph_clk(); > + /* Find DDR clock input */ > + clk_sel = (cbcmr >> 10) & 0x3; > + switch (clk_sel) { > + case 0: > + shift = 16; > + break; > + case 1: > + shift = 19; > + break; > + case 2: > + shift = 22; > + break; > + case 3: > + shift = 10; > + break; > + default: > + return -1; > + } > + > + if ((clk_src % emi_clk) < 10000000) > + div = clk_src / emi_clk; > + else > + div = (clk_src / emi_clk) + 1; > + if (div > 8) > + div = 8; > + > + cbcdr = cbcdr & ~(0x7 << shift); > + cbcdr |= ((div - 1) << shift); > + __raw_writel(cbcdr, &mxc_ccm->cbcdr); > + while (__raw_readl(&mxc_ccm->cdhipr) != 0) > + ; > + __raw_writel(0x0, &mxc_ccm->ccdr); > + > + return 0; > +} > + > +/*! Drop the "!" Best regards, Stefano Babic -- ===================================================================== DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: off...@denx.de ===================================================================== _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot