clock.c 7.1 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *  linux/arch/arm/plat-omap/clock.c
L
Linus Torvalds 已提交
3
 *
4
 *  Copyright (C) 2004 - 2005 Nokia corporation
L
Linus Torvalds 已提交
5 6
 *  Written by Tuukka Tikkanen <tuukka.tikkanen@elektrobit.com>
 *
7 8
 *  Modified for omap shared clock framework by Tony Lindgren <tony@atomide.com>
 *
L
Linus Torvalds 已提交
9 10 11 12
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */
13
#include <linux/version.h>
L
Linus Torvalds 已提交
14
#include <linux/kernel.h>
15 16
#include <linux/init.h>
#include <linux/module.h>
L
Linus Torvalds 已提交
17 18 19
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
T
Tim Schmielau 已提交
20
#include <linux/string.h>
21
#include <linux/clk.h>
22
#include <linux/mutex.h>
23
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
24

25
#include <asm/io.h>
L
Linus Torvalds 已提交
26 27
#include <asm/semaphore.h>

28
#include <asm/arch/clock.h>
L
Linus Torvalds 已提交
29

30
static LIST_HEAD(clocks);
31
static DEFINE_MUTEX(clocks_mutex);
32
static DEFINE_SPINLOCK(clockfw_lock);
L
Linus Torvalds 已提交
33

34
static struct clk_functions *arch_clock;
L
Linus Torvalds 已提交
35

36
/*-------------------------------------------------------------------------
37
 * Standard clock functions defined in include/linux/clk.h
38
 *-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
39

40 41 42 43
/*
 * Returns a clock. Note that we first try to use device id on the bus
 * and clock name. If this fails, we try to use clock name only.
 */
44
struct clk * clk_get(struct device *dev, const char *id)
L
Linus Torvalds 已提交
45 46
{
	struct clk *p, *clk = ERR_PTR(-ENOENT);
47 48 49 50 51 52
	int idno;

	if (dev == NULL || dev->bus != &platform_bus_type)
		idno = -1;
	else
		idno = to_platform_device(dev)->id;
L
Linus Torvalds 已提交
53

54
	mutex_lock(&clocks_mutex);
55 56 57 58 59

	list_for_each_entry(p, &clocks, node) {
		if (p->id == idno &&
		    strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
			clk = p;
60
			goto found;
61 62 63
		}
	}

L
Linus Torvalds 已提交
64 65 66 67 68 69
	list_for_each_entry(p, &clocks, node) {
		if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) {
			clk = p;
			break;
		}
	}
70

71
found:
72
	mutex_unlock(&clocks_mutex);
L
Linus Torvalds 已提交
73 74 75 76 77 78 79 80

	return clk;
}
EXPORT_SYMBOL(clk_get);

int clk_enable(struct clk *clk)
{
	unsigned long flags;
81
	int ret = 0;
L
Linus Torvalds 已提交
82

83 84 85
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

L
Linus Torvalds 已提交
86
	spin_lock_irqsave(&clockfw_lock, flags);
87
	if (arch_clock->clk_enable)
88
		ret = arch_clock->clk_enable(clk);
L
Linus Torvalds 已提交
89
	spin_unlock_irqrestore(&clockfw_lock, flags);
90

L
Linus Torvalds 已提交
91 92 93 94 95 96 97 98
	return ret;
}
EXPORT_SYMBOL(clk_enable);

void clk_disable(struct clk *clk)
{
	unsigned long flags;

99 100 101
	if (clk == NULL || IS_ERR(clk))
		return;

L
Linus Torvalds 已提交
102
	spin_lock_irqsave(&clockfw_lock, flags);
103
	if (arch_clock->clk_disable)
104
		arch_clock->clk_disable(clk);
L
Linus Torvalds 已提交
105 106 107 108 109 110
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

int clk_get_usecount(struct clk *clk)
{
111 112
	unsigned long flags;
	int ret = 0;
L
Linus Torvalds 已提交
113

114 115 116
	if (clk == NULL || IS_ERR(clk))
		return 0;

117 118 119
	spin_lock_irqsave(&clockfw_lock, flags);
	ret = clk->usecount;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
120

121
	return ret;
L
Linus Torvalds 已提交
122
}
123
EXPORT_SYMBOL(clk_get_usecount);
L
Linus Torvalds 已提交
124

125
unsigned long clk_get_rate(struct clk *clk)
L
Linus Torvalds 已提交
126
{
127 128
	unsigned long flags;
	unsigned long ret = 0;
L
Linus Torvalds 已提交
129

130 131 132
	if (clk == NULL || IS_ERR(clk))
		return 0;

133 134 135
	spin_lock_irqsave(&clockfw_lock, flags);
	ret = clk->rate;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
136

137
	return ret;
L
Linus Torvalds 已提交
138
}
139
EXPORT_SYMBOL(clk_get_rate);
L
Linus Torvalds 已提交
140

141
void clk_put(struct clk *clk)
142
{
143 144
	if (clk && !IS_ERR(clk))
		module_put(clk->owner);
145
}
146
EXPORT_SYMBOL(clk_put);
147

148
/*-------------------------------------------------------------------------
149
 * Optional clock functions defined in include/linux/clk.h
150
 *-------------------------------------------------------------------------*/
151

L
Linus Torvalds 已提交
152 153
long clk_round_rate(struct clk *clk, unsigned long rate)
{
154 155
	unsigned long flags;
	long ret = 0;
L
Linus Torvalds 已提交
156

157 158 159
	if (clk == NULL || IS_ERR(clk))
		return ret;

160 161 162 163
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_round_rate)
		ret = arch_clock->clk_round_rate(clk, rate);
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
164

165
	return ret;
L
Linus Torvalds 已提交
166 167 168
}
EXPORT_SYMBOL(clk_round_rate);

