X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farmv7%2Fmx6%2Fclock.c;h=6a5117394e557ab8d0fc82d207bf5e719c672c5d;hp=b6983e60d7363fa03752ffd7b821b60e8dd917c1;hb=2e62c0bab7fe68ba2ab3baf6777537267a26ac02;hpb=5523cc2b8aa01c81c1df476e4e466d418510eae3 diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index b6983e60d7..6a5117394e 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -14,13 +14,107 @@ #include enum pll_clocks { - PLL_SYS, /* System PLL */ - PLL_BUS, /* System Bus PLL*/ - PLL_USBOTG, /* OTG USB PLL */ - PLL_ENET, /* ENET PLL */ + PLL_ARM, /* PLL1: ARM PLL */ + PLL_528, /* PLL2: System Bus PLL*/ + PLL_USBOTG, /* PLL3: OTG USB PLL */ + PLL_AUDIO, /* PLL4: Audio PLL */ + PLL_VIDEO, /* PLL5: Video PLL */ + PLL_ENET, /* PLL6: ENET PLL */ + PLL_USB2, /* PLL7: USB2 PLL */ + PLL_MLB, /* PLL8: MLB PLL */ }; -struct mxc_ccm_reg *imx_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; +struct mxc_ccm_reg *const imx_ccm = (void *)CCM_BASE_ADDR; +struct anatop_regs *const anatop = (void *)ANATOP_BASE_ADDR; + +int clk_enable(struct clk *clk) +{ + int ret = 0; + + if (!clk) + return 0; + if (clk->usecount == 0) { + debug("%s: Enabling %s clock\n", __func__, clk->name); + ret = clk->enable(clk); + if (ret) + return ret; + clk->usecount++; + } + assert(clk->usecount > 0); + return ret; +} + +void clk_disable(struct clk *clk) +{ + if (!clk) + return; + + assert(clk->usecount > 0); + if (!(--clk->usecount)) { + if (clk->disable) { + debug("%s: Disabling %s clock\n", __func__, clk->name); + clk->disable(clk); + } + } +} + +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; +} #ifdef CONFIG_MXC_OCOTP void enable_ocotp_clk(unsigned char enable) @@ -78,46 +172,40 @@ void enable_usboh3_clk(unsigned char enable) } -#if defined(CONFIG_FEC_MXC) && !defined(CONFIG_MX6SX) +#if defined(CONFIG_FEC_MXC) && !defined(CONFIG_SOC_MX6SX) void enable_enet_clk(unsigned char enable) { - u32 mask = MXC_CCM_CCGR1_ENET_CLK_ENABLE_MASK; + u32 mask, *addr; + + if (is_cpu_type(MXC_CPU_MX6UL)) { + mask = MXC_CCM_CCGR3_ENET_MASK; + addr = &imx_ccm->CCGR3; + } else { + mask = MXC_CCM_CCGR1_ENET_MASK; + addr = &imx_ccm->CCGR1; + } if (enable) - setbits_le32(&imx_ccm->CCGR1, mask); + setbits_le32(addr, mask); else - clrbits_le32(&imx_ccm->CCGR1, mask); + clrbits_le32(addr, mask); } #endif #ifdef CONFIG_MXC_UART void enable_uart_clk(unsigned char enable) -{ - u32 mask = MXC_CCM_CCGR5_UART_MASK | MXC_CCM_CCGR5_UART_SERIAL_MASK; - - if (enable) - setbits_le32(&imx_ccm->CCGR5, mask); - else - clrbits_le32(&imx_ccm->CCGR5, mask); -} -#endif - -#ifdef CONFIG_SPI -/* spi_num can be from 0 - 4 */ -int enable_cspi_clock(unsigned char enable, unsigned spi_num) { u32 mask; - if (spi_num > 4) - return -EINVAL; + if (is_cpu_type(MXC_CPU_MX6UL)) + mask = MXC_CCM_CCGR5_UART_MASK; + else + mask = MXC_CCM_CCGR5_UART_MASK | MXC_CCM_CCGR5_UART_SERIAL_MASK; - mask = MXC_CCM_CCGR_CG_MASK << (spi_num * 2); if (enable) - setbits_le32(&imx_ccm->CCGR1, mask); + setbits_le32(&imx_ccm->CCGR5, mask); else - clrbits_le32(&imx_ccm->CCGR1, mask); - - return 0; + clrbits_le32(&imx_ccm->CCGR5, mask); } #endif @@ -140,23 +228,40 @@ int enable_usdhc_clk(unsigned char enable, unsigned bus_num) #endif #ifdef CONFIG_SYS_I2C_MXC -/* i2c_num can be from 0 - 2 */ +/* i2c_num can be from 0 - 3 */ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) { u32 reg; u32 mask; + u32 *addr; - if (i2c_num > 2) + if (i2c_num > 3) return -EINVAL; - - mask = MXC_CCM_CCGR_CG_MASK - << (MXC_CCM_CCGR2_I2C1_SERIAL_OFFSET + (i2c_num << 1)); - reg = __raw_readl(&imx_ccm->CCGR2); - if (enable) - reg |= mask; - else - reg &= ~mask; - __raw_writel(reg, &imx_ccm->CCGR2); + if (i2c_num < 3) { + mask = MXC_CCM_CCGR_CG_MASK + << (MXC_CCM_CCGR2_I2C1_SERIAL_OFFSET + + (i2c_num << 1)); + reg = __raw_readl(&imx_ccm->CCGR2); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, &imx_ccm->CCGR2); + } else { + if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL)) { + mask = MXC_CCM_CCGR6_I2C4_MASK; + addr = &imx_ccm->CCGR6; + } else { + mask = MXC_CCM_CCGR1_I2C4_SERIAL_MASK; + addr = &imx_ccm->CCGR1; + } + reg = __raw_readl(addr); + if (enable) + reg |= mask; + else + reg &= ~mask; + __raw_writel(reg, addr); + } return 0; } #endif @@ -184,51 +289,88 @@ static u32 decode_pll(enum pll_clocks pll, u32 infreq) u32 div; switch (pll) { - case PLL_SYS: - div = __raw_readl(&imx_ccm->analog_pll_sys); - div &= BM_ANADIG_PLL_SYS_DIV_SELECT; - - return (infreq * div) >> 1; - case PLL_BUS: - div = __raw_readl(&imx_ccm->analog_pll_528); + case PLL_ARM: + div = __raw_readl(&anatop->pll_arm); + if (div & BM_ANADIG_PLL_ARM_BYPASS) + /* Assume the bypass clock is always derived from OSC */ + return infreq; + div &= BM_ANADIG_PLL_ARM_DIV_SELECT; + + return infreq * div / 2; + case PLL_528: + div = __raw_readl(&anatop->pll_528); + if (div & BM_ANADIG_PLL_528_BYPASS) + return infreq; div &= BM_ANADIG_PLL_528_DIV_SELECT; - return infreq * (20 + (div << 1)); + return infreq * (20 + div * 2); case PLL_USBOTG: - div = __raw_readl(&imx_ccm->analog_usb1_pll_480_ctrl); + div = __raw_readl(&anatop->usb1_pll_480_ctrl); + if (div & BM_ANADIG_USB1_PLL_480_CTRL_BYPASS) + return infreq; div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT; - return infreq * (20 + (div << 1)); + return infreq * (20 + div * 2); + case PLL_AUDIO: + div = __raw_readl(&anatop->pll_audio); + if (div & BM_ANADIG_PLL_AUDIO_BYPASS) + return infreq; + div &= BM_ANADIG_PLL_AUDIO_DIV_SELECT; + + return infreq * div; + case PLL_VIDEO: + div = __raw_readl(&anatop->pll_video); + if (div & BM_ANADIG_PLL_VIDEO_BYPASS) + return infreq; + div &= BM_ANADIG_PLL_VIDEO_DIV_SELECT; + + return infreq * div; case PLL_ENET: - div = __raw_readl(&imx_ccm->analog_pll_enet); + div = __raw_readl(&anatop->pll_enet); + if (div & BM_ANADIG_PLL_ENET_BYPASS) + return infreq; div &= BM_ANADIG_PLL_ENET_DIV_SELECT; return 25000000 * (div + (div >> 1) + 1); - default: + case PLL_USB2: + div = __raw_readl(&anatop->usb2_pll_480_ctrl); + if (div & BM_ANADIG_USB1_PLL_480_CTRL_BYPASS) + return infreq; + div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT; + + return infreq * (20 + div * 2); + case PLL_MLB: + div = __raw_readl(&anatop->pll_mlb); + if (div & BM_ANADIG_PLL_MLB_BYPASS) + return infreq; + /* unknown external clock provided on MLB_CLK pin */ return 0; } - /* NOTREACHED */ + return 0; } static u32 mxc_get_pll_pfd(enum pll_clocks pll, int pfd_num) { u32 div; u64 freq; + struct anatop_regs *anatop = (struct anatop_regs *)ANATOP_BASE_ADDR; switch (pll) { - case PLL_BUS: - if (pfd_num == 3) { - /* No PFD3 on PPL2 */ - return 0; + case PLL_528: + if (!is_cpu_type(MXC_CPU_MX6UL)) { + if (pfd_num == 3) { + /* No PFD3 on PPL2 */ + return 0; + } } - div = __raw_readl(&imx_ccm->analog_pfd_528); - freq = (u64)decode_pll(PLL_BUS, MXC_HCLK); + div = __raw_readl(&anatop->pfd_528); + freq = (u64)decode_pll(PLL_528, MXC_HCLK); break; case PLL_USBOTG: - div = __raw_readl(&imx_ccm->analog_pfd_480); + div = __raw_readl(&anatop->pfd_480); freq = (u64)decode_pll(PLL_USBOTG, MXC_HCLK); break; default: - /* No PFD on other PLL */ + /* No PFD on other PLL */ return 0; } @@ -243,17 +385,19 @@ static u32 get_mcu_main_clk(void) reg = __raw_readl(&imx_ccm->cacrr); reg &= MXC_CCM_CACRR_ARM_PODF_MASK; reg >>= MXC_CCM_CACRR_ARM_PODF_OFFSET; - freq = decode_pll(PLL_SYS, MXC_HCLK); + freq = decode_pll(PLL_ARM, MXC_HCLK); return freq / (reg + 1); } u32 get_periph_clk(void) { - u32 reg, freq = 0; + u32 reg, div = 0, freq = 0; reg = __raw_readl(&imx_ccm->cbcdr); if (reg & MXC_CCM_CBCDR_PERIPH_CLK_SEL) { + div = (reg & MXC_CCM_CBCDR_PERIPH_CLK2_PODF_MASK) >> + MXC_CCM_CBCDR_PERIPH_CLK2_PODF_OFFSET; reg = __raw_readl(&imx_ccm->cbcmr); reg &= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_MASK; reg >>= MXC_CCM_CBCMR_PERIPH_CLK2_SEL_OFFSET; @@ -266,8 +410,6 @@ u32 get_periph_clk(void) case 2: freq = MXC_HCLK; break; - default: - break; } } else { reg = __raw_readl(&imx_ccm->cbcmr); @@ -276,24 +418,22 @@ u32 get_periph_clk(void) switch (reg) { case 0: - freq = decode_pll(PLL_BUS, MXC_HCLK); + freq = decode_pll(PLL_528, MXC_HCLK); break; case 1: - freq = mxc_get_pll_pfd(PLL_BUS, 2); + freq = mxc_get_pll_pfd(PLL_528, 2); break; case 2: - freq = mxc_get_pll_pfd(PLL_BUS, 0); + freq = mxc_get_pll_pfd(PLL_528, 0); break; case 3: /* static / 2 divider */ - freq = mxc_get_pll_pfd(PLL_BUS, 2) / 2; - break; - default: + freq = mxc_get_pll_pfd(PLL_528, 2) / 2; break; } } - return freq; + return freq / (div + 1); } static u32 get_ipg_clk(void) @@ -312,10 +452,12 @@ static u32 get_ipg_per_clk(void) u32 reg, perclk_podf; reg = __raw_readl(&imx_ccm->cscmr1); -#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) - if (reg & MXC_CCM_CSCMR1_PER_CLK_SEL_MASK) - return MXC_HCLK; /* OSC 24Mhz */ -#endif + if (is_cpu_type(MXC_CPU_MX6SL) || is_cpu_type(MXC_CPU_MX6SX) || + is_mx6dqp() || is_cpu_type(MXC_CPU_MX6UL)) { + if (reg & MXC_CCM_CSCMR1_PER_CLK_SEL_MASK) + return MXC_HCLK; /* OSC 24Mhz */ + } + perclk_podf = reg & MXC_CCM_CSCMR1_PERCLK_PODF_MASK; return get_ipg_clk() / (perclk_podf + 1); @@ -326,10 +468,13 @@ static u32 get_uart_clk(void) u32 reg, uart_podf; u32 freq = decode_pll(PLL_USBOTG, MXC_HCLK) / 6; /* static divider */ reg = __raw_readl(&imx_ccm->cscdr1); -#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) - if (reg & MXC_CCM_CSCDR1_UART_CLK_SEL) - freq = MXC_HCLK; -#endif + + if (is_cpu_type(MXC_CPU_MX6SL) || is_cpu_type(MXC_CPU_MX6SX) || + is_mx6dqp() || is_cpu_type(MXC_CPU_MX6UL)) { + if (reg & MXC_CCM_CSCDR1_UART_CLK_SEL) + freq = MXC_HCLK; + } + reg &= MXC_CCM_CSCDR1_UART_CLK_PODF_MASK; uart_podf = reg >> MXC_CCM_CSCDR1_UART_CLK_PODF_OFFSET; @@ -341,8 +486,14 @@ static u32 get_cspi_clk(void) u32 reg, cspi_podf; reg = __raw_readl(&imx_ccm->cscdr2); - reg &= MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK; - cspi_podf = reg >> MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET; + cspi_podf = (reg & MXC_CCM_CSCDR2_ECSPI_CLK_PODF_MASK) >> + MXC_CCM_CSCDR2_ECSPI_CLK_PODF_OFFSET; + + if (is_mx6dqp() || is_cpu_type(MXC_CPU_MX6SL) || + is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL)) { + if (reg & MXC_CCM_CSCDR2_ECSPI_CLK_SEL_MASK) + return MXC_HCLK / (cspi_podf + 1); + } return decode_pll(PLL_USBOTG, MXC_HCLK) / (8 * (cspi_podf + 1)); } @@ -350,19 +501,19 @@ static u32 get_cspi_clk(void) static u32 get_axi_clk(void) { u32 root_freq, axi_podf; - u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); + u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); axi_podf = cbcdr & MXC_CCM_CBCDR_AXI_PODF_MASK; axi_podf >>= MXC_CCM_CBCDR_AXI_PODF_OFFSET; if (cbcdr & MXC_CCM_CBCDR_AXI_SEL) { if (cbcdr & MXC_CCM_CBCDR_AXI_ALT_SEL) - root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + root_freq = mxc_get_pll_pfd(PLL_528, 2); else root_freq = mxc_get_pll_pfd(PLL_USBOTG, 1); - } else + } else { root_freq = get_periph_clk(); - + } return root_freq / (axi_podf + 1); } @@ -384,53 +535,216 @@ static u32 get_emi_slow_clk(void) root_freq = decode_pll(PLL_USBOTG, MXC_HCLK); break; case 2: - root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + root_freq = mxc_get_pll_pfd(PLL_528, 2); break; case 3: - root_freq = mxc_get_pll_pfd(PLL_BUS, 0); + root_freq = mxc_get_pll_pfd(PLL_528, 0); break; } return root_freq / (emi_slow_podf + 1); } -#if (defined(CONFIG_MX6SL) || defined(CONFIG_MX6SX)) -static u32 get_mmdc_ch0_clk(void) +static u32 get_nfc_clk(void) { - u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); - u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); - u32 freq, podf; - - podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH1_PODF_MASK) \ - >> MXC_CCM_CBCDR_MMDC_CH1_PODF_OFFSET; - - switch ((cbcmr & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK) >> - MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET) { + u32 cs2cdr = __raw_readl(&imx_ccm->cs2cdr); + u32 podf = (cs2cdr & MXC_CCM_CS2CDR_ENFC_CLK_PODF_MASK) >> MXC_CCM_CS2CDR_ENFC_CLK_PODF_OFFSET; + u32 pred = (cs2cdr & MXC_CCM_CS2CDR_ENFC_CLK_PRED_MASK) >> MXC_CCM_CS2CDR_ENFC_CLK_PRED_OFFSET; + int nfc_clk_sel = (cs2cdr & MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK) >> + MXC_CCM_CS2CDR_ENFC_CLK_SEL_OFFSET; + u32 root_freq; + + switch (nfc_clk_sel) { case 0: - freq = decode_pll(PLL_BUS, MXC_HCLK); + root_freq = mxc_get_pll_pfd(PLL_528, 0); break; case 1: - freq = mxc_get_pll_pfd(PLL_BUS, 2); + root_freq = decode_pll(PLL_528, MXC_HCLK); break; case 2: - freq = mxc_get_pll_pfd(PLL_BUS, 0); + root_freq = decode_pll(PLL_USBOTG, MXC_HCLK); break; case 3: - /* static / 2 divider */ - freq = mxc_get_pll_pfd(PLL_BUS, 2) / 2; + root_freq = mxc_get_pll_pfd(PLL_528, 2); + break; + default: + return 0; + } + + return root_freq / (pred + 1) / (podf + 1); +} + +#define CS2CDR_ENFC_MASK (MXC_CCM_CS2CDR_ENFC_CLK_PODF_MASK | \ + MXC_CCM_CS2CDR_ENFC_CLK_PRED_MASK | \ + MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK) + +static int set_nfc_clk(u32 ref, u32 freq_khz) +{ + u32 cs2cdr = __raw_readl(&imx_ccm->cs2cdr); + u32 podf; + u32 pred; + int nfc_clk_sel; + u32 root_freq; + u32 min_err = ~0; + u32 nfc_val = ~0; + u32 freq = freq_khz * 1000; + + for (nfc_clk_sel = 0; nfc_clk_sel < 4; nfc_clk_sel++) { + u32 act_freq; + u32 err; + + if (ref < 4 && ref != nfc_clk_sel) + continue; + + switch (nfc_clk_sel) { + case 0: + root_freq = mxc_get_pll_pfd(PLL_528, 0); + break; + case 1: + root_freq = decode_pll(PLL_528, MXC_HCLK); + break; + case 2: + root_freq = decode_pll(PLL_USBOTG, MXC_HCLK); + break; + case 3: + root_freq = mxc_get_pll_pfd(PLL_528, 2); + break; + } + if (root_freq < freq) + continue; + + podf = min(DIV_ROUND_UP(root_freq, freq), 1U << 6); + pred = min(DIV_ROUND_UP(root_freq / podf, freq), 8U); + act_freq = root_freq / pred / podf; + err = (freq - act_freq) * 100 / freq; + debug("root=%d[%u] freq=%u pred=%u podf=%u act=%u err=%d\n", + nfc_clk_sel, root_freq, freq, pred, podf, act_freq, err); + if (act_freq > freq) + continue; + if (err < min_err) { + nfc_val = (podf - 1) << MXC_CCM_CS2CDR_ENFC_CLK_PODF_OFFSET; + nfc_val |= (pred - 1) << MXC_CCM_CS2CDR_ENFC_CLK_PRED_OFFSET; + nfc_val |= nfc_clk_sel << MXC_CCM_CS2CDR_ENFC_CLK_SEL_OFFSET; + min_err = err; + if (err == 0) + break; + } } - return freq / (podf + 1); + if (nfc_val == ~0 || min_err > 10) + return -EINVAL; + if ((cs2cdr & CS2CDR_ENFC_MASK) != nfc_val) { + debug("changing cs2cdr from %08x to %08x\n", cs2cdr, + (cs2cdr & ~CS2CDR_ENFC_MASK) | nfc_val); + __raw_writel((cs2cdr & ~CS2CDR_ENFC_MASK) | nfc_val, + &imx_ccm->cs2cdr); + } else { + debug("Leaving cs2cdr unchanged [%08x]\n", cs2cdr); + } + return 0; } -#else + static u32 get_mmdc_ch0_clk(void) { + u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); u32 cbcdr = __raw_readl(&imx_ccm->cbcdr); - u32 mmdc_ch0_podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK) >> - MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET; - return get_periph_clk() / (mmdc_ch0_podf + 1); + u32 freq, podf, per2_clk2_podf; + + if (is_cpu_type(MXC_CPU_MX6SX) || is_cpu_type(MXC_CPU_MX6UL) || + is_cpu_type(MXC_CPU_MX6SL)) { + podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH1_PODF_MASK) >> + MXC_CCM_CBCDR_MMDC_CH1_PODF_OFFSET; + if (cbcdr & MXC_CCM_CBCDR_PERIPH2_CLK_SEL) { + per2_clk2_podf = (cbcdr & MXC_CCM_CBCDR_PERIPH2_CLK2_PODF_MASK) >> + MXC_CCM_CBCDR_PERIPH2_CLK2_PODF_OFFSET; + if (is_cpu_type(MXC_CPU_MX6SL)) { + if (cbcmr & MXC_CCM_CBCMR_PERIPH2_CLK2_SEL) + freq = MXC_HCLK; + else + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + } else { + if (cbcmr & MXC_CCM_CBCMR_PERIPH2_CLK2_SEL) + freq = decode_pll(PLL_528, MXC_HCLK); + else + freq = decode_pll(PLL_USBOTG, MXC_HCLK); + } + } else { + per2_clk2_podf = 0; + switch ((cbcmr & + MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK) >> + MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET) { + case 0: + freq = decode_pll(PLL_528, MXC_HCLK); + break; + case 1: + freq = mxc_get_pll_pfd(PLL_528, 2); + break; + case 2: + freq = mxc_get_pll_pfd(PLL_528, 0); + break; + case 3: + /* static / 2 divider */ + freq = mxc_get_pll_pfd(PLL_528, 2) / 2; + break; + } + } + return freq / (podf + 1) / (per2_clk2_podf + 1); + } else { + podf = (cbcdr & MXC_CCM_CBCDR_MMDC_CH0_PODF_MASK) >> + MXC_CCM_CBCDR_MMDC_CH0_PODF_OFFSET; + return get_periph_clk() / (podf + 1); + } +} + +#ifdef CONFIG_FSL_QSPI +/* qspi_num can be from 0 - 1 */ +void enable_qspi_clk(int qspi_num) +{ + u32 reg = 0; + /* Enable QuadSPI clock */ + switch (qspi_num) { + case 0: + /* disable the clock gate */ + clrbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_QSPI1_MASK); + + /* set 50M : (50 = 396 / 2 / 4) */ + reg = readl(&imx_ccm->cscmr1); + reg &= ~(MXC_CCM_CSCMR1_QSPI1_PODF_MASK | + MXC_CCM_CSCMR1_QSPI1_CLK_SEL_MASK); + reg |= ((1 << MXC_CCM_CSCMR1_QSPI1_PODF_OFFSET) | + (2 << MXC_CCM_CSCMR1_QSPI1_CLK_SEL_OFFSET)); + writel(reg, &imx_ccm->cscmr1); + + /* enable the clock gate */ + setbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_QSPI1_MASK); + break; + case 1: + /* + * disable the clock gate + * QSPI2 and GPMI_BCH_INPUT_GPMI_IO share the same clock gate, + * disable both of them. + */ + clrbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK); + + /* set 50M : (50 = 396 / 2 / 4) */ + reg = readl(&imx_ccm->cs2cdr); + reg &= ~(MXC_CCM_CS2CDR_QSPI2_CLK_PODF_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_PRED_MASK | + MXC_CCM_CS2CDR_QSPI2_CLK_SEL_MASK); + reg |= (MXC_CCM_CS2CDR_QSPI2_CLK_PRED(0x1) | + MXC_CCM_CS2CDR_QSPI2_CLK_SEL(0x3)); + writel(reg, &imx_ccm->cs2cdr); + + /*enable the clock gate*/ + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_QSPI2_ENFC_MASK | + MXC_CCM_CCGR4_RAWNAND_U_GPMI_BCH_INPUT_GPMI_IO_MASK); + break; + default: + break; + } } #endif @@ -467,7 +781,7 @@ int enable_fec_anatop_clock(enum enet_freq freq) reg &= ~BM_ANADIG_PLL_ENET_BYPASS; writel(reg, &anatop->pll_enet); -#ifdef CONFIG_MX6SX +#ifdef CONFIG_SOC_MX6SX /* * Set enet ahb clock to 200MHz * pll2_pfd2_396m-> ENET_PODF-> ENET_AHB @@ -528,9 +842,9 @@ static u32 get_usdhc_clk(u32 port) } if (clk_sel) - root_freq = mxc_get_pll_pfd(PLL_BUS, 0); + root_freq = mxc_get_pll_pfd(PLL_528, 0); else - root_freq = mxc_get_pll_pfd(PLL_BUS, 2); + root_freq = mxc_get_pll_pfd(PLL_528, 2); return root_freq / (usdhc_podf + 1); } @@ -545,32 +859,32 @@ u32 imx_get_fecclk(void) return mxc_get_clock(MXC_IPG_CLK); } +#if defined(CONFIG_CMD_SATA) || defined(CONFIG_PCIE_IMX) static int enable_enet_pll(uint32_t en) { - struct mxc_ccm_reg *const imx_ccm - = (struct mxc_ccm_reg *) CCM_BASE_ADDR; + u32 reg; s32 timeout = 100000; - u32 reg = 0; /* Enable PLLs */ - reg = readl(&imx_ccm->analog_pll_enet); - reg &= ~BM_ANADIG_PLL_SYS_POWERDOWN; - writel(reg, &imx_ccm->analog_pll_enet); - reg |= BM_ANADIG_PLL_SYS_ENABLE; + reg = readl(&anatop->pll_enet); + reg &= ~BM_ANADIG_PLL_ENET_POWERDOWN; + writel(reg, &anatop->pll_enet); + reg |= BM_ANADIG_PLL_ENET_ENABLE; while (timeout--) { - if (readl(&imx_ccm->analog_pll_enet) & BM_ANADIG_PLL_SYS_LOCK) + if (readl(&anatop->pll_enet) & BM_ANADIG_PLL_ENET_LOCK) break; } if (timeout <= 0) return -EIO; - reg &= ~BM_ANADIG_PLL_SYS_BYPASS; - writel(reg, &imx_ccm->analog_pll_enet); + reg &= ~BM_ANADIG_PLL_ENET_BYPASS; + writel(reg, &anatop->pll_enet); reg |= en; - writel(reg, &imx_ccm->analog_pll_enet); + writel(reg, &anatop->pll_enet); return 0; } +#endif -#ifndef CONFIG_MX6SX +#ifdef CONFIG_CMD_SATA static void ungate_sata_clock(void) { struct mxc_ccm_reg *const imx_ccm = @@ -579,18 +893,7 @@ static void ungate_sata_clock(void) /* Enable SATA clock. */ setbits_le32(&imx_ccm->CCGR5, MXC_CCM_CCGR5_SATA_MASK); } -#endif -static void ungate_pcie_clock(void) -{ - struct mxc_ccm_reg *const imx_ccm = - (struct mxc_ccm_reg *)CCM_BASE_ADDR; - - /* Enable PCIe clock. */ - setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_PCIE_MASK); -} - -#ifndef CONFIG_MX6SX int enable_sata_clock(void) { ungate_sata_clock(); @@ -606,6 +909,16 @@ void disable_sata_clock(void) } #endif +#ifdef CONFIG_PCIE_IMX +static void ungate_pcie_clock(void) +{ + struct mxc_ccm_reg *const imx_ccm = + (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + /* Enable PCIe clock. */ + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_PCIE_MASK); +} + int enable_pcie_clock(void) { struct anatop_regs *anatop_regs = @@ -645,7 +958,7 @@ int enable_pcie_clock(void) clrbits_le32(&ccm_regs->cbcmr, MXC_CCM_CBCMR_PCIE_AXI_CLK_SEL); /* Party time! Ungate the clock to the PCIe. */ -#ifndef CONFIG_MX6SX +#ifdef CONFIG_CMD_SATA ungate_sata_clock(); #endif ungate_pcie_clock(); @@ -653,6 +966,7 @@ int enable_pcie_clock(void) return enable_enet_pll(BM_ANADIG_PLL_ENET_ENABLE_SATA | BM_ANADIG_PLL_ENET_ENABLE_PCIE); } +#endif #ifdef CONFIG_SECURE_BOOT void hab_caam_clock_enable(unsigned char enable) @@ -711,6 +1025,84 @@ void enable_thermal_clk(void) enable_pll3(); } +void ipu_clk_enable(void) +{ + u32 reg = readl(&imx_ccm->CCGR3); + reg |= MXC_CCM_CCGR3_IPU1_IPU_MASK; + writel(reg, &imx_ccm->CCGR3); +} + +void ipu_clk_disable(void) +{ + u32 reg = readl(&imx_ccm->CCGR3); + reg &= ~MXC_CCM_CCGR3_IPU1_IPU_MASK; + writel(reg, &imx_ccm->CCGR3); +} + +void ipu_di_clk_enable(int di) +{ + switch (di) { + case 0: + setbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_IPU1_IPU_DI0_MASK); + break; + case 1: + setbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_IPU1_IPU_DI1_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(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_IPU1_IPU_DI0_MASK); + break; + case 1: + clrbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_IPU1_IPU_DI1_MASK); + break; + default: + printf("%s: Invalid DI index %d\n", __func__, di); + } +} + +void ldb_clk_enable(int ldb) +{ + switch (ldb) { + case 0: + setbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_LDB_DI0_MASK); + break; + case 1: + setbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_LDB_DI1_MASK); + break; + default: + printf("%s: Invalid LDB index %d\n", __func__, ldb); + } +} + +void ldb_clk_disable(int ldb) +{ + switch (ldb) { + case 0: + clrbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_LDB_DI0_MASK); + break; + case 1: + clrbits_le32(&imx_ccm->CCGR3, + MXC_CCM_CCGR3_LDB_DI1_MASK); + break; + default: + printf("%s: Invalid LDB index %d\n", __func__, ldb); + } +} + unsigned int mxc_get_clock(enum mxc_clock clk) { switch (clk) { @@ -745,49 +1137,278 @@ unsigned int mxc_get_clock(enum mxc_clock clk) return get_usdhc_clk(3); case MXC_SATA_CLK: return get_ahb_clk(); + case MXC_NFC_CLK: + return get_nfc_clk(); default: printf("Unsupported MXC CLK: %d\n", clk); - break; } return 0; } +static inline 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; +} + +/* Config CPU clock */ +static int set_arm_clk(u32 ref, u32 freq_khz) +{ + int d; + int div = 0; + int mul = 0; + u32 min_err = ~0; + u32 reg; + + if (freq_khz > ref / 1000 * 108 / 2 || freq_khz < ref / 1000 * 54 / 8 / 2) { + printf("Frequency %u.%03uMHz is out of range: %u.%03u..%u.%03u\n", + freq_khz / 1000, freq_khz % 1000, + 54 * ref / 1000000 / 8 / 2, 54 * ref / 1000 / 8 / 2 % 1000, + 108 * ref / 1000000 / 2, 108 * ref / 1000 / 2 % 1000); + return -EINVAL; + } + + for (d = DIV_ROUND_UP(648000, freq_khz); d <= 8; d++) { + int m = freq_khz * 2 * d / (ref / 1000); + u32 f; + u32 err; + + if (m > 108) { + debug("%s@%d: d=%d m=%d\n", __func__, __LINE__, + d, m); + break; + } + + f = ref * m / d / 2; + if (f > freq_khz * 1000) { + debug("%s@%d: d=%d m=%d f=%u freq=%u\n", __func__, __LINE__, + d, m, f, freq_khz); + if (--m < 54) + return -EINVAL; + f = ref * m / d / 2; + } + err = freq_khz * 1000 - f; + debug("%s@%d: d=%d m=%d f=%u freq=%u err=%d\n", __func__, __LINE__, + d, m, f, freq_khz, err); + if (err < min_err) { + mul = m; + div = d; + min_err = err; + if (err == 0) + break; + } + } + if (min_err == ~0) + return -EINVAL; + debug("Setting M=%3u D=%2u for %u.%03uMHz (actual: %u.%03uMHz)\n", + mul, div, freq_khz / 1000, freq_khz % 1000, + ref * mul / 2 / div / 1000000, ref * mul / 2 / div / 1000 % 1000); + + reg = readl(&anatop->pll_arm); + debug("anadig_pll_arm=%08x -> %08x\n", + reg, (reg & ~0x7f) | mul); + + reg |= 1 << 16; + writel(reg, &anatop->pll_arm); /* bypass PLL */ + + reg = (reg & ~0x7f) | mul; + writel(reg, &anatop->pll_arm); + + writel(div - 1, &imx_ccm->cacrr); + + reg &= ~(1 << 16); + writel(reg, &anatop->pll_arm); /* disable PLL bypass */ + + return 0; +} + /* - * Dump some core clockes. + * 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 do_mx6_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - u32 freq; - freq = decode_pll(PLL_SYS, MXC_HCLK); - printf("PLL_SYS %8d MHz\n", freq / 1000000); - freq = decode_pll(PLL_BUS, MXC_HCLK); - printf("PLL_BUS %8d MHz\n", freq / 1000000); - freq = decode_pll(PLL_USBOTG, MXC_HCLK); - printf("PLL_OTG %8d MHz\n", freq / 1000000); - freq = decode_pll(PLL_ENET, MXC_HCLK); - printf("PLL_NET %8d MHz\n", freq / 1000000); +int mxc_set_clock(u32 ref, u32 freq, enum mxc_clock clk) +{ + int ret; + + freq *= 1000; + + switch (clk) { + case MXC_ARM_CLK: + ret = set_arm_clk(ref, freq); + break; + + case MXC_NFC_CLK: + ret = set_nfc_clk(ref, freq); + break; + + default: + printf("Warning: Unsupported or invalid clock type: %d\n", + clk); + return -EINVAL; + } + return ret; +} + +/* + * Dump some core clocks. + */ +#define print_pll(pll) { \ + u32 __pll = decode_pll(pll, MXC_HCLK); \ + printf("%-12s %4d.%03d MHz\n", #pll, \ + __pll / 1000000, __pll / 1000 % 1000); \ + } + +#define MXC_IPG_PER_CLK MXC_IPG_PERCLK + +#define print_clk(clk) { \ + u32 __clk = mxc_get_clock(MXC_##clk##_CLK); \ + printf("%-12s %4d.%03d MHz\n", #clk, \ + __clk / 1000000, __clk / 1000 % 1000); \ + } + +#define print_pfd(pll, pfd) { \ + u32 __pfd = readl(&anatop->pfd_##pll); \ + if (__pfd & (0x80 << 8 * pfd)) { \ + printf("PFD_%s[%d] OFF\n", #pll, pfd); \ + } else { \ + __pfd = (__pfd >> 8 * pfd) & 0x3f; \ + printf("PFD_%s[%d] %4d.%03d MHz\n", #pll, pfd, \ + pll * 18 / __pfd, \ + pll * 18 * 1000 / __pfd % 1000); \ + } \ +} + +static void do_mx6_showclocks(void) +{ + print_pll(PLL_ARM); + print_pll(PLL_528); + print_pll(PLL_USBOTG); + print_pll(PLL_AUDIO); + print_pll(PLL_VIDEO); + print_pll(PLL_ENET); + print_pll(PLL_USB2); printf("\n"); - printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); - printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); -#ifdef CONFIG_MXC_SPI - printf("CSPI %8d kHz\n", mxc_get_clock(MXC_CSPI_CLK) / 1000); -#endif - printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); - printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); - printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); - printf("USDHC1 %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); - printf("USDHC2 %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); - printf("USDHC3 %8d kHz\n", mxc_get_clock(MXC_ESDHC3_CLK) / 1000); - printf("USDHC4 %8d kHz\n", mxc_get_clock(MXC_ESDHC4_CLK) / 1000); - printf("EMI SLOW %8d kHz\n", mxc_get_clock(MXC_EMI_SLOW_CLK) / 1000); - printf("IPG PERCLK %8d kHz\n", mxc_get_clock(MXC_IPG_PERCLK) / 1000); - return 0; + print_pfd(480, 0); + print_pfd(480, 1); + print_pfd(480, 2); + print_pfd(480, 3); + print_pfd(528, 0); + print_pfd(528, 1); + print_pfd(528, 2); + printf("\n"); + + print_clk(IPG); + print_clk(UART); + print_clk(CSPI); + print_clk(AHB); + print_clk(AXI); + print_clk(DDR); + print_clk(ESDHC); + print_clk(ESDHC2); + print_clk(ESDHC3); + print_clk(ESDHC4); + print_clk(EMI_SLOW); + print_clk(NFC); + print_clk(IPG_PER); + print_clk(ARM); +} + +static struct clk_lookup { + const char *name; + unsigned int index; +} mx6_clk_lookup[] = { + { "arm", MXC_ARM_CLK, }, + { "nfc", MXC_NFC_CLK, }, +}; + +int do_clocks(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) +{ + int i; + unsigned long freq; + unsigned long ref = ~0UL; + + if (argc < 2) { + do_mx6_showclocks(); + return CMD_RET_SUCCESS; + } else if (argc == 2 || argc > 4) { + return CMD_RET_USAGE; + } + + freq = simple_strtoul(argv[2], NULL, 0); + if (freq == 0) { + printf("Invalid clock frequency %lu\n", freq); + return CMD_RET_FAILURE; + } + if (argc > 3) { + ref = simple_strtoul(argv[3], NULL, 0); + } + for (i = 0; i < ARRAY_SIZE(mx6_clk_lookup); i++) { + if (strcasecmp(argv[1], mx6_clk_lookup[i].name) == 0) { + switch (mx6_clk_lookup[i].index) { + case MXC_ARM_CLK: + if (argc > 3) + return CMD_RET_USAGE; + ref = MXC_HCLK; + break; + + case MXC_NFC_CLK: + if (argc > 3 && ref > 3) { + printf("Invalid clock selector value: %lu\n", ref); + return CMD_RET_FAILURE; + } + break; + } + printf("Setting %s clock to %lu MHz\n", + mx6_clk_lookup[i].name, freq); + if (mxc_set_clock(ref, freq, mx6_clk_lookup[i].index)) + break; + freq = mxc_get_clock(mx6_clk_lookup[i].index); + printf("%s clock set to %lu.%03lu MHz\n", + mx6_clk_lookup[i].name, + freq / 1000000, freq / 1000 % 1000); + return CMD_RET_SUCCESS; + } + } + if (i == ARRAY_SIZE(mx6_clk_lookup)) { + printf("clock %s not found; supported clocks are:\n", argv[1]); + for (i = 0; i < ARRAY_SIZE(mx6_clk_lookup); i++) { + printf("\t%s\n", mx6_clk_lookup[i].name); + } + } else { + printf("Failed to set clock %s to %s MHz\n", + argv[1], argv[2]); + } + return CMD_RET_FAILURE; } -#ifndef CONFIG_MX6SX +#ifndef CONFIG_SOC_MX6SX void enable_ipu_clock(void) { struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; @@ -795,12 +1416,18 @@ void enable_ipu_clock(void) reg = readl(&mxc_ccm->CCGR3); reg |= MXC_CCM_CCGR3_IPU1_IPU_MASK; writel(reg, &mxc_ccm->CCGR3); + + if (is_mx6dqp()) { + setbits_le32(&mxc_ccm->CCGR6, MXC_CCM_CCGR6_PRG_CLK0_MASK); + setbits_le32(&mxc_ccm->CCGR3, MXC_CCM_CCGR3_IPU2_IPU_MASK); + } } #endif /***************************************************/ U_BOOT_CMD( - clocks, CONFIG_SYS_MAXARGS, 1, do_mx6_showclocks, - "display clocks", - "" + clocks, 4, 0, do_clocks, + "display/set clocks", + " - display clock settings\n" + "clocks - set clock to MHz" );