clock.c 9.4 KB
Newer Older
L
Linus Torvalds 已提交
1
/*
2
 *  linux/arch/arm/plat-omap/clock.c
L
Linus Torvalds 已提交
3
 *
4
 *  Copyright (C) 2004 - 2008 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 13
 * 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.
 */
#include <linux/kernel.h>
14 15
#include <linux/init.h>
#include <linux/module.h>
L
Linus Torvalds 已提交
16 17 18
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/err.h>
T
Tim Schmielau 已提交
19
#include <linux/string.h>
20
#include <linux/clk.h>
21
#include <linux/mutex.h>
22
#include <linux/platform_device.h>
23
#include <linux/cpufreq.h>
24
#include <linux/debugfs.h>
25
#include <linux/io.h>
L
Linus Torvalds 已提交
26

27
#include <mach/clock.h>
L
Linus Torvalds 已提交
28

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

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

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

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

44 45 46
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

L
Linus Torvalds 已提交
47
	spin_lock_irqsave(&clockfw_lock, flags);
48
	if (arch_clock->clk_enable)
49
		ret = arch_clock->clk_enable(clk);
L
Linus Torvalds 已提交
50
	spin_unlock_irqrestore(&clockfw_lock, flags);
51

L
Linus Torvalds 已提交
52 53 54 55 56 57 58 59
	return ret;
}
EXPORT_SYMBOL(clk_enable);

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

60 61 62
	if (clk == NULL || IS_ERR(clk))
		return;

L
Linus Torvalds 已提交
63
	spin_lock_irqsave(&clockfw_lock, flags);
64 65 66 67 68 69 70
	if (clk->usecount == 0) {
		printk(KERN_ERR "Trying disable clock %s with 0 usecount\n",
		       clk->name);
		WARN_ON(1);
		goto out;
	}

71
	if (arch_clock->clk_disable)
72
		arch_clock->clk_disable(clk);
73 74

out:
L
Linus Torvalds 已提交
75 76 77 78 79 80
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

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

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

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

91
	return ret;
L
Linus Torvalds 已提交
92
}
93
EXPORT_SYMBOL(clk_get_usecount);
L
Linus Torvalds 已提交
94

95
unsigned long clk_get_rate(struct clk *clk)
L
Linus Torvalds 已提交
96
{
97 98
	unsigned long flags;
	unsigned long ret = 0;
L
Linus Torvalds 已提交
99

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

103 104 105
	spin_lock_irqsave(&clockfw_lock, flags);
	ret = clk->rate;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
106

107
	return ret;
L
Linus Torvalds 已提交
108
}
109
EXPORT_SYMBOL(clk_get_rate);
L
Linus Torvalds 已提交
110

111
/*-------------------------------------------------------------------------
112
 * Optional clock functions defined in include/linux/clk.h
113
 *-------------------------------------------------------------------------*/
114

L
Linus Torvalds 已提交
115 116
long clk_round_rate(struct clk *clk, unsigned long rate)
{
117 118
	unsigned long flags;
	long ret = 0;
L
Linus Torvalds 已提交
119

120 121 122
	if (clk == NULL || IS_ERR(clk))
		return ret;

123 124 125 126
	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 已提交
127

128
	return ret;
L
Linus Torvalds 已提交
129 130 131
}
EXPORT_SYMBOL(clk_round_rate);

132
int clk_set_rate(struct clk *clk, unsigned long rate)
L
Linus Torvalds 已提交
133
{
134
	unsigned long flags;
135 136 137 138
	int ret = -EINVAL;

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

140 141 142
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_rate)
		ret = arch_clock->clk_set_rate(clk, rate);
143 144
	if (ret == 0 && (clk->flags & RATE_PROPAGATES))
		propagate_rate(clk);
145
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
146

147
	return ret;
L
Linus Torvalds 已提交
148
}
149
EXPORT_SYMBOL(clk_set_rate);
L
Linus Torvalds 已提交
150

