arm: mxs: Add LCDIF clock configuration function
authorMarek Vasut <marex@denx.de>
Sun, 28 Apr 2013 09:20:01 +0000 (09:20 +0000)
committerStefano Babic <sbabic@denx.de>
Mon, 6 May 2013 15:40:22 +0000 (17:40 +0200)
This function turns on the LCDIF clock and configures it's frequency. The
dividers settings are calculated within the function and the current
implementation should be fast and accurate.

Signed-off-by: Marek Vasut <marex@denx.de>
Cc: Fabio Estevam <fabio.estevam@freescale.com>
Cc: Otavio Salvador <otavio@ossystems.com.br>
Cc: Stefano Babic <sbabic@denx.de>
arch/arm/cpu/arm926ejs/mxs/clock.c
arch/arm/include/asm/arch-mxs/clock.h

index 43e7663..f94107f 100644 (file)
@@ -325,6 +325,99 @@ void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq)
                bus, tgtclk, freq);
 }
 
+void mxs_set_lcdclk(uint32_t freq)
+{
+       struct mxs_clkctrl_regs *clkctrl_regs =
+               (struct mxs_clkctrl_regs *)MXS_CLKCTRL_BASE;
+       uint32_t fp, x, k_rest, k_best, x_best, tk;
+       int32_t k_best_l = 999, k_best_t = 0, x_best_l = 0xff, x_best_t = 0xff;
+
+       if (freq == 0)
+               return;
+
+#if defined(CONFIG_MX23)
+       writel(CLKCTRL_CLKSEQ_BYPASS_PIX, &clkctrl_regs->hw_clkctrl_clkseq_clr);
+#elif defined(CONFIG_MX28)
+       writel(CLKCTRL_CLKSEQ_BYPASS_DIS_LCDIF, &clkctrl_regs->hw_clkctrl_clkseq_clr);
+#endif
+
+       /*
+        *             /               18 \     1       1
+        * freq kHz = | 480000000 Hz * --  | * --- * ------
+        *             \                x /     k     1000
+        *
+        *      480000000 Hz   18
+        *      ------------ * --
+        *        freq kHz      x
+        * k = -------------------
+        *             1000
+        */
+
+       fp = ((PLL_FREQ_KHZ * 1000) / freq) * 18;
+
+       for (x = 18; x <= 35; x++) {
+               tk = fp / x;
+               if ((tk / 1000 == 0) || (tk / 1000 > 255))
+                       continue;
+
+               k_rest = tk % 1000;
+
+               if (k_rest < (k_best_l % 1000)) {
+                       k_best_l = tk;
+                       x_best_l = x;
+               }
+
+               if (k_rest > (k_best_t % 1000)) {
+                       k_best_t = tk;
+                       x_best_t = x;
+               }
+       }
+
+       if (1000 - (k_best_t % 1000) > (k_best_l % 1000)) {
+               k_best = k_best_l;
+               x_best = x_best_l;
+       } else {
+               k_best = k_best_t;
+               x_best = x_best_t;
+       }
+
+       k_best /= 1000;
+
+#if defined(CONFIG_MX23)
+       writeb(CLKCTRL_FRAC_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_frac0_set[CLKCTRL_FRAC0_PIX]);
+       writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
+               &clkctrl_regs->hw_clkctrl_frac0[CLKCTRL_FRAC0_PIX]);
+       writeb(CLKCTRL_FRAC_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_frac0_clr[CLKCTRL_FRAC0_PIX]);
+
+       writel(CLKCTRL_PIX_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_pix_set);
+       clrsetbits_le32(&clkctrl_regs->hw_clkctrl_pix,
+                       CLKCTRL_PIX_DIV_MASK | CLKCTRL_PIX_CLKGATE,
+                       k_best << CLKCTRL_PIX_DIV_OFFSET);
+
+       while (readl(&clkctrl_regs->hw_clkctrl_pix) & CLKCTRL_PIX_BUSY)
+               ;
+#elif defined(CONFIG_MX28)
+       writeb(CLKCTRL_FRAC_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_frac1_set[CLKCTRL_FRAC1_PIX]);
+       writeb(CLKCTRL_FRAC_CLKGATE | (x_best & CLKCTRL_FRAC_FRAC_MASK),
+               &clkctrl_regs->hw_clkctrl_frac1[CLKCTRL_FRAC1_PIX]);
+       writeb(CLKCTRL_FRAC_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_frac1_clr[CLKCTRL_FRAC1_PIX]);
+
+       writel(CLKCTRL_DIS_LCDIF_CLKGATE,
+               &clkctrl_regs->hw_clkctrl_lcdif_set);
+       clrsetbits_le32(&clkctrl_regs->hw_clkctrl_lcdif,
+                       CLKCTRL_DIS_LCDIF_DIV_MASK | CLKCTRL_DIS_LCDIF_CLKGATE,
+                       k_best << CLKCTRL_DIS_LCDIF_DIV_OFFSET);
+
+       while (readl(&clkctrl_regs->hw_clkctrl_lcdif) & CLKCTRL_DIS_LCDIF_BUSY)
+               ;
+#endif
+}
+
 uint32_t mxc_get_clock(enum mxc_clock clk)
 {
        switch (clk) {
index 3f7d3f0..9be53f0 100644 (file)
@@ -59,6 +59,7 @@ uint32_t mxc_get_clock(enum mxc_clock clk);
 void mxs_set_ioclk(enum mxs_ioclock io, uint32_t freq);
 void mxs_set_sspclk(enum mxs_sspclock ssp, uint32_t freq, int xtal);
 void mxs_set_ssp_busclock(unsigned int bus, uint32_t freq);
+void mxs_set_lcdclk(uint32_t freq);
 
 /* Compatibility with the FEC Ethernet driver */
 #define        imx_get_fecclk()        mxc_get_clock(MXC_AHB_CLK)