blob: 7ebc5a29db8d68d6de060d8142fbc4f851e7f3e8 [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>
14#include <linux/config.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070015#include <linux/kernel.h>
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000016#include <linux/init.h>
17#include <linux/module.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070018#include <linux/list.h>
19#include <linux/errno.h>
20#include <linux/err.h>
Tim Schmielau4e57b682005-10-30 15:03:48 -080021#include <linux/string.h>
Russell Kingf8ce2542006-01-07 16:15:52 +000022#include <linux/clk.h>
Arjan van de Ven00431702006-01-12 18:42:23 +000023#include <linux/mutex.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#include <asm/semaphore.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070027
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000028#include <asm/arch/clock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070029
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000030LIST_HEAD(clocks);
Arjan van de Ven00431702006-01-12 18:42:23 +000031static DEFINE_MUTEX(clocks_mutex);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000032DEFINE_SPINLOCK(clockfw_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070033
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000034static struct clk_functions *arch_clock;
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000036/*-------------------------------------------------------------------------
37 * Standard clock functions defined in asm/hardware/clock.h
38 *-------------------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -070039
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000040struct clk * clk_get(struct device *dev, const char *id)
Linus Torvalds1da177e2005-04-16 15:20:36 -070041{
42 struct clk *p, *clk = ERR_PTR(-ENOENT);
43
Arjan van de Ven00431702006-01-12 18:42:23 +000044 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 list_for_each_entry(p, &clocks, node) {
46 if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
47 clk = p;
48 break;
49 }
50 }
Arjan van de Ven00431702006-01-12 18:42:23 +000051 mutex_unlock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53 return clk;
54}
55EXPORT_SYMBOL(clk_get);
56
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000057int clk_enable(struct clk *clk)
58{
59 unsigned long flags;
60 int ret = 0;
61
62 spin_lock_irqsave(&clockfw_lock, flags);
63 if (clk->enable)
64 ret = clk->enable(clk);
65 else if (arch_clock->clk_enable)
66 ret = arch_clock->clk_enable(clk);
67 else
68 printk(KERN_ERR "Could not enable clock %s\n", clk->name);
69 spin_unlock_irqrestore(&clockfw_lock, flags);
70
71 return ret;
72}
73EXPORT_SYMBOL(clk_enable);
74
75void clk_disable(struct clk *clk)
76{
77 unsigned long flags;
78
79 spin_lock_irqsave(&clockfw_lock, flags);
80 if (clk->disable)
81 clk->disable(clk);
82 else if (arch_clock->clk_disable)
83 arch_clock->clk_disable(clk);
84 else
85 printk(KERN_ERR "Could not disable clock %s\n", clk->name);
86 spin_unlock_irqrestore(&clockfw_lock, flags);
87}
88EXPORT_SYMBOL(clk_disable);
89
90int clk_use(struct clk *clk)
91{
92 unsigned long flags;
93 int ret = 0;
94
95 spin_lock_irqsave(&clockfw_lock, flags);
96 if (arch_clock->clk_use)
97 ret = arch_clock->clk_use(clk);
98 spin_unlock_irqrestore(&clockfw_lock, flags);
99
100 return ret;
101}
102EXPORT_SYMBOL(clk_use);
103
104void clk_unuse(struct clk *clk)
105{
106 unsigned long flags;
107
108 spin_lock_irqsave(&clockfw_lock, flags);
109 if (arch_clock->clk_unuse)
110 arch_clock->clk_unuse(clk);
111 spin_unlock_irqrestore(&clockfw_lock, flags);
112}
113EXPORT_SYMBOL(clk_unuse);
114
115int clk_get_usecount(struct clk *clk)
116{
117 unsigned long flags;
118 int ret = 0;
119
120 spin_lock_irqsave(&clockfw_lock, flags);
121 ret = clk->usecount;
122 spin_unlock_irqrestore(&clockfw_lock, flags);
123
124 return ret;
125}
126EXPORT_SYMBOL(clk_get_usecount);
127
128unsigned long clk_get_rate(struct clk *clk)
129{
130 unsigned long flags;
131 unsigned long ret = 0;
132
133 spin_lock_irqsave(&clockfw_lock, flags);
134 ret = clk->rate;
135 spin_unlock_irqrestore(&clockfw_lock, flags);
136
137 return ret;
138}
139EXPORT_SYMBOL(clk_get_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700140
141void clk_put(struct clk *clk)
142{
143 if (clk && !IS_ERR(clk))
144 module_put(clk->owner);
145}
146EXPORT_SYMBOL(clk_put);
147
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000148/*-------------------------------------------------------------------------
149 * Optional clock functions defined in asm/hardware/clock.h
150 *-------------------------------------------------------------------------*/
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100151
Linus Torvalds1da177e2005-04-16 15:20:36 -0700152long clk_round_rate(struct clk *clk, unsigned long rate)
153{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000154 unsigned long flags;
155 long ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000157 spin_lock_irqsave(&clockfw_lock, flags);
158 if (arch_clock->clk_round_rate)
159 ret = arch_clock->clk_round_rate(clk, rate);
160 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000162 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163}
164EXPORT_SYMBOL(clk_round_rate);
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166int clk_set_rate(struct clk *clk, unsigned long rate)
167{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000168 unsigned long flags;
169 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000171 spin_lock_irqsave(&clockfw_lock, flags);
172 if (arch_clock->clk_set_rate)
173 ret = arch_clock->clk_set_rate(clk, rate);
174 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175
176 return ret;
177}
178EXPORT_SYMBOL(clk_set_rate);
179
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000180int clk_set_parent(struct clk *clk, struct clk *parent)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000182 unsigned long flags;
183 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000185 spin_lock_irqsave(&clockfw_lock, flags);
186 if (arch_clock->clk_set_parent)
187 ret = arch_clock->clk_set_parent(clk, parent);
188 spin_unlock_irqrestore(&clockfw_lock, flags);
189
190 return ret;
191}
192EXPORT_SYMBOL(clk_set_parent);
193
194struct clk *clk_get_parent(struct clk *clk)
195{
196 unsigned long flags;
197 struct clk * ret = NULL;
198
199 spin_lock_irqsave(&clockfw_lock, flags);
200 if (arch_clock->clk_get_parent)
201 ret = arch_clock->clk_get_parent(clk);
202 spin_unlock_irqrestore(&clockfw_lock, flags);
203
204 return ret;
205}
206EXPORT_SYMBOL(clk_get_parent);
207
208/*-------------------------------------------------------------------------
209 * OMAP specific clock functions shared between omap1 and omap2
210 *-------------------------------------------------------------------------*/
211
212unsigned int __initdata mpurate;
213
214/*
215 * By default we use the rate set by the bootloader.
216 * You can override this with mpurate= cmdline option.
217 */
218static int __init omap_clk_setup(char *str)
219{
220 get_option(&str, &mpurate);
221
222 if (!mpurate)
223 return 1;
224
225 if (mpurate < 1000)
226 mpurate *= 1000000;
227
228 return 1;
229}
230__setup("mpurate=", omap_clk_setup);
231
232/* Used for clocks that always have same value as the parent clock */
233void followparent_recalc(struct clk *clk)
234{
235 clk->rate = clk->parent->rate;
236}
237
238/* Propagate rate to children */
239void propagate_rate(struct clk * tclk)
240{
241 struct clk *clkp;
242
243 list_for_each_entry(clkp, &clocks, node) {
244 if (likely(clkp->parent != tclk))
245 continue;
246 if (likely((u32)clkp->recalc))
247 clkp->recalc(clkp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249}
250
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251int clk_register(struct clk *clk)
252{
Arjan van de Ven00431702006-01-12 18:42:23 +0000253 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 list_add(&clk->node, &clocks);
255 if (clk->init)
256 clk->init(clk);
Arjan van de Ven00431702006-01-12 18:42:23 +0000257 mutex_unlock(&clocks_mutex);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000258
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259 return 0;
260}
261EXPORT_SYMBOL(clk_register);
262
263void clk_unregister(struct clk *clk)
264{
Arjan van de Ven00431702006-01-12 18:42:23 +0000265 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700266 list_del(&clk->node);
Arjan van de Ven00431702006-01-12 18:42:23 +0000267 mutex_unlock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268}
269EXPORT_SYMBOL(clk_unregister);
270
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000271void clk_deny_idle(struct clk *clk)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100272{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000273 unsigned long flags;
274
275 spin_lock_irqsave(&clockfw_lock, flags);
276 if (arch_clock->clk_deny_idle)
277 arch_clock->clk_deny_idle(clk);
278 spin_unlock_irqrestore(&clockfw_lock, flags);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100279}
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000280EXPORT_SYMBOL(clk_deny_idle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000282void clk_allow_idle(struct clk *clk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000284 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000286 spin_lock_irqsave(&clockfw_lock, flags);
287 if (arch_clock->clk_allow_idle)
288 arch_clock->clk_allow_idle(clk);
289 spin_unlock_irqrestore(&clockfw_lock, flags);
290}
291EXPORT_SYMBOL(clk_allow_idle);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100292
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000293/*-------------------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000295int __init clk_init(struct clk_functions * custom_clocks)
296{
297 if (!custom_clocks) {
298 printk(KERN_ERR "No custom clock functions registered\n");
299 BUG();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 }
301
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000302 arch_clock = custom_clocks;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
304 return 0;
305}