169
int clk_set_rate(struct clk *clk, unsigned long rate)
L
Linus Torvalds 已提交
170
{
171
	unsigned long flags;
172 173 174 175
	int ret = -EINVAL;

	if (clk == NULL || IS_ERR(clk))
		return ret;
176

177 178 179 180
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_rate)
		ret = arch_clock->clk_set_rate(clk, rate);
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
181

182
	return ret;
L
Linus Torvalds 已提交
183
}
184
EXPORT_SYMBOL(clk_set_rate);
L
Linus Torvalds 已提交
185

186
int clk_set_parent(struct clk *clk, struct clk *parent)
L
Linus Torvalds 已提交
187
{
188
	unsigned long flags;
189 190 191 192
	int ret = -EINVAL;

	if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent))
		return ret;
L
Linus Torvalds 已提交
193

194 195 196 197
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_parent)
		ret =  arch_clock->clk_set_parent(clk, parent);
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
198

199
	return ret;
L
Linus Torvalds 已提交
200
}
201
EXPORT_SYMBOL(clk_set_parent);
L
Linus Torvalds 已提交
202

203
struct clk *clk_get_parent(struct clk *clk)
L
Linus Torvalds 已提交
204
{
205 206
	unsigned long flags;
	struct clk * ret = NULL;
L
Linus Torvalds 已提交
207

208 209 210
	if (clk == NULL || IS_ERR(clk))
		return ret;

211 212 213 214
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_get_parent)
		ret = arch_clock->clk_get_parent(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
215 216 217

	return ret;
}
218
EXPORT_SYMBOL(clk_get_parent);
L
Linus Torvalds 已提交
219

220 221 222
/*-------------------------------------------------------------------------
 * OMAP specific clock functions shared between omap1 and omap2
 *-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
223

224
unsigned int __initdata mpurate;
L
Linus Torvalds 已提交
225

226 227 228 229 230
/*
 * By default we use the rate set by the bootloader.
 * You can override this with mpurate= cmdline option.
 */
static int __init omap_clk_setup(char *str)
L
Linus Torvalds 已提交
231
{
232
	get_option(&str, &mpurate);
L
Linus Torvalds 已提交
233

234 235
	if (!mpurate)
		return 1;
L
Linus Torvalds 已提交
236

237 238
	if (mpurate < 1000)
		mpurate *= 1000000;
L
Linus Torvalds 已提交
239

240
	return 1;
L
Linus Torvalds 已提交
241
}
242
__setup("mpurate=", omap_clk_setup);
L
Linus Torvalds 已提交
243

244 245
/* Used for clocks that always have same value as the parent clock */
void followparent_recalc(struct clk *clk)
L
Linus Torvalds 已提交
246
{
247 248 249
	if (clk == NULL || IS_ERR(clk))
		return;

250
	clk->rate = clk->parent->rate;
L
Linus Torvalds 已提交
251 252
}

253 254
/* Propagate rate to children */
void propagate_rate(struct clk * tclk)
L
Linus Torvalds 已提交
255
{
256
	struct clk *clkp;
L
Linus Torvalds 已提交
257

258 259 260
	if (tclk == NULL || IS_ERR(tclk))
		return;

261 262 263 264 265 266
	list_for_each_entry(clkp, &clocks, node) {
		if (likely(clkp->parent != tclk))
			continue;
		if (likely((u32)clkp->recalc))
			clkp->recalc(clkp);
	}
L
Linus Torvalds 已提交
267 268 269 270
}

int clk_register(struct clk *clk)
{
271 272 273
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

274
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
275 276 277
	list_add(&clk->node, &clocks);
	if (clk->init)
		clk->init(clk);
278
	mutex_unlock(&clocks_mutex);
279

L
Linus Torvalds 已提交
280 281 282 283 284 285
	return 0;
}
EXPORT_SYMBOL(clk_register);

void clk_unregister(struct clk *clk)
{
286 287 288
	if (clk == NULL || IS_ERR(clk))
		return;

289
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
290
	list_del(&clk->node);
291
	mutex_unlock(&clocks_mutex);
L
Linus Torvalds 已提交
292 293 294
}
EXPORT_SYMBOL(clk_unregister);

295
void clk_deny_idle(struct clk *clk)
296
{
297 298
	unsigned long flags;

299 300 301
	if (clk == NULL || IS_ERR(clk))
		return;

302 303 304 305
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_deny_idle)
		arch_clock->clk_deny_idle(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
306
}
307
EXPORT_SYMBOL(clk_deny_idle);
L
Linus Torvalds 已提交
308

309
void clk_allow_idle(struct clk *clk)
L
Linus Torvalds 已提交
310
{
311
	unsigned long flags;
L
Linus Torvalds 已提交
312

313 314 315
	if (clk == NULL || IS_ERR(clk))
		return;

316 317 318 319
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_allow_idle)
		arch_clock->clk_allow_idle(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
320
}
321
EXPORT_SYMBOL(clk_allow_idle);
322

323
/*-------------------------------------------------------------------------*/
324

325
int __init clk_init(struct clk_functions * custom_clocks)
326
{
327 328 329
	if (!custom_clocks) {
		printk(KERN_ERR "No custom clock functions registered\n");
		BUG();
330 331
	}

332 333
	arch_clock = custom_clocks;

334 335
	return 0;
}