Commit | Line | Data |
---|---|---|
aa3b0a6f HS |
1 | /* |
2 | * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. | |
3 | * Copyright 2008 Juergen Beisert, kernel@pengutronix.de | |
4 | * Copyright 2008 Martin Fuzzey, mfuzzey@gmail.com | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License | |
8 | * as published by the Free Software Foundation; either version 2 | |
9 | * of the License, or (at your option) any later version. | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License | |
16 | * along with this program; if not, write to the Free Software | |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |
18 | * MA 02110-1301, USA. | |
19 | */ | |
20 | ||
21 | #include <linux/clk.h> | |
22 | #include <linux/io.h> | |
23 | #include <linux/module.h> | |
24 | ||
25 | #include <mach/clock.h> | |
26 | #include <mach/common.h> | |
27 | #include <asm/clkdev.h> | |
28 | #include <asm/div64.h> | |
29 | ||
30 | #include "crm_regs.h" | |
31 | ||
32 | static int _clk_enable(struct clk *clk) | |
33 | { | |
34 | u32 reg; | |
35 | ||
36 | reg = __raw_readl(clk->enable_reg); | |
37 | reg |= 1 << clk->enable_shift; | |
38 | __raw_writel(reg, clk->enable_reg); | |
39 | return 0; | |
40 | } | |
41 | ||
42 | static void _clk_disable(struct clk *clk) | |
43 | { | |
44 | u32 reg; | |
45 | ||
46 | reg = __raw_readl(clk->enable_reg); | |
47 | reg &= ~(1 << clk->enable_shift); | |
48 | __raw_writel(reg, clk->enable_reg); | |
49 | } | |
50 | ||
51 | static int _clk_spll_enable(struct clk *clk) | |
52 | { | |
53 | u32 reg; | |
54 | ||
55 | reg = __raw_readl(CCM_CSCR); | |
56 | reg |= CCM_CSCR_SPEN; | |
57 | __raw_writel(reg, CCM_CSCR); | |
58 | ||
59 | while ((__raw_readl(CCM_SPCTL1) & CCM_SPCTL1_LF) == 0) | |
60 | ; | |
61 | return 0; | |
62 | } | |
63 | ||
64 | static void _clk_spll_disable(struct clk *clk) | |
65 | { | |
66 | u32 reg; | |
67 | ||
68 | reg = __raw_readl(CCM_CSCR); | |
69 | reg &= ~CCM_CSCR_SPEN; | |
70 | __raw_writel(reg, CCM_CSCR); | |
71 | } | |
72 | ||
73 | ||
74 | #define CSCR() (__raw_readl(CCM_CSCR)) | |
75 | #define PCDR0() (__raw_readl(CCM_PCDR0)) | |
76 | #define PCDR1() (__raw_readl(CCM_PCDR1)) | |
77 | ||
78 | static unsigned long _clk_perclkx_round_rate(struct clk *clk, | |
79 | unsigned long rate) | |
80 | { | |
81 | u32 div; | |
82 | unsigned long parent_rate; | |
83 | ||
84 | parent_rate = clk_get_rate(clk->parent); | |
85 | ||
86 | div = parent_rate / rate; | |
87 | if (parent_rate % rate) | |
88 | div++; | |
89 | ||
90 | if (div > 64) | |
91 | div = 64; | |
92 | ||
93 | return parent_rate / div; | |
94 | } | |
95 | ||
96 | static int _clk_perclkx_set_rate(struct clk *clk, unsigned long rate) | |
97 | { | |
98 | u32 reg; | |
99 | u32 div; | |
100 | unsigned long parent_rate; | |
101 | ||
102 | parent_rate = clk_get_rate(clk->parent); | |
103 | ||
104 | if (clk->id < 0 || clk->id > 3) | |
105 | return -EINVAL; | |
106 | ||
107 | div = parent_rate / rate; | |
108 | if (div > 64 || div < 1 || ((parent_rate / div) != rate)) | |
109 | return -EINVAL; | |
110 | div--; | |
111 | ||
112 | reg = | |
113 | __raw_readl(CCM_PCDR1) & ~(CCM_PCDR1_PERDIV1_MASK << | |
114 | (clk->id << 3)); | |
115 | reg |= div << (clk->id << 3); | |
116 | __raw_writel(reg, CCM_PCDR1); | |
117 | ||
118 | return 0; | |
119 | } | |
120 | ||
121 | static unsigned long _clk_usb_recalc(struct clk *clk) | |
122 | { | |
123 | unsigned long usb_pdf; | |
124 | unsigned long parent_rate; | |
125 | ||
126 | parent_rate = clk_get_rate(clk->parent); | |
127 | ||
128 | usb_pdf = (CSCR() & CCM_CSCR_USB_MASK) >> CCM_CSCR_USB_OFFSET; | |
129 | ||
130 | return parent_rate / (usb_pdf + 1U); | |
131 | } | |
132 | ||
133 | static unsigned long _clk_ssix_recalc(struct clk *clk, unsigned long pdf) | |
134 | { | |
135 | unsigned long parent_rate; | |
136 | ||
137 | parent_rate = clk_get_rate(clk->parent); | |
138 | ||
139 | pdf = (pdf < 2) ? 124UL : pdf; /* MX21 & MX27 TO1 */ | |
140 | ||
141 | return 2UL * parent_rate / pdf; | |
142 | } | |
143 | ||
144 | static unsigned long _clk_ssi1_recalc(struct clk *clk) | |
145 | { | |
146 | return _clk_ssix_recalc(clk, | |
147 | (PCDR0() & CCM_PCDR0_SSI1BAUDDIV_MASK) | |
148 | >> CCM_PCDR0_SSI1BAUDDIV_OFFSET); | |
149 | } | |
150 | ||
151 | static unsigned long _clk_ssi2_recalc(struct clk *clk) | |
152 | { | |
153 | return _clk_ssix_recalc(clk, | |
154 | (PCDR0() & CCM_PCDR0_SSI2BAUDDIV_MASK) >> | |
155 | CCM_PCDR0_SSI2BAUDDIV_OFFSET); | |
156 | } | |
157 | ||
158 | static unsigned long _clk_nfc_recalc(struct clk *clk) | |
159 | { | |
160 | unsigned long nfc_pdf; | |
161 | unsigned long parent_rate; | |
162 | ||
163 | parent_rate = clk_get_rate(clk->parent); | |
164 | ||
165 | nfc_pdf = (PCDR0() & CCM_PCDR0_NFCDIV_MASK) | |
166 | >> CCM_PCDR0_NFCDIV_OFFSET; | |
167 | ||
168 | return parent_rate / (nfc_pdf + 1); | |
169 | } | |
170 | ||
171 | static unsigned long _clk_parent_round_rate(struct clk *clk, unsigned long rate) | |
172 | { | |
173 | return clk->parent->round_rate(clk->parent, rate); | |
174 | } | |
175 | ||
176 | static int _clk_parent_set_rate(struct clk *clk, unsigned long rate) | |
177 | { | |
178 | return clk->parent->set_rate(clk->parent, rate); | |
179 | } | |
180 | ||
181 | static unsigned long external_high_reference; /* in Hz */ | |
182 | ||
183 | static unsigned long get_high_reference_clock_rate(struct clk *clk) | |
184 | { | |
185 | return external_high_reference; | |
186 | } | |
187 | ||
188 | /* | |
189 | * the high frequency external clock reference | |
190 | * Default case is 26MHz. | |
191 | */ | |
192 | static struct clk ckih_clk = { | |
193 | .get_rate = get_high_reference_clock_rate, | |
194 | }; | |
195 | ||
196 | static unsigned long external_low_reference; /* in Hz */ | |
197 | ||
198 | static unsigned long get_low_reference_clock_rate(struct clk *clk) | |
199 | { | |
200 | return external_low_reference; | |
201 | } | |
202 | ||
203 | /* | |
204 | * the low frequency external clock reference | |
205 | * Default case is 32.768kHz. | |
206 | */ | |
207 | static struct clk ckil_clk = { | |
208 | .get_rate = get_low_reference_clock_rate, | |
209 | }; | |
210 | ||
211 | ||
212 | static unsigned long _clk_fpm_recalc(struct clk *clk) | |
213 | { | |
214 | return clk_get_rate(clk->parent) * 512; | |
215 | } | |
216 | ||
217 | /* Output of frequency pre multiplier */ | |
218 | static struct clk fpm_clk = { | |
219 | .parent = &ckil_clk, | |
220 | .get_rate = _clk_fpm_recalc, | |
221 | }; | |
222 | ||
223 | static unsigned long get_mpll_clk(struct clk *clk) | |
224 | { | |
225 | uint32_t reg; | |
226 | unsigned long ref_clk; | |
227 | unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0; | |
228 | unsigned long long temp; | |
229 | ||
230 | ref_clk = clk_get_rate(clk->parent); | |
231 | ||
232 | reg = __raw_readl(CCM_MPCTL0); | |
233 | pdf = (reg & CCM_MPCTL0_PD_MASK) >> CCM_MPCTL0_PD_OFFSET; | |
234 | mfd = (reg & CCM_MPCTL0_MFD_MASK) >> CCM_MPCTL0_MFD_OFFSET; | |
235 | mfi = (reg & CCM_MPCTL0_MFI_MASK) >> CCM_MPCTL0_MFI_OFFSET; | |
236 | mfn = (reg & CCM_MPCTL0_MFN_MASK) >> CCM_MPCTL0_MFN_OFFSET; | |
237 | ||
238 | mfi = (mfi <= 5) ? 5 : mfi; | |
239 | temp = 2LL * ref_clk * mfn; | |
240 | do_div(temp, mfd + 1); | |
241 | temp = 2LL * ref_clk * mfi + temp; | |
242 | do_div(temp, pdf + 1); | |
243 | ||
244 | return (unsigned long)temp; | |
245 | } | |
246 | ||
247 | static struct clk mpll_clk = { | |
248 | .parent = &ckih_clk, | |
249 | .get_rate = get_mpll_clk, | |
250 | }; | |
251 | ||
252 | static unsigned long _clk_fclk_get_rate(struct clk *clk) | |
253 | { | |
254 | unsigned long parent_rate; | |
255 | u32 div; | |
256 | ||
257 | div = (CSCR() & CCM_CSCR_PRESC_MASK) >> CCM_CSCR_PRESC_OFFSET; | |
258 | parent_rate = clk_get_rate(clk->parent); | |
259 | ||
260 | return parent_rate / (div+1); | |
261 | } | |
262 | ||
263 | static struct clk fclk_clk = { | |
264 | .parent = &mpll_clk, | |
265 | .get_rate = _clk_fclk_get_rate | |
266 | }; | |
267 | ||
268 | static unsigned long get_spll_clk(struct clk *clk) | |
269 | { | |
270 | uint32_t reg; | |
271 | unsigned long ref_clk; | |
272 | unsigned long mfi = 0, mfn = 0, mfd = 0, pdf = 0; | |
273 | unsigned long long temp; | |
274 | ||
275 | ref_clk = clk_get_rate(clk->parent); | |
276 | ||
277 | reg = __raw_readl(CCM_SPCTL0); | |
278 | pdf = (reg & CCM_SPCTL0_PD_MASK) >> CCM_SPCTL0_PD_OFFSET; | |
279 | mfd = (reg & CCM_SPCTL0_MFD_MASK) >> CCM_SPCTL0_MFD_OFFSET; | |
280 | mfi = (reg & CCM_SPCTL0_MFI_MASK) >> CCM_SPCTL0_MFI_OFFSET; | |
281 | mfn = (reg & CCM_SPCTL0_MFN_MASK) >> CCM_SPCTL0_MFN_OFFSET; | |
282 | ||
283 | mfi = (mfi <= 5) ? 5 : mfi; | |
284 | temp = 2LL * ref_clk * mfn; | |
285 | do_div(temp, mfd + 1); | |
286 | temp = 2LL * ref_clk * mfi + temp; | |
287 | do_div(temp, pdf + 1); | |
288 | ||
289 | return (unsigned long)temp; | |
290 | } | |
291 | ||
292 | static struct clk spll_clk = { | |
293 | .parent = &ckih_clk, | |
294 | .get_rate = get_spll_clk, | |
295 | .enable = _clk_spll_enable, | |
296 | .disable = _clk_spll_disable, | |
297 | }; | |
298 | ||
299 | static unsigned long get_hclk_clk(struct clk *clk) | |
300 | { | |
301 | unsigned long rate; | |
302 | unsigned long bclk_pdf; | |
303 | ||
304 | bclk_pdf = (CSCR() & CCM_CSCR_BCLK_MASK) | |
305 | >> CCM_CSCR_BCLK_OFFSET; | |
306 | ||
307 | rate = clk_get_rate(clk->parent); | |
308 | return rate / (bclk_pdf + 1); | |
309 | } | |
310 | ||
311 | static struct clk hclk_clk = { | |
312 | .parent = &fclk_clk, | |
313 | .get_rate = get_hclk_clk, | |
314 | }; | |
315 | ||
316 | static unsigned long get_ipg_clk(struct clk *clk) | |
317 | { | |
318 | unsigned long rate; | |
319 | unsigned long ipg_pdf; | |
320 | ||
321 | ipg_pdf = (CSCR() & CCM_CSCR_IPDIV) >> CCM_CSCR_IPDIV_OFFSET; | |
322 | ||
323 | rate = clk_get_rate(clk->parent); | |
324 | return rate / (ipg_pdf + 1); | |
325 | } | |
326 | ||
327 | static struct clk ipg_clk = { | |
328 | .parent = &hclk_clk, | |
329 | .get_rate = get_ipg_clk, | |
330 | }; | |
331 | ||
332 | static unsigned long _clk_perclkx_recalc(struct clk *clk) | |
333 | { | |
334 | unsigned long perclk_pdf; | |
335 | unsigned long parent_rate; | |
336 | ||
337 | parent_rate = clk_get_rate(clk->parent); | |
338 | ||
339 | if (clk->id < 0 || clk->id > 3) | |
340 | return 0; | |
341 | ||
342 | perclk_pdf = (PCDR1() >> (clk->id << 3)) & CCM_PCDR1_PERDIV1_MASK; | |
343 | ||
344 | return parent_rate / (perclk_pdf + 1); | |
345 | } | |
346 | ||
347 | static struct clk per_clk[] = { | |
348 | { | |
349 | .id = 0, | |
350 | .parent = &mpll_clk, | |
351 | .get_rate = _clk_perclkx_recalc, | |
352 | }, { | |
353 | .id = 1, | |
354 | .parent = &mpll_clk, | |
355 | .get_rate = _clk_perclkx_recalc, | |
356 | }, { | |
357 | .id = 2, | |
358 | .parent = &mpll_clk, | |
359 | .round_rate = _clk_perclkx_round_rate, | |
360 | .set_rate = _clk_perclkx_set_rate, | |
361 | .get_rate = _clk_perclkx_recalc, | |
362 | /* Enable/Disable done via lcd_clkc[1] */ | |
363 | }, { | |
364 | .id = 3, | |
365 | .parent = &mpll_clk, | |
366 | .round_rate = _clk_perclkx_round_rate, | |
367 | .set_rate = _clk_perclkx_set_rate, | |
368 | .get_rate = _clk_perclkx_recalc, | |
369 | /* Enable/Disable done via csi_clk[1] */ | |
370 | }, | |
371 | }; | |
372 | ||
373 | static struct clk uart_ipg_clk[]; | |
374 | ||
375 | static struct clk uart_clk[] = { | |
376 | { | |
377 | .id = 0, | |
378 | .parent = &per_clk[0], | |
379 | .secondary = &uart_ipg_clk[0], | |
380 | }, { | |
381 | .id = 1, | |
382 | .parent = &per_clk[0], | |
383 | .secondary = &uart_ipg_clk[1], | |
384 | }, { | |
385 | .id = 2, | |
386 | .parent = &per_clk[0], | |
387 | .secondary = &uart_ipg_clk[2], | |
388 | }, { | |
389 | .id = 3, | |
390 | .parent = &per_clk[0], | |
391 | .secondary = &uart_ipg_clk[3], | |
392 | }, | |
393 | }; | |
394 | ||
395 | static struct clk uart_ipg_clk[] = { | |
396 | { | |
397 | .id = 0, | |
398 | .parent = &ipg_clk, | |
399 | .enable = _clk_enable, | |
400 | .enable_reg = CCM_PCCR_UART1_REG, | |
401 | .enable_shift = CCM_PCCR_UART1_OFFSET, | |
402 | .disable = _clk_disable, | |
403 | }, { | |
404 | .id = 1, | |
405 | .parent = &ipg_clk, | |
406 | .enable = _clk_enable, | |
407 | .enable_reg = CCM_PCCR_UART2_REG, | |
408 | .enable_shift = CCM_PCCR_UART2_OFFSET, | |
409 | .disable = _clk_disable, | |
410 | }, { | |
411 | .id = 2, | |
412 | .parent = &ipg_clk, | |
413 | .enable = _clk_enable, | |
414 | .enable_reg = CCM_PCCR_UART3_REG, | |
415 | .enable_shift = CCM_PCCR_UART3_OFFSET, | |
416 | .disable = _clk_disable, | |
417 | }, { | |
418 | .id = 3, | |
419 | .parent = &ipg_clk, | |
420 | .enable = _clk_enable, | |
421 | .enable_reg = CCM_PCCR_UART4_REG, | |
422 | .enable_shift = CCM_PCCR_UART4_OFFSET, | |
423 | .disable = _clk_disable, | |
424 | }, | |
425 | }; | |
426 | ||
427 | static struct clk gpt_ipg_clk[]; | |
428 | ||
429 | static struct clk gpt_clk[] = { | |
430 | { | |
431 | .id = 0, | |
432 | .parent = &per_clk[0], | |
433 | .secondary = &gpt_ipg_clk[0], | |
434 | }, { | |
435 | .id = 1, | |
436 | .parent = &per_clk[0], | |
437 | .secondary = &gpt_ipg_clk[1], | |
438 | }, { | |
439 | .id = 2, | |
440 | .parent = &per_clk[0], | |
441 | .secondary = &gpt_ipg_clk[2], | |
442 | }, | |
443 | }; | |
444 | ||
445 | static struct clk gpt_ipg_clk[] = { | |
446 | { | |
447 | .id = 0, | |
448 | .parent = &ipg_clk, | |
449 | .enable = _clk_enable, | |
450 | .enable_reg = CCM_PCCR_GPT1_REG, | |
451 | .enable_shift = CCM_PCCR_GPT1_OFFSET, | |
452 | .disable = _clk_disable, | |
453 | }, { | |
454 | .id = 1, | |
455 | .parent = &ipg_clk, | |
456 | .enable = _clk_enable, | |
457 | .enable_reg = CCM_PCCR_GPT2_REG, | |
458 | .enable_shift = CCM_PCCR_GPT2_OFFSET, | |
459 | .disable = _clk_disable, | |
460 | }, { | |
461 | .id = 2, | |
462 | .parent = &ipg_clk, | |
463 | .enable = _clk_enable, | |
464 | .enable_reg = CCM_PCCR_GPT3_REG, | |
465 | .enable_shift = CCM_PCCR_GPT3_OFFSET, | |
466 | .disable = _clk_disable, | |
467 | }, | |
468 | }; | |
469 | ||
470 | static struct clk pwm_clk[] = { | |
471 | { | |
472 | .parent = &per_clk[0], | |
473 | .secondary = &pwm_clk[1], | |
474 | }, { | |
475 | .parent = &ipg_clk, | |
476 | .enable = _clk_enable, | |
477 | .enable_reg = CCM_PCCR_PWM_REG, | |
478 | .enable_shift = CCM_PCCR_PWM_OFFSET, | |
479 | .disable = _clk_disable, | |
480 | }, | |
481 | }; | |
482 | ||
483 | static struct clk sdhc_ipg_clk[]; | |
484 | ||
485 | static struct clk sdhc_clk[] = { | |
486 | { | |
487 | .id = 0, | |
488 | .parent = &per_clk[1], | |
489 | .secondary = &sdhc_ipg_clk[0], | |
490 | }, { | |
491 | .id = 1, | |
492 | .parent = &per_clk[1], | |
493 | .secondary = &sdhc_ipg_clk[1], | |
494 | }, | |
495 | }; | |
496 | ||
497 | static struct clk sdhc_ipg_clk[] = { | |
498 | { | |
499 | .id = 0, | |
500 | .parent = &ipg_clk, | |
501 | .enable = _clk_enable, | |
502 | .enable_reg = CCM_PCCR_SDHC1_REG, | |
503 | .enable_shift = CCM_PCCR_SDHC1_OFFSET, | |
504 | .disable = _clk_disable, | |
505 | }, { | |
506 | .id = 1, | |
507 | .parent = &ipg_clk, | |
508 | .enable = _clk_enable, | |
509 | .enable_reg = CCM_PCCR_SDHC2_REG, | |
510 | .enable_shift = CCM_PCCR_SDHC2_OFFSET, | |
511 | .disable = _clk_disable, | |
512 | }, | |
513 | }; | |
514 | ||
515 | static struct clk cspi_ipg_clk[]; | |
516 | ||
517 | static struct clk cspi_clk[] = { | |
518 | { | |
519 | .id = 0, | |
520 | .parent = &per_clk[1], | |
521 | .secondary = &cspi_ipg_clk[0], | |
522 | }, { | |
523 | .id = 1, | |
524 | .parent = &per_clk[1], | |
525 | .secondary = &cspi_ipg_clk[1], | |
526 | }, { | |
527 | .id = 2, | |
528 | .parent = &per_clk[1], | |
529 | .secondary = &cspi_ipg_clk[2], | |
530 | }, | |
531 | }; | |
532 | ||
533 | static struct clk cspi_ipg_clk[] = { | |
534 | { | |
535 | .id = 0, | |
536 | .parent = &ipg_clk, | |
537 | .enable = _clk_enable, | |
538 | .enable_reg = CCM_PCCR_CSPI1_REG, | |
539 | .enable_shift = CCM_PCCR_CSPI1_OFFSET, | |
540 | .disable = _clk_disable, | |
541 | }, { | |
542 | .id = 1, | |
543 | .parent = &ipg_clk, | |
544 | .enable = _clk_enable, | |
545 | .enable_reg = CCM_PCCR_CSPI2_REG, | |
546 | .enable_shift = CCM_PCCR_CSPI2_OFFSET, | |
547 | .disable = _clk_disable, | |
548 | }, { | |
549 | .id = 3, | |
550 | .parent = &ipg_clk, | |
551 | .enable = _clk_enable, | |
552 | .enable_reg = CCM_PCCR_CSPI3_REG, | |
553 | .enable_shift = CCM_PCCR_CSPI3_OFFSET, | |
554 | .disable = _clk_disable, | |
555 | }, | |
556 | }; | |
557 | ||
558 | static struct clk lcdc_clk[] = { | |
559 | { | |
560 | .parent = &per_clk[2], | |
561 | .secondary = &lcdc_clk[1], | |
562 | .round_rate = _clk_parent_round_rate, | |
563 | .set_rate = _clk_parent_set_rate, | |
564 | }, { | |
565 | .parent = &ipg_clk, | |
566 | .secondary = &lcdc_clk[2], | |
567 | .enable = _clk_enable, | |
568 | .enable_reg = CCM_PCCR_LCDC_REG, | |
569 | .enable_shift = CCM_PCCR_LCDC_OFFSET, | |
570 | .disable = _clk_disable, | |
571 | }, { | |
572 | .parent = &hclk_clk, | |
573 | .enable = _clk_enable, | |
574 | .enable_reg = CCM_PCCR_HCLK_LCDC_REG, | |
575 | .enable_shift = CCM_PCCR_HCLK_LCDC_OFFSET, | |
576 | .disable = _clk_disable, | |
577 | }, | |
578 | }; | |
579 | ||
580 | static struct clk csi_clk[] = { | |
581 | { | |
582 | .parent = &per_clk[3], | |
583 | .secondary = &csi_clk[1], | |
584 | .round_rate = _clk_parent_round_rate, | |
585 | .set_rate = _clk_parent_set_rate, | |
586 | }, { | |
587 | .parent = &hclk_clk, | |
588 | .enable = _clk_enable, | |
589 | .enable_reg = CCM_PCCR_HCLK_CSI_REG, | |
590 | .enable_shift = CCM_PCCR_HCLK_CSI_OFFSET, | |
591 | .disable = _clk_disable, | |
592 | }, | |
593 | }; | |
594 | ||
595 | static struct clk usb_clk[] = { | |
596 | { | |
597 | .parent = &spll_clk, | |
598 | .get_rate = _clk_usb_recalc, | |
599 | .enable = _clk_enable, | |
600 | .enable_reg = CCM_PCCR_USBOTG_REG, | |
601 | .enable_shift = CCM_PCCR_USBOTG_OFFSET, | |
602 | .disable = _clk_disable, | |
603 | }, { | |
604 | .parent = &hclk_clk, | |
605 | .enable = _clk_enable, | |
606 | .enable_reg = CCM_PCCR_HCLK_USBOTG_REG, | |
607 | .enable_shift = CCM_PCCR_HCLK_USBOTG_OFFSET, | |
608 | .disable = _clk_disable, | |
609 | } | |
610 | }; | |
611 | ||
612 | static struct clk ssi_ipg_clk[]; | |
613 | ||
614 | static struct clk ssi_clk[] = { | |
615 | { | |
616 | .id = 0, | |
617 | .parent = &mpll_clk, | |
618 | .secondary = &ssi_ipg_clk[0], | |
619 | .get_rate = _clk_ssi1_recalc, | |
620 | .enable = _clk_enable, | |
621 | .enable_reg = CCM_PCCR_SSI1_BAUD_REG, | |
622 | .enable_shift = CCM_PCCR_SSI1_BAUD_OFFSET, | |
623 | .disable = _clk_disable, | |
624 | }, { | |
625 | .id = 1, | |
626 | .parent = &mpll_clk, | |
627 | .secondary = &ssi_ipg_clk[1], | |
628 | .get_rate = _clk_ssi2_recalc, | |
629 | .enable = _clk_enable, | |
630 | .enable_reg = CCM_PCCR_SSI2_BAUD_REG, | |
631 | .enable_shift = CCM_PCCR_SSI2_BAUD_OFFSET, | |
632 | .disable = _clk_disable, | |
633 | }, | |
634 | }; | |
635 | ||
636 | static struct clk ssi_ipg_clk[] = { | |
637 | { | |
638 | .id = 0, | |
639 | .parent = &ipg_clk, | |
640 | .enable = _clk_enable, | |
641 | .enable_reg = CCM_PCCR_SSI1_REG, | |
642 | .enable_shift = CCM_PCCR_SSI1_IPG_OFFSET, | |
643 | .disable = _clk_disable, | |
644 | }, { | |
645 | .id = 1, | |
646 | .parent = &ipg_clk, | |
647 | .enable = _clk_enable, | |
648 | .enable_reg = CCM_PCCR_SSI2_REG, | |
649 | .enable_shift = CCM_PCCR_SSI2_IPG_OFFSET, | |
650 | .disable = _clk_disable, | |
651 | }, | |
652 | }; | |
653 | ||
654 | ||
655 | static struct clk nfc_clk = { | |
656 | .parent = &fclk_clk, | |
657 | .get_rate = _clk_nfc_recalc, | |
658 | .enable = _clk_enable, | |
659 | .enable_reg = CCM_PCCR_NFC_REG, | |
660 | .enable_shift = CCM_PCCR_NFC_OFFSET, | |
661 | .disable = _clk_disable, | |
662 | }; | |
663 | ||
664 | static struct clk dma_clk[] = { | |
665 | { | |
666 | .parent = &hclk_clk, | |
667 | .enable = _clk_enable, | |
668 | .enable_reg = CCM_PCCR_DMA_REG, | |
669 | .enable_shift = CCM_PCCR_DMA_OFFSET, | |
670 | .disable = _clk_disable, | |
671 | .secondary = &dma_clk[1], | |
672 | }, { | |
673 | .enable = _clk_enable, | |
674 | .enable_reg = CCM_PCCR_HCLK_DMA_REG, | |
675 | .enable_shift = CCM_PCCR_HCLK_DMA_OFFSET, | |
676 | .disable = _clk_disable, | |
677 | }, | |
678 | }; | |
679 | ||
680 | static struct clk brom_clk = { | |
681 | .parent = &hclk_clk, | |
682 | .enable = _clk_enable, | |
683 | .enable_reg = CCM_PCCR_HCLK_BROM_REG, | |
684 | .enable_shift = CCM_PCCR_HCLK_BROM_OFFSET, | |
685 | .disable = _clk_disable, | |
686 | }; | |
687 | ||
688 | static struct clk emma_clk[] = { | |
689 | { | |
690 | .parent = &hclk_clk, | |
691 | .enable = _clk_enable, | |
692 | .enable_reg = CCM_PCCR_EMMA_REG, | |
693 | .enable_shift = CCM_PCCR_EMMA_OFFSET, | |
694 | .disable = _clk_disable, | |
695 | .secondary = &emma_clk[1], | |
696 | }, { | |
697 | .enable = _clk_enable, | |
698 | .enable_reg = CCM_PCCR_HCLK_EMMA_REG, | |
699 | .enable_shift = CCM_PCCR_HCLK_EMMA_OFFSET, | |
700 | .disable = _clk_disable, | |
701 | } | |
702 | }; | |
703 | ||
704 | static struct clk slcdc_clk[] = { | |
705 | { | |
706 | .parent = &hclk_clk, | |
707 | .enable = _clk_enable, | |
708 | .enable_reg = CCM_PCCR_SLCDC_REG, | |
709 | .enable_shift = CCM_PCCR_SLCDC_OFFSET, | |
710 | .disable = _clk_disable, | |
711 | .secondary = &slcdc_clk[1], | |
712 | }, { | |
713 | .enable = _clk_enable, | |
714 | .enable_reg = CCM_PCCR_HCLK_SLCDC_REG, | |
715 | .enable_shift = CCM_PCCR_HCLK_SLCDC_OFFSET, | |
716 | .disable = _clk_disable, | |
717 | } | |
718 | }; | |
719 | ||
720 | static struct clk wdog_clk = { | |
721 | .parent = &ipg_clk, | |
722 | .enable = _clk_enable, | |
723 | .enable_reg = CCM_PCCR_WDT_REG, | |
724 | .enable_shift = CCM_PCCR_WDT_OFFSET, | |
725 | .disable = _clk_disable, | |
726 | }; | |
727 | ||
728 | static struct clk gpio_clk = { | |
729 | .parent = &ipg_clk, | |
730 | .enable = _clk_enable, | |
731 | .enable_reg = CCM_PCCR_GPIO_REG, | |
732 | .enable_shift = CCM_PCCR_GPIO_OFFSET, | |
733 | .disable = _clk_disable, | |
734 | }; | |
735 | ||
736 | static struct clk i2c_clk = { | |
737 | .id = 0, | |
738 | .parent = &ipg_clk, | |
739 | .enable = _clk_enable, | |
740 | .enable_reg = CCM_PCCR_I2C1_REG, | |
741 | .enable_shift = CCM_PCCR_I2C1_OFFSET, | |
742 | .disable = _clk_disable, | |
743 | }; | |
744 | ||
745 | static struct clk kpp_clk = { | |
746 | .parent = &ipg_clk, | |
747 | .enable = _clk_enable, | |
748 | .enable_reg = CCM_PCCR_KPP_REG, | |
749 | .enable_shift = CCM_PCCR_KPP_OFFSET, | |
750 | .disable = _clk_disable, | |
751 | }; | |
752 | ||
753 | static struct clk owire_clk = { | |
754 | .parent = &ipg_clk, | |
755 | .enable = _clk_enable, | |
756 | .enable_reg = CCM_PCCR_OWIRE_REG, | |
757 | .enable_shift = CCM_PCCR_OWIRE_OFFSET, | |
758 | .disable = _clk_disable, | |
759 | }; | |
760 | ||
761 | static struct clk rtc_clk = { | |
762 | .parent = &ipg_clk, | |
763 | .enable = _clk_enable, | |
764 | .enable_reg = CCM_PCCR_RTC_REG, | |
765 | .enable_shift = CCM_PCCR_RTC_OFFSET, | |
766 | .disable = _clk_disable, | |
767 | }; | |
768 | ||
769 | static unsigned long _clk_clko_round_rate(struct clk *clk, unsigned long rate) | |
770 | { | |
771 | u32 div; | |
772 | unsigned long parent_rate; | |
773 | ||
774 | parent_rate = clk_get_rate(clk->parent); | |
775 | div = parent_rate / rate; | |
776 | if (parent_rate % rate) | |
777 | div++; | |
778 | ||
779 | if (div > 8) | |
780 | div = 8; | |
781 | ||
782 | return parent_rate / div; | |
783 | } | |
784 | ||
785 | static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) | |
786 | { | |
787 | u32 reg; | |
788 | u32 div; | |
789 | unsigned long parent_rate; | |
790 | ||
791 | parent_rate = clk_get_rate(clk->parent); | |
792 | ||
793 | div = parent_rate / rate; | |
794 | ||
795 | if (div > 8 || div < 1 || ((parent_rate / div) != rate)) | |
796 | return -EINVAL; | |
797 | div--; | |
798 | ||
799 | reg = __raw_readl(CCM_PCDR0); | |
800 | ||
801 | if (clk->parent == &usb_clk[0]) { | |
802 | reg &= ~CCM_PCDR0_48MDIV_MASK; | |
803 | reg |= div << CCM_PCDR0_48MDIV_OFFSET; | |
804 | } | |
805 | __raw_writel(reg, CCM_PCDR0); | |
806 | ||
807 | return 0; | |
808 | } | |
809 | ||
810 | static unsigned long _clk_clko_recalc(struct clk *clk) | |
811 | { | |
812 | u32 div = 0; | |
813 | unsigned long parent_rate; | |
814 | ||
815 | parent_rate = clk_get_rate(clk->parent); | |
816 | ||
817 | if (clk->parent == &usb_clk[0]) /* 48M */ | |
818 | div = __raw_readl(CCM_PCDR0) & CCM_PCDR0_48MDIV_MASK | |
819 | >> CCM_PCDR0_48MDIV_OFFSET; | |
820 | div++; | |
821 | ||
822 | return parent_rate / div; | |
823 | } | |
824 | ||
825 | static struct clk clko_clk; | |
826 | ||
827 | static int _clk_clko_set_parent(struct clk *clk, struct clk *parent) | |
828 | { | |
829 | u32 reg; | |
830 | ||
831 | reg = __raw_readl(CCM_CCSR) & ~CCM_CCSR_CLKOSEL_MASK; | |
832 | ||
833 | if (parent == &ckil_clk) | |
834 | reg |= 0 << CCM_CCSR_CLKOSEL_OFFSET; | |
835 | else if (parent == &fpm_clk) | |
836 | reg |= 1 << CCM_CCSR_CLKOSEL_OFFSET; | |
837 | else if (parent == &ckih_clk) | |
838 | reg |= 2 << CCM_CCSR_CLKOSEL_OFFSET; | |
839 | else if (parent == mpll_clk.parent) | |
840 | reg |= 3 << CCM_CCSR_CLKOSEL_OFFSET; | |
841 | else if (parent == spll_clk.parent) | |
842 | reg |= 4 << CCM_CCSR_CLKOSEL_OFFSET; | |
843 | else if (parent == &mpll_clk) | |
844 | reg |= 5 << CCM_CCSR_CLKOSEL_OFFSET; | |
845 | else if (parent == &spll_clk) | |
846 | reg |= 6 << CCM_CCSR_CLKOSEL_OFFSET; | |
847 | else if (parent == &fclk_clk) | |
848 | reg |= 7 << CCM_CCSR_CLKOSEL_OFFSET; | |
849 | else if (parent == &hclk_clk) | |
850 | reg |= 8 << CCM_CCSR_CLKOSEL_OFFSET; | |
851 | else if (parent == &ipg_clk) | |
852 | reg |= 9 << CCM_CCSR_CLKOSEL_OFFSET; | |
853 | else if (parent == &per_clk[0]) | |
854 | reg |= 0xA << CCM_CCSR_CLKOSEL_OFFSET; | |
855 | else if (parent == &per_clk[1]) | |
856 | reg |= 0xB << CCM_CCSR_CLKOSEL_OFFSET; | |
857 | else if (parent == &per_clk[2]) | |
858 | reg |= 0xC << CCM_CCSR_CLKOSEL_OFFSET; | |
859 | else if (parent == &per_clk[3]) | |
860 | reg |= 0xD << CCM_CCSR_CLKOSEL_OFFSET; | |
861 | else if (parent == &ssi_clk[0]) | |
862 | reg |= 0xE << CCM_CCSR_CLKOSEL_OFFSET; | |
863 | else if (parent == &ssi_clk[1]) | |
864 | reg |= 0xF << CCM_CCSR_CLKOSEL_OFFSET; | |
865 | else if (parent == &nfc_clk) | |
866 | reg |= 0x10 << CCM_CCSR_CLKOSEL_OFFSET; | |
867 | else if (parent == &usb_clk[0]) | |
868 | reg |= 0x14 << CCM_CCSR_CLKOSEL_OFFSET; | |
869 | else if (parent == &clko_clk) | |
870 | reg |= 0x15 << CCM_CCSR_CLKOSEL_OFFSET; | |
871 | else | |
872 | return -EINVAL; | |
873 | ||
874 | __raw_writel(reg, CCM_CCSR); | |
875 | ||
876 | return 0; | |
877 | } | |
878 | ||
879 | static struct clk clko_clk = { | |
880 | .get_rate = _clk_clko_recalc, | |
881 | .set_rate = _clk_clko_set_rate, | |
882 | .round_rate = _clk_clko_round_rate, | |
883 | .set_parent = _clk_clko_set_parent, | |
884 | }; | |
885 | ||
886 | ||
887 | #define _REGISTER_CLOCK(d, n, c) \ | |
888 | { \ | |
889 | .dev_id = d, \ | |
890 | .con_id = n, \ | |
891 | .clk = &c, \ | |
892 | }, | |
6b4bfb87 | 893 | static struct clk_lookup lookups[] = { |
aa3b0a6f HS |
894 | /* It's unlikely that any driver wants one of them directly: |
895 | _REGISTER_CLOCK(NULL, "ckih", ckih_clk) | |
896 | _REGISTER_CLOCK(NULL, "ckil", ckil_clk) | |
897 | _REGISTER_CLOCK(NULL, "fpm", fpm_clk) | |
898 | _REGISTER_CLOCK(NULL, "mpll", mpll_clk) | |
899 | _REGISTER_CLOCK(NULL, "spll", spll_clk) | |
900 | _REGISTER_CLOCK(NULL, "fclk", fclk_clk) | |
901 | _REGISTER_CLOCK(NULL, "hclk", hclk_clk) | |
902 | _REGISTER_CLOCK(NULL, "ipg", ipg_clk) | |
903 | */ | |
904 | _REGISTER_CLOCK(NULL, "perclk1", per_clk[0]) | |
905 | _REGISTER_CLOCK(NULL, "perclk2", per_clk[1]) | |
906 | _REGISTER_CLOCK(NULL, "perclk3", per_clk[2]) | |
907 | _REGISTER_CLOCK(NULL, "perclk4", per_clk[3]) | |
908 | _REGISTER_CLOCK(NULL, "clko", clko_clk) | |
909 | _REGISTER_CLOCK("imx-uart.0", NULL, uart_clk[0]) | |
910 | _REGISTER_CLOCK("imx-uart.1", NULL, uart_clk[1]) | |
911 | _REGISTER_CLOCK("imx-uart.2", NULL, uart_clk[2]) | |
912 | _REGISTER_CLOCK("imx-uart.3", NULL, uart_clk[3]) | |
913 | _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[0]) | |
914 | _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[1]) | |
915 | _REGISTER_CLOCK(NULL, "gpt1", gpt_clk[2]) | |
916 | _REGISTER_CLOCK(NULL, "pwm", pwm_clk[0]) | |
917 | _REGISTER_CLOCK(NULL, "sdhc1", sdhc_clk[0]) | |
918 | _REGISTER_CLOCK(NULL, "sdhc2", sdhc_clk[1]) | |
919 | _REGISTER_CLOCK(NULL, "cspi1", cspi_clk[0]) | |
920 | _REGISTER_CLOCK(NULL, "cspi2", cspi_clk[1]) | |
921 | _REGISTER_CLOCK(NULL, "cspi3", cspi_clk[2]) | |
289a689b | 922 | _REGISTER_CLOCK("imx-fb.0", NULL, lcdc_clk[0]) |
aa3b0a6f HS |
923 | _REGISTER_CLOCK(NULL, "csi", csi_clk[0]) |
924 | _REGISTER_CLOCK(NULL, "usb", usb_clk[0]) | |
925 | _REGISTER_CLOCK(NULL, "ssi1", ssi_clk[0]) | |
926 | _REGISTER_CLOCK(NULL, "ssi2", ssi_clk[1]) | |
289a689b | 927 | _REGISTER_CLOCK("mxc_nand.0", NULL, nfc_clk) |
aa3b0a6f HS |
928 | _REGISTER_CLOCK(NULL, "dma", dma_clk[0]) |
929 | _REGISTER_CLOCK(NULL, "brom", brom_clk) | |
930 | _REGISTER_CLOCK(NULL, "emma", emma_clk[0]) | |
931 | _REGISTER_CLOCK(NULL, "slcdc", slcdc_clk[0]) | |
289a689b | 932 | _REGISTER_CLOCK("imx-wdt.0", NULL, wdog_clk) |
aa3b0a6f | 933 | _REGISTER_CLOCK(NULL, "gpio", gpio_clk) |
1b3c9bf2 | 934 | _REGISTER_CLOCK("imx-i2c.0", NULL, i2c_clk) |
aa3b0a6f HS |
935 | _REGISTER_CLOCK("mxc-keypad", NULL, kpp_clk) |
936 | _REGISTER_CLOCK(NULL, "owire", owire_clk) | |
937 | _REGISTER_CLOCK(NULL, "rtc", rtc_clk) | |
938 | }; | |
939 | ||
940 | /* | |
941 | * must be called very early to get information about the | |
942 | * available clock rate when the timer framework starts | |
943 | */ | |
944 | int __init mx21_clocks_init(unsigned long lref, unsigned long href) | |
945 | { | |
946 | int i; | |
947 | u32 cscr; | |
948 | ||
949 | external_low_reference = lref; | |
950 | external_high_reference = href; | |
951 | ||
952 | /* detect clock reference for both system PLL */ | |
953 | cscr = CSCR(); | |
954 | if (cscr & CCM_CSCR_MCU) | |
955 | mpll_clk.parent = &ckih_clk; | |
956 | else | |
957 | mpll_clk.parent = &fpm_clk; | |
958 | ||
959 | if (cscr & CCM_CSCR_SP) | |
960 | spll_clk.parent = &ckih_clk; | |
961 | else | |
962 | spll_clk.parent = &fpm_clk; | |
963 | ||
964 | for (i = 0; i < ARRAY_SIZE(lookups); i++) | |
965 | clkdev_add(&lookups[i]); | |
966 | ||
967 | /* Turn off all clock gates */ | |
968 | __raw_writel(0, CCM_PCCR0); | |
969 | __raw_writel(CCM_PCCR_GPT1_MASK, CCM_PCCR1); | |
970 | ||
971 | /* This turns of the serial PLL as well */ | |
972 | spll_clk.disable(&spll_clk); | |
973 | ||
974 | /* This will propagate to all children and init all the clock rates. */ | |
975 | clk_enable(&per_clk[0]); | |
976 | clk_enable(&gpio_clk); | |
977 | ||
978 | #ifdef CONFIG_DEBUG_LL_CONSOLE | |
979 | clk_enable(&uart_clk[0]); | |
980 | #endif | |
981 | ||
982 | mxc_timer_init(&gpt_clk[0]); | |
983 | return 0; | |
984 | } |