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