blob: 5c85075d8a56b9b6c40059785dd9a4cd69658632 [file] [log] [blame]
Dmitriy Taychenachevfd6ac7b2009-07-31 20:29:22 +09001#include <linux/clk.h>
2#include <linux/kernel.h>
3#include <linux/init.h>
4#include <linux/io.h>
5
6#include <mach/clock.h>
7#include <mach/hardware.h>
8#include <mach/common.h>
9
10#include <asm/clkdev.h>
11#include <asm/bug.h>
12#include <asm/div64.h>
13
14#include "crm_regs.h"
15
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) \
21 crm_divider(base, \
22 base ## _ ## name ## _OFFSET, \
23 base ## _ ## name ## _MASK, 1)
24#define CRM_16DIVIDER(base, name) \
25 crm_divider(base, \
26 base ## _ ## name ## _OFFSET, \
27 base ## _ ## name ## _MASK, 16)
28
29static u32 crm_small_divider(void __iomem *reg, u8 offset, u32 mask)
30{
31 static const u32 crm_small_dividers[] = {
32 2, 3, 4, 5, 6, 8, 10, 12
33 };
34 u8 idx;
35
36 idx = (__raw_readl(reg) & mask) >> offset;
37 if (idx > 7)
38 return 1;
39
40 return crm_small_dividers[idx];
41}
42
43static u32 crm_divider(void __iomem *reg, u8 offset, u32 mask, u32 z)
44{
45 u32 div;
46 div = (__raw_readl(reg) & mask) >> offset;
47 return div ? div : z;
48}
49
50static int _clk_1bit_enable(struct clk *clk)
51{
52 u32 reg;
53
54 reg = __raw_readl(clk->enable_reg);
55 reg |= 1 << clk->enable_shift;
56 __raw_writel(reg, clk->enable_reg);
57
58 return 0;
59}
60
61static void _clk_1bit_disable(struct clk *clk)
62{
63 u32 reg;
64
65 reg = __raw_readl(clk->enable_reg);
66 reg &= ~(1 << clk->enable_shift);
67 __raw_writel(reg, clk->enable_reg);
68}
69
70static int _clk_3bit_enable(struct clk *clk)
71{
72 u32 reg;
73
74 reg = __raw_readl(clk->enable_reg);
75 reg |= 0x7 << clk->enable_shift;
76 __raw_writel(reg, clk->enable_reg);
77
78 return 0;
79}
80
81static void _clk_3bit_disable(struct clk *clk)
82{
83 u32 reg;
84
85 reg = __raw_readl(clk->enable_reg);
86 reg &= ~(0x7 << clk->enable_shift);
87 __raw_writel(reg, clk->enable_reg);
88}
89
90static unsigned long ckih_rate;
91
92static unsigned long clk_ckih_get_rate(struct clk *clk)
93{
94 return ckih_rate;
95}
96
97static struct clk ckih_clk = {
98 .get_rate = clk_ckih_get_rate,
99};
100
101static unsigned long clk_ckih_x2_get_rate(struct clk *clk)
102{
103 return 2 * clk_get_rate(clk->parent);
104}
105
106static struct clk ckih_x2_clk = {
107 .parent = &ckih_clk,
108 .get_rate = clk_ckih_x2_get_rate,
109};
110
111static unsigned long clk_ckil_get_rate(struct clk *clk)
112{
113 return CKIL_CLK_FREQ;
114}
115
116static struct clk ckil_clk = {
117 .get_rate = clk_ckil_get_rate,
118};
119
120/* plls stuff */
121static struct clk mcu_pll_clk;
122static struct clk dsp_pll_clk;
123static struct clk usb_pll_clk;
124
125static struct clk *pll_clk(u8 sel)
126{
127 switch (sel) {
128 case 0:
129 return &mcu_pll_clk;
130 case 1:
131 return &dsp_pll_clk;
132 case 2:
133 return &usb_pll_clk;
134 }
135 BUG();
136}
137
138static void __iomem *pll_base(struct clk *clk)
139{
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;
146 BUG();
147}
148
149static unsigned long clk_pll_get_rate(struct clk *clk)
150{
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;
154 s64 temp;
155 pllbase = pll_base(clk);
156
157 pll_hfsm = __raw_readl(pllbase + MXC_PLL_DP_CTL) & MXC_PLL_DP_CTL_HFSM;
158 if (pll_hfsm == 0) {
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);
162 } else {
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);
166 }
167
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);
174
175 if (mfn < 0)
176 mfn_abs = -mfn;
177 else
178 mfn_abs = mfn;
179
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.
182 */
183 ref_clk = clk_get_rate(&ckih_clk);
184
185 ref_clk *= 2;
186 ref_clk /= pdf + 1;
187
188 temp = (u64) ref_clk * mfn_abs;
189 do_div(temp, mfd);
190 if (mfn < 0)
191 temp = -temp;
192 temp += ref_clk * mfi;
193
194 return temp;
195}
196
197static int clk_pll_enable(struct clk *clk)
198{
199 void __iomem *ctl;
200 u32 reg;
201
202 ctl = pll_base(clk);
203 reg = __raw_readl(ctl);
204 reg |= (MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
205 __raw_writel(reg, ctl);
206 do {
207 reg = __raw_readl(ctl);
208 } while ((reg & MXC_PLL_DP_CTL_LRF) != MXC_PLL_DP_CTL_LRF);
209 return 0;
210}
211
212static void clk_pll_disable(struct clk *clk)
213{
214 void __iomem *ctl;
215 u32 reg;
216
217 ctl = pll_base(clk);
218 reg = __raw_readl(ctl);
219 reg &= ~(MXC_PLL_DP_CTL_RST | MXC_PLL_DP_CTL_UPEN);
220 __raw_writel(reg, ctl);
221}
222
223static struct clk mcu_pll_clk = {
224 .parent = &ckih_clk,
225 .get_rate = clk_pll_get_rate,
226 .enable = clk_pll_enable,
227 .disable = clk_pll_disable,
228};
229
230static struct clk dsp_pll_clk = {
231 .parent = &ckih_clk,
232 .get_rate = clk_pll_get_rate,
233 .enable = clk_pll_enable,
234 .disable = clk_pll_disable,
235};
236
237static struct clk usb_pll_clk = {
238 .parent = &ckih_clk,
239 .get_rate = clk_pll_get_rate,
240 .enable = clk_pll_enable,
241 .disable = clk_pll_disable,
242};
243/* plls stuff end */
244
245/* ap_ref_clk stuff */
246static struct clk ap_ref_clk;
247
248static unsigned long clk_ap_ref_get_rate(struct clk *clk)
249{
250 u32 ascsr, acsr;
251 u8 ap_pat_ref_div_2, ap_isel, acs, ads;
252
253 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
254 acsr = __raw_readl(MXC_CRMAP_ACSR);
255
256 /* 0 for ckih, 1 for ckih*2 */
257 ap_isel = ascsr & MXC_CRMAP_ASCSR_APISEL;
258 /* reg divider */
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;
264
265 if (acs & !ads)
266 /* use divided clock */
267 return clk_get_rate(clk->parent) / (ap_pat_ref_div_2 ? 2 : 1);
268
269 return clk_get_rate(clk->parent) * (ap_isel ? 2 : 1);
270}
271
272static struct clk ap_ref_clk = {
273 .parent = &ckih_clk,
274 .get_rate = clk_ap_ref_get_rate,
275};
276/* ap_ref_clk stuff end */
277
278/* ap_pre_dfs_clk stuff */
279static struct clk ap_pre_dfs_clk;
280
281static unsigned long clk_ap_pre_dfs_get_rate(struct clk *clk)
282{
283 u32 acsr, ascsr;
284
285 acsr = __raw_readl(MXC_CRMAP_ACSR);
286 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
287
288 if (acsr & MXC_CRMAP_ACSR_ACS) {
289 u8 sel;
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);
294 }
295 return clk_get_rate(&ap_ref_clk);
296}
297
298static struct clk ap_pre_dfs_clk = {
299 .get_rate = clk_ap_pre_dfs_get_rate,
300};
301/* ap_pre_dfs_clk stuff end */
302
303/* usb_clk stuff */
304static struct clk usb_clk;
305
306static struct clk *clk_usb_parent(struct clk *clk)
307{
308 u32 acsr, ascsr;
309
310 acsr = __raw_readl(MXC_CRMAP_ACSR);
311 ascsr = __raw_readl(MXC_CRMAP_ASCSR);
312
313 if (acsr & MXC_CRMAP_ACSR_ACS) {
314 u8 sel;
315 sel = (ascsr & MXC_CRMAP_ASCSR_USBSEL_MASK) >>
316 MXC_CRMAP_ASCSR_USBSEL_OFFSET;
317 return pll_clk(sel);
318 }
319 return &ap_ref_clk;
320}
321
322static unsigned long clk_usb_get_rate(struct clk *clk)
323{
324 return clk_get_rate(clk->parent) /
325 CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, USBDIV);
326}
327
328static 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,
334};
335/* usb_clk stuff end */
336
337static unsigned long clk_ipg_get_rate(struct clk *clk)
338{
339 return clk_get_rate(clk->parent) / CRM_16DIVIDER(MXC_CRMAP_ACDR, IPDIV);
340}
341
342static unsigned long clk_ahb_get_rate(struct clk *clk)
343{
344 return clk_get_rate(clk->parent) /
345 CRM_16DIVIDER(MXC_CRMAP_ACDR, AHBDIV);
346}
347
348static struct clk ipg_clk = {
349 .parent = &ap_pre_dfs_clk,
350 .get_rate = clk_ipg_get_rate,
351};
352
353static struct clk ahb_clk = {
354 .parent = &ap_pre_dfs_clk,
355 .get_rate = clk_ahb_get_rate,
356};
357
358/* perclk_clk stuff */
359static struct clk perclk_clk;
360
361static unsigned long clk_perclk_get_rate(struct clk *clk)
362{
363 u32 acder2;
364
365 acder2 = __raw_readl(MXC_CRMAP_ACDER2);
366 if (acder2 & MXC_CRMAP_ACDER2_BAUD_ISEL_MASK)
367 return 2 * clk_get_rate(clk->parent);
368
369 return clk_get_rate(clk->parent);
370}
371
372static struct clk perclk_clk = {
373 .parent = &ckih_clk,
374 .get_rate = clk_perclk_get_rate,
375};
376/* perclk_clk stuff end */
377
378/* uart_clk stuff */
379static struct clk uart_clk[];
380
381static unsigned long clk_uart_get_rate(struct clk *clk)
382{
383 u32 div;
384
385 switch (clk->id) {
386 case 0:
387 case 1:
388 div = CRM_SMALL_DIVIDER(MXC_CRMAP_ACDER2, BAUDDIV);
389 break;
390 case 2:
391 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRA, UART3DIV);
392 break;
393 default:
394 BUG();
395 }
396 return clk_get_rate(clk->parent) / div;
397}
398
399static struct clk uart_clk[] = {
400 {
401 .id = 0,
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,
408 }, {
409 .id = 1,
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,
416 }, {
417 .id = 2,
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,
424 },
425};
426/* uart_clk stuff end */
427
428/* sdhc_clk stuff */
429static struct clk nfc_clk;
430
431static unsigned long clk_nfc_get_rate(struct clk *clk)
432{
433 return clk_get_rate(clk->parent) /
434 CRM_1DIVIDER(MXC_CRMAP_ACDER2, NFCDIV);
435}
436
437static struct clk nfc_clk = {
438 .parent = &ahb_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,
444};
445/* sdhc_clk stuff end */
446
447/* sdhc_clk stuff */
448static struct clk sdhc_clk[];
449
450static struct clk *clk_sdhc_parent(struct clk *clk)
451{
452 u32 aprb;
453 u8 sel;
454 u32 mask;
455 int offset;
456
457 aprb = __raw_readl(MXC_CRMAP_APRB);
458
459 switch (clk->id) {
460 case 0:
461 mask = MXC_CRMAP_APRB_SDHC1_ISEL_MASK;
462 offset = MXC_CRMAP_APRB_SDHC1_ISEL_OFFSET;
463 break;
464 case 1:
465 mask = MXC_CRMAP_APRB_SDHC2_ISEL_MASK;
466 offset = MXC_CRMAP_APRB_SDHC2_ISEL_OFFSET;
467 break;
468 default:
469 BUG();
470 }
471 sel = (aprb & mask) >> offset;
472
473 switch (sel) {
474 case 0:
475 return &ckih_clk;
476 case 1:
477 return &ckih_x2_clk;
478 }
479 return &usb_clk;
480}
481
482static unsigned long clk_sdhc_get_rate(struct clk *clk)
483{
484 u32 div;
485
486 switch (clk->id) {
487 case 0:
488 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC1_DIV);
489 break;
490 case 1:
491 div = CRM_SMALL_DIVIDER(MXC_CRMAP_APRB, SDHC2_DIV);
492 break;
493 default:
494 BUG();
495 }
496
497 return clk_get_rate(clk->parent) / div;
498}
499
500static int clk_sdhc_enable(struct clk *clk)
501{
502 u32 amlpmre1, aprb;
503
504 amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
505 aprb = __raw_readl(MXC_CRMAP_APRB);
506 switch (clk->id) {
507 case 0:
508 amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
509 aprb |= (0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
510 break;
511 case 1:
512 amlpmre1 |= (0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
513 aprb |= (0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
514 break;
515 }
516 __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
517 __raw_writel(aprb, MXC_CRMAP_APRB);
518 return 0;
519}
520
521static void clk_sdhc_disable(struct clk *clk)
522{
523 u32 amlpmre1, aprb;
524
525 amlpmre1 = __raw_readl(MXC_CRMAP_AMLPMRE1);
526 aprb = __raw_readl(MXC_CRMAP_APRB);
527 switch (clk->id) {
528 case 0:
529 amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME4_OFFSET);
530 aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC1EN_OFFSET);
531 break;
532 case 1:
533 amlpmre1 &= ~(0x7 << MXC_CRMAP_AMLPMRE1_MLPME5_OFFSET);
534 aprb &= ~(0x1 << MXC_CRMAP_APRB_SDHC2EN_OFFSET);
535 break;
536 }
537 __raw_writel(amlpmre1, MXC_CRMAP_AMLPMRE1);
538 __raw_writel(aprb, MXC_CRMAP_APRB);
539}
540
541static struct clk sdhc_clk[] = {
542 {
543 .id = 0,
544 .get_rate = clk_sdhc_get_rate,
545 .enable = clk_sdhc_enable,
546 .disable = clk_sdhc_disable,
547 }, {
548 .id = 1,
549 .get_rate = clk_sdhc_get_rate,
550 .enable = clk_sdhc_enable,
551 .disable = clk_sdhc_disable,
552 },
553};
554/* sdhc_clk stuff end */
555
556/* wdog_clk stuff */
557static struct clk wdog_clk[] = {
558 {
559 .id = 0,
560 .parent = &ipg_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,
565 }, {
566 .id = 1,
567 .parent = &ipg_clk,
568 .enable_reg = MXC_CRMAP_AMLPMRD,
569 .enable_shift = MXC_CRMAP_AMLPMRD_MLPMD3_OFFSET,
570 .enable = _clk_3bit_enable,
571 .disable = _clk_3bit_disable,
572 },
573};
574/* wdog_clk stuff end */
575
576/* gpt_clk stuff */
577static struct clk gpt_clk = {
578 .parent = &ipg_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,
583};
584/* gpt_clk stuff end */
585
586/* cspi_clk stuff */
587static struct clk cspi_clk[] = {
588 {
589 .id = 0,
590 .parent = &ipg_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,
595 }, {
596 .id = 1,
597 .parent = &ipg_clk,
598 .enable_reg = MXC_CRMAP_AMLPMRE1,
599 .enable_shift = MXC_CRMAP_AMLPMRE1_MLPME6_OFFSET,
600 .enable = _clk_3bit_enable,
601 .disable = _clk_3bit_disable,
602 },
603};
604/* cspi_clk stuff end */
605
606#define _REGISTER_CLOCK(d, n, c) \
607 { \
608 .dev_id = d, \
609 .con_id = n, \
610 .clk = &c, \
611 },
612
613static 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])
622};
623
624int __init mxc91231_clocks_init(unsigned long fref)
625{
626 void __iomem *gpt_base;
Dmitriy Taychenachevfd6ac7b2009-07-31 20:29:22 +0900627
628 ckih_rate = fref;
629
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]);
633
Russell King0a0300d2010-01-12 12:28:00 +0000634 clkdev_add_table(lookups, ARRAY_SIZE(lookups));
Dmitriy Taychenachevfd6ac7b2009-07-31 20:29:22 +0900635
636 gpt_base = MXC91231_IO_ADDRESS(MXC91231_GPT1_BASE_ADDR);
637 mxc_timer_init(&gpt_clk, gpt_base, MXC91231_INT_GPT);
638
639 return 0;
640}