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 TL |
13 | #include <linux/version.h> |
14 | #include <linux/config.h> | |
1da177e4 | 15 | #include <linux/kernel.h> |
1a8bfa1e TL |
16 | #include <linux/init.h> |
17 | #include <linux/module.h> | |
1da177e4 LT |
18 | #include <linux/list.h> |
19 | #include <linux/errno.h> | |
20 | #include <linux/err.h> | |
4e57b681 | 21 | #include <linux/string.h> |
f8ce2547 | 22 | #include <linux/clk.h> |
00431707 | 23 | #include <linux/mutex.h> |
b824efae | 24 | #include <linux/platform_device.h> |
1da177e4 | 25 | |
bb13b5fd | 26 | #include <asm/io.h> |
1da177e4 | 27 | #include <asm/semaphore.h> |
1da177e4 | 28 | |
1a8bfa1e | 29 | #include <asm/arch/clock.h> |
1da177e4 | 30 | |
1a8bfa1e | 31 | LIST_HEAD(clocks); |
00431707 | 32 | static DEFINE_MUTEX(clocks_mutex); |
1a8bfa1e | 33 | DEFINE_SPINLOCK(clockfw_lock); |
1da177e4 | 34 | |
1a8bfa1e | 35 | static struct clk_functions *arch_clock; |
1da177e4 | 36 | |
1a8bfa1e | 37 | /*------------------------------------------------------------------------- |
f07adc59 | 38 | * Standard clock functions defined in include/linux/clk.h |
1a8bfa1e | 39 | *-------------------------------------------------------------------------*/ |
1da177e4 | 40 | |
b824efae TL |
41 | /* |
42 | * Returns a clock. Note that we first try to use device id on the bus | |
43 | * and clock name. If this fails, we try to use clock name only. | |
44 | */ | |
1a8bfa1e | 45 | struct clk * clk_get(struct device *dev, const char *id) |
1da177e4 LT |
46 | { |
47 | struct clk *p, *clk = ERR_PTR(-ENOENT); | |
b824efae TL |
48 | int idno; |
49 | ||
50 | if (dev == NULL || dev->bus != &platform_bus_type) | |
51 | idno = -1; | |
52 | else | |
53 | idno = to_platform_device(dev)->id; | |
1da177e4 | 54 | |
00431707 | 55 | mutex_lock(&clocks_mutex); |
b824efae TL |
56 | |
57 | list_for_each_entry(p, &clocks, node) { | |
58 | if (p->id == idno && | |
59 | strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | |
60 | clk = p; | |
67d4d835 | 61 | goto found; |
b824efae TL |
62 | } |
63 | } | |
64 | ||
1da177e4 LT |
65 | list_for_each_entry(p, &clocks, node) { |
66 | if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { | |
67 | clk = p; | |
68 | break; | |
69 | } | |
70 | } | |
b824efae | 71 | |
67d4d835 | 72 | found: |
00431707 | 73 | mutex_unlock(&clocks_mutex); |
1da177e4 LT |
74 | |
75 | return clk; | |
76 | } | |
77 | EXPORT_SYMBOL(clk_get); | |
78 | ||
1da177e4 LT |
79 | int clk_enable(struct clk *clk) |
80 | { | |
81 | unsigned long flags; | |
1a8bfa1e | 82 | int ret = 0; |
1da177e4 | 83 | |
b824efae TL |
84 | if (clk == NULL || IS_ERR(clk)) |
85 | return -EINVAL; | |
86 | ||
1da177e4 | 87 | spin_lock_irqsave(&clockfw_lock, flags); |
f07adc59 | 88 | if (arch_clock->clk_enable) |
1a8bfa1e | 89 | ret = arch_clock->clk_enable(clk); |
1da177e4 | 90 | spin_unlock_irqrestore(&clockfw_lock, flags); |
1a8bfa1e | 91 | |
1da177e4 LT |
92 | return ret; |
93 | } | |
94 | EXPORT_SYMBOL(clk_enable); | |
95 | ||
1da177e4 LT |
96 | void clk_disable(struct clk *clk) |
97 | { | |
98 | unsigned long flags; | |
99 | ||
b824efae TL |
100 | if (clk == NULL || IS_ERR(clk)) |
101 | return; | |
102 | ||
1da177e4 | 103 | spin_lock_irqsave(&clockfw_lock, flags); |
f07adc59 | 104 | if (arch_clock->clk_disable) |
1a8bfa1e | 105 | arch_clock->clk_disable(clk); |
1da177e4 LT |
106 | spin_unlock_irqrestore(&clockfw_lock, flags); |
107 | } | |
108 | EXPORT_SYMBOL(clk_disable); | |
109 | ||
1da177e4 LT |
110 | int clk_get_usecount(struct clk *clk) |
111 | { | |
1a8bfa1e TL |
112 | unsigned long flags; |
113 | int ret = 0; | |
1da177e4 | 114 | |
b824efae TL |
115 | if (clk == NULL || IS_ERR(clk)) |
116 | return 0; | |
117 | ||
1a8bfa1e TL |
118 | spin_lock_irqsave(&clockfw_lock, flags); |
119 | ret = clk->usecount; | |
120 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 121 | |
1a8bfa1e | 122 | return ret; |
1da177e4 | 123 | } |
1a8bfa1e | 124 | EXPORT_SYMBOL(clk_get_usecount); |
1da177e4 | 125 | |
1a8bfa1e | 126 | unsigned long clk_get_rate(struct clk *clk) |
1da177e4 | 127 | { |
1a8bfa1e TL |
128 | unsigned long flags; |
129 | unsigned long ret = 0; | |
1da177e4 | 130 | |
b824efae TL |
131 | if (clk == NULL || IS_ERR(clk)) |
132 | return 0; | |
133 | ||
1a8bfa1e TL |
134 | spin_lock_irqsave(&clockfw_lock, flags); |
135 | ret = clk->rate; | |
136 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 137 | |
1a8bfa1e | 138 | return ret; |
1da177e4 | 139 | } |
1a8bfa1e | 140 | EXPORT_SYMBOL(clk_get_rate); |
1da177e4 | 141 | |
1a8bfa1e | 142 | void clk_put(struct clk *clk) |
bb13b5fd | 143 | { |
1a8bfa1e TL |
144 | if (clk && !IS_ERR(clk)) |
145 | module_put(clk->owner); | |
bb13b5fd | 146 | } |
1a8bfa1e | 147 | EXPORT_SYMBOL(clk_put); |
bb13b5fd | 148 | |
1a8bfa1e | 149 | /*------------------------------------------------------------------------- |
f07adc59 | 150 | * Optional clock functions defined in include/linux/clk.h |
1a8bfa1e | 151 | *-------------------------------------------------------------------------*/ |
bb13b5fd | 152 | |
1da177e4 LT |
153 | long clk_round_rate(struct clk *clk, unsigned long rate) |
154 | { | |
1a8bfa1e TL |
155 | unsigned long flags; |
156 | long ret = 0; | |
1da177e4 | 157 | |
b824efae TL |
158 | if (clk == NULL || IS_ERR(clk)) |
159 | return ret; | |
160 | ||
1a8bfa1e TL |
161 | spin_lock_irqsave(&clockfw_lock, flags); |
162 | if (arch_clock->clk_round_rate) | |
163 | ret = arch_clock->clk_round_rate(clk, rate); | |
164 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 165 | |
1a8bfa1e | 166 | return ret; |
1da177e4 LT |
167 | } |
168 | EXPORT_SYMBOL(clk_round_rate); | |
169 | ||
1a8bfa1e | 170 | int clk_set_rate(struct clk *clk, unsigned long rate) |
1da177e4 | 171 | { |
1a8bfa1e | 172 | unsigned long flags; |
b824efae TL |
173 | int ret = -EINVAL; |
174 | ||
175 | if (clk == NULL || IS_ERR(clk)) | |
176 | return ret; | |
bb13b5fd | 177 | |
1a8bfa1e TL |
178 | spin_lock_irqsave(&clockfw_lock, flags); |
179 | if (arch_clock->clk_set_rate) | |
180 | ret = arch_clock->clk_set_rate(clk, rate); | |
181 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 182 | |
1a8bfa1e | 183 | return ret; |
1da177e4 | 184 | } |
1a8bfa1e | 185 | EXPORT_SYMBOL(clk_set_rate); |
1da177e4 | 186 | |
1a8bfa1e | 187 | int clk_set_parent(struct clk *clk, struct clk *parent) |
1da177e4 | 188 | { |
1a8bfa1e | 189 | unsigned long flags; |
b824efae TL |
190 | int ret = -EINVAL; |
191 | ||
192 | if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) | |
193 | return ret; | |
1da177e4 | 194 | |
1a8bfa1e TL |
195 | spin_lock_irqsave(&clockfw_lock, flags); |
196 | if (arch_clock->clk_set_parent) | |
197 | ret = arch_clock->clk_set_parent(clk, parent); | |
198 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 199 | |
1a8bfa1e | 200 | return ret; |
1da177e4 | 201 | } |
1a8bfa1e | 202 | EXPORT_SYMBOL(clk_set_parent); |
1da177e4 | 203 | |
1a8bfa1e | 204 | struct clk *clk_get_parent(struct clk *clk) |
1da177e4 | 205 | { |
1a8bfa1e TL |
206 | unsigned long flags; |
207 | struct clk * ret = NULL; | |
1da177e4 | 208 | |
b824efae TL |
209 | if (clk == NULL || IS_ERR(clk)) |
210 | return ret; | |
211 | ||
1a8bfa1e TL |
212 | spin_lock_irqsave(&clockfw_lock, flags); |
213 | if (arch_clock->clk_get_parent) | |
214 | ret = arch_clock->clk_get_parent(clk); | |
215 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 LT |
216 | |
217 | return ret; | |
218 | } | |
1a8bfa1e | 219 | EXPORT_SYMBOL(clk_get_parent); |
1da177e4 | 220 | |
1a8bfa1e TL |
221 | /*------------------------------------------------------------------------- |
222 | * OMAP specific clock functions shared between omap1 and omap2 | |
223 | *-------------------------------------------------------------------------*/ | |
1da177e4 | 224 | |
1a8bfa1e | 225 | unsigned int __initdata mpurate; |
1da177e4 | 226 | |
1a8bfa1e TL |
227 | /* |
228 | * By default we use the rate set by the bootloader. | |
229 | * You can override this with mpurate= cmdline option. | |
230 | */ | |
231 | static int __init omap_clk_setup(char *str) | |
1da177e4 | 232 | { |
1a8bfa1e | 233 | get_option(&str, &mpurate); |
1da177e4 | 234 | |
1a8bfa1e TL |
235 | if (!mpurate) |
236 | return 1; | |
1da177e4 | 237 | |
1a8bfa1e TL |
238 | if (mpurate < 1000) |
239 | mpurate *= 1000000; | |
1da177e4 | 240 | |
1a8bfa1e | 241 | return 1; |
1da177e4 | 242 | } |
1a8bfa1e | 243 | __setup("mpurate=", omap_clk_setup); |
1da177e4 | 244 | |
1a8bfa1e TL |
245 | /* Used for clocks that always have same value as the parent clock */ |
246 | void followparent_recalc(struct clk *clk) | |
1da177e4 | 247 | { |
b824efae TL |
248 | if (clk == NULL || IS_ERR(clk)) |
249 | return; | |
250 | ||
1a8bfa1e | 251 | clk->rate = clk->parent->rate; |
1da177e4 LT |
252 | } |
253 | ||
1a8bfa1e TL |
254 | /* Propagate rate to children */ |
255 | void propagate_rate(struct clk * tclk) | |
1da177e4 | 256 | { |
1a8bfa1e | 257 | struct clk *clkp; |
1da177e4 | 258 | |
b824efae TL |
259 | if (tclk == NULL || IS_ERR(tclk)) |
260 | return; | |
261 | ||
1a8bfa1e TL |
262 | list_for_each_entry(clkp, &clocks, node) { |
263 | if (likely(clkp->parent != tclk)) | |
264 | continue; | |
265 | if (likely((u32)clkp->recalc)) | |
266 | clkp->recalc(clkp); | |
267 | } | |
1da177e4 LT |
268 | } |
269 | ||
1da177e4 LT |
270 | int clk_register(struct clk *clk) |
271 | { | |
b824efae TL |
272 | if (clk == NULL || IS_ERR(clk)) |
273 | return -EINVAL; | |
274 | ||
00431707 | 275 | mutex_lock(&clocks_mutex); |
1da177e4 LT |
276 | list_add(&clk->node, &clocks); |
277 | if (clk->init) | |
278 | clk->init(clk); | |
00431707 | 279 | mutex_unlock(&clocks_mutex); |
1a8bfa1e | 280 | |
1da177e4 LT |
281 | return 0; |
282 | } | |
283 | EXPORT_SYMBOL(clk_register); | |
284 | ||
285 | void clk_unregister(struct clk *clk) | |
286 | { | |
b824efae TL |
287 | if (clk == NULL || IS_ERR(clk)) |
288 | return; | |
289 | ||
00431707 | 290 | mutex_lock(&clocks_mutex); |
1da177e4 | 291 | list_del(&clk->node); |
00431707 | 292 | mutex_unlock(&clocks_mutex); |
1da177e4 LT |
293 | } |
294 | EXPORT_SYMBOL(clk_unregister); | |
295 | ||
1a8bfa1e | 296 | void clk_deny_idle(struct clk *clk) |
bb13b5fd | 297 | { |
1a8bfa1e TL |
298 | unsigned long flags; |
299 | ||
b824efae TL |
300 | if (clk == NULL || IS_ERR(clk)) |
301 | return; | |
302 | ||
1a8bfa1e TL |
303 | spin_lock_irqsave(&clockfw_lock, flags); |
304 | if (arch_clock->clk_deny_idle) | |
305 | arch_clock->clk_deny_idle(clk); | |
306 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
bb13b5fd | 307 | } |
1a8bfa1e | 308 | EXPORT_SYMBOL(clk_deny_idle); |
1da177e4 | 309 | |
1a8bfa1e | 310 | void clk_allow_idle(struct clk *clk) |
1da177e4 | 311 | { |
1a8bfa1e | 312 | unsigned long flags; |
1da177e4 | 313 | |
b824efae TL |
314 | if (clk == NULL || IS_ERR(clk)) |
315 | return; | |
316 | ||
1a8bfa1e TL |
317 | spin_lock_irqsave(&clockfw_lock, flags); |
318 | if (arch_clock->clk_allow_idle) | |
319 | arch_clock->clk_allow_idle(clk); | |
320 | spin_unlock_irqrestore(&clockfw_lock, flags); | |
1da177e4 | 321 | } |
1a8bfa1e | 322 | EXPORT_SYMBOL(clk_allow_idle); |
bb13b5fd | 323 | |
1a8bfa1e | 324 | /*-------------------------------------------------------------------------*/ |
bb13b5fd | 325 | |
1a8bfa1e | 326 | int __init clk_init(struct clk_functions * custom_clocks) |
bb13b5fd | 327 | { |
1a8bfa1e TL |
328 | if (!custom_clocks) { |
329 | printk(KERN_ERR "No custom clock functions registered\n"); | |
330 | BUG(); | |
bb13b5fd TL |
331 | } |
332 | ||
1a8bfa1e TL |
333 | arch_clock = custom_clocks; |
334 | ||
bb13b5fd TL |
335 | return 0; |
336 | } |