]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - arch/arm/cpu/armv7/mx6/clock.c
mx6: clock: use setup_gpmi_io_clk() to change nfc clk divider for CONFIG_NAND_MXS
[karo-tx-uboot.git] / arch / arm / cpu / armv7 / mx6 / clock.c
index 2f5af3e25b4458493cb41f12c6cec5669536c229..c3c9c5e6512ba13ebacd96181056e915db9e14da 100644 (file)
@@ -306,6 +306,8 @@ int enable_spi_clk(unsigned char enable, unsigned spi_num)
 static u32 decode_pll(enum pll_clocks pll, u32 infreq)
 {
        u32 div, post_div;
+       u32 pll_num, pll_denom;
+       u64 freq;
 
        switch (pll) {
        case PLL_ARM:
@@ -332,24 +334,46 @@ static u32 decode_pll(enum pll_clocks pll, u32 infreq)
                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 lldiv((u64)infreq * div, post_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 lldiv((u64)infreq * div, post_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)
@@ -368,10 +392,11 @@ static u32 decode_pll(enum pll_clocks pll, u32 infreq)
                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)
@@ -570,36 +595,38 @@ static u32 get_emi_slow_clk(void)
        return root_freq / (emi_slow_podf + 1);
 }
 
-static u32 get_nfc_clk(void)
+static inline unsigned long get_nfc_root_clk(int nfc_clk_sel)
 {
-       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:
-               root_freq = mxc_get_pll_pfd(PLL_528, 0);
+               return mxc_get_pll_pfd(PLL_528, 0);
                break;
        case 1:
-               root_freq = decode_pll(PLL_528, MXC_HCLK);
+               return decode_pll(PLL_528, MXC_HCLK);
                break;
        case 2:
-               root_freq = decode_pll(PLL_USBOTG, MXC_HCLK);
+               return decode_pll(PLL_USBOTG, MXC_HCLK);
                break;
        case 3:
-               root_freq = mxc_get_pll_pfd(PLL_528, 2);
+               return mxc_get_pll_pfd(PLL_528, 2);
                break;
        case 4:
-               root_freq = mxc_get_pll_pfd(PLL_USBOTG, 3);
-               break;
+               return mxc_get_pll_pfd(PLL_USBOTG, 3);
        default:
                return 0;
        }
+}
+
+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;
+       int nfc_clk_sel = (cs2cdr & MXC_CCM_CS2CDR_ENFC_CLK_SEL_MASK) >>
+               MXC_CCM_CS2CDR_ENFC_CLK_SEL_OFFSET;
+       u32 root_freq = get_nfc_root_clk(nfc_clk_sel);
 
        return root_freq / (pred + 1) / (podf + 1);
 }
@@ -618,12 +645,13 @@ static int set_nfc_clk(u32 ref, u32 freq_khz)
        u32 min_err = ~0;
        u32 nfc_val = ~0;
        u32 freq = freq_khz * 1000;
+       int num_sel = is_mx6dqp() || is_cpu_type(MXC_CPU_MX6UL) ? 5 : 4;
 
-       for (nfc_clk_sel = 0; nfc_clk_sel < 4; nfc_clk_sel++) {
+       for (nfc_clk_sel = 0; nfc_clk_sel < num_sel; nfc_clk_sel++) {
                u32 act_freq;
                u32 err;
 
-               if (ref < 4 && ref != nfc_clk_sel)
+               if (ref < num_sel && ref != nfc_clk_sel)
                        continue;
 
                switch (nfc_clk_sel) {
@@ -639,6 +667,9 @@ static int set_nfc_clk(u32 ref, u32 freq_khz)
                case 3:
                        root_freq = mxc_get_pll_pfd(PLL_528, 2);
                        break;
+               case 4:
+                       root_freq = mxc_get_pll_pfd(PLL_USBOTG, 3);
+                       break;
                }
                if (root_freq < freq)
                        continue;
@@ -646,7 +677,7 @@ static int set_nfc_clk(u32 ref, u32 freq_khz)
                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;
+               err = (freq - act_freq) / (freq / 1000);
                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)
@@ -661,14 +692,18 @@ static int set_nfc_clk(u32 ref, u32 freq_khz)
                }
        }
 
-       if (nfc_val == ~0 || min_err > 10)
+       if (nfc_val == ~0 || min_err > 100)
                return -EINVAL;
 
        if ((cs2cdr & CS2CDR_ENFC_MASK) != nfc_val) {
                debug("changing cs2cdr from %08x to %08x\n", cs2cdr,
                        (cs2cdr & ~CS2CDR_ENFC_MASK) | nfc_val);
+#ifdef CONFIG_NAND_MXS
+               setup_gpmi_io_clk(nfc_val);
+#else
                __raw_writel((cs2cdr & ~CS2CDR_ENFC_MASK) | nfc_val,
                        &imx_ccm->cs2cdr);
+#endif
        } else {
                debug("Leaving cs2cdr unchanged [%08x]\n", cs2cdr);
        }