blob: 72d34a23a2ec6276073af8906e916075b6808945 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Tony Lindgrenb9158552005-07-10 19:58:14 +01002 * linux/arch/arm/plat-omap/clock.c
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00004 * Copyright (C) 2004 - 2005 Nokia corporation
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
6 *
Tony Lindgren1a8bfa12005-11-10 14:26:50 +00007 * Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
8 *
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 * 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 */
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000013#include <linux/version.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/kernel.h>
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000015#include <linux/init.h>
16#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/list.h>
18#include <linux/errno.h>
19#include <linux/err.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080020#include <linux/string.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000021#include <linux/clk.h>
Arjan van de Ven00431702006-01-12 18:42:23 +000022#include <linux/mutex.h>
Tony Lindgrenb824efa2006-04-02 17:46:20 +010023#include <linux/platform_device.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070024
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +010025#include <asm/io.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070026
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000027#include <asm/arch/clock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
Juha Yrjola7df34502006-06-26 16:16:22 -070029static LIST_HEAD(clocks);
Arjan van de Ven00431702006-01-12 18:42:23 +000030static DEFINE_MUTEX(clocks_mutex);
Juha Yrjola7df34502006-06-26 16:16:22 -070031static DEFINE_SPINLOCK(clockfw_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070032
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000033static struct clk_functions *arch_clock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070034
Juha Yrjola0ce33562006-12-06 17:13:49 -080035#ifdef CONFIG_PM_DEBUG
36
37static void print_parents(struct clk *clk)
38{
39 struct clk *p;
40 int printed = 0;
41
42 list_for_each_entry(p, &clocks, node) {
43 if (p->parent == clk && p->usecount) {
44 if (!clk->usecount && !printed) {
45 printk("MISMATCH: %s\n", clk->name);
46 printed = 1;
47 }
48 printk("\t%-15s\n", p->name);
49 }
50 }
51}
52
53void clk_print_usecounts(void)
54{
55 unsigned long flags;
56 struct clk *p;
57
58 spin_lock_irqsave(&clockfw_lock, flags);
59 list_for_each_entry(p, &clocks, node) {
60 if (p->usecount)
61 printk("%-15s: %d\n", p->name, p->usecount);
62 print_parents(p);
63
64 }
65 spin_unlock_irqrestore(&clockfw_lock, flags);
66}
67
68#endif
69
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000070/*-------------------------------------------------------------------------
Tony Lindgrenf07adc52006-01-17 15:27:09 -080071 * Standard clock functions defined in include/linux/clk.h
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000072 *-------------------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070073
Tony Lindgrenb824efa2006-04-02 17:46:20 +010074/*
75 * Returns a clock. Note that we first try to use device id on the bus
76 * and clock name. If this fails, we try to use clock name only.
77 */
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000078struct clk * clk_get(struct device *dev, const char *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
80 struct clk *p, *clk = ERR_PTR(-ENOENT);
Tony Lindgrenb824efa2006-04-02 17:46:20 +010081 int idno;
82
83 if (dev == NULL || dev->bus != &platform_bus_type)
84 idno = -1;
85 else
86 idno = to_platform_device(dev)->id;
Linus Torvalds1da177e2005-04-16 15:20:36 -070087
Arjan van de Ven00431702006-01-12 18:42:23 +000088 mutex_lock(&clocks_mutex);
Tony Lindgrenb824efa2006-04-02 17:46:20 +010089
90 list_for_each_entry(p, &clocks, node) {
91 if (p->id == idno &&
92 strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
93 clk = p;
Tony Lindgren67d4d832006-04-09 22:21:05 +010094 goto found;
Tony Lindgrenb824efa2006-04-02 17:46:20 +010095 }
96 }
97
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 list_for_each_entry(p, &clocks, node) {
99 if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
100 clk = p;
101 break;
102 }
103 }
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100104
Tony Lindgren67d4d832006-04-09 22:21:05 +0100105found:
Arjan van de Ven00431702006-01-12 18:42:23 +0000106 mutex_unlock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108 return clk;
109}
110EXPORT_SYMBOL(clk_get);
111
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000112int clk_enable(struct clk *clk)
113{
114 unsigned long flags;
115 int ret = 0;
116
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100117 if (clk == NULL || IS_ERR(clk))
118 return -EINVAL;
119
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000120 spin_lock_irqsave(&clockfw_lock, flags);
Tony Lindgrenf07adc52006-01-17 15:27:09 -0800121 if (arch_clock->clk_enable)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000122 ret = arch_clock->clk_enable(clk);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000123 spin_unlock_irqrestore(&clockfw_lock, flags);
124
125 return ret;
126}
127EXPORT_SYMBOL(clk_enable);
128
129void clk_disable(struct clk *clk)
130{
131 unsigned long flags;
132
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100133 if (clk == NULL || IS_ERR(clk))
134 return;
135
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000136 spin_lock_irqsave(&clockfw_lock, flags);
Juha Yrjoladee45642006-09-25 12:41:47 +0300137 BUG_ON(clk->usecount == 0);
Tony Lindgrenf07adc52006-01-17 15:27:09 -0800138 if (arch_clock->clk_disable)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000139 arch_clock->clk_disable(clk);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000140 spin_unlock_irqrestore(&clockfw_lock, flags);
141}
142EXPORT_SYMBOL(clk_disable);
143
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000144int clk_get_usecount(struct clk *clk)
145{
146 unsigned long flags;
147 int ret = 0;
148
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100149 if (clk == NULL || IS_ERR(clk))
150 return 0;
151
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000152 spin_lock_irqsave(&clockfw_lock, flags);
153 ret = clk->usecount;
154 spin_unlock_irqrestore(&clockfw_lock, flags);
155
156 return ret;
157}
158EXPORT_SYMBOL(clk_get_usecount);
159
160unsigned long clk_get_rate(struct clk *clk)
161{
162 unsigned long flags;
163 unsigned long ret = 0;
164
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100165 if (clk == NULL || IS_ERR(clk))
166 return 0;
167
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000168 spin_lock_irqsave(&clockfw_lock, flags);
169 ret = clk->rate;
170 spin_unlock_irqrestore(&clockfw_lock, flags);
171
172 return ret;
173}
174EXPORT_SYMBOL(clk_get_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176void clk_put(struct clk *clk)
177{
178 if (clk && !IS_ERR(clk))
179 module_put(clk->owner);
180}
181EXPORT_SYMBOL(clk_put);
182
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000183/*-------------------------------------------------------------------------
Tony Lindgrenf07adc52006-01-17 15:27:09 -0800184 * Optional clock functions defined in include/linux/clk.h
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000185 *-------------------------------------------------------------------------*/
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187long clk_round_rate(struct clk *clk, unsigned long rate)
188{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000189 unsigned long flags;
190 long ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100192 if (clk == NULL || IS_ERR(clk))
193 return ret;
194
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000195 spin_lock_irqsave(&clockfw_lock, flags);
196 if (arch_clock->clk_round_rate)
197 ret = arch_clock->clk_round_rate(clk, rate);
198 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000200 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201}
202EXPORT_SYMBOL(clk_round_rate);
203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204int clk_set_rate(struct clk *clk, unsigned long rate)
205{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000206 unsigned long flags;
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100207 int ret = -EINVAL;
208
209 if (clk == NULL || IS_ERR(clk))
210 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000212 spin_lock_irqsave(&clockfw_lock, flags);
213 if (arch_clock->clk_set_rate)
214 ret = arch_clock->clk_set_rate(clk, rate);
215 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 return ret;
218}
219EXPORT_SYMBOL(clk_set_rate);
220
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000221int clk_set_parent(struct clk *clk, struct clk *parent)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000223 unsigned long flags;
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100224 int ret = -EINVAL;
225
226 if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
227 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000229 spin_lock_irqsave(&clockfw_lock, flags);
230 if (arch_clock->clk_set_parent)
231 ret = arch_clock->clk_set_parent(clk, parent);
232 spin_unlock_irqrestore(&clockfw_lock, flags);
233
234 return ret;
235}
236EXPORT_SYMBOL(clk_set_parent);
237
238struct clk *clk_get_parent(struct clk *clk)
239{
240 unsigned long flags;
241 struct clk * ret = NULL;
242
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100243 if (clk == NULL || IS_ERR(clk))
244 return ret;
245
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000246 spin_lock_irqsave(&clockfw_lock, flags);
247 if (arch_clock->clk_get_parent)
248 ret = arch_clock->clk_get_parent(clk);
249 spin_unlock_irqrestore(&clockfw_lock, flags);
250
251 return ret;
252}
253EXPORT_SYMBOL(clk_get_parent);
254
255/*-------------------------------------------------------------------------
256 * OMAP specific clock functions shared between omap1 and omap2
257 *-------------------------------------------------------------------------*/
258
259unsigned int __initdata mpurate;
260
261/*
262 * By default we use the rate set by the bootloader.
263 * You can override this with mpurate= cmdline option.
264 */
265static int __init omap_clk_setup(char *str)
266{
267 get_option(&str, &mpurate);
268
269 if (!mpurate)
270 return 1;
271
272 if (mpurate < 1000)
273 mpurate *= 1000000;
274
275 return 1;
276}
277__setup("mpurate=", omap_clk_setup);
278
279/* Used for clocks that always have same value as the parent clock */
280void followparent_recalc(struct clk *clk)
281{
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100282 if (clk == NULL || IS_ERR(clk))
283 return;
284
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000285 clk->rate = clk->parent->rate;
Imre Deakb1465bf2007-03-06 03:52:01 -0800286 if (unlikely(clk->flags & RATE_PROPAGATES))
287 propagate_rate(clk);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000288}
289
290/* Propagate rate to children */
291void propagate_rate(struct clk * tclk)
292{
293 struct clk *clkp;
294
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100295 if (tclk == NULL || IS_ERR(tclk))
296 return;
297
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000298 list_for_each_entry(clkp, &clocks, node) {
299 if (likely(clkp->parent != tclk))
300 continue;
301 if (likely((u32)clkp->recalc))
302 clkp->recalc(clkp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304}
305
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200306/**
307 * recalculate_root_clocks - recalculate and propagate all root clocks
308 *
309 * Recalculates all root clocks (clocks with no parent), which if the
310 * clock's .recalc is set correctly, should also propagate their rates.
311 * Called at init.
312 */
313void recalculate_root_clocks(void)
314{
315 struct clk *clkp;
316
317 list_for_each_entry(clkp, &clocks, node) {
318 if (unlikely(!clkp->parent) && likely((u32)clkp->recalc))
319 clkp->recalc(clkp);
320 }
321}
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323int clk_register(struct clk *clk)
324{
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100325 if (clk == NULL || IS_ERR(clk))
326 return -EINVAL;
327
Arjan van de Ven00431702006-01-12 18:42:23 +0000328 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 list_add(&clk->node, &clocks);
330 if (clk->init)
331 clk->init(clk);
Arjan van de Ven00431702006-01-12 18:42:23 +0000332 mutex_unlock(&clocks_mutex);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000333
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 return 0;
335}
336EXPORT_SYMBOL(clk_register);
337
338void clk_unregister(struct clk *clk)
339{
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100340 if (clk == NULL || IS_ERR(clk))
341 return;
342
Arjan van de Ven00431702006-01-12 18:42:23 +0000343 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 list_del(&clk->node);
Arjan van de Ven00431702006-01-12 18:42:23 +0000345 mutex_unlock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700346}
347EXPORT_SYMBOL(clk_unregister);
348
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000349void clk_deny_idle(struct clk *clk)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100350{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000351 unsigned long flags;
352
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100353 if (clk == NULL || IS_ERR(clk))
354 return;
355
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000356 spin_lock_irqsave(&clockfw_lock, flags);
357 if (arch_clock->clk_deny_idle)
358 arch_clock->clk_deny_idle(clk);
359 spin_unlock_irqrestore(&clockfw_lock, flags);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100360}
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000361EXPORT_SYMBOL(clk_deny_idle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000363void clk_allow_idle(struct clk *clk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000365 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700366
Tony Lindgrenb824efa2006-04-02 17:46:20 +0100367 if (clk == NULL || IS_ERR(clk))
368 return;
369
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000370 spin_lock_irqsave(&clockfw_lock, flags);
371 if (arch_clock->clk_allow_idle)
372 arch_clock->clk_allow_idle(clk);
373 spin_unlock_irqrestore(&clockfw_lock, flags);
374}
375EXPORT_SYMBOL(clk_allow_idle);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100376
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200377void clk_enable_init_clocks(void)
378{
379 struct clk *clkp;
380
381 list_for_each_entry(clkp, &clocks, node) {
382 if (clkp->flags & ENABLE_ON_INIT)
383 clk_enable(clkp);
384 }
385}
386EXPORT_SYMBOL(clk_enable_init_clocks);
387
388#ifdef CONFIG_CPU_FREQ
389void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
390{
391 unsigned long flags;
392
393 spin_lock_irqsave(&clockfw_lock, flags);
394 if (arch_clock->clk_init_cpufreq_table)
395 arch_clock->clk_init_cpufreq_table(table);
396 spin_unlock_irqrestore(&clockfw_lock, flags);
397}
398EXPORT_SYMBOL(clk_init_cpufreq_table);
399#endif
400
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000401/*-------------------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402
Tony Lindgren90afd5c2006-09-25 13:27:20 +0300403#ifdef CONFIG_OMAP_RESET_CLOCKS
404/*
405 * Disable any unused clocks left on by the bootloader
406 */
407static int __init clk_disable_unused(void)
408{
409 struct clk *ck;
410 unsigned long flags;
411
412 list_for_each_entry(ck, &clocks, node) {
413 if (ck->usecount > 0 || (ck->flags & ALWAYS_ENABLED) ||
414 ck->enable_reg == 0)
415 continue;
416
417 spin_lock_irqsave(&clockfw_lock, flags);
418 if (arch_clock->clk_disable_unused)
419 arch_clock->clk_disable_unused(ck);
420 spin_unlock_irqrestore(&clockfw_lock, flags);
421 }
422
423 return 0;
424}
425late_initcall(clk_disable_unused);
426#endif
427
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000428int __init clk_init(struct clk_functions * custom_clocks)
429{
430 if (!custom_clocks) {
431 printk(KERN_ERR "No custom clock functions registered\n");
432 BUG();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 }
434
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000435 arch_clock = custom_clocks;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700436
437 return 0;
438}
Paul Walmsley6b8858a2008-03-18 10:35:15 +0200439