fec02c92f95f4406da0dc2a1e2ca4a9e933c4159
[safe/jmp/linux-2.6] / arch / arm / mach-s3c2410 / clock.c
1 /* linux/arch/arm/mach-s3c2410/clock.c
2  *
3  * Copyright (c) 2004-2005 Simtec Electronics
4  *      Ben Dooks <ben@simtec.co.uk>
5  *
6  * S3C2410 Clock control support
7  *
8  * Based on, and code from linux/arch/arm/mach-versatile/clock.c
9  **
10  **  Copyright (C) 2004 ARM Limited.
11  **  Written by Deep Blue Solutions Limited.
12  *
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27 */
28
29 #include <linux/init.h>
30 #include <linux/module.h>
31 #include <linux/kernel.h>
32 #include <linux/list.h>
33 #include <linux/errno.h>
34 #include <linux/err.h>
35 #include <linux/platform_device.h>
36 #include <linux/sysdev.h>
37 #include <linux/interrupt.h>
38 #include <linux/ioport.h>
39 #include <linux/clk.h>
40 #include <linux/mutex.h>
41 #include <linux/delay.h>
42
43 #include <asm/hardware.h>
44 #include <asm/irq.h>
45 #include <asm/io.h>
46
47 #include <asm/arch/regs-clock.h>
48 #include <asm/arch/regs-gpio.h>
49
50 #include "clock.h"
51 #include "cpu.h"
52
53 /* clock information */
54
55 static LIST_HEAD(clocks);
56
57 DEFINE_MUTEX(clocks_mutex);
58
59 /* old functions */
60
61 void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable)
62 {
63         unsigned long clkcon;
64
65         clkcon = __raw_readl(S3C2410_CLKCON);
66
67         if (enable)
68                 clkcon |= clocks;
69         else
70                 clkcon &= ~clocks;
71
72         /* ensure none of the special function bits set */
73         clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
74
75         __raw_writel(clkcon, S3C2410_CLKCON);
76 }
77
78 /* enable and disable calls for use with the clk struct */
79
80 static int clk_null_enable(struct clk *clk, int enable)
81 {
82         return 0;
83 }
84
85 int s3c24xx_clkcon_enable(struct clk *clk, int enable)
86 {
87         s3c24xx_clk_enable(clk->ctrlbit, enable);
88         return 0;
89 }
90
91 /* Clock API calls */
92
93 struct clk *clk_get(struct device *dev, const char *id)
94 {
95         struct clk *p;
96         struct clk *clk = ERR_PTR(-ENOENT);
97         int idno;
98
99         if (dev == NULL || dev->bus != &platform_bus_type)
100                 idno = -1;
101         else
102                 idno = to_platform_device(dev)->id;
103
104         mutex_lock(&clocks_mutex);
105
106         list_for_each_entry(p, &clocks, list) {
107                 if (p->id == idno &&
108                     strcmp(id, p->name) == 0 &&
109                     try_module_get(p->owner)) {
110                         clk = p;
111                         break;
112                 }
113         }
114
115         /* check for the case where a device was supplied, but the
116          * clock that was being searched for is not device specific */
117
118         if (IS_ERR(clk)) {
119                 list_for_each_entry(p, &clocks, list) {
120                         if (p->id == -1 && strcmp(id, p->name) == 0 &&
121                             try_module_get(p->owner)) {
122                                 clk = p;
123                                 break;
124                         }
125                 }
126         }
127
128         mutex_unlock(&clocks_mutex);
129         return clk;
130 }
131
132 void clk_put(struct clk *clk)
133 {
134         module_put(clk->owner);
135 }
136
137 int clk_enable(struct clk *clk)
138 {
139         if (IS_ERR(clk) || clk == NULL)
140                 return -EINVAL;
141
142         clk_enable(clk->parent);
143
144         mutex_lock(&clocks_mutex);
145
146         if ((clk->usage++) == 0)
147                 (clk->enable)(clk, 1);
148
149         mutex_unlock(&clocks_mutex);
150         return 0;
151 }
152
153 void clk_disable(struct clk *clk)
154 {
155         if (IS_ERR(clk) || clk == NULL)
156                 return;
157
158         mutex_lock(&clocks_mutex);
159
160         if ((--clk->usage) == 0)
161                 (clk->enable)(clk, 0);
162
163         mutex_unlock(&clocks_mutex);
164         clk_disable(clk->parent);
165 }
166
167
168 unsigned long clk_get_rate(struct clk *clk)
169 {
170         if (IS_ERR(clk))
171                 return 0;
172
173         if (clk->rate != 0)
174                 return clk->rate;
175
176         while (clk->parent != NULL && clk->rate == 0)
177                 clk = clk->parent;
178
179         return clk->rate;
180 }
181
182 long clk_round_rate(struct clk *clk, unsigned long rate)
183 {
184         if (!IS_ERR(clk) && clk->round_rate)
185                 return (clk->round_rate)(clk, rate);
186
187         return rate;
188 }
189
190 int clk_set_rate(struct clk *clk, unsigned long rate)
191 {
192         int ret;
193
194         if (IS_ERR(clk))
195                 return -EINVAL;
196
197         mutex_lock(&clocks_mutex);
198         ret = (clk->set_rate)(clk, rate);
199         mutex_unlock(&clocks_mutex);
200
201         return ret;
202 }
203
204 struct clk *clk_get_parent(struct clk *clk)
205 {
206         return clk->parent;
207 }
208
209 int clk_set_parent(struct clk *clk, struct clk *parent)
210 {
211         int ret = 0;
212
213         if (IS_ERR(clk))
214                 return -EINVAL;
215
216         mutex_lock(&clocks_mutex);
217
218         if (clk->set_parent)
219                 ret = (clk->set_parent)(clk, parent);
220
221         mutex_unlock(&clocks_mutex);
222
223         return ret;
224 }
225
226 EXPORT_SYMBOL(clk_get);
227 EXPORT_SYMBOL(clk_put);
228 EXPORT_SYMBOL(clk_enable);
229 EXPORT_SYMBOL(clk_disable);
230 EXPORT_SYMBOL(clk_get_rate);
231 EXPORT_SYMBOL(clk_round_rate);
232 EXPORT_SYMBOL(clk_set_rate);
233 EXPORT_SYMBOL(clk_get_parent);
234 EXPORT_SYMBOL(clk_set_parent);
235
236 /* base clock enable */
237
238 static int s3c24xx_upll_enable(struct clk *clk, int enable)
239 {
240         unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
241         unsigned long orig = clkslow;
242
243         if (enable)
244                 clkslow &= ~S3C2410_CLKSLOW_UCLK_OFF;
245         else
246                 clkslow |= S3C2410_CLKSLOW_UCLK_OFF;
247
248         __raw_writel(clkslow, S3C2410_CLKSLOW);
249
250         /* if we started the UPLL, then allow to settle */
251
252         if (enable && !(orig & S3C2410_CLKSLOW_UCLK_OFF))
253                 udelay(200);
254
255         return 0;
256 }
257
258 /* base clocks */
259
260 static struct clk clk_xtal = {
261         .name           = "xtal",
262         .id             = -1,
263         .rate           = 0,
264         .parent         = NULL,
265         .ctrlbit        = 0,
266 };
267
268 static struct clk clk_upll = {
269         .name           = "upll",
270         .id             = -1,
271         .parent         = NULL,
272         .enable         = s3c24xx_upll_enable,
273         .ctrlbit        = 0,
274 };
275
276 static struct clk clk_f = {
277         .name           = "fclk",
278         .id             = -1,
279         .rate           = 0,
280         .parent         = NULL,
281         .ctrlbit        = 0,
282 };
283
284 static struct clk clk_h = {
285         .name           = "hclk",
286         .id             = -1,
287         .rate           = 0,
288         .parent         = NULL,
289         .ctrlbit        = 0,
290 };
291
292 static struct clk clk_p = {
293         .name           = "pclk",
294         .id             = -1,
295         .rate           = 0,
296         .parent         = NULL,
297         .ctrlbit        = 0,
298 };
299
300 struct clk clk_usb_bus = {
301         .name           = "usb-bus",
302         .id             = -1,
303         .rate           = 0,
304         .parent         = &clk_upll,
305 };
306
307 /* clocks that could be registered by external code */
308
309 static int s3c24xx_dclk_enable(struct clk *clk, int enable)
310 {
311         unsigned long dclkcon = __raw_readl(S3C2410_DCLKCON);
312
313         if (enable)
314                 dclkcon |= clk->ctrlbit;
315         else
316                 dclkcon &= ~clk->ctrlbit;
317
318         __raw_writel(dclkcon, S3C2410_DCLKCON);
319
320         return 0;
321 }
322
323 static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
324 {
325         unsigned long dclkcon;
326         unsigned int uclk;
327
328         if (parent == &clk_upll)
329                 uclk = 1;
330         else if (parent == &clk_p)
331                 uclk = 0;
332         else
333                 return -EINVAL;
334
335         clk->parent = parent;
336
337         dclkcon = __raw_readl(S3C2410_DCLKCON);
338
339         if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) {
340                 if (uclk)
341                         dclkcon |= S3C2410_DCLKCON_DCLK0_UCLK;
342                 else
343                         dclkcon &= ~S3C2410_DCLKCON_DCLK0_UCLK;
344         } else {
345                 if (uclk)
346                         dclkcon |= S3C2410_DCLKCON_DCLK1_UCLK;
347                 else
348                         dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK;
349         }
350
351         __raw_writel(dclkcon, S3C2410_DCLKCON);
352
353         return 0;
354 }
355
356
357 static int s3c24xx_clkout_setparent(struct clk *clk, struct clk *parent)
358 {
359         unsigned long mask;
360         unsigned long source;
361
362         /* calculate the MISCCR setting for the clock */
363
364         if (parent == &clk_xtal)
365                 source = S3C2410_MISCCR_CLK0_MPLL;
366         else if (parent == &clk_upll)
367                 source = S3C2410_MISCCR_CLK0_UPLL;
368         else if (parent == &clk_f)
369                 source = S3C2410_MISCCR_CLK0_FCLK;
370         else if (parent == &clk_p)
371                 source = S3C2410_MISCCR_CLK0_PCLK;
372         else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0)
373                 source = S3C2410_MISCCR_CLK0_DCLK0;
374         else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1)
375                 source = S3C2410_MISCCR_CLK0_DCLK0;
376         else
377                 return -EINVAL;
378
379         if (clk == &s3c24xx_dclk0)
380                 mask = S3C2410_MISCCR_CLK0_MASK;
381         else {
382                 source <<= 4;
383                 mask = S3C2410_MISCCR_CLK1_MASK;
384         }
385
386         s3c2410_modify_misccr(mask, source);
387         return 0;
388 }
389
390 /* external clock definitions */
391
392 struct clk s3c24xx_dclk0 = {
393         .name           = "dclk0",
394         .id             = -1,
395         .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
396         .enable         = s3c24xx_dclk_enable,
397         .set_parent     = s3c24xx_dclk_setparent,
398 };
399
400 struct clk s3c24xx_dclk1 = {
401         .name           = "dclk1",
402         .id             = -1,
403         .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
404         .enable         = s3c24xx_dclk_enable,
405         .set_parent     = s3c24xx_dclk_setparent,
406 };
407
408 struct clk s3c24xx_clkout0 = {
409         .name           = "clkout0",
410         .id             = -1,
411         .set_parent     = s3c24xx_clkout_setparent,
412 };
413
414 struct clk s3c24xx_clkout1 = {
415         .name           = "clkout1",
416         .id             = -1,
417         .set_parent     = s3c24xx_clkout_setparent,
418 };
419
420 struct clk s3c24xx_uclk = {
421         .name           = "uclk",
422         .id             = -1,
423 };
424
425
426 /* standard clock definitions */
427
428 static struct clk init_clocks[] = {
429         {
430                 .name           = "nand",
431                 .id             = -1,
432                 .parent         = &clk_h,
433                 .enable         = s3c24xx_clkcon_enable,
434                 .ctrlbit        = S3C2410_CLKCON_NAND,
435         }, {
436                 .name           = "lcd",
437                 .id             = -1,
438                 .parent         = &clk_h,
439                 .enable         = s3c24xx_clkcon_enable,
440                 .ctrlbit        = S3C2410_CLKCON_LCDC,
441         }, {
442                 .name           = "usb-host",
443                 .id             = -1,
444                 .parent         = &clk_h,
445                 .enable         = s3c24xx_clkcon_enable,
446                 .ctrlbit        = S3C2410_CLKCON_USBH,
447         }, {
448                 .name           = "usb-device",
449                 .id             = -1,
450                 .parent         = &clk_h,
451                 .enable         = s3c24xx_clkcon_enable,
452                 .ctrlbit        = S3C2410_CLKCON_USBD,
453         }, {
454                 .name           = "timers",
455                 .id             = -1,
456                 .parent         = &clk_p,
457                 .enable         = s3c24xx_clkcon_enable,
458                 .ctrlbit        = S3C2410_CLKCON_PWMT,
459         }, {
460                 .name           = "sdi",
461                 .id             = -1,
462                 .parent         = &clk_p,
463                 .enable         = s3c24xx_clkcon_enable,
464                 .ctrlbit        = S3C2410_CLKCON_SDI,
465         }, {
466                 .name           = "uart",
467                 .id             = 0,
468                 .parent         = &clk_p,
469                 .enable         = s3c24xx_clkcon_enable,
470                 .ctrlbit        = S3C2410_CLKCON_UART0,
471         }, {
472                 .name           = "uart",
473                 .id             = 1,
474                 .parent         = &clk_p,
475                 .enable         = s3c24xx_clkcon_enable,
476                 .ctrlbit        = S3C2410_CLKCON_UART1,
477         }, {
478                 .name           = "uart",
479                 .id             = 2,
480                 .parent         = &clk_p,
481                 .enable         = s3c24xx_clkcon_enable,
482                 .ctrlbit        = S3C2410_CLKCON_UART2,
483         }, {
484                 .name           = "gpio",
485                 .id             = -1,
486                 .parent         = &clk_p,
487                 .enable         = s3c24xx_clkcon_enable,
488                 .ctrlbit        = S3C2410_CLKCON_GPIO,
489         }, {
490                 .name           = "rtc",
491                 .id             = -1,
492                 .parent         = &clk_p,
493                 .enable         = s3c24xx_clkcon_enable,
494                 .ctrlbit        = S3C2410_CLKCON_RTC,
495         }, {
496                 .name           = "adc",
497                 .id             = -1,
498                 .parent         = &clk_p,
499                 .enable         = s3c24xx_clkcon_enable,
500                 .ctrlbit        = S3C2410_CLKCON_ADC,
501         }, {
502                 .name           = "i2c",
503                 .id             = -1,
504                 .parent         = &clk_p,
505                 .enable         = s3c24xx_clkcon_enable,
506                 .ctrlbit        = S3C2410_CLKCON_IIC,
507         }, {
508                 .name           = "iis",
509                 .id             = -1,
510                 .parent         = &clk_p,
511                 .enable         = s3c24xx_clkcon_enable,
512                 .ctrlbit        = S3C2410_CLKCON_IIS,
513         }, {
514                 .name           = "spi",
515                 .id             = -1,
516                 .parent         = &clk_p,
517                 .enable         = s3c24xx_clkcon_enable,
518                 .ctrlbit        = S3C2410_CLKCON_SPI,
519         }, {
520                 .name           = "watchdog",
521                 .id             = -1,
522                 .parent         = &clk_p,
523                 .ctrlbit        = 0,
524         }
525 };
526
527 /* initialise the clock system */
528
529 int s3c24xx_register_clock(struct clk *clk)
530 {
531         clk->owner = THIS_MODULE;
532
533         if (clk->enable == NULL)
534                 clk->enable = clk_null_enable;
535
536         /* if this is a standard clock, set the usage state */
537
538         if (clk->ctrlbit && clk->enable == s3c24xx_clkcon_enable) {
539                 unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
540
541                 clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0;
542         }
543
544         /* add to the list of available clocks */
545
546         mutex_lock(&clocks_mutex);
547         list_add(&clk->list, &clocks);
548         mutex_unlock(&clocks_mutex);
549
550         return 0;
551 }
552
553 /* initalise all the clocks */
554
555 int __init s3c24xx_setup_clocks(unsigned long xtal,
556                                 unsigned long fclk,
557                                 unsigned long hclk,
558                                 unsigned long pclk)
559 {
560         unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
561         unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
562         struct clk *clkp = init_clocks;
563         int ptr;
564         int ret;
565
566         printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n");
567
568         /* initialise the main system clocks */
569
570         clk_xtal.rate = xtal;
571         clk_upll.rate = s3c2410_get_pll(upllcon, xtal);
572
573         clk_h.rate = hclk;
574         clk_p.rate = pclk;
575         clk_f.rate = fclk;
576
577         /* We must be careful disabling the clocks we are not intending to
578          * be using at boot time, as subsytems such as the LCD which do
579          * their own DMA requests to the bus can cause the system to lockup
580          * if they where in the middle of requesting bus access.
581          *
582          * Disabling the LCD clock if the LCD is active is very dangerous,
583          * and therefore the bootloader should be  careful to not enable
584          * the LCD clock if it is not needed.
585         */
586
587         mutex_lock(&clocks_mutex);
588
589         s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0);
590         s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0);
591         s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0);
592         s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0);
593         s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0);
594         s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0);
595
596         mutex_unlock(&clocks_mutex);
597
598         /* assume uart clocks are correctly setup */
599
600         /* register our clocks */
601
602         if (s3c24xx_register_clock(&clk_xtal) < 0)
603                 printk(KERN_ERR "failed to register master xtal\n");
604
605         if (s3c24xx_register_clock(&clk_upll) < 0)
606                 printk(KERN_ERR "failed to register upll clock\n");
607
608         if (s3c24xx_register_clock(&clk_f) < 0)
609                 printk(KERN_ERR "failed to register cpu fclk\n");
610
611         if (s3c24xx_register_clock(&clk_h) < 0)
612                 printk(KERN_ERR "failed to register cpu hclk\n");
613
614         if (s3c24xx_register_clock(&clk_p) < 0)
615                 printk(KERN_ERR "failed to register cpu pclk\n");
616
617
618         if (s3c24xx_register_clock(&clk_usb_bus) < 0)
619                 printk(KERN_ERR "failed to register usb bus clock\n");
620
621         /* register clocks from clock array */
622
623         for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
624                 ret = s3c24xx_register_clock(clkp);
625                 if (ret < 0) {
626                         printk(KERN_ERR "Failed to register clock %s (%d)\n",
627                                clkp->name, ret);
628                 }
629         }
630
631         /* show the clock-slow value */
632
633         printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
634                print_mhz(xtal / ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
635                (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
636                (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
637                (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
638
639         return 0;
640 }