Merge branch 'upstream'
[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/device.h>
36 #include <linux/sysdev.h>
37
38 #include <linux/interrupt.h>
39 #include <linux/ioport.h>
40
41 #include <asm/hardware.h>
42 #include <asm/atomic.h>
43 #include <asm/irq.h>
44 #include <asm/io.h>
45
46 #include <asm/hardware/clock.h>
47 #include <asm/arch/regs-clock.h>
48
49 #include "clock.h"
50 #include "cpu.h"
51
52 /* clock information */
53
54 static LIST_HEAD(clocks);
55 static DECLARE_MUTEX(clocks_sem);
56
57 /* old functions */
58
59 void inline s3c24xx_clk_enable(unsigned int clocks, unsigned int enable)
60 {
61         unsigned long clkcon;
62         unsigned long flags;
63
64         local_irq_save(flags);
65
66         clkcon = __raw_readl(S3C2410_CLKCON);
67         clkcon &= ~clocks;
68
69         if (enable)
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         local_irq_restore(flags);
78 }
79
80 /* enable and disable calls for use with the clk struct */
81
82 static int clk_null_enable(struct clk *clk, int enable)
83 {
84         return 0;
85 }
86
87 int s3c24xx_clkcon_enable(struct clk *clk, int enable)
88 {
89         s3c24xx_clk_enable(clk->ctrlbit, enable);
90         return 0;
91 }
92
93 /* Clock API calls */
94
95 struct clk *clk_get(struct device *dev, const char *id)
96 {
97         struct clk *p;
98         struct clk *clk = ERR_PTR(-ENOENT);
99         int idno;
100
101         if (dev == NULL || dev->bus != &platform_bus_type)
102                 idno = -1;
103         else
104                 idno = to_platform_device(dev)->id;
105
106         down(&clocks_sem);
107
108         list_for_each_entry(p, &clocks, list) {
109                 if (p->id == idno &&
110                     strcmp(id, p->name) == 0 &&
111                     try_module_get(p->owner)) {
112                         clk = p;
113                         break;
114                 }
115         }
116
117         /* check for the case where a device was supplied, but the
118          * clock that was being searched for is not device specific */
119
120         if (IS_ERR(clk)) {
121                 list_for_each_entry(p, &clocks, list) {
122                         if (p->id == -1 && strcmp(id, p->name) == 0 &&
123                             try_module_get(p->owner)) {
124                                 clk = p;
125                                 break;
126                         }
127                 }
128         }
129
130         up(&clocks_sem);
131         return clk;
132 }
133
134 void clk_put(struct clk *clk)
135 {
136         module_put(clk->owner);
137 }
138
139 int clk_enable(struct clk *clk)
140 {
141         if (IS_ERR(clk))
142                 return -EINVAL;
143
144         return (clk->enable)(clk, 1);
145 }
146
147 void clk_disable(struct clk *clk)
148 {
149         if (!IS_ERR(clk))
150                 (clk->enable)(clk, 0);
151 }
152
153
154 int clk_use(struct clk *clk)
155 {
156         atomic_inc(&clk->used);
157         return 0;
158 }
159
160
161 void clk_unuse(struct clk *clk)
162 {
163         atomic_dec(&clk->used);
164 }
165
166 unsigned long clk_get_rate(struct clk *clk)
167 {
168         if (IS_ERR(clk))
169                 return 0;
170
171         if (clk->rate != 0)
172                 return clk->rate;
173
174         while (clk->parent != NULL && clk->rate == 0)
175                 clk = clk->parent;
176
177         return clk->rate;
178 }
179
180 long clk_round_rate(struct clk *clk, unsigned long rate)
181 {
182         return rate;
183 }
184
185 int clk_set_rate(struct clk *clk, unsigned long rate)
186 {
187         return -EINVAL;
188 }
189
190 struct clk *clk_get_parent(struct clk *clk)
191 {
192         return clk->parent;
193 }
194
195 EXPORT_SYMBOL(clk_get);
196 EXPORT_SYMBOL(clk_put);
197 EXPORT_SYMBOL(clk_enable);
198 EXPORT_SYMBOL(clk_disable);
199 EXPORT_SYMBOL(clk_use);
200 EXPORT_SYMBOL(clk_unuse);
201 EXPORT_SYMBOL(clk_get_rate);
202 EXPORT_SYMBOL(clk_round_rate);
203 EXPORT_SYMBOL(clk_set_rate);
204 EXPORT_SYMBOL(clk_get_parent);
205
206 /* base clocks */
207
208 static struct clk clk_xtal = {
209         .name           = "xtal",
210         .id             = -1,
211         .rate           = 0,
212         .parent         = NULL,
213         .ctrlbit        = 0,
214 };
215
216 static struct clk clk_f = {
217         .name           = "fclk",
218         .id             = -1,
219         .rate           = 0,
220         .parent         = NULL,
221         .ctrlbit        = 0,
222 };
223
224 static struct clk clk_h = {
225         .name           = "hclk",
226         .id             = -1,
227         .rate           = 0,
228         .parent         = NULL,
229         .ctrlbit        = 0,
230 };
231
232 static struct clk clk_p = {
233         .name           = "pclk",
234         .id             = -1,
235         .rate           = 0,
236         .parent         = NULL,
237         .ctrlbit        = 0,
238 };
239
240 /* clocks that could be registered by external code */
241
242 struct clk s3c24xx_dclk0 = {
243         .name           = "dclk0",
244         .id             = -1,
245 };
246
247 struct clk s3c24xx_dclk1 = {
248         .name           = "dclk1",
249         .id             = -1,
250 };
251
252 struct clk s3c24xx_clkout0 = {
253         .name           = "clkout0",
254         .id             = -1,
255 };
256
257 struct clk s3c24xx_clkout1 = {
258         .name           = "clkout1",
259         .id             = -1,
260 };
261
262 struct clk s3c24xx_uclk = {
263         .name           = "uclk",
264         .id             = -1,
265 };
266
267
268 /* clock definitions */
269
270 static struct clk init_clocks[] = {
271         { .name    = "nand",
272           .id      = -1,
273           .parent  = &clk_h,
274           .enable  = s3c24xx_clkcon_enable,
275           .ctrlbit = S3C2410_CLKCON_NAND
276         },
277         { .name    = "lcd",
278           .id      = -1,
279           .parent  = &clk_h,
280           .enable  = s3c24xx_clkcon_enable,
281           .ctrlbit = S3C2410_CLKCON_LCDC
282         },
283         { .name    = "usb-host",
284           .id      = -1,
285           .parent  = &clk_h,
286           .enable  = s3c24xx_clkcon_enable,
287           .ctrlbit = S3C2410_CLKCON_USBH
288         },
289         { .name    = "usb-device",
290           .id      = -1,
291           .parent  = &clk_h,
292           .enable  = s3c24xx_clkcon_enable,
293           .ctrlbit = S3C2410_CLKCON_USBD
294         },
295         { .name    = "timers",
296           .id      = -1,
297           .parent  = &clk_p,
298           .enable  = s3c24xx_clkcon_enable,
299           .ctrlbit = S3C2410_CLKCON_PWMT
300         },
301         { .name    = "sdi",
302           .id      = -1,
303           .parent  = &clk_p,
304           .enable  = s3c24xx_clkcon_enable,
305           .ctrlbit = S3C2410_CLKCON_SDI
306         },
307         { .name    = "uart",
308           .id      = 0,
309           .parent  = &clk_p,
310           .enable  = s3c24xx_clkcon_enable,
311           .ctrlbit = S3C2410_CLKCON_UART0
312         },
313         { .name    = "uart",
314           .id      = 1,
315           .parent  = &clk_p,
316           .enable  = s3c24xx_clkcon_enable,
317           .ctrlbit = S3C2410_CLKCON_UART1
318         },
319         { .name    = "uart",
320           .id      = 2,
321           .parent  = &clk_p,
322           .enable  = s3c24xx_clkcon_enable,
323           .ctrlbit = S3C2410_CLKCON_UART2
324         },
325         { .name    = "gpio",
326           .id      = -1,
327           .parent  = &clk_p,
328           .enable  = s3c24xx_clkcon_enable,
329           .ctrlbit = S3C2410_CLKCON_GPIO
330         },
331         { .name    = "rtc",
332           .id      = -1,
333           .parent  = &clk_p,
334           .enable  = s3c24xx_clkcon_enable,
335           .ctrlbit = S3C2410_CLKCON_RTC
336         },
337         { .name    = "adc",
338           .id      = -1,
339           .parent  = &clk_p,
340           .enable  = s3c24xx_clkcon_enable,
341           .ctrlbit = S3C2410_CLKCON_ADC
342         },
343         { .name    = "i2c",
344           .id      = -1,
345           .parent  = &clk_p,
346           .enable  = s3c24xx_clkcon_enable,
347           .ctrlbit = S3C2410_CLKCON_IIC
348         },
349         { .name    = "iis",
350           .id      = -1,
351           .parent  = &clk_p,
352           .enable  = s3c24xx_clkcon_enable,
353           .ctrlbit = S3C2410_CLKCON_IIS
354         },
355         { .name    = "spi",
356           .id      = -1,
357           .parent  = &clk_p,
358           .enable  = s3c24xx_clkcon_enable,
359           .ctrlbit = S3C2410_CLKCON_SPI
360         },
361         { .name    = "watchdog",
362           .id      = -1,
363           .parent  = &clk_p,
364           .ctrlbit = 0
365         }
366 };
367
368 /* initialise the clock system */
369
370 int s3c24xx_register_clock(struct clk *clk)
371 {
372         clk->owner = THIS_MODULE;
373         atomic_set(&clk->used, 0);
374
375         if (clk->enable == NULL)
376                 clk->enable = clk_null_enable;
377
378         /* add to the list of available clocks */
379
380         down(&clocks_sem);
381         list_add(&clk->list, &clocks);
382         up(&clocks_sem);
383
384         return 0;
385 }
386
387 /* initalise all the clocks */
388
389 int __init s3c24xx_setup_clocks(unsigned long xtal,
390                                 unsigned long fclk,
391                                 unsigned long hclk,
392                                 unsigned long pclk)
393 {
394         unsigned long clkslow = __raw_readl(S3C2410_CLKSLOW);
395         struct clk *clkp = init_clocks;
396         int ptr;
397         int ret;
398
399         printk(KERN_INFO "S3C2410 Clocks, (c) 2004 Simtec Electronics\n");
400
401         /* initialise the main system clocks */
402
403         clk_xtal.rate = xtal;
404
405         clk_h.rate = hclk;
406         clk_p.rate = pclk;
407         clk_f.rate = fclk;
408
409         /* it looks like just setting the register here is not good
410          * enough, and causes the odd hang at initial boot time, so
411          * do all of them indivdually.
412          *
413          * I think disabling the LCD clock if the LCD is active is
414          * very dangerous, and therefore the bootloader should be
415          * careful to not enable the LCD clock if it is not needed.
416          *
417          * and of course, this looks neater
418          */
419
420         s3c24xx_clk_enable(S3C2410_CLKCON_NAND, 0);
421         s3c24xx_clk_enable(S3C2410_CLKCON_USBH, 0);
422         s3c24xx_clk_enable(S3C2410_CLKCON_USBD, 0);
423         s3c24xx_clk_enable(S3C2410_CLKCON_ADC, 0);
424         s3c24xx_clk_enable(S3C2410_CLKCON_IIC, 0);
425         s3c24xx_clk_enable(S3C2410_CLKCON_SPI, 0);
426
427         /* assume uart clocks are correctly setup */
428
429         /* register our clocks */
430
431         if (s3c24xx_register_clock(&clk_xtal) < 0)
432                 printk(KERN_ERR "failed to register master xtal\n");
433
434         if (s3c24xx_register_clock(&clk_f) < 0)
435                 printk(KERN_ERR "failed to register cpu fclk\n");
436
437         if (s3c24xx_register_clock(&clk_h) < 0)
438                 printk(KERN_ERR "failed to register cpu hclk\n");
439
440         if (s3c24xx_register_clock(&clk_p) < 0)
441                 printk(KERN_ERR "failed to register cpu pclk\n");
442
443         /* register clocks from clock array */
444
445         for (ptr = 0; ptr < ARRAY_SIZE(init_clocks); ptr++, clkp++) {
446                 ret = s3c24xx_register_clock(clkp);
447                 if (ret < 0) {
448                         printk(KERN_ERR "Failed to register clock %s (%d)\n",
449                                clkp->name, ret);
450                 }
451         }
452
453         /* show the clock-slow value */
454
455         printk("CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s\n",
456                print_mhz(xtal / ( 2 * S3C2410_CLKSLOW_GET_SLOWVAL(clkslow))),
457                (clkslow & S3C2410_CLKSLOW_SLOW) ? "slow" : "fast",
458                (clkslow & S3C2410_CLKSLOW_MPLL_OFF) ? "off" : "on",
459                (clkslow & S3C2410_CLKSLOW_UCLK_OFF) ? "off" : "on");
460
461         return 0;
462 }