151
int clk_set_parent(struct clk *clk, struct clk *parent)
L
Linus Torvalds 已提交
152
{
153
	unsigned long flags;
154 155 156 157
	int ret = -EINVAL;

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

159 160
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_parent)
161 162 163
		ret = arch_clock->clk_set_parent(clk, parent);
	if (ret == 0 && (clk->flags & RATE_PROPAGATES))
		propagate_rate(clk);
164
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
165

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

170
struct clk *clk_get_parent(struct clk *clk)
L
Linus Torvalds 已提交
171
{
172
	return clk->parent;
L
Linus Torvalds 已提交
173
}
174
EXPORT_SYMBOL(clk_get_parent);
L
Linus Torvalds 已提交
175

176 177 178
/*-------------------------------------------------------------------------
 * OMAP specific clock functions shared between omap1 and omap2
 *-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
179

180
unsigned int __initdata mpurate;
L
Linus Torvalds 已提交
181

182 183 184 185 186
/*
 * 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 已提交
187
{
188
	get_option(&str, &mpurate);
L
Linus Torvalds 已提交
189

190 191
	if (!mpurate)
		return 1;
L
Linus Torvalds 已提交
192

193 194
	if (mpurate < 1000)
		mpurate *= 1000000;
L
Linus Torvalds 已提交
195

196
	return 1;
L
Linus Torvalds 已提交
197
}
198
__setup("mpurate=", omap_clk_setup);
L
Linus Torvalds 已提交
199

200 201
/* Used for clocks that always have same value as the parent clock */
void followparent_recalc(struct clk *clk)
L
Linus Torvalds 已提交
202
{
203 204 205
	if (clk == NULL || IS_ERR(clk))
		return;

206
	clk->rate = clk->parent->rate;
L
Linus Torvalds 已提交
207 208
}

209 210
/* Propagate rate to children */
void propagate_rate(struct clk * tclk)
L
Linus Torvalds 已提交
211
{
212
	struct clk *clkp;
L
Linus Torvalds 已提交
213

214 215 216
	if (tclk == NULL || IS_ERR(tclk))
		return;

217 218 219
	list_for_each_entry(clkp, &clocks, node) {
		if (likely(clkp->parent != tclk))
			continue;
220
		if (clkp->recalc)
221
			clkp->recalc(clkp);
222 223
		if (clkp->flags & RATE_PROPAGATES)
			propagate_rate(clkp);
224
	}
L
Linus Torvalds 已提交
225 226
}

227 228 229 230 231 232 233 234 235 236 237 238
/**
 * recalculate_root_clocks - recalculate and propagate all root clocks
 *
 * Recalculates all root clocks (clocks with no parent), which if the
 * clock's .recalc is set correctly, should also propagate their rates.
 * Called at init.
 */
void recalculate_root_clocks(void)
{
	struct clk *clkp;

	list_for_each_entry(clkp, &clocks, node) {
239 240 241 242 243 244
		if (!clkp->parent) {
			if (clkp->recalc)
				clkp->recalc(clkp);
			if (clkp->flags & RATE_PROPAGATES)
				propagate_rate(clkp);
		}
245 246 247
	}
}

L
Linus Torvalds 已提交
248 249
int clk_register(struct clk *clk)
{
250 251 252
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

253 254 255 256 257 258
	/*
	 * trap out already registered clocks
	 */
	if (clk->node.next || clk->node.prev)
		return 0;

259
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
260 261 262
	list_add(&clk->node, &clocks);
	if (clk->init)
		clk->init(clk);
263
	mutex_unlock(&clocks_mutex);
264

L
Linus Torvalds 已提交
265 266 267 268 269 270
	return 0;
}
EXPORT_SYMBOL(clk_register);

void clk_unregister(struct clk *clk)
{
271 272 273
	if (clk == NULL || IS_ERR(clk))
		return;

274
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
275
	list_del(&clk->node);
276
	mutex_unlock(&clocks_mutex);
L
Linus Torvalds 已提交
277 278 279
}
EXPORT_SYMBOL(clk_unregister);

280 281 282 283 284 285 286 287 288 289 290
void clk_enable_init_clocks(void)
{
	struct clk *clkp;

	list_for_each_entry(clkp, &clocks, node) {
		if (clkp->flags & ENABLE_ON_INIT)
			clk_enable(clkp);
	}
}
EXPORT_SYMBOL(clk_enable_init_clocks);

