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:
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)
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)
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);
}
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) {
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;
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)
}
}
- 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);
}