ARM: Consolidate clks_register() and similar
[linux-2.6.git] / arch / arm / mach-mxc91231 / clock.c
1 #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
29 static 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
43 static 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
50 static 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
61 static 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
70 static 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
81 static 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
90 static unsigned long ckih_rate;
91
92 static unsigned long clk_ckih_get_rate(struct clk *clk)
93 {
94         return ckih_rate;
95 }
96
97 static struct clk ckih_clk = {
98         .get_rate = clk_ckih_get_rate,
99 };
100
101 static unsigned long clk_ckih_x2_get_rate(struct clk *clk)
102 {
103         return 2 * clk_get_rate(clk->parent);
104 }
105
106 static struct clk ckih_x2_clk = {
107         .parent = &ckih_clk,
108         .get_rate = clk_ckih_x2_get_rate,
109 };
110
111 static unsigned long clk_ckil_get_rate(struct clk *clk)
112 {
113         return CKIL_CLK_FREQ;
114 }
115
116 static struct clk ckil_clk = {
117         .get_rate = clk_ckil_get_rate,
118 };
119
120 /* plls stuff */
121 static struct clk mcu_pll_clk;
122 static struct clk dsp_pll_clk;
123 static struct clk usb_pll_clk;
124
125 static 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
138 static 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
149 static 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
197 static 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
212 static 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
223 static 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
230 static 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
237 static 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 */
246 static struct clk ap_ref_clk;
247
248 static 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
272 static 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 */
279 static struct clk ap_pre_dfs_clk;
280
281 static 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
298 static 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 */
304 static struct clk usb_clk;
305
306 static 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
322 static 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
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,
334 };
335 /* usb_clk stuff end */
336
337 static 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
342 static 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
348 static struct clk ipg_clk = {
349         .parent = &ap_pre_dfs_clk,
350         .get_rate = clk_ipg_get_rate,
351 };
352
353 static struct clk ahb_clk = {
354         .parent = &ap_pre_dfs_clk,
355         .get_rate = clk_ahb_get_rate,
356 };
357
358 /* perclk_clk stuff */
359 static struct clk perclk_clk;
360
361 static 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
372 static 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 */
379 static struct clk uart_clk[];
380
381 static 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
399 static 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 */
429 static struct clk nfc_clk;
430
431 static 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
437 static 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 */
448 static struct clk sdhc_clk[];
449
450 static 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
482 static 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
500 static 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
521 static 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
541 static 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 */
557 static 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 */
577 static 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 */
587 static 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
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])
622 };
623
624 int __init mxc91231_clocks_init(unsigned long fref)
625 {
626         void __iomem *gpt_base;
627
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
634         clkdev_add_table(lookups, ARRAY_SIZE(lookups));
635
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 }