291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
/*
 * Low level helpers
 */
static int clkll_enable_null(struct clk *clk)
{
	return 0;
}

static void clkll_disable_null(struct clk *clk)
{
}

const struct clkops clkops_null = {
	.enable		= clkll_enable_null,
	.disable	= clkll_disable_null,
};

308 309 310 311 312 313 314 315 316 317 318 319 320
#ifdef CONFIG_CPU_FREQ
void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
{
	unsigned long flags;

	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_init_cpufreq_table)
		arch_clock->clk_init_cpufreq_table(table);
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_init_cpufreq_table);
#endif

321
/*-------------------------------------------------------------------------*/
322

323 324 325 326 327 328 329 330 331 332
#ifdef CONFIG_OMAP_RESET_CLOCKS
/*
 * Disable any unused clocks left on by the bootloader
 */
static int __init clk_disable_unused(void)
{
	struct clk *ck;
	unsigned long flags;

	list_for_each_entry(ck, &clocks, node) {
333 334 335 336
		if (ck->ops == &clkops_null)
			continue;

		if (ck->usecount > 0 || ck->enable_reg == 0)
337 338 339 340 341 342 343 344 345 346 347 348 349
			continue;

		spin_lock_irqsave(&clockfw_lock, flags);
		if (arch_clock->clk_disable_unused)
			arch_clock->clk_disable_unused(ck);
		spin_unlock_irqrestore(&clockfw_lock, flags);
	}

	return 0;
}
late_initcall(clk_disable_unused);
#endif

350
int __init clk_init(struct clk_functions * custom_clocks)
351
{
352 353 354
	if (!custom_clocks) {
		printk(KERN_ERR "No custom clock functions registered\n");
		BUG();
355 356
	}

357 358
	arch_clock = custom_clocks;

359 360
	return 0;
}
361

362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
#if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
/*
 *	debugfs support to trace clock tree hierarchy and attributes
 */
static struct dentry *clk_debugfs_root;

static int clk_debugfs_register_one(struct clk *c)
{
	int err;
	struct dentry *d, *child;
	struct clk *pa = c->parent;
	char s[255];
	char *p = s;

	p += sprintf(p, "%s", c->name);
	if (c->id != 0)
		sprintf(p, ":%d", c->id);
	d = debugfs_create_dir(s, pa ? pa->dent : clk_debugfs_root);
380 381
	if (!d)
		return -ENOMEM;
382 383 384
	c->dent = d;

	d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount);
385 386
	if (!d) {
		err = -ENOMEM;
387 388 389
		goto err_out;
	}
	d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
390 391
	if (!d) {
		err = -ENOMEM;
392 393 394
		goto err_out;
	}
	d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
395 396
	if (!d) {
		err = -ENOMEM;
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434
		goto err_out;
	}
	return 0;

err_out:
	d = c->dent;
	list_for_each_entry(child, &d->d_subdirs, d_u.d_child)
		debugfs_remove(child);
	debugfs_remove(c->dent);
	return err;
}

static int clk_debugfs_register(struct clk *c)
{
	int err;
	struct clk *pa = c->parent;

	if (pa && !pa->dent) {
		err = clk_debugfs_register(pa);
		if (err)
			return err;
	}

	if (!c->dent) {
		err = clk_debugfs_register_one(c);
		if (err)
			return err;
	}
	return 0;
}

static int __init clk_debugfs_init(void)
{
	struct clk *c;
	struct dentry *d;
	int err;

	d = debugfs_create_dir("clock", NULL);
435 436
	if (!d)
		return -ENOMEM;
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451
	clk_debugfs_root = d;

	list_for_each_entry(c, &clocks, node) {
		err = clk_debugfs_register(c);
		if (err)
			goto err_out;
	}
	return 0;
err_out:
	debugfs_remove(clk_debugfs_root); /* REVISIT: Cleanup correctly */
	return err;
}
late_initcall(clk_debugfs_init);

#endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */