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

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

53
	mutex_lock(&clocks_mutex);
54 55

	list_for_each_entry(p, &clocks, node) {
R
Russell King 已提交
56
		if (p->id == idno && strcmp(id, p->name) == 0) {
57
			clk = p;
58
			goto found;
59 60 61
		}
	}

L
Linus Torvalds 已提交
62
	list_for_each_entry(p, &clocks, node) {
R
Russell King 已提交
63
		if (strcmp(id, p->name) == 0) {
L
Linus Torvalds 已提交
64 65 66 67
			clk = p;
			break;
		}
	}
68

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

	return clk;
}
EXPORT_SYMBOL(clk_get);

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

81 82 83
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

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

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

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

97 98 99
	if (clk == NULL || IS_ERR(clk))
		return;

L
Linus Torvalds 已提交
100
	spin_lock_irqsave(&clockfw_lock, flags);
101 102 103 104 105 106 107
	if (clk->usecount == 0) {
		printk(KERN_ERR "Trying disable clock %s with 0 usecount\n",
		       clk->name);
		WARN_ON(1);
		goto out;
	}

108
	if (arch_clock->clk_disable)
109
		arch_clock->clk_disable(clk);
110 111

out:
L
Linus Torvalds 已提交
112 113 114 115 116 117
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

int clk_get_usecount(struct clk *clk)
{
118 119
	unsigned long flags;
	int ret = 0;
L
Linus Torvalds 已提交
120

121 122 123
	if (clk == NULL || IS_ERR(clk))
		return 0;

124 125 126
	spin_lock_irqsave(&clockfw_lock, flags);
	ret = clk->usecount;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
127

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

132
unsigned long clk_get_rate(struct clk *clk)
L
Linus Torvalds 已提交
133
{
134 135
	unsigned long flags;
	unsigned long ret = 0;
L
Linus Torvalds 已提交
136

137 138 139
	if (clk == NULL || IS_ERR(clk))
		return 0;

140 141 142
	spin_lock_irqsave(&clockfw_lock, flags);
	ret = clk->rate;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
143

144
	return ret;
L
Linus Torvalds 已提交
145
}
146
EXPORT_SYMBOL(clk_get_rate);
L
Linus Torvalds 已提交
147

148
void clk_put(struct clk *clk)
149 150
{
}
151
EXPORT_SYMBOL(clk_put);
152

153
/*-------------------------------------------------------------------------
154
 * Optional clock functions defined in include/linux/clk.h
155
 *-------------------------------------------------------------------------*/
156

L
Linus Torvalds 已提交
157 158
long clk_round_rate(struct clk *clk, unsigned long rate)
{
159 160
	unsigned long flags;
	long ret = 0;
L
Linus Torvalds 已提交
161

162 163 164
	if (clk == NULL || IS_ERR(clk))
		return ret;

165 166 167 168
	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 已提交
169

170
	return ret;
L
Linus Torvalds 已提交
171 172 173
}
EXPORT_SYMBOL(clk_round_rate);

174
int clk_set_rate(struct clk *clk, unsigned long rate)
L
Linus Torvalds 已提交
175
{
176
	unsigned long flags;
177 178 179 180
	int ret = -EINVAL;

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

182 183 184 185
	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 已提交
186

187
	return ret;
L
Linus Torvalds 已提交
188
}
189
EXPORT_SYMBOL(clk_set_rate);
L
Linus Torvalds 已提交
190

191
int clk_set_parent(struct clk *clk, struct clk *parent)
L
Linus Torvalds 已提交
192
{
193
	unsigned long flags;
194 195 196 197
	int ret = -EINVAL;

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

199 200 201 202
	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 已提交
203

204
	return ret;
L
Linus Torvalds 已提交
205
}
206
EXPORT_SYMBOL(clk_set_parent);
L
Linus Torvalds 已提交
207

208
struct clk *clk_get_parent(struct clk *clk)
L
Linus Torvalds 已提交
209
{
210 211
	unsigned long flags;
	struct clk * ret = NULL;
L
Linus Torvalds 已提交
212

213 214 215
	if (clk == NULL || IS_ERR(clk))
		return ret;

216 217 218 219
	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 已提交
220 221 222

	return ret;
}
223
EXPORT_SYMBOL(clk_get_parent);
L
Linus Torvalds 已提交
224

225 226 227
/*-------------------------------------------------------------------------
 * OMAP specific clock functions shared between omap1 and omap2
 *-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
228

229
unsigned int __initdata mpurate;
L
Linus Torvalds 已提交
230

231 232 233 234 235
/*
 * 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 已提交
236
{
237
	get_option(&str, &mpurate);
L
Linus Torvalds 已提交
238

239 240
	if (!mpurate)
		return 1;
L
Linus Torvalds 已提交
241

242 243
	if (mpurate < 1000)
		mpurate *= 1000000;
L
Linus Torvalds 已提交
244

245
	return 1;
L
Linus Torvalds 已提交
246
}
247
__setup("mpurate=", omap_clk_setup);
L
Linus Torvalds 已提交
248

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

255
	clk->rate = clk->parent->rate;
256 257
	if (unlikely(clk->flags & RATE_PROPAGATES))
		propagate_rate(clk);
L
Linus Torvalds 已提交
258 259
}

260 261
/* Propagate rate to children */
void propagate_rate(struct clk * tclk)
L
Linus Torvalds 已提交
262
{
263
	struct clk *clkp;
L
Linus Torvalds 已提交
264

265 266 267
	if (tclk == NULL || IS_ERR(tclk))
		return;

268 269 270 271 272 273
	list_for_each_entry(clkp, &clocks, node) {
		if (likely(clkp->parent != tclk))
			continue;
		if (likely((u32)clkp->recalc))
			clkp->recalc(clkp);
	}
L
Linus Torvalds 已提交
274 275
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292
/**
 * 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) {
		if (unlikely(!clkp->parent) && likely((u32)clkp->recalc))
			clkp->recalc(clkp);
	}
}

L
Linus Torvalds 已提交
293 294
int clk_register(struct clk *clk)
{
295 296 297
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

298
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
299 300 301
	list_add(&clk->node, &clocks);
	if (clk->init)
		clk->init(clk);
302
	mutex_unlock(&clocks_mutex);
303

L
Linus Torvalds 已提交
304 305 306 307 308 309
	return 0;
}
EXPORT_SYMBOL(clk_register);

void clk_unregister(struct clk *clk)
{
310 311 312
	if (clk == NULL || IS_ERR(clk))
		return;

313
	mutex_lock(&clocks_mutex);
L
Linus Torvalds 已提交
314
	list_del(&clk->node);
315
	mutex_unlock(&clocks_mutex);
L
Linus Torvalds 已提交
316 317 318
}
EXPORT_SYMBOL(clk_unregister);

319
void clk_deny_idle(struct clk *clk)
320
{
321 322
	unsigned long flags;

323 324 325
	if (clk == NULL || IS_ERR(clk))
		return;

326 327 328 329
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_deny_idle)
		arch_clock->clk_deny_idle(clk);
	spin_unlock_irqrestore(&clockfw_lock, flags);
330
}
331
EXPORT_SYMBOL(clk_deny_idle);
L
Linus Torvalds 已提交
332

333
void clk_allow_idle(struct clk *clk)
L
Linus Torvalds 已提交
334
{
335
	unsigned long flags;
L
Linus Torvalds 已提交
336

337 338 339
	if (clk == NULL || IS_ERR(clk))
		return;

340 341 342 343
	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 已提交
344
}
345
EXPORT_SYMBOL(clk_allow_idle);
346

347 348 349 350 351 352 353 354 355 356 357
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);

358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
/*
 * 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,
};

375 376 377 378 379 380 381 382 383 384 385 386 387
#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

388
/*-------------------------------------------------------------------------*/
389

390 391 392 393 394 395 396 397 398 399
#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) {
400 401 402 403
		if (ck->ops == &clkops_null)
			continue;

		if (ck->usecount > 0 || ck->enable_reg == 0)
404 405 406 407 408 409 410 411 412 413 414 415 416
			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

417
int __init clk_init(struct clk_functions * custom_clocks)
418
{
419 420 421
	if (!custom_clocks) {
		printk(KERN_ERR "No custom clock functions registered\n");
		BUG();
422 423
	}

424 425
	arch_clock = custom_clocks;

426 427
	return 0;
}
428

429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446
#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);
447 448
	if (!d)
		return -ENOMEM;
449 450 451
	c->dent = d;

	d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount);
452 453
	if (!d) {
		err = -ENOMEM;
454 455 456
		goto err_out;
	}
	d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
457 458
	if (!d) {
		err = -ENOMEM;
459 460 461
		goto err_out;
	}
	d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
462 463
	if (!d) {
		err = -ENOMEM;
464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
		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);
502 503
	if (!d)
		return -ENOMEM;
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518
	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) */