diff options
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.patch | 574 |
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 + |