aboutsummaryrefslogtreecommitdiffstats
path: root/recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch
diff options
context:
space:
mode:
Diffstat (limited to 'recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch')
-rw-r--r--recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch574
1 files changed, 574 insertions, 0 deletions
diff --git a/recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch b/recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch
new file mode 100644
index 0000000..a995502
--- /dev/null
+++ b/recipes-bsp/u-boot/u-boot-v2012.04.01/0017-mx5-Add-clock-config-interface.patch
@@ -0,0 +1,574 @@
+From a835391f8dddc9ca51c880e5328979577cb0685d Mon Sep 17 00:00:00 2001
+From: Fabio Estevam <festevam@gmail.com>
+Date: Mon, 30 Apr 2012 08:12:02 +0000
+Subject: [PATCH 17/56] mx5: Add clock config interface
+
+mx5: Add clock config interface
+
+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>
+Signed-off-by: Fabio Estevam <fabio.estevam@freescale.com>
+---
+ arch/arm/cpu/armv7/mx5/clock.c | 448 +++++++++++++++++++++++++++++-
+ arch/arm/include/asm/arch-mx5/clock.h | 5 +-
+ arch/arm/include/asm/arch-mx5/crm_regs.h | 6 +
+ 3 files changed, 454 insertions(+), 5 deletions(-)
+
+diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c
+index 903e207..fc2406b 100644
+--- a/arch/arm/cpu/armv7/mx5/clock.c
++++ b/arch/arm/cpu/armv7/mx5/clock.c
+@@ -49,6 +49,42 @@ 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
++
++#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[] = {
++ {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)
+@@ -291,7 +327,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)
+ {
+ u32 ret_val = 0;
+ u32 ccsr = __raw_readl(&mxc_ccm->ccsr);
+@@ -307,7 +343,7 @@ u32 get_lp_apm(void)
+ /*
+ * get cspi clock rate.
+ */
+-u32 imx_get_cspiclk(void)
++static u32 imx_get_cspiclk(void)
+ {
+ u32 ret_val = 0, pdf, pre_pdf, clk_sel;
+ u32 cscmr1 = __raw_readl(&mxc_ccm->cscmr1);
+@@ -344,8 +380,77 @@ u32 imx_get_cspiclk(void)
+ return ret_val;
+ }
+
++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_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.
++ * The API of get mxc clocks.
+ */
+ unsigned int mxc_get_clock(enum mxc_clock clk)
+ {
+@@ -367,10 +472,12 @@ unsigned int mxc_get_clock(enum mxc_clock clk)
+ CONFIG_SYS_MX5_HCLK);
+ case MXC_SATA_CLK:
+ return get_ahb_clk();
++ case MXC_DDR_CLK:
++ return get_ddr_clk();
+ default:
+ break;
+ }
+- return -1;
++ return -EINVAL;
+ }
+
+ u32 imx_get_uartclk(void)
+@@ -384,6 +491,338 @@ u32 imx_get_fecclk(void)
+ return mxc_get_clock(MXC_IPG_CLK);
+ }
+
++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.
++ */
++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;
++
++ /*
++ * 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",
++ PLL_FREQ_MIN(ref) / SZ_DEC_1M,
++ PLL_FREQ_MAX(ref) / SZ_DEC_1M);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < ARRAY_SIZE(fixed_mfd); i++) {
++ if (fixed_mfd[i].ref_clk_hz == ref) {
++ mfd = fixed_mfd[i].mfd;
++ break;
++ }
++ }
++
++ 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));
++ mfi = t1;
++ if (mfi > PLL_MFI_MAX)
++ return -EINVAL;
++ else if (mfi < 5)
++ continue;
++ break;
++ }
++ /*
++ * Now got pd and mfi already
++ *
++ * mfn = (((n_target * pd) / 4 - n_ref * mfi) * mfd) / n_ref;
++ */
++ t1 = n_target * pd;
++ do_div(t1, 4);
++ t1 -= n_ref * mfi;
++ t1 *= mfd;
++ do_div(t1, n_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)
++ 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;
++
++ return 0;
++}
++
++#define calc_div(tgt_clk, src_clk, limit) ({ \
++ u32 v = 0; \
++ if (((src_clk) % (tgt_clk)) <= 100) \
++ v = (src_clk) / (tgt_clk); \
++ else \
++ v = ((src_clk) / (tgt_clk)) + 1;\
++ if (v > limit) \
++ v = limit; \
++ (v - 1); \
++ })
++
++#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 -EINVAL;
++ }
++
++ 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 -EINVAL;
++ 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 -EINVAL;
++ }
++ }
++
++ 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 -EINVAL;
++ }
++
++ 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;
++}
++
++/*
++ * This function assumes the expected core clock has to be changed by
++ * modifying the PLL. This is NOT true always but for most of the times,
++ * it is. So it assumes the PLL output freq is the same as the expected
++ * core clock (presc=1) unless the core clock is less than PLL_FREQ_MIN.
++ * In the latter case, it will try to increase the presc value until
++ * (presc*core_clk) is greater than PLL_FREQ_MIN. It then makes call to
++ * calc_pll_params() and obtains the values of PD, MFI,MFN, MFD based
++ * on the targeted PLL and reference input clock to the PLL. Lastly,
++ * it sets the register based on these values along with the dividers.
++ * Note 1) There is no value checking for the passed-in divider values
++ * so the caller has to make sure those values are sensible.
++ * 2) Also adjust the NFC divider such that the NFC clock doesn't
++ * exceed NFC_CLK_MAX.
++ * 3) IPU HSP clock is independent of AHB clock. Even it can go up to
++ * 177MHz for higher voltage, this function fixes the max to 133MHz.
++ * 4) This function should not have allowed diag_printf() calls since
++ * the serial driver has been stoped. But leave then here to allow
++ * easy debugging by NOT calling the cyg_hal_plf_serial_stop().
++ */
++int mxc_set_clock(u32 ref, u32 freq, enum mxc_clock clk)
++{
++ freq *= SZ_DEC_1M;
++
++ switch (clk) {
++ case MXC_ARM_CLK:
++ if (config_core_clk(ref, freq))
++ return -EINVAL;
++ break;
++ case MXC_PERIPH_CLK:
++ if (config_periph_clk(ref, freq))
++ return -EINVAL;
++ break;
++ case MXC_DDR_CLK:
++ if (config_ddr_clk(freq))
++ return -EINVAL;
++ break;
++ case MXC_NFC_CLK:
++ if (config_nfc_clk(freq))
++ return -EINVAL;
++ break;
++ default:
++ printf("Warning:Unsupported or invalid clock type\n");
++ }
++
++ return 0;
++}
++
+ #ifdef CONFIG_MX53
+ /*
+ * The clock for the external interface can be set to use internal clock
+@@ -430,6 +869,7 @@ int do_mx5_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+ 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);
+
+ return 0;
+ }
+diff --git a/arch/arm/include/asm/arch-mx5/clock.h b/arch/arm/include/asm/arch-mx5/clock.h
+index e822809..35ee815 100644
+--- a/arch/arm/include/asm/arch-mx5/clock.h
++++ b/arch/arm/include/asm/arch-mx5/clock.h
+@@ -33,6 +33,9 @@ enum mxc_clock {
+ MXC_CSPI_CLK,
+ MXC_FEC_CLK,
+ MXC_SATA_CLK,
++ MXC_DDR_CLK,
++ MXC_NFC_CLK,
++ MXC_PERIPH_CLK,
+ };
+
+ unsigned int imx_decode_pll(unsigned int pll, unsigned int f_ref);
+@@ -40,7 +43,7 @@ unsigned int imx_decode_pll(unsigned int pll, unsigned int f_ref);
+ u32 imx_get_uartclk(void);
+ u32 imx_get_fecclk(void);
+ unsigned int mxc_get_clock(enum mxc_clock clk);
+-
++int mxc_set_clock(u32 ref, u32 freq, u32 clk_type);
+ void set_usb_phy2_clk(void);
+ void enable_usb_phy2_clk(unsigned char enable);
+ void set_usboh3_clk(void);
+diff --git a/arch/arm/include/asm/arch-mx5/crm_regs.h b/arch/arm/include/asm/arch-mx5/crm_regs.h
+index bdeafbc..4e0fc1b 100644
+--- a/arch/arm/include/asm/arch-mx5/crm_regs.h
++++ b/arch/arm/include/asm/arch-mx5/crm_regs.h
+@@ -76,6 +76,9 @@ struct mxc_ccm_reg {
+ u32 CCGR4;
+ u32 CCGR5;
+ u32 CCGR6; /* 0x0080 */
++#ifdef CONFIG_MX53
++ u32 CCGR7; /* 0x0084 */
++#endif
+ u32 cmeor;
+ };
+
+@@ -84,6 +87,9 @@ struct mxc_ccm_reg {
+ #define MXC_CCM_CACRR_ARM_PODF_MASK 0x7
+
+ /* Define the bits in register CBCDR */
++#define MXC_CCM_CBCDR_DDR_HIFREQ_SEL (0x1 << 30)
++#define MXC_CCM_CBCDR_DDR_PODF_MASK (0x7 << 27)
++#define MXC_CCM_CBCDR_DDR_PODF_OFFSET 27
+ #define MXC_CCM_CBCDR_EMI_CLK_SEL (0x1 << 26)
+ #define MXC_CCM_CBCDR_PERIPH_CLK_SEL (0x1 << 25)
+ #define MXC_CCM_CBCDR_EMI_PODF_OFFSET 22
+--
+1.7.10
+