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