2 #include <linux/kernel.h>
3 #include <linux/init.h>
6 #include <mach/clock.h>
7 #include <mach/hardware.h>
8 #include <mach/common.h>
10 #include <asm/clkdev.h>
12 #include <asm/div64.h>
16 #define CRM_SMALL_DIVIDER(base, name) \
17 crm_small_divider(base, \
18 base ## _ ## name ## _OFFSET, \
19 base ## _ ## name ## _MASK)
20 #define CRM_1DIVIDER(base, name) \
22 base ## _ ## name ## _OFFSET, \
23 base ## _ ## name ## _MASK, 1)
24 #define CRM_16DIVIDER(base, name) \
26 base ## _ ## name ## _OFFSET, \
27 base ## _ ## name ## _MASK, 16)
29 static u32 crm_small_divider(void __iomem *reg, u8 offset, u32 mask)
31 static const u32 crm_small_dividers[] = {
32 2, 3, 4, 5, 6, 8, 10, 12
36 idx = (__raw_readl(reg) & mask) >> offset;
40 return crm_small_dividers[idx];
43 static u32 crm_divider(void __iomem *reg, u8 offset, u32 mask, u32 z)
46 div = (__raw_readl(reg) & mask) >> offset;
50 static int _clk_1bit_enable(struct clk *clk)
54 reg = __raw_readl(clk->enable_reg);
55 reg |= 1 << clk->enable_shift;
56 __raw_writel(reg, clk->enable_reg);
61 static void _clk_1bit_disable(struct clk *clk)
65 reg = __raw_readl(clk->enable_reg);
66 reg &= ~(1 << clk->enable_shift);
67 __raw_writel(reg, clk->enable_reg);
70 static int _clk_3bit_enable(struct clk *clk)
74 reg = __raw_readl(clk->enable_reg);
75 reg |= 0x7 << clk->enable_shift;
76 __raw_writel(reg, clk->enable_reg);
81 static void _clk_3bit_disable(struct clk *clk)
85 reg = __raw_readl(clk->enable_reg);
86 reg &= ~(0x7 << clk->enable_shift);
87 __raw_writel(reg, clk->enable_reg);
90 static unsigned long ckih_rate;
92 static unsigned long clk_ckih_get_rate(struct clk *clk)
97 static struct clk ckih_clk = {
98 .get_rate = clk_ckih_get_rate,
101 static unsigned long clk_ckih_x2_get_rate(struct clk *clk)
103 return 2 * clk_get_rate(clk->parent);
106 static struct clk ckih_x2_clk = {
108 .get_rate = clk_ckih_x2_get_rate,
111 static unsigned long clk_ckil_get_rate(struct clk *clk)
113 return CKIL_CLK_FREQ;
116 static struct clk ckil_clk = {
117 .get_rate = clk_ckil_get_rate,
121 static struct clk mcu_pll_clk;
122 static struct clk dsp_pll_clk;
123 static struct clk usb_pll_clk;
125 static struct clk *pll_clk(u8 sel)
138 static void __iomem *pll_base(struct clk *clk)
140 if (clk == &mcu_pll_clk)
141 return MXC_PLL0_BASE;
142 else if (clk == &dsp_pll_clk)
143 return MXC_PLL1_BASE;
144 else if (clk == &usb_pll_clk)
145 return MXC_PLL2_BASE;
149 static unsigned long clk_pll_get_rate(struct clk *clk)
151 const void __iomem *pllbase;
152 unsigned long dp_op, dp_mfd, dp_mfn, pll_hfsm, ref_clk, mfi;
153 long mfn, mfn_abs, mfd, pdf;
155 pllbase = pll_base(clk);
157 pll_hfsm = __raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;
159 dp_op = __raw_readl(pllbase + MXC_PLL_DP_OP);
160 dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_MFD);
161 dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_MFN);
163 dp_op = __raw_readl(pllbase + MXC_PLL_DP_HFS_OP);
164 dp_mfd = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFD);
165 dp_mfn = __raw_readl(pllbase + MXC_PLL_DP_HFS_MFN);
168 pdf = dp_op & MXC_PLL_DP_OP_PDF_MASK;
169 mfi = (dp_op >> MXC_PLL_DP_OP_MFI_OFFSET) & MXC_PLL_DP_OP_PDF_MASK;
170 mfi = (mfi <= 5) ? 5 : mfi;
171 mfd = dp_mfd & MXC_PLL_DP_MFD_MASK;
172 mfn = dp_mfn & MXC_PLL_DP_MFN_MASK;
173 mfn = (mfn <= 0x4000000) ? mfn : (mfn - 0x10000000);
180 /* XXX: actually this asumes that ckih is fed to pll, but spec says
181 * that ckih_x2 is also possible. need to check this out.
183 ref_clk = clk_get_rate(&ckih_clk);
188 temp = (u64) ref_clk * mfn_abs;
192 temp += ref_clk * mfi;
197 static int clk_pll_enable(struct clk *clk)
203 reg = __raw_readl(ctl);
204 reg |= (MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
205 __raw_writel(reg, ctl);
207 reg = __raw_readl(ctl);
208 } while ((reg & MXC_PLL_DP_CTL_LRF) != MXC_PLL_DP_CTL_LRF);
212 static void clk_pll_disable(struct clk *clk)
218 reg = __raw_readl(ctl);
219 reg &= ~(MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
220 __raw_writel(reg, ctl);
223 static struct clk mcu_pll_clk = {
225 .get_rate = clk_pll_get_rate,
226 .enable = clk_pll_enable,
227 .disable = clk_pll_disable,
230 static struct clk dsp_pll_clk = {
232 .get_rate = clk_pll_get_rate,
233 .enable = clk_pll_enable,
234 .disable = clk_pll_disable,
237 static struct clk usb_pll_clk = {
239 .get_rate = clk_pll_get_rate,
240 .enable = clk_pll_enable,
241 .disable = clk_pll_disable,
245 /* ap_ref_clk stuff */
246 static struct clk ap_ref_clk;
248 static unsigned long clk_ap_ref_get_rate(struct clk *clk)
251 u8 ap_pat_ref_div_2, ap_isel, acs, ads;
253 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
254 acsr = __raw_readl(MXC_CRMAP_ACSR);
256 /* 0 for ckih, 1 for ckih*2 */
257 ap_isel = ascsr & MXC_CRMAP_ASCSR_APISEL;
259 ap_pat_ref_div_2 = (ascsr >> MXC_CRMAP_ASCSR_AP_PATDIV2_OFFSET) & 0x1;
260 /* undocumented, 1 for disabling divider */
261 ads = (acsr >> MXC_CRMAP_ACSR_ADS_OFFSET) & 0x1;
262 /* 0 for pat_ref, 1 for divider out */
263 acs = acsr & MXC_CRMAP_ACSR_ACS;
266 /* use divided clock */
267 return clk_get_rate(clk->parent) / (ap_pat_ref_div_2 ? 2 : 1);
269 return clk_get_rate(clk->parent) * (ap_isel ? 2 : 1);
272 static struct clk ap_ref_clk = {
274 .get_rate = clk_ap_ref_get_rate,
276 /* ap_ref_clk stuff end */
278 /* ap_pre_dfs_clk stuff */
279 static struct clk ap_pre_dfs_clk;
281 static unsigned long clk_ap_pre_dfs_get_rate(struct clk *clk)
285 acsr = __raw_readl(MXC_CRMAP_ACSR);
286 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
288 if (acsr & MXC_CRMAP_ACSR_ACS) {
290 sel = (ascsr & MXC_CRMAP_ASCSR_APSEL_MASK) >>
291 MXC_CRMAP_ASCSR_APSEL_OFFSET;
292 return clk_get_rate(pll_clk(sel)) /
293 CRM_SMALL_DIVIDER(MXC_CRMAP_ACDR, ARMDIV);
295 return clk_get_rate(&ap_ref_clk);
298 static struct clk ap_pre_dfs_clk = {
299 .get_rate = clk_ap_pre_dfs_get_rate,
301 /* ap_pre_dfs_clk stuff end */
304 static struct clk usb_clk;
306 static struct clk *clk_usb_parent(struct clk *clk)
310 acsr = __raw_readl(MXC_CRMAP_ACSR);
311 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
313 if (acsr & MXC_CRMAP_ACSR_ACS) {
315 sel = (ascsr & MXC_CRMAP_ASCSR_USBSEL_MASK) >>
316 MXC_CRMAP_ASCSR_USBSEL_OFFSET;
322 static unsigned long clk_usb_get_rate(struct clk *clk)
324 return clk_get_rate(clk->parent) /
325 CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, USBDIV);
328 static struct clk usb_clk = {
329 .enable_reg = MXC_CRMAP_ACDER2,
330 .enable_shift = MXC_CRMAP_ACDER2_USBEN_OFFSET,
331 .get_rate = clk_usb_get_rate,
332 .enable = _clk_1bit_enable,
333 .disable = _clk_1bit_disable,
335 /* usb_clk stuff end */
337 static unsigned long clk_ipg_get_rate(struct clk *clk)
339 return clk_get_rate(clk->parent) / CRM_16DIVIDER(MXC_CRMAP_ACDR, IPDIV);
342 static unsigned long clk_ahb_get_rate(struct clk *clk)
344 return clk_get_rate(clk->parent) /
345 CRM_16DIVIDER(MXC_CRMAP_ACDR, AHBDIV);
348 static struct clk ipg_clk = {
349 .parent = &ap_pre_dfs_clk,
350 .get_rate = clk_ipg_get_rate,
353 static struct clk ahb_clk = {
354 .parent = &ap_pre_dfs_clk,
355 .get_rate = clk_ahb_get_rate,
358 /* perclk_clk stuff */
359 static struct clk perclk_clk;
361 static unsigned long clk_perclk_get_rate(struct clk *clk)
365 acder2 = __raw_readl(MXC_CRMAP_ACDER2);
366 if (acder2 & MXC_CRMAP_ACDER2_BAUD_ISEL_MASK)
367 return 2 * clk_get_rate(clk->parent);
369 return clk_get_rate(clk->parent);
372 static struct clk perclk_clk = {
374 .get_rate = clk_perclk_get_rate,
376 /* perclk_clk stuff end */
379 static struct clk uart_clk[];
381 static unsigned long clk_uart_get_rate(struct clk *clk)
388 div = CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, BAUDDIV);
391 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRA, UART3DIV);
396 return clk_get_rate(clk->parent) / div;
399 static struct clk uart_clk[] = {
402 .parent = &perclk_clk,
403 .enable_reg = MXC_CRMAP_APRA,
404 .enable_shift = MXC_CRMAP_APRA_UART1EN_OFFSET,
405 .get_rate = clk_uart_get_rate,
406 .enable = _clk_1bit_enable,
407 .disable = _clk_1bit_disable,
410 .parent = &perclk_clk,
411 .enable_reg = MXC_CRMAP_APRA,
412 .enable_shift = MXC_CRMAP_APRA_UART2EN_OFFSET,
413 .get_rate = clk_uart_get_rate,
414 .enable = _clk_1bit_enable,
415 .disable = _clk_1bit_disable,
418 .parent = &perclk_clk,
419 .enable_reg = MXC_CRMAP_APRA,
420 .enable_shift = MXC_CRMAP_APRA_UART3EN_OFFSET,
421 .get_rate = clk_uart_get_rate,
422 .enable = _clk_1bit_enable,
423 .disable = _clk_1bit_disable,
426 /* uart_clk stuff end */
429 static struct clk nfc_clk;
431 static unsigned long clk_nfc_get_rate(struct clk *clk)
433 return clk_get_rate(clk->parent) /
434 CRM_1DIVIDER(MXC_CRMAP_ACDER2, NFCDIV);
437 static struct clk nfc_clk = {
439 .enable_reg = MXC_CRMAP_ACDER2,
440 .enable_shift = MXC_CRMAP_ACDER2_NFCEN_OFFSET,
441 .get_rate = clk_nfc_get_rate,
442 .enable = _clk_1bit_enable,
443 .disable = _clk_1bit_disable,
445 /* sdhc_clk stuff end */
448 static struct clk sdhc_clk[];
450 static struct clk *clk_sdhc_parent(struct clk *clk)
457 aprb = __raw_readl(MXC_CRMAP_APRB);
461 mask = MXC_CRMAP_APRB_SDHC1_ISEL_MASK;
462 offset = MXC_CRMAP_APRB_SDHC1_ISEL_OFFSET;
465 mask = MXC_CRMAP_APRB_SDHC2_ISEL_MASK;
466 offset = MXC_CRMAP_APRB_SDHC2_ISEL_OFFSET;
471 sel = (aprb & mask) >> offset;
482 static unsigned long clk_sdhc_get_rate(struct clk *clk)
488 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC1_DIV);
491 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC2_DIV);
497 return clk_get_rate(clk->parent) / div;
500 static int clk_sdhc_enable(struct clk *clk)
504 amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
505 aprb = __raw_readl(MXC_CRMAP_APRB);
508 amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
509 aprb |= (0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
512 amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
513 aprb |= (0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
516 __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
517 __raw_writel(aprb, MXC_CRMAP_APRB);
521 static void clk_sdhc_disable(struct clk *clk)
525 amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
526 aprb = __raw_readl(MXC_CRMAP_APRB);
529 amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
530 aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
533 amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
534 aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
537 __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
538 __raw_writel(aprb, MXC_CRMAP_APRB);
541 static struct clk sdhc_clk[] = {
544 .get_rate = clk_sdhc_get_rate,
545 .enable = clk_sdhc_enable,
546 .disable = clk_sdhc_disable,
549 .get_rate = clk_sdhc_get_rate,
550 .enable = clk_sdhc_enable,
551 .disable = clk_sdhc_disable,
554 /* sdhc_clk stuff end */
557 static struct clk wdog_clk[] = {
561 .enable_reg = MXC_CRMAP_AMLPMRD,
562 .enable_shift = MXC_CRMAP_AMLPMRD_MLPMD7_OFFSET,
563 .enable = _clk_3bit_enable,
564 .disable = _clk_3bit_disable,
568 .enable_reg = MXC_CRMAP_AMLPMRD,
569 .enable_shift = MXC_CRMAP_AMLPMRD_MLPMD3_OFFSET,
570 .enable = _clk_3bit_enable,
571 .disable = _clk_3bit_disable,
574 /* wdog_clk stuff end */
577 static struct clk gpt_clk = {
579 .enable_reg = MXC_CRMAP_AMLPMRC,
580 .enable_shift = MXC_CRMAP_AMLPMRC_MLPMC4_OFFSET,
581 .enable = _clk_3bit_enable,
582 .disable = _clk_3bit_disable,
584 /* gpt_clk stuff end */
587 static struct clk cspi_clk[] = {
591 .enable_reg = MXC_CRMAP_AMLPMRE2,
592 .enable_shift = MXC_CRMAP_AMLPMRE2_MLPME0_OFFSET,
593 .enable = _clk_3bit_enable,
594 .disable = _clk_3bit_disable,
598 .enable_reg = MXC_CRMAP_AMLPMRE1,
599 .enable_shift = MXC_CRMAP_AMLPMRE1_MLPME6_OFFSET,
600 .enable = _clk_3bit_enable,
601 .disable = _clk_3bit_disable,
604 /* cspi_clk stuff end */
606 #define _REGISTER_CLOCK(d, n, c) \
613 static struct clk_lookup lookups[] = {
614 _REGISTER_CLOCK("imx-uart.0", NULL, uart_clk[0])
615 _REGISTER_CLOCK("imx-uart.1", NULL, uart_clk[1])
616 _REGISTER_CLOCK("imx-uart.2", NULL, uart_clk[2])
617 _REGISTER_CLOCK("mxc-mmc.0", NULL, sdhc_clk[0])
618 _REGISTER_CLOCK("mxc-mmc.1", NULL, sdhc_clk[1])
619 _REGISTER_CLOCK("mxc-wdt.0", NULL, wdog_clk[0])
620 _REGISTER_CLOCK("spi_imx.0", NULL, cspi_clk[0])
621 _REGISTER_CLOCK("spi_imx.1", NULL, cspi_clk[1])
624 int __init mxc91231_clocks_init(unsigned long fref)
626 void __iomem *gpt_base;
630 usb_clk.parent = clk_usb_parent(&usb_clk);
631 sdhc_clk[0].parent = clk_sdhc_parent(&sdhc_clk[0]);
632 sdhc_clk[1].parent = clk_sdhc_parent(&sdhc_clk[1]);
634 clkdev_add_table(lookups, ARRAY_SIZE(lookups));
636 gpt_base = MXC91231_IO_ADDRESS(MXC91231_GPT1_BASE_ADDR);
637 mxc_timer_init(&gpt_clk, gpt_base, MXC91231_INT_GPT);