
On 16/03/2012 21:21, Fabio Estevam wrote:
From: Jason Liu jason.hui@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.hui@linaro.org Signed-off-by: Eric Miao eric.miao@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();
case MXC_FEC_CLK: return decode_pll(mxc_plls[PLL1_CLOCK], CONFIG_SYS_MX5_HCLK);return get_cspi_clk();
- case MXC_DDR_CLK:
default: break; }return get_ddr_clk();
@@ -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