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
	spin_unlock_irqrestore(&clockfw_lock, flags);
}
EXPORT_SYMBOL(clk_disable);

79
unsigned long clk_get_rate(struct clk *clk)
L
Linus Torvalds 已提交
80
{
81 82
	unsigned long flags;
	unsigned long 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->rate;
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
90

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

95
/*-------------------------------------------------------------------------
96
 * Optional clock functions defined in include/linux/clk.h
97
 *-------------------------------------------------------------------------*/
98

L
Linus Torvalds 已提交
99 100
long clk_round_rate(struct clk *clk, unsigned long rate)
{
101 102
	unsigned long flags;
	long ret = 0;
L
Linus Torvalds 已提交
103

104 105 106
	if (clk == NULL || IS_ERR(clk))
		return ret;

107 108 109 110
	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 已提交
111

112
	return ret;
L
Linus Torvalds 已提交
113 114 115
}
EXPORT_SYMBOL(clk_round_rate);

116
int clk_set_rate(struct clk *clk, unsigned long rate)
L
Linus Torvalds 已提交
117
{
118
	unsigned long flags;
119 120 121 122
	int ret = -EINVAL;

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

124 125 126
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_rate)
		ret = arch_clock->clk_set_rate(clk, rate);
127 128
	if (ret == 0) {
		if (clk->recalc)
129
			clk->rate = clk->recalc(clk);
130
		propagate_rate(clk);
131
	}
132
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
133

134
	return ret;
L
Linus Torvalds 已提交
135
}
136
EXPORT_SYMBOL(clk_set_rate);
L
Linus Torvalds 已提交
137

138
int clk_set_parent(struct clk *clk, struct clk *parent)
L
Linus Torvalds 已提交
139
{
140
	unsigned long flags;
141 142 143 144
	int ret = -EINVAL;

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

146 147
	spin_lock_irqsave(&clockfw_lock, flags);
	if (arch_clock->clk_set_parent)
148
		ret = arch_clock->clk_set_parent(clk, parent);
149 150
	if (ret == 0) {
		if (clk->recalc)
151
			clk->rate = clk->recalc(clk);
152
		propagate_rate(clk);
153
	}
154
	spin_unlock_irqrestore(&clockfw_lock, flags);
L
Linus Torvalds 已提交
155

156
	return ret;
L
Linus Torvalds 已提交
157
}
158
EXPORT_SYMBOL(clk_set_parent);
L
Linus Torvalds 已提交
159

160
struct clk *clk_get_parent(struct clk *clk)
L
Linus Torvalds 已提交
161
{
162
	return clk->parent;
L
Linus Torvalds 已提交
163
}
164
EXPORT_SYMBOL(clk_get_parent);
L
Linus Torvalds 已提交
165

166 167 168
/*-------------------------------------------------------------------------
 * OMAP specific clock functions shared between omap1 and omap2
 *-------------------------------------------------------------------------*/
L
Linus Torvalds 已提交
169

170
unsigned int __initdata mpurate;
L
Linus Torvalds 已提交
171

172 173 174 175 176
/*
 * 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 已提交
177
{
178
	get_option(&str, &mpurate);
L
Linus Torvalds 已提交
179

180 181
	if (!mpurate)
		return 1;
L
Linus Torvalds 已提交
182

183 184
	if (mpurate < 1000)
		mpurate *= 1000000;
L
Linus Torvalds 已提交
185

186
	return 1;
L
Linus Torvalds 已提交
187
}
188
__setup("mpurate=", omap_clk_setup);
L
Linus Torvalds 已提交
189

190
/* Used for clocks that always have same value as the parent clock */
191
unsigned long followparent_recalc(struct clk *clk)
L
Linus Torvalds 已提交
192
{
193
	return clk->parent->rate;
L
Linus Torvalds 已提交
194 195
}

196 197 198 199 200 201 202 203 204 205 206
void clk_reparent(struct clk *child, struct clk *parent)
{
	list_del_init(&child->sibling);
	if (parent)
		list_add(&child->sibling, &parent->children);
	child->parent = parent;

	/* now do the debugfs renaming to reattach the child
	   to the proper parent */
}

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

212
	list_for_each_entry(clkp, &tclk->children, sibling) {
213
		if (clkp->recalc)
214
			clkp->rate = clkp->recalc(clkp);
215
		propagate_rate(clkp);
216
	}
L
Linus Torvalds 已提交
217 218
}

219 220
static LIST_HEAD(root_clks);

221 222 223 224 225 226 227 228 229 230 231
/**
 * 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;

232 233
	list_for_each_entry(clkp, &root_clks, sibling) {
		if (clkp->recalc)
234
			clkp->rate = clkp->recalc(clkp);
235
		propagate_rate(clkp);
236 237 238
	}
}

239 240 241 242 243
void clk_init_one(struct clk *clk)
{
	INIT_LIST_HEAD(&clk->children);
}

L
Linus Torvalds 已提交
244 245
int clk_register(struct clk *clk)
{
246 247 248
	if (clk == NULL || IS_ERR(clk))
		return -EINVAL;

249 250 251 252 253 254
	/*
	 * trap out already registered clocks
	 */
	if (clk->node.next || clk->node.prev)
		return 0;

255
	mutex_lock(&clocks_mutex);
256 257 258 259 260
	if (clk->parent)
		list_add(&clk->sibling, &clk->parent->children);
	else
		list_add(&clk->sibling, &root_clks);

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

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

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

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

282 283 284 285 286 287 288 289 290 291 292
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);

293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
/*
 * 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,
};

310 311 312 313 314 315 316 317 318 319 320 321 322
#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

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

325 326 327 328 329 330 331 332 333 334
#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) {
335 336 337 338
		if (ck->ops == &clkops_null)
			continue;

		if (ck->usecount > 0 || ck->enable_reg == 0)
339 340 341 342 343 344 345 346 347 348 349 350 351
			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

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

359 360
	arch_clock = custom_clocks;

361 362
	return 0;
}
363

364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
#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);
382 383
	if (!d)
		return -ENOMEM;
384 385 386
	c->dent = d;

	d = debugfs_create_u8("usecount", S_IRUGO, c->dent, (u8 *)&c->usecount);
387 388
	if (!d) {
		err = -ENOMEM;
389 390 391
		goto err_out;
	}
	d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate);
392 393
	if (!d) {
		err = -ENOMEM;
394 395 396
		goto err_out;
	}
	d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags);
397 398
	if (!d) {
		err = -ENOMEM;
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 435 436
		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);
437 438
	if (!d)
		return -ENOMEM;
439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
	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) */