X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=arch%2Farm%2Fcpu%2Farmv7%2Fmx6%2Fclock.c;h=d3860fcf2970a28a2d934ee742570a58a458ab66;hp=8e2086eed45e3bbe75ac968edd9b2b563a489506;hb=9d6b51ff1a11b47879e44a83a14c7b49c1893852;hpb=c625e5acc39ab28b471c8e8a537119e646934fbf diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index 8e2086eed4..d3860fcf29 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -116,6 +116,24 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return 0; } +#define PLL_LOCK_BIT (1 << 31) + +static inline int wait_pll_lock(u32 *reg) +{ + int loops = 0; + u32 val; + + while (!((val = readl(reg)) & PLL_LOCK_BIT)) { + loops++; + if (loops > 1000) + break; + udelay(1); + } + if (!(val & PLL_LOCK_BIT) && !(readl(reg) & PLL_LOCK_BIT)) + return -ETIMEDOUT; + return 0; +} + #ifdef CONFIG_MXC_OCOTP void enable_ocotp_clk(unsigned char enable) { @@ -175,43 +193,37 @@ void enable_usboh3_clk(unsigned char enable) #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 @@ -239,6 +251,7 @@ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) { u32 reg; u32 mask; + u32 *addr; if (i2c_num > 3) return -EINVAL; @@ -253,14 +266,19 @@ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) reg &= ~mask; __raw_writel(reg, &imx_ccm->CCGR2); } else { - mask = MXC_CCM_CCGR_CG_MASK - << (MXC_CCM_CCGR1_I2C4_SERIAL_OFFSET); - reg = __raw_readl(&imx_ccm->CCGR1); + 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, &imx_ccm->CCGR1); + __raw_writel(reg, addr); } return 0; } @@ -284,9 +302,12 @@ int enable_spi_clk(unsigned char enable, unsigned spi_num) __raw_writel(reg, &imx_ccm->CCGR1); return 0; } + static u32 decode_pll(enum pll_clocks pll, u32 infreq) { - u32 div; + u32 div, post_div; + u32 pll_num, pll_denom; + u64 freq; switch (pll) { case PLL_ARM: @@ -306,25 +327,53 @@ static u32 decode_pll(enum pll_clocks pll, u32 infreq) return infreq * (20 + div * 2); case PLL_USBOTG: div = __raw_readl(&anatop->usb1_pll_480_ctrl); - if (div & BM_ANADIG_USB_PLL_480_CTRL_BYPASS) + if (div & BM_ANADIG_USB1_PLL_480_CTRL_BYPASS) return infreq; - div &= BM_ANADIG_USB_PLL_480_CTRL_DIV_SELECT; + div &= BM_ANADIG_USB1_PLL_480_CTRL_DIV_SELECT; return infreq * (20 + div * 2); case PLL_AUDIO: div = __raw_readl(&anatop->pll_audio); + /* BM_ANADIG_PLL_AUDIO_BYPASS_CLK_SRC is ignored */ if (div & BM_ANADIG_PLL_AUDIO_BYPASS) return infreq; + + pll_num = __raw_readl(&anatop->pll_audio_num); + pll_denom = __raw_readl(&anatop->pll_audio_denom); + + post_div = (div & BM_ANADIG_PLL_AUDIO_POST_DIV_SELECT) >> + BP_ANADIG_PLL_AUDIO_POST_DIV_SELECT; + if (post_div == 3) { + printf("Invalid post divider value for PLL_AUDIO\n"); + return 0; + } + post_div = 1 << (2 - post_div); div &= BM_ANADIG_PLL_AUDIO_DIV_SELECT; - return infreq * div; + freq = (u64)infreq * pll_num / pll_denom; + freq += infreq * div; + return lldiv(freq, post_div); case PLL_VIDEO: div = __raw_readl(&anatop->pll_video); + /* BM_ANADIG_PLL_AUDIO_BYPASS_CLK_SRC is ignored */ if (div & BM_ANADIG_PLL_VIDEO_BYPASS) return infreq; + + pll_num = __raw_readl(&anatop->pll_video_num); + pll_denom = __raw_readl(&anatop->pll_video_denom); + + post_div = (div & BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT) >> + BP_ANADIG_PLL_VIDEO_POST_DIV_SELECT; + if (post_div == 3) { + printf("Invalid post divider value for PLL_VIDEO\n"); + return 0; + } + post_div = 1 << (2 - post_div); div &= BM_ANADIG_PLL_VIDEO_DIV_SELECT; - return infreq * div; + freq = (u64)infreq * pll_num / pll_denom; + freq += infreq * div; + return lldiv(freq, post_div); case PLL_ENET: div = __raw_readl(&anatop->pll_enet); if (div & BM_ANADIG_PLL_ENET_BYPASS) @@ -334,31 +383,34 @@ static u32 decode_pll(enum pll_clocks pll, u32 infreq) return 25000000 * (div + (div >> 1) + 1); case PLL_USB2: div = __raw_readl(&anatop->usb2_pll_480_ctrl); - if (div & BM_ANADIG_USB_PLL_480_CTRL_BYPASS) + if (div & BM_ANADIG_USB1_PLL_480_CTRL_BYPASS) return infreq; - div &= BM_ANADIG_USB_PLL_480_CTRL_DIV_SELECT; + 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 */ + /* fallthru: unknown external clock provided on MLB_CLK pin */ + default: return 0; } - return 0; + /* NOTREACHED */ } + 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_528: - if (pfd_num == 3) { - /* No PFD3 on PLL2 */ - return 0; + if (!is_cpu_type(MXC_CPU_MX6UL)) { + if (pfd_num == 3) { + /* No PFD3 on PPL2 */ + return 0; + } } div = __raw_readl(&anatop->pfd_528); freq = (u64)decode_pll(PLL_528, MXC_HCLK); @@ -390,10 +442,12 @@ static u32 get_mcu_main_clk(void) 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; @@ -429,7 +483,7 @@ u32 get_periph_clk(void) } } - return freq; + return freq / (div + 1); } static u32 get_ipg_clk(void) @@ -448,10 +502,12 @@ static u32 get_ipg_per_clk(void) u32 reg, perclk_podf; reg = __raw_readl(&imx_ccm->cscmr1); -#if (defined(CONFIG_SOC_MX6SL) || defined(CONFIG_SOC_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); @@ -462,10 +518,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_SOC_MX6SL) || defined(CONFIG_SOC_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; @@ -477,8 +536,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)); } @@ -486,7 +551,7 @@ 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; @@ -496,9 +561,9 @@ static u32 get_axi_clk(void) 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); } @@ -533,8 +598,10 @@ static u32 get_emi_slow_clk(void) static u32 get_nfc_clk(void) { 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; + 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; @@ -552,6 +619,11 @@ static u32 get_nfc_clk(void) case 3: root_freq = mxc_get_pll_pfd(PLL_528, 2); break; + case 4: + root_freq = mxc_get_pll_pfd(PLL_USBOTG, 3); + break; + default: + return 0; } return root_freq / (pred + 1) / (podf + 1); @@ -628,47 +700,60 @@ static int set_nfc_clk(u32 ref, u32 freq_khz) return 0; } -#if (defined(CONFIG_SOC_MX6SL) || defined(CONFIG_SOC_MX6SX)) static u32 get_mmdc_ch0_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) { - 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; + 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); } - - return freq / (podf + 1); - } -#else -static u32 get_mmdc_ch0_clk(void) -{ - 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); -} -#endif - -#ifdef CONFIG_SOC_MX6SX +#ifdef CONFIG_FSL_QSPI /* qspi_num can be from 0 - 1 */ void enable_qspi_clk(int qspi_num) { @@ -724,9 +809,6 @@ int enable_fec_anatop_clock(enum enet_freq freq) u32 reg = 0; s32 timeout = 100000; - struct anatop_regs __iomem *anatop = - (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; - if (freq < ENET_25MHZ || freq > ENET_125MHZ) return -EINVAL; @@ -829,6 +911,7 @@ 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) { u32 reg; @@ -851,28 +934,15 @@ static int enable_enet_pll(uint32_t en) writel(reg, &anatop->pll_enet); return 0; } +#endif -#ifndef CONFIG_SOC_MX6SX +#ifdef CONFIG_CMD_SATA static void ungate_sata_clock(void) { - struct mxc_ccm_reg *const imx_ccm = - (struct mxc_ccm_reg *)CCM_BASE_ADDR; - /* 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_SOC_MX6SX int enable_sata_clock(void) { ungate_sata_clock(); @@ -881,18 +951,19 @@ int enable_sata_clock(void) void disable_sata_clock(void) { - struct mxc_ccm_reg *const imx_ccm = - (struct mxc_ccm_reg *)CCM_BASE_ADDR; - clrbits_le32(&imx_ccm->CCGR5, MXC_CCM_CCGR5_SATA_MASK); } #endif +#ifdef CONFIG_PCIE_IMX +static void ungate_pcie_clock(void) +{ + /* Enable PCIe clock. */ + setbits_le32(&imx_ccm->CCGR4, MXC_CCM_CCGR4_PCIE_MASK); +} + int enable_pcie_clock(void) { - struct anatop_regs *anatop_regs = - (struct anatop_regs *)ANATOP_BASE_ADDR; - struct mxc_ccm_reg *ccm_regs = (struct mxc_ccm_reg *)CCM_BASE_ADDR; u32 lvds1_clk_sel; /* @@ -927,7 +998,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_SOC_MX6SX +#ifdef CONFIG_CMD_SATA ungate_sata_clock(); #endif ungate_pcie_clock(); @@ -935,6 +1006,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) @@ -965,25 +1037,22 @@ void hab_caam_clock_enable(unsigned char enable) static void enable_pll3(void) { - struct anatop_regs __iomem *anatop = - (struct anatop_regs __iomem *)ANATOP_BASE_ADDR; - /* make sure pll3 is enabled */ if ((readl(&anatop->usb1_pll_480_ctrl) & - BM_ANADIG_USB_PLL_480_CTRL_LOCK) == 0) { + BM_ANADIG_USB1_PLL_480_CTRL_LOCK) == 0) { /* enable pll's power */ - writel(BM_ANADIG_USB_PLL_480_CTRL_POWER, + writel(BM_ANADIG_USB1_PLL_480_CTRL_POWER, &anatop->usb1_pll_480_ctrl_set); writel(0x80, &anatop->ana_misc2_clr); /* wait for pll lock */ while ((readl(&anatop->usb1_pll_480_ctrl) & - BM_ANADIG_USB_PLL_480_CTRL_LOCK) == 0) + BM_ANADIG_USB1_PLL_480_CTRL_LOCK) == 0) ; /* disable bypass */ - writel(BM_ANADIG_USB_PLL_480_CTRL_BYPASS, + writel(BM_ANADIG_USB1_PLL_480_CTRL_BYPASS, &anatop->usb1_pll_480_ctrl_clr); /* enable pll output */ - writel(BM_ANADIG_USB_PLL_480_CTRL_ENABLE, + writel(BM_ANADIG_USB1_PLL_480_CTRL_ENABLE, &anatop->usb1_pll_480_ctrl_set); } } @@ -1071,20 +1140,278 @@ void ldb_clk_disable(int ldb) } } -void ocotp_clk_enable(void) +#ifdef CONFIG_VIDEO_MXS +void lcdif_clk_enable(void) +{ + setbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_LCDIF_MASK); + setbits_le32(&imx_ccm->CCGR2, MXC_CCM_CCGR2_LCD_MASK); +} + +void lcdif_clk_disable(void) +{ + clrbits_le32(&imx_ccm->CCGR2, MXC_CCM_CCGR2_LCD_MASK); + clrbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_LCDIF_MASK); +} + +#define CBCMR_LCDIF_MASK MXC_CCM_CBCMR_LCDIF_PODF_MASK +#define CSCDR2_LCDIF_MASK (MXC_CCM_CSCDR2_LCDIF_PRED_MASK | \ + MXC_CCM_CSCDR2_LCDIF_CLK_SEL_MASK) + +static u32 get_lcdif_root_clk(u32 cscdr2) +{ + int lcdif_pre_clk_sel = (cscdr2 & MXC_CCM_CSCDR2_LCDIF_PRE_CLK_SEL_MASK) >> + MXC_CCM_CSCDR2_LCDIF_PRE_CLK_SEL_OFFSET; + int lcdif_clk_sel = (cscdr2 & MXC_CCM_CSCDR2_LCDIF_CLK_SEL_MASK) >> + MXC_CCM_CSCDR2_LCDIF_CLK_SEL_OFFSET; + u32 root_freq; + + switch (lcdif_clk_sel) { + case 0: + switch (lcdif_pre_clk_sel) { + case 0: + root_freq = decode_pll(PLL_528, MXC_HCLK); + break; + case 1: + root_freq = mxc_get_pll_pfd(PLL_USBOTG, 3); + break; + case 2: + root_freq = decode_pll(PLL_VIDEO, MXC_HCLK); + break; + case 3: + root_freq = mxc_get_pll_pfd(PLL_528, 0); + break; + case 4: + root_freq = mxc_get_pll_pfd(PLL_528, 1); + break; + case 5: + root_freq = mxc_get_pll_pfd(PLL_USBOTG, 1); + break; + default: + return 0; + } + break; + case 1: + root_freq = mxc_get_pll_pfd(PLL_VIDEO, 0); + break; + case 2: + root_freq = decode_pll(PLL_USBOTG, MXC_HCLK); + break; + case 3: + root_freq = mxc_get_pll_pfd(PLL_528, 2); + break; + default: + return 0; + } + + return root_freq; +} + +static int set_lcdif_pll(u32 ref, u32 freq_khz, + unsigned post_div) { - u32 reg = readl(&imx_ccm->CCGR2); - reg |= MXC_CCM_CCGR2_OCOTP_CTRL_MASK; - writel(reg, &imx_ccm->CCGR2); + int ret; + u64 freq = freq_khz * 1000; + u32 post_div_mask = 1 << (2 - post_div); + int mul = 1; + u32 min_err = ~0; + u32 reg; + int num = 0; + int denom = 1; + const int min_div = 27; + const int max_div = 54; + const int div_mask = 0x7f; + const u32 max_freq = ref * max_div / post_div; + const u32 min_freq = ref * min_div / post_div; + + if (freq > max_freq || freq < min_freq) { + printf("Frequency %u.%03uMHz is out of range: %u.%03u..%u.%03uMHz\n", + freq_khz / 1000, freq_khz % 1000, + min_freq / 1000000, min_freq / 1000 % 1000, + max_freq / 1000000, max_freq / 1000 % 1000); + return -EINVAL; + } + { + int d = post_div; + int m = lldiv(freq * d + ref - 1, ref); + u32 err; + u32 f; + + debug("%s@%d: d=%d m=%d max_div=%u min_div=%u\n", __func__, __LINE__, + d, m, max_div, min_div); + if (m > max_div || m < min_div) + return -EINVAL; + + f = ref * m / d; + if (f > freq) { + debug("%s@%d: d=%d m=%d f=%u freq=%llu\n", __func__, __LINE__, + d, m, f, freq); + return -EINVAL; + } + err = freq - f; + debug("%s@%d: d=%d m=%d f=%u freq=%llu err=%d\n", __func__, __LINE__, + d, m, f, freq, err); + if (err < min_err) { + mul = m; + min_err = err; + } + } + if (min_err == ~0) { + printf("Cannot set VIDEO PLL to %u.%03uMHz\n", + freq_khz / 1000, freq_khz % 1000); + return -EINVAL; + } + + debug("Setting M=%3u D=%u N=%d DE=%u for %u.%03uMHz (actual: %u.%03uMHz)\n", + mul, post_div, num, denom, + freq_khz / post_div / 1000, freq_khz / post_div % 1000, + ref * mul / post_div / 1000000, + ref * mul / post_div / 1000 % 1000); + + reg = readl(&anatop->pll_video); + setbits_le32(&anatop->pll_video, BM_ANADIG_PLL_VIDEO_BYPASS); + + reg = (reg & ~(div_mask | + BM_ANADIG_PLL_VIDEO_POST_DIV_SELECT)) | + mul | (post_div_mask << BP_ANADIG_PLL_VIDEO_POST_DIV_SELECT); + writel(reg, &anatop->pll_video); + + ret = wait_pll_lock(&anatop->pll_video); + if (ret) { + printf("Video PLL failed to lock\n"); + return ret; + } + + clrbits_le32(&anatop->pll_video, BM_ANADIG_PLL_VIDEO_BYPASS); + return 0; } -void ocotp_clk_disable(void) +static int set_lcdif_clk(u32 ref, u32 freq_khz) { - u32 reg = readl(&imx_ccm->CCGR2); - reg &= ~MXC_CCM_CCGR2_OCOTP_CTRL_MASK; - writel(reg, &imx_ccm->CCGR2); + u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); + u32 cscdr2 = __raw_readl(&imx_ccm->cscdr2); + u32 cbcmr_val; + u32 cscdr2_val; + u32 freq = freq_khz * 1000; + u32 act_freq; + u32 err; + u32 min_div = 27; + u32 max_div = 54; + u32 min_pll_khz = ref * min_div / 4 / 1000; + u32 max_pll_khz = ref * max_div / 1000; + u32 pll_khz; + u32 post_div = 0; + u32 m; + u32 min_err = ~0; + u32 best_m = 0; + u32 best_pred = 1; + u32 best_podf = 1; + u32 div; + unsigned pd; + + if (freq_khz > max_pll_khz) + return -EINVAL; + + for (pd = 1; min_err && pd <= 4; pd <<= 1) { + for (m = max(min_div, DIV_ROUND_UP(648000 / pd, freq_khz * 64)); + m <= max_div; m++) { + u32 err; + int pred = 0; + int podf = 0; + u32 root_freq = ref * m / pd; + + div = DIV_ROUND_UP(root_freq, freq); + + while (pred * podf == 0 && div <= 64) { + int p1, p2; + + for (p1 = 1; p1 <= 8; p1++) { + for (p2 = 1; p2 <= 8; p2++) { + if (p1 * p2 == div) { + podf = p1; + pred = p2; + break; + } + } + } + if (pred * podf == 0) { + div++; + } + } + if (pred * podf == 0) + continue; + + /* relative error in per mille */ + act_freq = root_freq / div; + err = abs(act_freq - freq) / freq_khz; + + if (err < min_err) { + best_m = m; + best_pred = pred; + best_podf = podf; + post_div = pd; + min_err = err; + if (err <= 10) + break; + } + } + } + if (min_err > 50) + return -EINVAL; + + pll_khz = ref / 1000 * best_m; + if (pll_khz > max_pll_khz) + return -EINVAL; + + if (pll_khz < min_pll_khz) + return -EINVAL; + + err = set_lcdif_pll(ref, pll_khz / post_div, post_div); + if (err) + return err; + + cbcmr_val = (best_podf - 1) << MXC_CCM_CBCMR_LCDIF_PODF_OFFSET; + cscdr2_val = (best_pred - 1) << MXC_CCM_CSCDR2_LCDIF_PRED_OFFSET; + + if ((cbcmr & CBCMR_LCDIF_MASK) != cbcmr_val) { + debug("changing cbcmr from %08x to %08x\n", cbcmr, + (cbcmr & ~CBCMR_LCDIF_MASK) | cbcmr_val); + clrsetbits_le32(&imx_ccm->cbcmr, + CBCMR_LCDIF_MASK, + cbcmr_val); + } else { + debug("Leaving cbcmr unchanged [%08x]\n", cbcmr); + } + if ((cscdr2 & CSCDR2_LCDIF_MASK) != cscdr2_val) { + debug("changing cscdr2 from %08x to %08x\n", cscdr2, + (cscdr2 & ~CSCDR2_LCDIF_MASK) | cscdr2_val); + clrsetbits_le32(&imx_ccm->cscdr2, + CSCDR2_LCDIF_MASK, + cscdr2_val); + } else { + debug("Leaving cscdr2 unchanged [%08x]\n", cscdr2); + } + return 0; +} + +void mxs_set_lcdclk(u32 khz) +{ + set_lcdif_clk(CONFIG_SYS_MX6_HCLK, khz); } +static u32 get_lcdif_clk(void) +{ + u32 cbcmr = __raw_readl(&imx_ccm->cbcmr); + u32 podf = ((cbcmr & MXC_CCM_CBCMR_LCDIF_PODF_MASK) >> + MXC_CCM_CBCMR_LCDIF_PODF_OFFSET) + 1; + u32 cscdr2 = __raw_readl(&imx_ccm->cscdr2); + u32 pred = ((cscdr2 & MXC_CCM_CSCDR2_LCDIF_PRED_MASK) >> + MXC_CCM_CSCDR2_LCDIF_PRED_OFFSET) + 1; + u32 root_freq = get_lcdif_root_clk(cscdr2); + + return root_freq / pred / podf; +} +#endif + unsigned int mxc_get_clock(enum mxc_clock clk) { switch (clk) { @@ -1121,6 +1448,10 @@ unsigned int mxc_get_clock(enum mxc_clock clk) return get_ahb_clk(); case MXC_NFC_CLK: return get_nfc_clk(); +#ifdef CONFIG_VIDEO_MXS + case MXC_LCDIF_CLK: + return get_lcdif_clk(); +#endif default: printf("Unsupported MXC CLK: %d\n", clk); } @@ -1128,34 +1459,26 @@ unsigned int mxc_get_clock(enum mxc_clock clk) 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 ret; int d; int div = 0; int mul = 0; u32 min_err = ~0; u32 reg; + const int min_div = 54; + const int max_div = 108; + const int div_mask = 0x7f; + const u32 max_freq = ref * max_div / 2; + const u32 min_freq = ref * min_div / 8 / 2; - if (freq_khz > ref / 1000 * 108 / 2 || freq_khz < ref / 1000 * 54 / 8 / 2) { + if (freq_khz > max_freq / 1000 || freq_khz < min_freq / 1000) { 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); + min_freq / 1000000, min_freq / 1000 % 1000, + max_freq / 1000000, max_freq / 1000 % 1000); return -EINVAL; } @@ -1164,7 +1487,7 @@ static int set_arm_clk(u32 ref, u32 freq_khz) u32 f; u32 err; - if (m > 108) { + if (m > max_div) { debug("%s@%d: d=%d m=%d\n", __func__, __LINE__, d, m); break; @@ -1174,7 +1497,7 @@ static int set_arm_clk(u32 ref, u32 freq_khz) 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) + if (--m < min_div) return -EINVAL; f = ref * m / d / 2; } @@ -1196,19 +1519,20 @@ static int set_arm_clk(u32 ref, u32 freq_khz) 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 */ + setbits_le32(&anatop->pll_video, BM_ANADIG_PLL_ARM_BYPASS); - reg = (reg & ~0x7f) | mul; + reg = (reg & ~div_mask) | mul; writel(reg, &anatop->pll_arm); writel(div - 1, &imx_ccm->cacrr); - reg &= ~(1 << 16); - writel(reg, &anatop->pll_arm); /* disable PLL bypass */ + ret = wait_pll_lock(&anatop->pll_video); + if (ret) { + printf("ARM PLL failed to lock\n"); + return ret; + } + + clrbits_le32(&anatop->pll_video, BM_ANADIG_PLL_ARM_BYPASS); return 0; } @@ -1227,11 +1551,6 @@ static int set_arm_clk(u32 ref, u32 freq_khz) * 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) { @@ -1320,6 +1639,9 @@ static void do_mx6_showclocks(void) print_clk(NFC); print_clk(IPG_PER); print_clk(ARM); +#ifdef CONFIG_VIDEO_MXS + print_clk(LCDIF); +#endif } static struct clk_lookup { @@ -1393,11 +1715,14 @@ int do_clocks(cmd_tbl_t *cmdtp, int flag, int argc, char *const argv[]) #ifndef CONFIG_SOC_MX6SX void enable_ipu_clock(void) { - struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; - int reg; - reg = readl(&mxc_ccm->CCGR3); + int reg = readl(&imx_ccm->CCGR3); reg |= MXC_CCM_CCGR3_IPU1_IPU_MASK; - writel(reg, &mxc_ccm->CCGR3); + writel(reg, &imx_ccm->CCGR3); + + if (is_mx6dqp()) { + setbits_le32(&imx_ccm->CCGR6, MXC_CCM_CCGR6_PRG_CLK0_MASK); + setbits_le32(&imx_ccm->CCGR3, MXC_CCM_CCGR3_IPU2_IPU_MASK); + } } #endif /***************************************************/