blob: 5d061ea0c51378b3cb71e9a75bf808b322648c83 [file] [log] [blame]
Ben Dookse4d06e32007-02-16 12:12:31 +01001/* linux/arch/arm/mach-s3c2443/clock.c
2 *
3 * Copyright (c) 2007 Simtec Electronics
4 * Ben Dooks <ben@simtec.co.uk>
5 *
6 * S3C2443 Clock control support
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21*/
22
23#include <linux/init.h>
24#include <linux/module.h>
25#include <linux/kernel.h>
26#include <linux/list.h>
27#include <linux/errno.h>
28#include <linux/err.h>
29#include <linux/sysdev.h>
30#include <linux/clk.h>
31#include <linux/mutex.h>
Ben Dookse4d06e32007-02-16 12:12:31 +010032#include <linux/serial_core.h>
Russell Kingfced80c2008-09-06 12:10:45 +010033#include <linux/io.h>
Ben Dookse4d06e32007-02-16 12:12:31 +010034
35#include <asm/mach/map.h>
36
Russell Kinga09e64f2008-08-05 16:14:15 +010037#include <mach/hardware.h>
Ben Dookse4d06e32007-02-16 12:12:31 +010038
Russell Kinga09e64f2008-08-05 16:14:15 +010039#include <mach/regs-s3c2443-clock.h>
Ben Dookse4d06e32007-02-16 12:12:31 +010040
Ben Dookse4253822008-10-21 14:06:38 +010041#include <plat/cpu-freq.h>
42
Ben Dooksa2b7ba92008-10-07 22:26:09 +010043#include <plat/s3c2443.h>
Ben Dooksd5120ae2008-10-07 23:09:51 +010044#include <plat/clock.h>
Ben Dooksa2b7ba92008-10-07 22:26:09 +010045#include <plat/cpu.h>
Ben Dookse4d06e32007-02-16 12:12:31 +010046
47/* We currently have to assume that the system is running
48 * from the XTPll input, and that all ***REFCLKs are being
49 * fed from it, as we cannot read the state of OM[4] from
50 * software.
51 *
52 * It would be possible for each board initialisation to
53 * set the correct muxing at initialisation
54*/
55
Ben Dooks4ec07bb2010-01-30 15:02:58 +090056static int s3c2443_gate(void __iomem *reg, struct clk *clk, int enable)
Ben Dookse4d06e32007-02-16 12:12:31 +010057{
Ben Dooks4ec07bb2010-01-30 15:02:58 +090058 u32 ctrlbit = clk->ctrlbit;
59 u32 con = __raw_readl(reg);
Ben Dookse4d06e32007-02-16 12:12:31 +010060
61 if (enable)
Ben Dooks4ec07bb2010-01-30 15:02:58 +090062 con |= ctrlbit;
Ben Dookse4d06e32007-02-16 12:12:31 +010063 else
Ben Dooks4ec07bb2010-01-30 15:02:58 +090064 con &= ~ctrlbit;
Ben Dookse4d06e32007-02-16 12:12:31 +010065
Ben Dooks4ec07bb2010-01-30 15:02:58 +090066 __raw_writel(con, reg);
Ben Dookse4d06e32007-02-16 12:12:31 +010067 return 0;
68}
69
Ben Dooks4ec07bb2010-01-30 15:02:58 +090070static int s3c2443_clkcon_enable_h(struct clk *clk, int enable)
71{
72 return s3c2443_gate(S3C2443_HCLKCON, clk, enable);
73}
74
Ben Dookse4d06e32007-02-16 12:12:31 +010075static int s3c2443_clkcon_enable_p(struct clk *clk, int enable)
76{
Ben Dooks4ec07bb2010-01-30 15:02:58 +090077 return s3c2443_gate(S3C2443_PCLKCON, clk, enable);
Ben Dookse4d06e32007-02-16 12:12:31 +010078}
79
80static int s3c2443_clkcon_enable_s(struct clk *clk, int enable)
81{
Ben Dooks4ec07bb2010-01-30 15:02:58 +090082 return s3c2443_gate(S3C2443_SCLKCON, clk, enable);
Ben Dookse4d06e32007-02-16 12:12:31 +010083}
84
85static unsigned long s3c2443_roundrate_clksrc(struct clk *clk,
86 unsigned long rate,
87 unsigned int max)
88{
89 unsigned long parent_rate = clk_get_rate(clk->parent);
90 int div;
91
92 if (rate > parent_rate)
93 return parent_rate;
94
95 /* note, we remove the +/- 1 calculations as they cancel out */
96
97 div = (rate / parent_rate);
98
99 if (div < 1)
100 div = 1;
101 else if (div > max)
102 div = max;
103
104 return parent_rate / div;
105}
106
107static unsigned long s3c2443_roundrate_clksrc4(struct clk *clk,
108 unsigned long rate)
109{
110 return s3c2443_roundrate_clksrc(clk, rate, 4);
111}
112
113static unsigned long s3c2443_roundrate_clksrc16(struct clk *clk,
114 unsigned long rate)
115{
116 return s3c2443_roundrate_clksrc(clk, rate, 16);
117}
118
119static unsigned long s3c2443_roundrate_clksrc256(struct clk *clk,
120 unsigned long rate)
121{
122 return s3c2443_roundrate_clksrc(clk, rate, 256);
123}
124
125/* clock selections */
126
Ben Dookse4d06e32007-02-16 12:12:31 +0100127static struct clk clk_mpllref = {
128 .name = "mpllref",
129 .parent = &clk_xtal,
130 .id = -1,
131};
132
133#if 0
134static struct clk clk_mpll = {
135 .name = "mpll",
136 .parent = &clk_mpllref,
137 .id = -1,
138};
139#endif
140
Ben Dookse4d06e32007-02-16 12:12:31 +0100141static struct clk clk_i2s_ext = {
142 .name = "i2s-ext",
143 .id = -1,
144};
145
146static int s3c2443_setparent_epllref(struct clk *clk, struct clk *parent)
147{
148 unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
149
150 clksrc &= ~S3C2443_CLKSRC_EPLLREF_MASK;
151
152 if (parent == &clk_xtal)
153 clksrc |= S3C2443_CLKSRC_EPLLREF_XTAL;
154 else if (parent == &clk_ext)
155 clksrc |= S3C2443_CLKSRC_EPLLREF_EXTCLK;
156 else if (parent != &clk_mpllref)
157 return -EINVAL;
158
159 __raw_writel(clksrc, S3C2443_CLKSRC);
160 clk->parent = parent;
161
162 return 0;
163}
164
165static struct clk clk_epllref = {
166 .name = "epllref",
167 .id = -1,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000168 .ops = &(struct clk_ops) {
169 .set_parent = s3c2443_setparent_epllref,
170 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100171};
172
173static unsigned long s3c2443_getrate_mdivclk(struct clk *clk)
174{
175 unsigned long parent_rate = clk_get_rate(clk->parent);
176 unsigned long div = __raw_readl(S3C2443_CLKDIV0);
177
178 div &= S3C2443_CLKDIV0_EXTDIV_MASK;
179 div >>= (S3C2443_CLKDIV0_EXTDIV_SHIFT-1); /* x2 */
180
181 return parent_rate / (div + 1);
182}
183
184static struct clk clk_mdivclk = {
185 .name = "mdivclk",
186 .parent = &clk_mpllref,
187 .id = -1,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000188 .ops = &(struct clk_ops) {
189 .get_rate = s3c2443_getrate_mdivclk,
190 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100191};
192
Ben Dookse4d06e32007-02-16 12:12:31 +0100193static int s3c2443_setparent_msysclk(struct clk *clk, struct clk *parent)
194{
195 unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
196
197 clksrc &= ~(S3C2443_CLKSRC_MSYSCLK_MPLL |
198 S3C2443_CLKSRC_EXTCLK_DIV);
199
200 if (parent == &clk_mpll)
201 clksrc |= S3C2443_CLKSRC_MSYSCLK_MPLL;
202 else if (parent == &clk_mdivclk)
203 clksrc |= S3C2443_CLKSRC_EXTCLK_DIV;
204 else if (parent != &clk_mpllref)
205 return -EINVAL;
206
207 __raw_writel(clksrc, S3C2443_CLKSRC);
208 clk->parent = parent;
209
210 return 0;
211}
212
213static struct clk clk_msysclk = {
214 .name = "msysclk",
215 .parent = &clk_xtal,
216 .id = -1,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000217 .ops = &(struct clk_ops) {
218 .set_parent = s3c2443_setparent_msysclk,
219 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100220};
221
Ben Dooksba7622a2008-07-07 18:12:39 +0100222/* armdiv
223 *
224 * this clock is sourced from msysclk and can have a number of
225 * divider values applied to it to then be fed into armclk.
226*/
227
228static struct clk clk_armdiv = {
229 .name = "armdiv",
230 .id = -1,
231 .parent = &clk_msysclk,
232};
233
234/* armclk
235 *
236 * this is the clock fed into the ARM core itself, either from
237 * armdiv or from hclk.
238 */
239
240static int s3c2443_setparent_armclk(struct clk *clk, struct clk *parent)
241{
242 unsigned long clkdiv0;
243
244 clkdiv0 = __raw_readl(S3C2443_CLKDIV0);
245
246 if (parent == &clk_armdiv)
247 clkdiv0 &= ~S3C2443_CLKDIV0_DVS;
248 else if (parent == &clk_h)
249 clkdiv0 |= S3C2443_CLKDIV0_DVS;
250 else
251 return -EINVAL;
252
253 __raw_writel(clkdiv0, S3C2443_CLKDIV0);
254 return 0;
255}
256
257static struct clk clk_arm = {
258 .name = "armclk",
259 .id = -1,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000260 .ops = &(struct clk_ops) {
261 .set_parent = s3c2443_setparent_armclk,
262 },
Ben Dooksba7622a2008-07-07 18:12:39 +0100263};
Ben Dookse4d06e32007-02-16 12:12:31 +0100264
265/* esysclk
266 *
267 * this is sourced from either the EPLL or the EPLLref clock
268*/
269
270static int s3c2443_setparent_esysclk(struct clk *clk, struct clk *parent)
271{
272 unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
273
274 if (parent == &clk_epll)
275 clksrc |= S3C2443_CLKSRC_ESYSCLK_EPLL;
276 else if (parent == &clk_epllref)
277 clksrc &= ~S3C2443_CLKSRC_ESYSCLK_EPLL;
278 else
279 return -EINVAL;
280
281 __raw_writel(clksrc, S3C2443_CLKSRC);
282 clk->parent = parent;
283
284 return 0;
285}
286
287static struct clk clk_esysclk = {
288 .name = "esysclk",
289 .parent = &clk_epll,
290 .id = -1,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000291 .ops = &(struct clk_ops) {
292 .set_parent = s3c2443_setparent_esysclk,
293 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100294};
295
296/* uartclk
297 *
298 * UART baud-rate clock sourced from esysclk via a divisor
299*/
300
301static unsigned long s3c2443_getrate_uart(struct clk *clk)
302{
303 unsigned long parent_rate = clk_get_rate(clk->parent);
304 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
305
306 div &= S3C2443_CLKDIV1_UARTDIV_MASK;
307 div >>= S3C2443_CLKDIV1_UARTDIV_SHIFT;
308
309 return parent_rate / (div + 1);
310}
311
312
313static int s3c2443_setrate_uart(struct clk *clk, unsigned long rate)
314{
315 unsigned long parent_rate = clk_get_rate(clk->parent);
316 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
317
318 rate = s3c2443_roundrate_clksrc16(clk, rate);
319 rate = parent_rate / rate;
320
321 clkdivn &= ~S3C2443_CLKDIV1_UARTDIV_MASK;
322 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_UARTDIV_SHIFT;
323
324 __raw_writel(clkdivn, S3C2443_CLKDIV1);
325 return 0;
326}
327
328static struct clk clk_uart = {
329 .name = "uartclk",
330 .id = -1,
331 .parent = &clk_esysclk,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000332 .ops = &(struct clk_ops) {
333 .get_rate = s3c2443_getrate_uart,
334 .set_rate = s3c2443_setrate_uart,
335 .round_rate = s3c2443_roundrate_clksrc16,
336 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100337};
338
339/* hsspi
340 *
341 * high-speed spi clock, sourced from esysclk
342*/
343
344static unsigned long s3c2443_getrate_hsspi(struct clk *clk)
345{
346 unsigned long parent_rate = clk_get_rate(clk->parent);
347 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
348
349 div &= S3C2443_CLKDIV1_HSSPIDIV_MASK;
350 div >>= S3C2443_CLKDIV1_HSSPIDIV_SHIFT;
351
352 return parent_rate / (div + 1);
353}
354
355
356static int s3c2443_setrate_hsspi(struct clk *clk, unsigned long rate)
357{
358 unsigned long parent_rate = clk_get_rate(clk->parent);
359 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
360
361 rate = s3c2443_roundrate_clksrc4(clk, rate);
362 rate = parent_rate / rate;
363
364 clkdivn &= ~S3C2443_CLKDIV1_HSSPIDIV_MASK;
365 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_HSSPIDIV_SHIFT;
366
367 __raw_writel(clkdivn, S3C2443_CLKDIV1);
368 return 0;
369}
370
371static struct clk clk_hsspi = {
372 .name = "hsspi",
373 .id = -1,
374 .parent = &clk_esysclk,
375 .ctrlbit = S3C2443_SCLKCON_HSSPICLK,
376 .enable = s3c2443_clkcon_enable_s,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000377 .ops = &(struct clk_ops) {
378 .get_rate = s3c2443_getrate_hsspi,
379 .set_rate = s3c2443_setrate_hsspi,
380 .round_rate = s3c2443_roundrate_clksrc4,
381 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100382};
383
384/* usbhost
385 *
386 * usb host bus-clock, usually 48MHz to provide USB bus clock timing
387*/
388
389static unsigned long s3c2443_getrate_usbhost(struct clk *clk)
390{
391 unsigned long parent_rate = clk_get_rate(clk->parent);
392 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
393
394 div &= S3C2443_CLKDIV1_USBHOSTDIV_MASK;
395 div >>= S3C2443_CLKDIV1_USBHOSTDIV_SHIFT;
396
397 return parent_rate / (div + 1);
398}
399
400static int s3c2443_setrate_usbhost(struct clk *clk, unsigned long rate)
401{
402 unsigned long parent_rate = clk_get_rate(clk->parent);
403 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
404
405 rate = s3c2443_roundrate_clksrc4(clk, rate);
406 rate = parent_rate / rate;
407
408 clkdivn &= ~S3C2443_CLKDIV1_USBHOSTDIV_MASK;
409 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_USBHOSTDIV_SHIFT;
410
411 __raw_writel(clkdivn, S3C2443_CLKDIV1);
412 return 0;
413}
414
Ben Dooks0cc69da2007-05-28 18:55:43 +0100415static struct clk clk_usb_bus_host = {
Ben Dookse4d06e32007-02-16 12:12:31 +0100416 .name = "usb-bus-host-parent",
417 .id = -1,
418 .parent = &clk_esysclk,
419 .ctrlbit = S3C2443_SCLKCON_USBHOST,
420 .enable = s3c2443_clkcon_enable_s,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000421 .ops = &(struct clk_ops) {
422 .get_rate = s3c2443_getrate_usbhost,
423 .set_rate = s3c2443_setrate_usbhost,
424 .round_rate = s3c2443_roundrate_clksrc4,
425 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100426};
427
428/* clk_hsmcc_div
429 *
430 * this clock is sourced from epll, and is fed through a divider,
431 * to a mux controlled by sclkcon where either it or a extclk can
432 * be fed to the hsmmc block
433*/
434
435static unsigned long s3c2443_getrate_hsmmc_div(struct clk *clk)
436{
437 unsigned long parent_rate = clk_get_rate(clk->parent);
438 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
439
440 div &= S3C2443_CLKDIV1_HSMMCDIV_MASK;
441 div >>= S3C2443_CLKDIV1_HSMMCDIV_SHIFT;
442
443 return parent_rate / (div + 1);
444}
445
446static int s3c2443_setrate_hsmmc_div(struct clk *clk, unsigned long rate)
447{
448 unsigned long parent_rate = clk_get_rate(clk->parent);
449 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
450
451 rate = s3c2443_roundrate_clksrc4(clk, rate);
452 rate = parent_rate / rate;
453
454 clkdivn &= ~S3C2443_CLKDIV1_HSMMCDIV_MASK;
455 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_HSMMCDIV_SHIFT;
456
457 __raw_writel(clkdivn, S3C2443_CLKDIV1);
458 return 0;
459}
460
461static struct clk clk_hsmmc_div = {
462 .name = "hsmmc-div",
463 .id = -1,
464 .parent = &clk_esysclk,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000465 .ops = &(struct clk_ops) {
466 .get_rate = s3c2443_getrate_hsmmc_div,
467 .set_rate = s3c2443_setrate_hsmmc_div,
468 .round_rate = s3c2443_roundrate_clksrc4,
469 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100470};
471
472static int s3c2443_setparent_hsmmc(struct clk *clk, struct clk *parent)
473{
474 unsigned long clksrc = __raw_readl(S3C2443_SCLKCON);
475
476 clksrc &= ~(S3C2443_SCLKCON_HSMMCCLK_EXT |
477 S3C2443_SCLKCON_HSMMCCLK_EPLL);
478
479 if (parent == &clk_epll)
480 clksrc |= S3C2443_SCLKCON_HSMMCCLK_EPLL;
481 else if (parent == &clk_ext)
482 clksrc |= S3C2443_SCLKCON_HSMMCCLK_EXT;
483 else
484 return -EINVAL;
485
486 if (clk->usage > 0) {
487 __raw_writel(clksrc, S3C2443_SCLKCON);
488 }
489
490 clk->parent = parent;
491 return 0;
492}
493
494static int s3c2443_enable_hsmmc(struct clk *clk, int enable)
495{
496 return s3c2443_setparent_hsmmc(clk, clk->parent);
497}
498
499static struct clk clk_hsmmc = {
500 .name = "hsmmc-if",
501 .id = -1,
502 .parent = &clk_hsmmc_div,
503 .enable = s3c2443_enable_hsmmc,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000504 .ops = &(struct clk_ops) {
505 .set_parent = s3c2443_setparent_hsmmc,
506 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100507};
508
509/* i2s_eplldiv
510 *
511 * this clock is the output from the i2s divisor of esysclk
512*/
513
514static unsigned long s3c2443_getrate_i2s_eplldiv(struct clk *clk)
515{
516 unsigned long parent_rate = clk_get_rate(clk->parent);
517 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
518
519 div &= S3C2443_CLKDIV1_I2SDIV_MASK;
520 div >>= S3C2443_CLKDIV1_I2SDIV_SHIFT;
521
522 return parent_rate / (div + 1);
523}
524
525static int s3c2443_setrate_i2s_eplldiv(struct clk *clk, unsigned long rate)
526{
527 unsigned long parent_rate = clk_get_rate(clk->parent);
528 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
529
530 rate = s3c2443_roundrate_clksrc16(clk, rate);
531 rate = parent_rate / rate;
532
533 clkdivn &= ~S3C2443_CLKDIV1_I2SDIV_MASK;
534 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_I2SDIV_SHIFT;
535
536 __raw_writel(clkdivn, S3C2443_CLKDIV1);
537 return 0;
538}
539
540static struct clk clk_i2s_eplldiv = {
541 .name = "i2s-eplldiv",
542 .id = -1,
543 .parent = &clk_esysclk,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000544 .ops = &(struct clk_ops) {
545 .get_rate = s3c2443_getrate_i2s_eplldiv,
546 .set_rate = s3c2443_setrate_i2s_eplldiv,
547 .round_rate = s3c2443_roundrate_clksrc16,
548 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100549};
550
551/* i2s-ref
552 *
553 * i2s bus reference clock, selectable from external, esysclk or epllref
554*/
555
556static int s3c2443_setparent_i2s(struct clk *clk, struct clk *parent)
557{
558 unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
559
560 clksrc &= ~S3C2443_CLKSRC_I2S_MASK;
561
562 if (parent == &clk_epllref)
563 clksrc |= S3C2443_CLKSRC_I2S_EPLLREF;
564 else if (parent == &clk_i2s_ext)
565 clksrc |= S3C2443_CLKSRC_I2S_EXT;
566 else if (parent != &clk_i2s_eplldiv)
567 return -EINVAL;
568
569 clk->parent = parent;
570 __raw_writel(clksrc, S3C2443_CLKSRC);
571
572 return 0;
573}
574
575static struct clk clk_i2s = {
576 .name = "i2s-if",
577 .id = -1,
578 .parent = &clk_i2s_eplldiv,
579 .ctrlbit = S3C2443_SCLKCON_I2SCLK,
580 .enable = s3c2443_clkcon_enable_s,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000581 .ops = &(struct clk_ops) {
582 .set_parent = s3c2443_setparent_i2s,
583 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100584};
585
586/* cam-if
587 *
588 * camera interface bus-clock, divided down from esysclk
589*/
590
591static unsigned long s3c2443_getrate_cam(struct clk *clk)
592{
593 unsigned long parent_rate = clk_get_rate(clk->parent);
594 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
595
596 div &= S3C2443_CLKDIV1_CAMDIV_MASK;
597 div >>= S3C2443_CLKDIV1_CAMDIV_SHIFT;
598
599 return parent_rate / (div + 1);
600}
601
602static int s3c2443_setrate_cam(struct clk *clk, unsigned long rate)
603{
604 unsigned long parent_rate = clk_get_rate(clk->parent);
605 unsigned long clkdiv1 = __raw_readl(S3C2443_CLKDIV1);
606
607 rate = s3c2443_roundrate_clksrc16(clk, rate);
608 rate = parent_rate / rate;
609
610 clkdiv1 &= ~S3C2443_CLKDIV1_CAMDIV_MASK;
611 clkdiv1 |= (rate - 1) << S3C2443_CLKDIV1_CAMDIV_SHIFT;
612
613 __raw_writel(clkdiv1, S3C2443_CLKDIV1);
614 return 0;
615}
616
617static struct clk clk_cam = {
618 .name = "camif-upll", /* same as 2440 name */
619 .id = -1,
620 .parent = &clk_esysclk,
621 .ctrlbit = S3C2443_SCLKCON_CAMCLK,
622 .enable = s3c2443_clkcon_enable_s,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000623 .ops = &(struct clk_ops) {
624 .get_rate = s3c2443_getrate_cam,
625 .set_rate = s3c2443_setrate_cam,
626 .round_rate = s3c2443_roundrate_clksrc16,
627 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100628};
629
630/* display-if
631 *
632 * display interface clock, divided from esysclk
633*/
634
635static unsigned long s3c2443_getrate_display(struct clk *clk)
636{
637 unsigned long parent_rate = clk_get_rate(clk->parent);
638 unsigned long div = __raw_readl(S3C2443_CLKDIV1);
639
640 div &= S3C2443_CLKDIV1_DISPDIV_MASK;
641 div >>= S3C2443_CLKDIV1_DISPDIV_SHIFT;
642
643 return parent_rate / (div + 1);
644}
645
646static int s3c2443_setrate_display(struct clk *clk, unsigned long rate)
647{
648 unsigned long parent_rate = clk_get_rate(clk->parent);
649 unsigned long clkdivn = __raw_readl(S3C2443_CLKDIV1);
650
651 rate = s3c2443_roundrate_clksrc256(clk, rate);
652 rate = parent_rate / rate;
653
654 clkdivn &= ~S3C2443_CLKDIV1_UARTDIV_MASK;
655 clkdivn |= (rate - 1) << S3C2443_CLKDIV1_UARTDIV_SHIFT;
656
657 __raw_writel(clkdivn, S3C2443_CLKDIV1);
658 return 0;
659}
660
661static struct clk clk_display = {
662 .name = "display-if",
663 .id = -1,
664 .parent = &clk_esysclk,
665 .ctrlbit = S3C2443_SCLKCON_DISPCLK,
666 .enable = s3c2443_clkcon_enable_s,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000667 .ops = &(struct clk_ops) {
668 .get_rate = s3c2443_getrate_display,
669 .set_rate = s3c2443_setrate_display,
670 .round_rate = s3c2443_roundrate_clksrc256,
671 },
Ben Dookse4d06e32007-02-16 12:12:31 +0100672};
673
Ben Dooks2e16c272008-07-07 18:12:40 +0100674/* prediv
675 *
676 * this divides the msysclk down to pass to h/p/etc.
677 */
678
679static unsigned long s3c2443_prediv_getrate(struct clk *clk)
680{
681 unsigned long rate = clk_get_rate(clk->parent);
682 unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0);
683
684 clkdiv0 &= S3C2443_CLKDIV0_PREDIV_MASK;
685 clkdiv0 >>= S3C2443_CLKDIV0_PREDIV_SHIFT;
686
687 return rate / (clkdiv0 + 1);
688}
689
690static struct clk clk_prediv = {
691 .name = "prediv",
692 .id = -1,
693 .parent = &clk_msysclk,
Ben Dooksb3bf41b2009-12-01 01:24:37 +0000694 .ops = &(struct clk_ops) {
695 .get_rate = s3c2443_prediv_getrate,
696 },
Ben Dooks2e16c272008-07-07 18:12:40 +0100697};
698
Ben Dookse4d06e32007-02-16 12:12:31 +0100699/* standard clock definitions */
700
701static struct clk init_clocks_disable[] = {
702 {
703 .name = "nand",
704 .id = -1,
705 .parent = &clk_h,
706 }, {
707 .name = "sdi",
708 .id = -1,
709 .parent = &clk_p,
710 .enable = s3c2443_clkcon_enable_p,
711 .ctrlbit = S3C2443_PCLKCON_SDI,
712 }, {
713 .name = "adc",
714 .id = -1,
715 .parent = &clk_p,
716 .enable = s3c2443_clkcon_enable_p,
717 .ctrlbit = S3C2443_PCLKCON_ADC,
718 }, {
719 .name = "i2c",
720 .id = -1,
721 .parent = &clk_p,
722 .enable = s3c2443_clkcon_enable_p,
723 .ctrlbit = S3C2443_PCLKCON_IIC,
724 }, {
725 .name = "iis",
726 .id = -1,
727 .parent = &clk_p,
728 .enable = s3c2443_clkcon_enable_p,
729 .ctrlbit = S3C2443_PCLKCON_IIS,
730 }, {
731 .name = "spi",
732 .id = 0,
733 .parent = &clk_p,
734 .enable = s3c2443_clkcon_enable_p,
735 .ctrlbit = S3C2443_PCLKCON_SPI0,
736 }, {
737 .name = "spi",
738 .id = 1,
739 .parent = &clk_p,
740 .enable = s3c2443_clkcon_enable_p,
741 .ctrlbit = S3C2443_PCLKCON_SPI1,
742 }
743};
744
745static struct clk init_clocks[] = {
746 {
747 .name = "dma",
748 .id = 0,
749 .parent = &clk_h,
750 .enable = s3c2443_clkcon_enable_h,
751 .ctrlbit = S3C2443_HCLKCON_DMA0,
752 }, {
753 .name = "dma",
754 .id = 1,
755 .parent = &clk_h,
756 .enable = s3c2443_clkcon_enable_h,
757 .ctrlbit = S3C2443_HCLKCON_DMA1,
758 }, {
759 .name = "dma",
760 .id = 2,
761 .parent = &clk_h,
762 .enable = s3c2443_clkcon_enable_h,
763 .ctrlbit = S3C2443_HCLKCON_DMA2,
764 }, {
765 .name = "dma",
766 .id = 3,
767 .parent = &clk_h,
768 .enable = s3c2443_clkcon_enable_h,
769 .ctrlbit = S3C2443_HCLKCON_DMA3,
770 }, {
771 .name = "dma",
772 .id = 4,
773 .parent = &clk_h,
774 .enable = s3c2443_clkcon_enable_h,
775 .ctrlbit = S3C2443_HCLKCON_DMA4,
776 }, {
777 .name = "dma",
778 .id = 5,
779 .parent = &clk_h,
780 .enable = s3c2443_clkcon_enable_h,
781 .ctrlbit = S3C2443_HCLKCON_DMA5,
782 }, {
783 .name = "lcd",
784 .id = -1,
785 .parent = &clk_h,
786 .enable = s3c2443_clkcon_enable_h,
787 .ctrlbit = S3C2443_HCLKCON_LCDC,
788 }, {
789 .name = "gpio",
790 .id = -1,
791 .parent = &clk_p,
792 .enable = s3c2443_clkcon_enable_p,
793 .ctrlbit = S3C2443_PCLKCON_GPIO,
794 }, {
795 .name = "usb-host",
796 .id = -1,
797 .parent = &clk_h,
798 .enable = s3c2443_clkcon_enable_h,
799 .ctrlbit = S3C2443_HCLKCON_USBH,
800 }, {
801 .name = "usb-device",
802 .id = -1,
803 .parent = &clk_h,
804 .enable = s3c2443_clkcon_enable_h,
805 .ctrlbit = S3C2443_HCLKCON_USBD,
806 }, {
Ben Dooks67364332007-05-20 17:17:32 +0100807 .name = "hsmmc",
808 .id = -1,
809 .parent = &clk_h,
810 .enable = s3c2443_clkcon_enable_h,
811 .ctrlbit = S3C2443_HCLKCON_HSMMC,
812 }, {
813 .name = "cfc",
814 .id = -1,
815 .parent = &clk_h,
816 .enable = s3c2443_clkcon_enable_h,
817 .ctrlbit = S3C2443_HCLKCON_CFC,
Ben Dooks67364332007-05-20 17:17:32 +0100818 }, {
819 .name = "ssmc",
820 .id = -1,
821 .parent = &clk_h,
822 .enable = s3c2443_clkcon_enable_h,
823 .ctrlbit = S3C2443_HCLKCON_SSMC,
824 }, {
Ben Dookse4d06e32007-02-16 12:12:31 +0100825 .name = "timers",
826 .id = -1,
827 .parent = &clk_p,
828 .enable = s3c2443_clkcon_enable_p,
829 .ctrlbit = S3C2443_PCLKCON_PWMT,
830 }, {
831 .name = "uart",
832 .id = 0,
833 .parent = &clk_p,
834 .enable = s3c2443_clkcon_enable_p,
835 .ctrlbit = S3C2443_PCLKCON_UART0,
836 }, {
837 .name = "uart",
838 .id = 1,
839 .parent = &clk_p,
840 .enable = s3c2443_clkcon_enable_p,
841 .ctrlbit = S3C2443_PCLKCON_UART1,
842 }, {
843 .name = "uart",
844 .id = 2,
845 .parent = &clk_p,
846 .enable = s3c2443_clkcon_enable_p,
847 .ctrlbit = S3C2443_PCLKCON_UART2,
848 }, {
849 .name = "uart",
850 .id = 3,
851 .parent = &clk_p,
852 .enable = s3c2443_clkcon_enable_p,
853 .ctrlbit = S3C2443_PCLKCON_UART3,
854 }, {
855 .name = "rtc",
856 .id = -1,
857 .parent = &clk_p,
858 .enable = s3c2443_clkcon_enable_p,
859 .ctrlbit = S3C2443_PCLKCON_RTC,
860 }, {
861 .name = "watchdog",
862 .id = -1,
863 .parent = &clk_p,
864 .ctrlbit = S3C2443_PCLKCON_WDT,
865 }, {
866 .name = "usb-bus-host",
867 .id = -1,
868 .parent = &clk_usb_bus_host,
Ben Dooks67364332007-05-20 17:17:32 +0100869 }, {
870 .name = "ac97",
Graeme Gregoryb8b69702007-05-09 15:55:24 +0100871 .id = -1,
872 .parent = &clk_p,
873 .ctrlbit = S3C2443_PCLKCON_AC97,
Ben Dookse4d06e32007-02-16 12:12:31 +0100874 }
875};
876
877/* clocks to add where we need to check their parentage */
878
879/* s3c2443_clk_initparents
880 *
881 * Initialise the parents for the clocks that we get at start-time
882*/
883
884static int __init clk_init_set_parent(struct clk *clk, struct clk *parent)
885{
886 printk(KERN_DEBUG "clock %s: parent %s\n", clk->name, parent->name);
887 return clk_set_parent(clk, parent);
888}
889
890static void __init s3c2443_clk_initparents(void)
891{
892 unsigned long clksrc = __raw_readl(S3C2443_CLKSRC);
893 struct clk *parent;
894
895 switch (clksrc & S3C2443_CLKSRC_EPLLREF_MASK) {
896 case S3C2443_CLKSRC_EPLLREF_EXTCLK:
897 parent = &clk_ext;
898 break;
899
900 case S3C2443_CLKSRC_EPLLREF_XTAL:
901 default:
902 parent = &clk_xtal;
903 break;
904
905 case S3C2443_CLKSRC_EPLLREF_MPLLREF:
906 case S3C2443_CLKSRC_EPLLREF_MPLLREF2:
907 parent = &clk_mpllref;
908 break;
909 }
910
911 clk_init_set_parent(&clk_epllref, parent);
912
913 switch (clksrc & S3C2443_CLKSRC_I2S_MASK) {
914 case S3C2443_CLKSRC_I2S_EXT:
915 parent = &clk_i2s_ext;
916 break;
917
918 case S3C2443_CLKSRC_I2S_EPLLDIV:
919 default:
920 parent = &clk_i2s_eplldiv;
921 break;
922
923 case S3C2443_CLKSRC_I2S_EPLLREF:
924 case S3C2443_CLKSRC_I2S_EPLLREF3:
925 parent = &clk_epllref;
926 }
927
928 clk_init_set_parent(&clk_i2s, &clk_epllref);
929
930 /* esysclk source */
931
932 parent = (clksrc & S3C2443_CLKSRC_ESYSCLK_EPLL) ?
933 &clk_epll : &clk_epllref;
934
935 clk_init_set_parent(&clk_esysclk, parent);
936
937 /* msysclk source */
938
939 if (clksrc & S3C2443_CLKSRC_MSYSCLK_MPLL) {
940 parent = &clk_mpll;
941 } else {
942 parent = (clksrc & S3C2443_CLKSRC_EXTCLK_DIV) ?
943 &clk_mdivclk : &clk_mpllref;
944 }
945
946 clk_init_set_parent(&clk_msysclk, parent);
Ben Dooksba7622a2008-07-07 18:12:39 +0100947
948 /* arm */
949
950 if (__raw_readl(S3C2443_CLKDIV0) & S3C2443_CLKDIV0_DVS)
951 parent = &clk_h;
952 else
953 parent = &clk_armdiv;
954
955 clk_init_set_parent(&clk_arm, parent);
Ben Dookse4d06e32007-02-16 12:12:31 +0100956}
957
958/* armdiv divisor table */
959
960static unsigned int armdiv[16] = {
961 [S3C2443_CLKDIV0_ARMDIV_1 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 1,
962 [S3C2443_CLKDIV0_ARMDIV_2 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 2,
963 [S3C2443_CLKDIV0_ARMDIV_3 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 3,
964 [S3C2443_CLKDIV0_ARMDIV_4 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 4,
965 [S3C2443_CLKDIV0_ARMDIV_6 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 6,
966 [S3C2443_CLKDIV0_ARMDIV_8 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 8,
967 [S3C2443_CLKDIV0_ARMDIV_12 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 12,
968 [S3C2443_CLKDIV0_ARMDIV_16 >> S3C2443_CLKDIV0_ARMDIV_SHIFT] = 16,
969};
970
971static inline unsigned int s3c2443_fclk_div(unsigned long clkcon0)
972{
973 clkcon0 &= S3C2443_CLKDIV0_ARMDIV_MASK;
974
975 return armdiv[clkcon0 >> S3C2443_CLKDIV0_ARMDIV_SHIFT];
976}
977
Ben Dooks2e16c272008-07-07 18:12:40 +0100978static inline unsigned long s3c2443_get_hdiv(unsigned long clkcon0)
Ben Dookse4d06e32007-02-16 12:12:31 +0100979{
Ben Dooks2e16c272008-07-07 18:12:40 +0100980 clkcon0 &= S3C2443_CLKDIV0_HCLKDIV_MASK;
Ben Dookse4d06e32007-02-16 12:12:31 +0100981
982 return clkcon0 + 1;
983}
984
985/* clocks to add straight away */
986
987static struct clk *clks[] __initdata = {
988 &clk_ext,
989 &clk_epll,
990 &clk_usb_bus_host,
991 &clk_usb_bus,
992 &clk_esysclk,
993 &clk_epllref,
994 &clk_mpllref,
995 &clk_msysclk,
996 &clk_uart,
997 &clk_display,
998 &clk_cam,
999 &clk_i2s_eplldiv,
1000 &clk_i2s,
1001 &clk_hsspi,
1002 &clk_hsmmc_div,
1003 &clk_hsmmc,
Ben Dooksba7622a2008-07-07 18:12:39 +01001004 &clk_armdiv,
1005 &clk_arm,
Ben Dooks2e16c272008-07-07 18:12:40 +01001006 &clk_prediv,
Ben Dookse4d06e32007-02-16 12:12:31 +01001007};
1008
Ben Dookse4253822008-10-21 14:06:38 +01001009void __init_or_cpufreq s3c2443_setup_clocks(void)
Ben Dookse4d06e32007-02-16 12:12:31 +01001010{
Ben Dookse4d06e32007-02-16 12:12:31 +01001011 unsigned long mpllcon = __raw_readl(S3C2443_MPLLCON);
1012 unsigned long clkdiv0 = __raw_readl(S3C2443_CLKDIV0);
Ben Dookse4253822008-10-21 14:06:38 +01001013 struct clk *xtal_clk;
1014 unsigned long xtal;
Ben Dookse4d06e32007-02-16 12:12:31 +01001015 unsigned long pll;
1016 unsigned long fclk;
1017 unsigned long hclk;
1018 unsigned long pclk;
Ben Dookse4d06e32007-02-16 12:12:31 +01001019
Ben Dookse4253822008-10-21 14:06:38 +01001020 xtal_clk = clk_get(NULL, "xtal");
1021 xtal = clk_get_rate(xtal_clk);
1022 clk_put(xtal_clk);
Ben Dooks2e16c272008-07-07 18:12:40 +01001023
Ben Dookse4d06e32007-02-16 12:12:31 +01001024 pll = s3c2443_get_mpll(mpllcon, xtal);
Ben Dooks2e16c272008-07-07 18:12:40 +01001025 clk_msysclk.rate = pll;
Ben Dookse4d06e32007-02-16 12:12:31 +01001026
1027 fclk = pll / s3c2443_fclk_div(clkdiv0);
Ben Dooks2e16c272008-07-07 18:12:40 +01001028 hclk = s3c2443_prediv_getrate(&clk_prediv);
Ben Dooks5c378662008-10-16 16:46:09 +01001029 hclk /= s3c2443_get_hdiv(clkdiv0);
Ben Dookse4d06e32007-02-16 12:12:31 +01001030 pclk = hclk / ((clkdiv0 & S3C2443_CLKDIV0_HALF_PCLK) ? 2 : 1);
1031
Ben Dookse4253822008-10-21 14:06:38 +01001032 s3c24xx_setup_clocks(fclk, hclk, pclk);
Ben Dookse4d06e32007-02-16 12:12:31 +01001033
1034 printk("S3C2443: mpll %s %ld.%03ld MHz, cpu %ld.%03ld MHz, mem %ld.%03ld MHz, pclk %ld.%03ld MHz\n",
1035 (mpllcon & S3C2443_PLLCON_OFF) ? "off":"on",
1036 print_mhz(pll), print_mhz(fclk),
1037 print_mhz(hclk), print_mhz(pclk));
1038
Ben Dookse4253822008-10-21 14:06:38 +01001039 s3c24xx_setup_clocks(fclk, hclk, pclk);
1040}
1041
1042void __init s3c2443_init_clocks(int xtal)
1043{
1044 struct clk *clkp;
1045 unsigned long epllcon = __raw_readl(S3C2443_EPLLCON);
1046 int ret;
1047 int ptr;
1048
1049 /* s3c2443 parents h and p clocks from prediv */
1050 clk_h.parent = &clk_prediv;
1051 clk_p.parent = &clk_prediv;
1052
1053 s3c24xx_register_baseclocks(xtal);
1054 s3c2443_setup_clocks();
Ben Dookse4d06e32007-02-16 12:12:31 +01001055 s3c2443_clk_initparents();
1056
1057 for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) {
1058 clkp = clks[ptr];
1059
1060 ret = s3c24xx_register_clock(clkp);
1061 if (ret < 0) {
1062 printk(KERN_ERR "Failed to register clock %s (%d)\n",
1063 clkp->name, ret);
1064 }
1065 }
1066
1067 clk_epll.rate = s3c2443_get_epll(epllcon, xtal);
Ben Dooks4b31d8b2008-10-21 14:07:00 +01001068 clk_epll.parent = &clk_epllref;
Ben Dookse4d06e32007-02-16 12:12:31 +01001069 clk_usb_bus.parent = &clk_usb_bus_host;
1070
1071 /* ensure usb bus clock is within correct rate of 48MHz */
1072
1073 if (clk_get_rate(&clk_usb_bus_host) != (48 * 1000 * 1000)) {
1074 printk(KERN_INFO "Warning: USB host bus not at 48MHz\n");
1075 clk_set_rate(&clk_usb_bus_host, 48*1000*1000);
1076 }
1077
1078 printk("S3C2443: epll %s %ld.%03ld MHz, usb-bus %ld.%03ld MHz\n",
1079 (epllcon & S3C2443_PLLCON_OFF) ? "off":"on",
1080 print_mhz(clk_get_rate(&clk_epll)),
1081 print_mhz(clk_get_rate(&clk_usb_bus)));
1082
1083 /* register clocks from clock array */
1084
Ben Dooks1d9f13c2010-01-06 01:21:38 +09001085 s3c_register_clocks(init_clocks, ARRAY_SIZE(init_clocks));
Ben Dookse4d06e32007-02-16 12:12:31 +01001086
1087 /* We must be careful disabling the clocks we are not intending to
Robert P. J. Day3a4fa0a2007-10-19 23:10:43 +02001088 * be using at boot time, as subsystems such as the LCD which do
Ben Dookse4d06e32007-02-16 12:12:31 +01001089 * their own DMA requests to the bus can cause the system to lockup
1090 * if they where in the middle of requesting bus access.
1091 *
1092 * Disabling the LCD clock if the LCD is active is very dangerous,
1093 * and therefore the bootloader should be careful to not enable
1094 * the LCD clock if it is not needed.
1095 */
1096
1097 /* install (and disable) the clocks we do not need immediately */
1098
1099 clkp = init_clocks_disable;
1100 for (ptr = 0; ptr < ARRAY_SIZE(init_clocks_disable); ptr++, clkp++) {
1101
1102 ret = s3c24xx_register_clock(clkp);
1103 if (ret < 0) {
1104 printk(KERN_ERR "Failed to register clock %s (%d)\n",
1105 clkp->name, ret);
1106 }
1107
1108 (clkp->enable)(clkp, 0);
1109 }
Ben Dooks9d325f22008-11-21 10:36:05 +00001110
1111 s3c_pwmclk_init();
Ben Dookse4d06e32007-02-16 12:12:31 +01001112}