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 14
#include <linux/version.h>
#include <linux/config.h>
L
Linus Torvalds 已提交
15
#include <linux/kernel.h>
16 17
#include <linux/init.h>
#include <linux/module.h>
L
Linus Torvalds 已提交
18 19 20
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
T
Tim Schmielau 已提交
21
#include <linux/string.h>
22
#include <linux/clk.h>
23
#include <linux/mutex.h>
24
#include <linux/platform_device.h>
L
Linus Torvalds 已提交
25

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

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

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

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

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

41 42 43 44
/*
 * 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.
 */
45
struct clk * clk_get(struct device *dev, const char *id)
L
Linus Torvalds 已提交
46 47
{
	struct clk *p, *clk = ERR_PTR(-ENOENT);
48 49 50 51 52 53
	int idno;

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

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

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

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

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

	return clk;
}
EXPORT_SYMBOL(clk_get);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

161 162 163 164
	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 已提交
165

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

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

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

178 179 180 181
	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 已提交
182

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

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

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

195 196 197 198
	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 已提交
199

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

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

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

212 213 214 215
	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 已提交
216 217 218

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

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

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

227 228 229 230 231
/*
 * 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 已提交
232
{
233
	get_option(&str, &mpurate);
L
Linus Torvalds 已提交
234

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

317 318 319 320
	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 已提交
321
}
322
EXPORT_SYMBOL(clk_allow_idle);
323

324
/*-------------------------------------------------------------------------*/
325

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

333 334
	arch_clock = custom_clocks;

335 336
	return 0;
}