blob: 3c2bfc0efdaf6f5bdee3f7abcf0bc0db6f0d4a2a [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/*-------------------------------------------------------------------------
Tony Lindgrenf07adc52006-01-17 15:27:09 -080037 * Standard clock functions defined in include/linux/clk.h
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000038 *-------------------------------------------------------------------------*/
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);
Tony Lindgrenf07adc52006-01-17 15:27:09 -080063 if (arch_clock->clk_enable)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000064 ret = arch_clock->clk_enable(clk);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000065 spin_unlock_irqrestore(&clockfw_lock, flags);
66
67 return ret;
68}
69EXPORT_SYMBOL(clk_enable);
70
71void clk_disable(struct clk *clk)
72{
73 unsigned long flags;
74
75 spin_lock_irqsave(&clockfw_lock, flags);
Tony Lindgrenf07adc52006-01-17 15:27:09 -080076 if (arch_clock->clk_disable)
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000077 arch_clock->clk_disable(clk);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000078 spin_unlock_irqrestore(&clockfw_lock, flags);
79}
80EXPORT_SYMBOL(clk_disable);
81
Tony Lindgren1a8bfa12005-11-10 14:26:50 +000082int clk_get_usecount(struct clk *clk)
83{
84 unsigned long flags;
85 int ret = 0;
86
87 spin_lock_irqsave(&clockfw_lock, flags);
88 ret = clk->usecount;
89 spin_unlock_irqrestore(&clockfw_lock, flags);
90
91 return ret;
92}
93EXPORT_SYMBOL(clk_get_usecount);
94
95unsigned long clk_get_rate(struct clk *clk)
96{
97 unsigned long flags;
98 unsigned long ret = 0;
99
100 spin_lock_irqsave(&clockfw_lock, flags);
101 ret = clk->rate;
102 spin_unlock_irqrestore(&clockfw_lock, flags);
103
104 return ret;
105}
106EXPORT_SYMBOL(clk_get_rate);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700107
108void clk_put(struct clk *clk)
109{
110 if (clk && !IS_ERR(clk))
111 module_put(clk->owner);
112}
113EXPORT_SYMBOL(clk_put);
114
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000115/*-------------------------------------------------------------------------
Tony Lindgrenf07adc52006-01-17 15:27:09 -0800116 * Optional clock functions defined in include/linux/clk.h
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000117 *-------------------------------------------------------------------------*/
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119long clk_round_rate(struct clk *clk, unsigned long rate)
120{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000121 unsigned long flags;
122 long ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000124 spin_lock_irqsave(&clockfw_lock, flags);
125 if (arch_clock->clk_round_rate)
126 ret = arch_clock->clk_round_rate(clk, rate);
127 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000129 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130}
131EXPORT_SYMBOL(clk_round_rate);
132
Linus Torvalds1da177e2005-04-16 15:20:36 -0700133int clk_set_rate(struct clk *clk, unsigned long rate)
134{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000135 unsigned long flags;
136 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000138 spin_lock_irqsave(&clockfw_lock, flags);
139 if (arch_clock->clk_set_rate)
140 ret = arch_clock->clk_set_rate(clk, rate);
141 spin_unlock_irqrestore(&clockfw_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142
143 return ret;
144}
145EXPORT_SYMBOL(clk_set_rate);
146
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000147int clk_set_parent(struct clk *clk, struct clk *parent)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700148{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000149 unsigned long flags;
150 int ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000152 spin_lock_irqsave(&clockfw_lock, flags);
153 if (arch_clock->clk_set_parent)
154 ret = arch_clock->clk_set_parent(clk, parent);
155 spin_unlock_irqrestore(&clockfw_lock, flags);
156
157 return ret;
158}
159EXPORT_SYMBOL(clk_set_parent);
160
161struct clk *clk_get_parent(struct clk *clk)
162{
163 unsigned long flags;
164 struct clk * ret = NULL;
165
166 spin_lock_irqsave(&clockfw_lock, flags);
167 if (arch_clock->clk_get_parent)
168 ret = arch_clock->clk_get_parent(clk);
169 spin_unlock_irqrestore(&clockfw_lock, flags);
170
171 return ret;
172}
173EXPORT_SYMBOL(clk_get_parent);
174
175/*-------------------------------------------------------------------------
176 * OMAP specific clock functions shared between omap1 and omap2
177 *-------------------------------------------------------------------------*/
178
179unsigned int __initdata mpurate;
180
181/*
182 * By default we use the rate set by the bootloader.
183 * You can override this with mpurate= cmdline option.
184 */
185static int __init omap_clk_setup(char *str)
186{
187 get_option(&str, &mpurate);
188
189 if (!mpurate)
190 return 1;
191
192 if (mpurate < 1000)
193 mpurate *= 1000000;
194
195 return 1;
196}
197__setup("mpurate=", omap_clk_setup);
198
199/* Used for clocks that always have same value as the parent clock */
200void followparent_recalc(struct clk *clk)
201{
202 clk->rate = clk->parent->rate;
203}
204
205/* Propagate rate to children */
206void propagate_rate(struct clk * tclk)
207{
208 struct clk *clkp;
209
210 list_for_each_entry(clkp, &clocks, node) {
211 if (likely(clkp->parent != tclk))
212 continue;
213 if (likely((u32)clkp->recalc))
214 clkp->recalc(clkp);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216}
217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218int clk_register(struct clk *clk)
219{
Arjan van de Ven00431702006-01-12 18:42:23 +0000220 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 list_add(&clk->node, &clocks);
222 if (clk->init)
223 clk->init(clk);
Arjan van de Ven00431702006-01-12 18:42:23 +0000224 mutex_unlock(&clocks_mutex);
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000225
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 return 0;
227}
228EXPORT_SYMBOL(clk_register);
229
230void clk_unregister(struct clk *clk)
231{
Arjan van de Ven00431702006-01-12 18:42:23 +0000232 mutex_lock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 list_del(&clk->node);
Arjan van de Ven00431702006-01-12 18:42:23 +0000234 mutex_unlock(&clocks_mutex);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235}
236EXPORT_SYMBOL(clk_unregister);
237
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000238void clk_deny_idle(struct clk *clk)
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100239{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000240 unsigned long flags;
241
242 spin_lock_irqsave(&clockfw_lock, flags);
243 if (arch_clock->clk_deny_idle)
244 arch_clock->clk_deny_idle(clk);
245 spin_unlock_irqrestore(&clockfw_lock, flags);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100246}
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000247EXPORT_SYMBOL(clk_deny_idle);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000249void clk_allow_idle(struct clk *clk)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250{
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000251 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000253 spin_lock_irqsave(&clockfw_lock, flags);
254 if (arch_clock->clk_allow_idle)
255 arch_clock->clk_allow_idle(clk);
256 spin_unlock_irqrestore(&clockfw_lock, flags);
257}
258EXPORT_SYMBOL(clk_allow_idle);
Tony Lindgrenbb13b5f2005-07-10 19:58:18 +0100259
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000260/*-------------------------------------------------------------------------*/
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000262int __init clk_init(struct clk_functions * custom_clocks)
263{
264 if (!custom_clocks) {
265 printk(KERN_ERR "No custom clock functions registered\n");
266 BUG();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
268
Tony Lindgren1a8bfa12005-11-10 14:26:50 +0000269 arch_clock = custom_clocks;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
271 return 0;
272}