Commit | Line | Data |
---|---|---|
1da177e4 | 1 | /* |
b9158556 | 2 | * linux/arch/arm/plat-omap/clock.c |
1da177e4 | 3 | * |
1a8bfa1e | 4 | * Copyright (C) 2004 - 2005 Nokia corporation |
1da177e4 LT |
5 | * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com> |
6 | * | |
1a8bfa1e TL |
7 | * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com> |
8 | * | |
1da177e4 LT |
9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | |
11 | * published by the Free Software Foundation. | |
12 | */ | |
1a8bfa1e | 13 | #include <linux/version.h> |
1da177e4 | 14 | #include <linux/kernel.h> |
1a8bfa1e TL |
15 | #include <linux/init.h> |
16 | #include <linux/module.h> | |
1da177e4 LT |
17 | #include <linux/list.h> |
18 | #include <linux/errno.h> | |
19 | #include <linux/err.h> | |
4e57b681 | 20 | #include <linux/string.h> |
f8ce2547 | 21 | #include <linux/clk.h> |
00431707 | 22 | #include <linux/mutex.h> |
b824efae | 23 | #include <linux/platform_device.h> |
1da177e4 | 24 | |
bb13b5fd | 25 | #include <asm/io.h> |
1da177e4 | 26 | #include <asm/semaphore.h> |
1da177e4 | 27 | |
1a8bfa1e | 28 | #include <asm/arch/clock.h> |
1da177e4 | 29 | |
7df3450e | 30 | static LIST_HEAD(clocks); |
00431707 | 31 | static DEFINE_MUTEX(clocks_mutex); |
7df3450e | 32 | static DEFINE_SPINLOCK(clockfw_lock); |
1da177e4 | 33 | |
1a8bfa1e | 34 | static struct clk_functions *arch_clock; |
1da177e4 | 35 | |
0ce33563 JY |
36 | #ifdef CONFIG_PM_DEBUG |
37 | ||
38 | static void print_parents(struct clk *clk) | |
39 | { | |
40 | struct clk *p; | |
41 | int printed = 0; | |
42 | ||
43 | list_for_each_entry(p, &clocks, node) { | |
44 | if (p->parent == clk && p->usecount) { | |
45 | if (!clk->usecount && !printed) { | |
46 | printk("MISMATCH: %s\n", clk->name); | |
47 | printed = 1; | |
48 | } | |
49 | printk("\t%-15s\n", p->name); | |
50 | } | |
51 | } | |
52 | } | |
53 | ||
54 | void clk_print_usecounts(void) | |
55 | { | |
56 | unsigned long flags; | |
57 | struct clk *p; | |
58 | ||
59 | spin_lock_irqsave(&clockfw_lock, flags); | |
60 | list_for_each_entry(p, &clocks, node) { | |
61 | if (p->usecount) | |
62 | printk("%-15s: %d\n", p->name, p->usecount); | |
63 | print_parents(p); | |
64 | ||
65 | } | |
66 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
67 | } | |
68 | ||
69 | #endif | |
70 | ||
1a8bfa1e | 71 | /*------------------------------------------------------------------------- |
f07adc59 | 72 | * Standard clock functions defined in include/linux/clk.h |
1a8bfa1e | 73 | *-------------------------------------------------------------------------*/ |
1da177e4 | 74 | |
b824efae TL |
75 | /* |
76 | * Returns a clock. Note that we first try to use device id on the bus | |
77 | * and clock name. If this fails, we try to use clock name only. | |
78 | */ | |
1a8bfa1e | 79 | struct clk * clk_get(struct device *dev, const char *id) |
1da177e4 LT |
80 | { |
81 | struct clk *p, *clk = ERR_PTR(-ENOENT); | |
b824efae TL |
82 | int idno; |
83 | ||
84 | if (dev == NULL || dev->bus != &platform_bus_type) | |
85 | idno = -1; | |
86 | else | |
87 | idno = to_platform_device(dev)->id; | |
1da177e4 | 88 | |
00431707 | 89 | mutex_lock(&clocks_mutex); |
b824efae TL |
90 | |
91 | list_for_each_entry(p, &clocks, node) { | |
92 | if (p->id == idno && | |
93 | strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | |
94 | clk = p; | |
67d4d835 | 95 | goto found; |
b824efae TL |
96 | } |
97 | } | |
98 | ||
1da177e4 LT |
99 | list_for_each_entry(p, &clocks, node) { |
100 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | |
101 | clk = p; | |
102 | break; | |
103 | } | |
104 | } | |
b824efae | 105 | |
67d4d835 | 106 | found: |
00431707 | 107 | mutex_unlock(&clocks_mutex); |
1da177e4 LT |
108 | |
109 | return clk; | |
110 | } | |
111 | EXPORT_SYMBOL(clk_get); | |
112 | ||
1da177e4 LT |
113 | int clk_enable(struct clk *clk) |
114 | { | |
115 | unsigned long flags; | |
1a8bfa1e | 116 | int ret = 0; |
1da177e4 | 117 | |
b824efae TL |
118 | if (clk == NULL || IS_ERR(clk)) |
119 | return -EINVAL; | |
120 | ||
1da177e4 | 121 | spin_lock_irqsave(&clockfw_lock, flags); |
f07adc59 | 122 | if (arch_clock->clk_enable) |
1a8bfa1e | 123 | ret = arch_clock->clk_enable(clk); |
1da177e4 | 124 | spin_unlock_irqrestore(&clockfw_lock, flags); |
1a8bfa1e | 125 | |
1da177e4 LT |
126 | return ret; |
127 | } | |
128 | EXPORT_SYMBOL(clk_enable); | |
129 | ||
1da177e4 LT |
130 | void clk_disable(struct clk *clk) |
131 | { | |
132 | unsigned long flags; | |
133 | ||
b824efae TL |
134 | if (clk == NULL || IS_ERR(clk)) |
135 | return; | |
136 | ||
1da177e4 | 137 | spin_lock_irqsave(&clockfw_lock, flags); |
dee45648 | 138 | BUG_ON(clk->usecount == 0); |
f07adc59 | 139 | if (arch_clock->clk_disable) |
1a8bfa1e | 140 | arch_clock->clk_disable(clk); |
1da177e4 LT |
141 | spin_unlock_irqrestore(&clockfw_lock, flags); |
142 | } | |
143 | EXPORT_SYMBOL(clk_disable); | |
144 | ||
1da177e4 LT |
145 | int clk_get_usecount(struct clk *clk) |
146 | { | |
1a8bfa1e TL |
147 | unsigned long flags; |
148 | int ret = 0; | |
1da177e4 | 149 | |
b824efae TL |
150 | if (clk == NULL || IS_ERR(clk)) |
151 | return 0; | |
152 | ||
1a8bfa1e TL |
153 | spin_lock_irqsave(&clockfw_lock, flags); |
154 | ret = clk->usecount; | |
155 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 156 | |
1a8bfa1e | 157 | return ret; |
1da177e4 | 158 | } |
1a8bfa1e | 159 | EXPORT_SYMBOL(clk_get_usecount); |
1da177e4 | 160 | |
1a8bfa1e | 161 | unsigned long clk_get_rate(struct clk *clk) |
1da177e4 | 162 | { |
1a8bfa1e TL |
163 | unsigned long flags; |
164 | unsigned long ret = 0; | |
1da177e4 | 165 | |
b824efae TL |
166 | if (clk == NULL || IS_ERR(clk)) |
167 | return 0; | |
168 | ||
1a8bfa1e TL |
169 | spin_lock_irqsave(&clockfw_lock, flags); |
170 | ret = clk->rate; | |
171 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 172 | |
1a8bfa1e | 173 | return ret; |
1da177e4 | 174 | } |
1a8bfa1e | 175 | EXPORT_SYMBOL(clk_get_rate); |
1da177e4 | 176 | |
1a8bfa1e | 177 | void clk_put(struct clk *clk) |
bb13b5fd | 178 | { |
1a8bfa1e TL |
179 | if (clk && !IS_ERR(clk)) |
180 | module_put(clk->owner); | |
bb13b5fd | 181 | } |
1a8bfa1e | 182 | EXPORT_SYMBOL(clk_put); |
bb13b5fd | 183 | |
1a8bfa1e | 184 | /*------------------------------------------------------------------------- |
f07adc59 | 185 | * Optional clock functions defined in include/linux/clk.h |
1a8bfa1e | 186 | *-------------------------------------------------------------------------*/ |
bb13b5fd | 187 | |
1da177e4 LT |
188 | long clk_round_rate(struct clk *clk, unsigned long rate) |
189 | { | |
1a8bfa1e TL |
190 | unsigned long flags; |
191 | long ret = 0; | |
1da177e4 | 192 | |
b824efae TL |
193 | if (clk == NULL || IS_ERR(clk)) |
194 | return ret; | |
195 | ||
1a8bfa1e TL |
196 | spin_lock_irqsave(&clockfw_lock, flags); |
197 | if (arch_clock->clk_round_rate) | |
198 | ret = arch_clock->clk_round_rate(clk, rate); | |
199 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 200 | |
1a8bfa1e | 201 | return ret; |
1da177e4 LT |
202 | } |
203 | EXPORT_SYMBOL(clk_round_rate); | |
204 | ||
1a8bfa1e | 205 | int clk_set_rate(struct clk *clk, unsigned long rate) |
1da177e4 | 206 | { |
1a8bfa1e | 207 | unsigned long flags; |
b824efae TL |
208 | int ret = -EINVAL; |
209 | ||
210 | if (clk == NULL || IS_ERR(clk)) | |
211 | return ret; | |
bb13b5fd | 212 | |
1a8bfa1e TL |
213 | spin_lock_irqsave(&clockfw_lock, flags); |
214 | if (arch_clock->clk_set_rate) | |
215 | ret = arch_clock->clk_set_rate(clk, rate); | |
216 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 217 | |
1a8bfa1e | 218 | return ret; |
1da177e4 | 219 | } |
1a8bfa1e | 220 | EXPORT_SYMBOL(clk_set_rate); |
1da177e4 | 221 | |
1a8bfa1e | 222 | int clk_set_parent(struct clk *clk, struct clk *parent) |
1da177e4 | 223 | { |
1a8bfa1e | 224 | unsigned long flags; |
b824efae TL |
225 | int ret = -EINVAL; |
226 | ||
227 | if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) | |
228 | return ret; | |
1da177e4 | 229 | |
1a8bfa1e TL |
230 | spin_lock_irqsave(&clockfw_lock, flags); |
231 | if (arch_clock->clk_set_parent) | |
232 | ret = arch_clock->clk_set_parent(clk, parent); | |
233 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 234 | |
1a8bfa1e | 235 | return ret; |
1da177e4 | 236 | } |
1a8bfa1e | 237 | EXPORT_SYMBOL(clk_set_parent); |
1da177e4 | 238 | |
1a8bfa1e | 239 | struct clk *clk_get_parent(struct clk *clk) |
1da177e4 | 240 | { |
1a8bfa1e TL |
241 | unsigned long flags; |
242 | struct clk * ret = NULL; | |
1da177e4 | 243 | |
b824efae TL |
244 | if (clk == NULL || IS_ERR(clk)) |
245 | return ret; | |
246 | ||
1a8bfa1e TL |
247 | spin_lock_irqsave(&clockfw_lock, flags); |
248 | if (arch_clock->clk_get_parent) | |
249 | ret = arch_clock->clk_get_parent(clk); | |
250 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 LT |
251 | |
252 | return ret; | |
253 | } | |
1a8bfa1e | 254 | EXPORT_SYMBOL(clk_get_parent); |
1da177e4 | 255 | |
1a8bfa1e TL |
256 | /*------------------------------------------------------------------------- |
257 | * OMAP specific clock functions shared between omap1 and omap2 | |
258 | *-------------------------------------------------------------------------*/ | |
1da177e4 | 259 | |
1a8bfa1e | 260 | unsigned int __initdata mpurate; |
1da177e4 | 261 | |
1a8bfa1e TL |
262 | /* |
263 | * By default we use the rate set by the bootloader. | |
264 | * You can override this with mpurate= cmdline option. | |
265 | */ | |
266 | static int __init omap_clk_setup(char *str) | |
1da177e4 | 267 | { |
1a8bfa1e | 268 | get_option(&str, &mpurate); |
1da177e4 | 269 | |
1a8bfa1e TL |
270 | if (!mpurate) |
271 | return 1; | |
1da177e4 | 272 | |
1a8bfa1e TL |
273 | if (mpurate < 1000) |
274 | mpurate *= 1000000; | |
1da177e4 | 275 | |
1a8bfa1e | 276 | return 1; |
1da177e4 | 277 | } |
1a8bfa1e | 278 | __setup("mpurate=", omap_clk_setup); |
1da177e4 | 279 | |
1a8bfa1e TL |
280 | /* Used for clocks that always have same value as the parent clock */ |
281 | void followparent_recalc(struct clk *clk) | |
1da177e4 | 282 | { |
b824efae TL |
283 | if (clk == NULL || IS_ERR(clk)) |
284 | return; | |
285 | ||
1a8bfa1e | 286 | clk->rate = clk->parent->rate; |
b1465bf7 ID |
287 | if (unlikely(clk->flags & RATE_PROPAGATES)) |
288 | propagate_rate(clk); | |
1da177e4 LT |
289 | } |
290 | ||
1a8bfa1e TL |
291 | /* Propagate rate to children */ |
292 | void propagate_rate(struct clk * tclk) | |
1da177e4 | 293 | { |
1a8bfa1e | 294 | struct clk *clkp; |
1da177e4 | 295 | |
b824efae TL |
296 | if (tclk == NULL || IS_ERR(tclk)) |
297 | return; | |
298 | ||
1a8bfa1e TL |
299 | list_for_each_entry(clkp, &clocks, node) { |
300 | if (likely(clkp->parent != tclk)) | |
301 | continue; | |
302 | if (likely((u32)clkp->recalc)) | |
303 | clkp->recalc(clkp); | |
304 | } | |
1da177e4 LT |
305 | } |
306 | ||
1da177e4 LT |
307 | int clk_register(struct clk *clk) |
308 | { | |
b824efae TL |
309 | if (clk == NULL || IS_ERR(clk)) |
310 | return -EINVAL; | |
311 | ||
00431707 | 312 | mutex_lock(&clocks_mutex); |
1da177e4 LT |
313 | list_add(&clk->node, &clocks); |
314 | if (clk->init) | |
315 | clk->init(clk); | |
00431707 | 316 | mutex_unlock(&clocks_mutex); |
1a8bfa1e | 317 | |
1da177e4 LT |
318 | return 0; |
319 | } | |
320 | EXPORT_SYMBOL(clk_register); | |
321 | ||
322 | void clk_unregister(struct clk *clk) | |
323 | { | |
b824efae TL |
324 | if (clk == NULL || IS_ERR(clk)) |
325 | return; | |
326 | ||
00431707 | 327 | mutex_lock(&clocks_mutex); |
1da177e4 | 328 | list_del(&clk->node); |
00431707 | 329 | mutex_unlock(&clocks_mutex); |
1da177e4 LT |
330 | } |
331 | EXPORT_SYMBOL(clk_unregister); | |
332 | ||
1a8bfa1e | 333 | void clk_deny_idle(struct clk *clk) |
bb13b5fd | 334 | { |
1a8bfa1e TL |
335 | unsigned long flags; |
336 | ||
b824efae TL |
337 | if (clk == NULL || IS_ERR(clk)) |
338 | return; | |
339 | ||
1a8bfa1e TL |
340 | spin_lock_irqsave(&clockfw_lock, flags); |
341 | if (arch_clock->clk_deny_idle) | |
342 | arch_clock->clk_deny_idle(clk); | |
343 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
bb13b5fd | 344 | } |
1a8bfa1e | 345 | EXPORT_SYMBOL(clk_deny_idle); |
1da177e4 | 346 | |
1a8bfa1e | 347 | void clk_allow_idle(struct clk *clk) |
1da177e4 | 348 | { |
1a8bfa1e | 349 | unsigned long flags; |
1da177e4 | 350 | |
b824efae TL |
351 | if (clk == NULL || IS_ERR(clk)) |
352 | return; | |
353 | ||
1a8bfa1e TL |
354 | spin_lock_irqsave(&clockfw_lock, flags); |
355 | if (arch_clock->clk_allow_idle) | |
356 | arch_clock->clk_allow_idle(clk); | |
357 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 358 | } |
1a8bfa1e | 359 | EXPORT_SYMBOL(clk_allow_idle); |
bb13b5fd | 360 | |
1a8bfa1e | 361 | /*-------------------------------------------------------------------------*/ |
bb13b5fd | 362 | |
90afd5cb TL |
363 | #ifdef CONFIG_OMAP_RESET_CLOCKS |
364 | /* | |
365 | * Disable any unused clocks left on by the bootloader | |
366 | */ | |
367 | static int __init clk_disable_unused(void) | |
368 | { | |
369 | struct clk *ck; | |
370 | unsigned long flags; | |
371 | ||
372 | list_for_each_entry(ck, &clocks, node) { | |
373 | if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) || | |
374 | ck->enable_reg == 0) | |
375 | continue; | |
376 | ||
377 | spin_lock_irqsave(&clockfw_lock, flags); | |
378 | if (arch_clock->clk_disable_unused) | |
379 | arch_clock->clk_disable_unused(ck); | |
380 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
381 | } | |
382 | ||
383 | return 0; | |
384 | } | |
385 | late_initcall(clk_disable_unused); | |
386 | #endif | |
387 | ||
1a8bfa1e | 388 | int __init clk_init(struct clk_functions * custom_clocks) |
bb13b5fd | 389 | { |
1a8bfa1e TL |
390 | if (!custom_clocks) { |
391 | printk(KERN_ERR "No custom clock functions registered\n"); | |
392 | BUG(); | |
bb13b5fd TL |
393 | } |
394 | ||
1a8bfa1e TL |
395 | arch_clock = custom_clocks; |
396 | ||
bb13b5fd TL |
397 | return 0; |
398 | } |