Merge git://git.infradead.org/~dwmw2/rbtree-2.6
[pandora-kernel.git] / 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 | 3);
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_h)
371                 source = S3C2410_MISCCR_CLK0_HCLK;
372         else if (parent == &clk_p)
373                 source = S3C2410_MISCCR_CLK0_PCLK;
374         else if (clk == &s3c24xx_clkout0 && parent == &s3c24xx_dclk0)
375                 source = S3C2410_MISCCR_CLK0_DCLK0;
376         else if (clk == &s3c24xx_clkout1 && parent == &s3c24xx_dclk1)
377                 source = S3C2410_MISCCR_CLK0_DCLK0;
378         else
379                 return -EINVAL;
380
381         clk->parent = parent;
382
383         if (clk == &s3c24xx_dclk0)
384                 mask = S3C2410_MISCCR_CLK0_MASK;
385         else {
386                 source <<= 4;
387                 mask = S3C2410_MISCCR_CLK1_MASK;
388         }
389
390         s3c2410_modify_misccr(mask, source);
391         return 0;
392 }
393
394 /* external clock definitions */
395
396 struct clk s3c24xx_dclk0 = {
397         .name           = "dclk0",
398         .id             = -1,
399         .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
400         .enable         = s3c24xx_dclk_enable,
401         .set_parent     = s3c24xx_dclk_setparent,
402 };
403
404 struct clk s3c24xx_dclk1 = {
405         .name           = "dclk1",
406         .id             = -1,
407         .ctrlbit        = S3C2410_DCLKCON_DCLK0EN,
408         .enable         = s3c24xx_dclk_enable,
409         .set_parent     = s3c24xx_dclk_setparent,
410 };
411
412 struct clk s3c24xx_clkout0 = {
413         .name           = "clkout0",
414         .id             = -1,
415         .set_parent     = s3c24xx_clkout_setparent,
416 };
417
418 struct clk s3c24xx_clkout1 = {
419         .name           = "clkout1",
420         .id             = -1,
421         .set_parent     = s3c24xx_clkout_setparent,
422 };
423
424 struct clk s3c24xx_uclk = {
425         .name           = "uclk",
426         .id             = -1,
427 };
428
429
430 /* standard clock definitions */
431
432 static struct clk init_clocks[] = {
433         {
434                 .name           = "nand",
435                 .id             = -1,
436                 .parent         = &clk_h,
437                 .enable         = s3c24xx_clkcon_enable,
438                 .ctrlbit        = S3C2410_CLKCON_NAND,
439         }, {
440                 .name           = "lcd",
441                 .id             = -1,
442                 .parent         = &clk_h,
443                 .enable         = s3c24xx_clkcon_enable,
444                 .ctrlbit        = S3C2410_CLKCON_LCDC,
445         }, {
446                 .name           = "usb-host",
447                 .id             = -1,
448                 .parent         = &clk_h,
449                 .enable         = s3c24xx_clkcon_enable,
450                 .ctrlbit        = S3C2410_CLKCON_USBH,
451         }, {
452                 .name           = "usb-device",
453                 .id             = -1,
454                 .parent         = &clk_h,
455                 .enable         = s3c24xx_clkcon_enable,
456                 .ctrlbit        = S3C2410_CLKCON_USBD,
457         }, {
458                 .name           = "timers",
459                 .id             = -1,
460                 .parent         = &clk_p,
461                 .enable         = s3c24xx_clkcon_enable,
462                 .ctrlbit        = S3C2410_CLKCON_PWMT,
463         }, {
464                 .name           = "sdi",
465                 .id             = -1,
466                 .parent         = &clk_p,
467                 .enable         = s3c24xx_clkcon_enable,
468                 .ctrlbit        = S3C2410_CLKCON_SDI,
469         }, {
470                 .name           = "uart",
471                 .id             = 0,
472                 .parent         = &clk_p,
473                 .enable         = s3c24xx_clkcon_enable,
474                 .ctrlbit        = S3C2410_CLKCON_UART0,
475         }, {
476                 .name           = "uart",
477                 .id             = 1,
478                 .parent         = &clk_p,
479                 .enable         = s3c24xx_clkcon_enable,
480                 .ctrlbit        = S3C2410_CLKCON_UART1,
481         }, {
482                 .name           = "uart",
483                 .id             = 2,
484                 .parent         = &clk_p,
485                 .enable         = s3c24xx_clkcon_enable,
486                 .ctrlbit        = S3C2410_CLKCON_UART2,
487         }, {
488                 .name           = "gpio",
489                 .id             = -1,
490                 .parent         = &clk_p,
491                 .enable         = s3c24xx_clkcon_enable,
492                 .ctrlbit        = S3C2410_CLKCON_GPIO,
493         }, {
494                 .name           = "rtc",
495                 .id             = -1,
496                 .parent         = &clk_p,
497                 .enable         = s3c24xx_clkcon_enable,
498                 .ctrlbit        = S3C2410_CLKCON_RTC,
499         }, {
500                 .name           = "adc",
501                 .id             = -1,
502                 .parent         = &clk_p,
503                 .enable         = s3c24xx_clkcon_enable,
504                 .ctrlbit        = S3C2410_CLKCON_ADC,
505         }, {
506                 .name           = "i2c",
507                 .id             = -1,
508                 .parent         = &clk_p,
509                 .enable         = s3c24xx_clkcon_enable,
510                 .ctrlbit        = S3C2410_CLKCON_IIC,
511         }, {
512                 .name           = "iis",
513                 .id             = -1,
514                 .parent         = &clk_p,
515                 .enable         = s3c24xx_clkcon_enable,
516                 .ctrlbit        = S3C2410_CLKCON_IIS,
517         }, {
518                 .name           = "spi",
519                 .id             = -1,
520                 .parent         = &clk_p,
521                 .enable         = s3c24xx_clkcon_enable,
522                 .ctrlbit        = S3C2410_CLKCON_SPI,
523         }, {
524                 .name           = "watchdog",
525                 .id             = -1,
526                 .parent         = &clk_p,
527                 .ctrlbit        = 0,
528         }
529 };
530
531 /* initialise the clock system */
532
533 int s3c24xx_register_clock(struct clk *clk)
534 {
535         clk->owner = THIS_MODULE;
536
537         if (clk->enable == NULL)
538                 clk->enable = clk_null_enable;
539
540         /* if this is a standard clock, set the usage state */
541
542         if (clk->ctrlbit && clk->enable == s3c24xx_clkcon_enable) {
543                 unsigned long clkcon = __raw_readl(S3C2410_CLKCON);
544
545                 clk->usage = (clkcon & clk->ctrlbit) ? 1 : 0;
546         }
547
548         /* add to the list of available clocks */
549
550         mutex_lock(&clocks_mutex);
551         list_add(&clk->list, &clocks);
552         mutex_unlock(&clocks_mutex);
553
554         return 0;
555 }
556
557 /* initalise all the clocks */
558
559 int __init s3c24xx_setup_clocks(unsigned long xtal,
560                                 unsigned long fclk,
561                                 unsigned long hclk,
562                                 unsigned long pclk)
563 {
564         unsigned long upllcon = __raw_readl(S3C2410_UPLLCON);
565         unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
566         struct clk *clkp = init_clocks;
567         int ptr;
568         int ret;
569
570         printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n");
571
572         /* initialise the main system clocks */
573
574         clk_xtal.rate = xtal;
575         clk_upll.rate = s3c2410_get_pll(upllcon, xtal);
576
577         clk_h.rate = hclk;
578         clk_p.rate = pclk;
579         clk_f.rate = fclk;
580
581         /* We must be careful disabling the clocks we are not intending to
582          * be using at boot time, as subsytems such as the LCD which do
583          * their own DMA requests to the bus can cause the system to lockup
584          * if they where in the middle of requesting bus access.
585          *
586          * Disabling the LCD clock if the LCD is active is very dangerous,
587          * and therefore the bootloader should be  careful to not enable
588          * the LCD clock if it is not needed.
589         */
590
591         mutex_lock(&clocks_mutex);
592
593         s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0);
594         s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0);
595         s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0);
596         s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0);
597         s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0);
598         s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0);
599
600         mutex_unlock(&clocks_mutex);
601
602         /* assume uart clocks are correctly setup */
603
604         /* register our clocks */
605
606         if (s3c24xx_register_clock(&clk_xtal) < 0)
607                 printk(KERN_ERR "failed to register master xtal\n");
608
609         if (s3c24xx_register_clock(&clk_upll) < 0)
610                 printk(KERN_ERR "failed to register upll clock\n");
611
612         if (s3c24xx_register_clock(&clk_f) < 0)
613                 printk(KERN_ERR "failed to register cpu fclk\n");
614
615         if (s3c24xx_register_clock(&clk_h) < 0)
616                 printk(KERN_ERR "failed to register cpu hclk\n");
617
618         if (s3c24xx_register_clock(&clk_p) < 0)
619                 printk(KERN_ERR "failed to register cpu pclk\n");
620
621
622         if (s3c24xx_register_clock(&clk_usb_bus) < 0)
623                 printk(KERN_ERR "failed to register usb bus clock\n");
624
625         /* register clocks from clock array */
626
627         for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
628                 ret = s3c24xx_register_clock(clkp);
629                 if (ret < 0) {
630                         printk(KERN_ERR "Failed to register clock %s (%d)\n",
631                                clkp->name, ret);
632                 }
633         }
634
635         /* show the clock-slow value */
636
637         printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
638                print_mhz(xtal / ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
639                (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
640                (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
641                (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
642
643         return 0;
644 }