diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-02 03:24:44 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-02 03:24:44 +0200 |
commit | 11801e9de26992d37cb869cc74f389b6a7677e0e (patch) | |
tree | 322b7ea2b475d52da27d3e01f5bc2992bb708d59 /arch/arm/mach-tegra/clock.c | |
parent | Merge tag 'maintainers' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/... (diff) | |
parent | Merge branch 'lpc32xx/core' of git://git.antcom.de/linux-2.6 into next/soc (diff) | |
download | linux-11801e9de26992d37cb869cc74f389b6a7677e0e.tar.xz linux-11801e9de26992d37cb869cc74f389b6a7677e0e.zip |
Merge tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM soc-specific updates from Olof Johansson:
"Most notable here is probably the addition of basic support for the
BCM2835, an SoC used in some of the Roku 2 players as well as the
much-hyped Raspberry Pi, cleaned up and contributed by Stephen Warren.
It's still early days on mainline support, with just the basics
working. But it has to start somewhere!
Beyond that there's some conversions of clock infrastructure on tegra
to common clock, misc updates for several other platforms, and OMAP
now has its own bus (under drivers/bus) to manage its devices through.
This branch adds two new directories outside of arch/arm:
drivers/irqchip for new irq controllers, and drivers/bus for the above
OMAP bus. It's expected that some of the other platforms will migrate
parts of their platforms to those directories over time as well."
Fix up trivial conflicts with the clk infrastructure changes.
* tag 'soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (62 commits)
ARM: shmobile: add new __iomem annotation for new code
ARM: LPC32xx: Support GPI 28
ARM: LPC32xx: Platform update for devicetree completion of spi-pl022
ARM: LPC32xx: Board cleanup
irqchip: fill in empty Kconfig
ARM: SAMSUNG: Add check for NULL in clock interface
ARM: EXYNOS: Put PCM, Slimbus, Spdif clocks to off state
ARM: EXYNOS: Add bus clock for FIMD
ARM: SAMSUNG: Fix HDMI related warnings
ARM: S3C24XX: Add .get_rate callback for "camif-upll" clock
ARM: EXYNOS: Fix incorrect help text
ARM: EXYNOS: Turn off clocks for NAND, OneNAND and TSI controllers
ARM: OMAP: AM33xx hwmod: fixup SPI after platform_data move
MAINTAINERS: add an entry for the BCM2835 ARM sub-architecture
ARM: bcm2835: instantiate console UART
ARM: bcm2835: add stub clock driver
ARM: bcm2835: add system timer
ARM: bcm2835: add interrupt controller driver
ARM: add infra-structure for BCM2835 and Raspberry Pi
ARM: tegra20: add CPU hotplug support
...
Diffstat (limited to 'arch/arm/mach-tegra/clock.c')
-rw-r--r-- | arch/arm/mach-tegra/clock.c | 574 |
1 files changed, 35 insertions, 539 deletions
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 58f981c0819c..fd82085eca5d 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -1,6 +1,7 @@ /* * * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2012 NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@google.com> @@ -19,8 +20,6 @@ #include <linux/kernel.h> #include <linux/clk.h> #include <linux/clkdev.h> -#include <linux/debugfs.h> -#include <linux/delay.h> #include <linux/init.h> #include <linux/list.h> #include <linux/module.h> @@ -32,325 +31,75 @@ #include "board.h" #include "clock.h" +#include "tegra_cpu_car.h" + +/* Global data of Tegra CPU CAR ops */ +struct tegra_cpu_car_ops *tegra_cpu_car_ops; /* * Locking: * - * Each struct clk has a spinlock. - * - * To avoid AB-BA locking problems, locks must always be traversed from child - * clock to parent clock. For example, when enabling a clock, the clock's lock - * is taken, and then clk_enable is called on the parent, which take's the - * parent clock's lock. There is one exceptions to this ordering: When dumping - * the clock tree through debugfs. In this case, clk_lock_all is called, - * which attemps to iterate through the entire list of clocks and take every - * clock lock. If any call to spin_trylock fails, all locked clocks are - * unlocked, and the process is retried. When all the locks are held, - * the only clock operation that can be called is clk_get_rate_all_locked. - * - * Within a single clock, no clock operation can call another clock operation - * on itself, except for clk_get_rate_locked and clk_set_rate_locked. Any - * clock operation can call any other clock operation on any of it's possible - * parents. - * * An additional mutex, clock_list_lock, is used to protect the list of all * clocks. * - * The clock operations must lock internally to protect against - * read-modify-write on registers that are shared by multiple clocks */ static DEFINE_MUTEX(clock_list_lock); static LIST_HEAD(clocks); -struct clk *tegra_get_clock_by_name(const char *name) -{ - struct clk *c; - struct clk *ret = NULL; - mutex_lock(&clock_list_lock); - list_for_each_entry(c, &clocks, node) { - if (strcmp(c->name, name) == 0) { - ret = c; - break; - } - } - mutex_unlock(&clock_list_lock); - return ret; -} - -/* Must be called with c->spinlock held */ -static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p) -{ - u64 rate; - - rate = clk_get_rate(p); - - if (c->mul != 0 && c->div != 0) { - rate *= c->mul; - rate += c->div - 1; /* round up */ - do_div(rate, c->div); - } - - return rate; -} - -/* Must be called with c->spinlock held */ -unsigned long clk_get_rate_locked(struct clk *c) -{ - unsigned long rate; - - if (c->parent) - rate = clk_predict_rate_from_parent(c, c->parent); - else - rate = c->rate; - - return rate; -} - -unsigned long clk_get_rate(struct clk *c) +void tegra_clk_add(struct clk *clk) { - unsigned long flags; - unsigned long rate; - - spin_lock_irqsave(&c->spinlock, flags); - - rate = clk_get_rate_locked(c); - - spin_unlock_irqrestore(&c->spinlock, flags); - - return rate; -} -EXPORT_SYMBOL(clk_get_rate); - -int clk_reparent(struct clk *c, struct clk *parent) -{ - c->parent = parent; - return 0; -} - -void clk_init(struct clk *c) -{ - spin_lock_init(&c->spinlock); - - if (c->ops && c->ops->init) - c->ops->init(c); - - if (!c->ops || !c->ops->enable) { - c->refcnt++; - c->set = true; - if (c->parent) - c->state = c->parent->state; - else - c->state = ON; - } + struct clk_tegra *c = to_clk_tegra(__clk_get_hw(clk)); mutex_lock(&clock_list_lock); list_add(&c->node, &clocks); mutex_unlock(&clock_list_lock); } -int clk_enable(struct clk *c) -{ - int ret = 0; - unsigned long flags; - - spin_lock_irqsave(&c->spinlock, flags); - - if (c->refcnt == 0) { - if (c->parent) { - ret = clk_enable(c->parent); - if (ret) - goto out; - } - - if (c->ops && c->ops->enable) { - ret = c->ops->enable(c); - if (ret) { - if (c->parent) - clk_disable(c->parent); - goto out; - } - c->state = ON; - c->set = true; - } - } - c->refcnt++; -out: - spin_unlock_irqrestore(&c->spinlock, flags); - return ret; -} -EXPORT_SYMBOL(clk_enable); - -void clk_disable(struct clk *c) -{ - unsigned long flags; - - spin_lock_irqsave(&c->spinlock, flags); - - if (c->refcnt == 0) { - WARN(1, "Attempting to disable clock %s with refcnt 0", c->name); - spin_unlock_irqrestore(&c->spinlock, flags); - return; - } - if (c->refcnt == 1) { - if (c->ops && c->ops->disable) - c->ops->disable(c); - - if (c->parent) - clk_disable(c->parent); - - c->state = OFF; - } - c->refcnt--; - - spin_unlock_irqrestore(&c->spinlock, flags); -} -EXPORT_SYMBOL(clk_disable); - -int clk_set_parent(struct clk *c, struct clk *parent) -{ - int ret; - unsigned long flags; - unsigned long new_rate; - unsigned long old_rate; - - spin_lock_irqsave(&c->spinlock, flags); - - if (!c->ops || !c->ops->set_parent) { - ret = -ENOSYS; - goto out; - } - - new_rate = clk_predict_rate_from_parent(c, parent); - old_rate = clk_get_rate_locked(c); - - ret = c->ops->set_parent(c, parent); - if (ret) - goto out; - -out: - spin_unlock_irqrestore(&c->spinlock, flags); - return ret; -} -EXPORT_SYMBOL(clk_set_parent); - -struct clk *clk_get_parent(struct clk *c) -{ - return c->parent; -} -EXPORT_SYMBOL(clk_get_parent); - -int clk_set_rate_locked(struct clk *c, unsigned long rate) -{ - long new_rate; - - if (!c->ops || !c->ops->set_rate) - return -ENOSYS; - - if (rate > c->max_rate) - rate = c->max_rate; - - if (c->ops && c->ops->round_rate) { - new_rate = c->ops->round_rate(c, rate); - - if (new_rate < 0) - return new_rate; - - rate = new_rate; - } - - return c->ops->set_rate(c, rate); -} - -int clk_set_rate(struct clk *c, unsigned long rate) -{ - int ret; - unsigned long flags; - - spin_lock_irqsave(&c->spinlock, flags); - - ret = clk_set_rate_locked(c, rate); - - spin_unlock_irqrestore(&c->spinlock, flags); - - return ret; -} -EXPORT_SYMBOL(clk_set_rate); - - -/* Must be called with clocks lock and all indvidual clock locks held */ -unsigned long clk_get_rate_all_locked(struct clk *c) +struct clk *tegra_get_clock_by_name(const char *name) { - u64 rate; - int mul = 1; - int div = 1; - struct clk *p = c; - - while (p) { - c = p; - if (c->mul != 0 && c->div != 0) { - mul *= c->mul; - div *= c->div; + struct clk_tegra *c; + struct clk *ret = NULL; + mutex_lock(&clock_list_lock); + list_for_each_entry(c, &clocks, node) { + if (strcmp(__clk_get_name(c->hw.clk), name) == 0) { + ret = c->hw.clk; + break; } - p = c->parent; - } - - rate = c->rate; - rate *= mul; - do_div(rate, div); - - return rate; -} - -long clk_round_rate(struct clk *c, unsigned long rate) -{ - unsigned long flags; - long ret; - - spin_lock_irqsave(&c->spinlock, flags); - - if (!c->ops || !c->ops->round_rate) { - ret = -ENOSYS; - goto out; } - - if (rate > c->max_rate) - rate = c->max_rate; - - ret = c->ops->round_rate(c, rate); - -out: - spin_unlock_irqrestore(&c->spinlock, flags); + mutex_unlock(&clock_list_lock); return ret; } -EXPORT_SYMBOL(clk_round_rate); static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) { struct clk *c; struct clk *p; + struct clk *parent; int ret = 0; c = tegra_get_clock_by_name(table->name); if (!c) { - pr_warning("Unable to initialize clock %s\n", + pr_warn("Unable to initialize clock %s\n", table->name); return -ENODEV; } + parent = clk_get_parent(c); + if (table->parent) { p = tegra_get_clock_by_name(table->parent); if (!p) { - pr_warning("Unable to find parent %s of clock %s\n", + pr_warn("Unable to find parent %s of clock %s\n", table->parent, table->name); return -ENODEV; } - if (c->parent != p) { + if (parent != p) { ret = clk_set_parent(c, p); if (ret) { - pr_warning("Unable to set parent %s of clock %s: %d\n", + pr_warn("Unable to set parent %s of clock %s: %d\n", table->parent, table->name, ret); return -EINVAL; } @@ -360,16 +109,16 @@ static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table) if (table->rate && table->rate != clk_get_rate(c)) { ret = clk_set_rate(c, table->rate); if (ret) { - pr_warning("Unable to set clock %s to rate %lu: %d\n", + pr_warn("Unable to set clock %s to rate %lu: %d\n", table->name, table->rate, ret); return -EINVAL; } } if (table->enabled) { - ret = clk_enable(c); + ret = clk_prepare_enable(c); if (ret) { - pr_warning("Unable to enable clock %s: %d\n", + pr_warn("Unable to enable clock %s: %d\n", table->name, ret); return -EINVAL; } @@ -383,19 +132,20 @@ void tegra_clk_init_from_table(struct tegra_clk_init_table *table) for (; table->name; table++) tegra_clk_init_one_from_table(table); } -EXPORT_SYMBOL(tegra_clk_init_from_table); void tegra_periph_reset_deassert(struct clk *c) { - BUG_ON(!c->ops->reset); - c->ops->reset(c, false); + struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c)); + BUG_ON(!clk->reset); + clk->reset(__clk_get_hw(c), false); } EXPORT_SYMBOL(tegra_periph_reset_deassert); void tegra_periph_reset_assert(struct clk *c) { - BUG_ON(!c->ops->reset); - c->ops->reset(c, true); + struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c)); + BUG_ON(!clk->reset); + clk->reset(__clk_get_hw(c), true); } EXPORT_SYMBOL(tegra_periph_reset_assert); @@ -405,268 +155,14 @@ EXPORT_SYMBOL(tegra_periph_reset_assert); int tegra_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting) { int ret = 0; - unsigned long flags; + struct clk_tegra *clk = to_clk_tegra(__clk_get_hw(c)); - spin_lock_irqsave(&c->spinlock, flags); - - if (!c->ops || !c->ops->clk_cfg_ex) { + if (!clk->clk_cfg_ex) { ret = -ENOSYS; goto out; } - ret = c->ops->clk_cfg_ex(c, p, setting); + ret = clk->clk_cfg_ex(__clk_get_hw(c), p, setting); out: - spin_unlock_irqrestore(&c->spinlock, flags); - return ret; } - -#ifdef CONFIG_DEBUG_FS - -static int __clk_lock_all_spinlocks(void) -{ - struct clk *c; - - list_for_each_entry(c, &clocks, node) - if (!spin_trylock(&c->spinlock)) - goto unlock_spinlocks; - - return 0; - -unlock_spinlocks: - list_for_each_entry_continue_reverse(c, &clocks, node) - spin_unlock(&c->spinlock); - - return -EAGAIN; -} - -static void __clk_unlock_all_spinlocks(void) -{ - struct clk *c; - - list_for_each_entry_reverse(c, &clocks, node) - spin_unlock(&c->spinlock); -} - -/* - * This function retries until it can take all locks, and may take - * an arbitrarily long time to complete. - * Must be called with irqs enabled, returns with irqs disabled - * Must be called with clock_list_lock held - */ -static void clk_lock_all(void) -{ - int ret; -retry: - local_irq_disable(); - - ret = __clk_lock_all_spinlocks(); - if (ret) - goto failed_spinlocks; - - /* All locks taken successfully, return */ - return; - -failed_spinlocks: - local_irq_enable(); - yield(); - goto retry; -} - -/* - * Unlocks all clocks after a clk_lock_all - * Must be called with irqs disabled, returns with irqs enabled - * Must be called with clock_list_lock held - */ -static void clk_unlock_all(void) -{ - __clk_unlock_all_spinlocks(); - - local_irq_enable(); -} - -static struct dentry *clk_debugfs_root; - - -static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level) -{ - struct clk *child; - const char *state = "uninit"; - char div[8] = {0}; - - if (c->state == ON) - state = "on"; - else if (c->state == OFF) - state = "off"; - - if (c->mul != 0 && c->div != 0) { - if (c->mul > c->div) { - int mul = c->mul / c->div; - int mul2 = (c->mul * 10 / c->div) % 10; - int mul3 = (c->mul * 10) % c->div; - if (mul2 == 0 && mul3 == 0) - snprintf(div, sizeof(div), "x%d", mul); - else if (mul3 == 0) - snprintf(div, sizeof(div), "x%d.%d", mul, mul2); - else - snprintf(div, sizeof(div), "x%d.%d..", mul, mul2); - } else { - snprintf(div, sizeof(div), "%d%s", c->div / c->mul, - (c->div % c->mul) ? ".5" : ""); - } - } - - seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n", - level * 3 + 1, "", - c->rate > c->max_rate ? '!' : ' ', - !c->set ? '*' : ' ', - 30 - level * 3, c->name, - state, c->refcnt, div, clk_get_rate_all_locked(c)); - - list_for_each_entry(child, &clocks, node) { - if (child->parent != c) - continue; - - clock_tree_show_one(s, child, level + 1); - } -} - -static int clock_tree_show(struct seq_file *s, void *data) -{ - struct clk *c; - seq_printf(s, " clock state ref div rate\n"); - seq_printf(s, "--------------------------------------------------------------\n"); - - mutex_lock(&clock_list_lock); - - clk_lock_all(); - - list_for_each_entry(c, &clocks, node) - if (c->parent == NULL) - clock_tree_show_one(s, c, 0); - - clk_unlock_all(); - - mutex_unlock(&clock_list_lock); - return 0; -} - -static int clock_tree_open(struct inode *inode, struct file *file) -{ - return single_open(file, clock_tree_show, inode->i_private); -} - -static const struct file_operations clock_tree_fops = { - .open = clock_tree_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int possible_parents_show(struct seq_file *s, void *data) -{ - struct clk *c = s->private; - int i; - - for (i = 0; c->inputs[i].input; i++) { - char *first = (i == 0) ? "" : " "; - seq_printf(s, "%s%s", first, c->inputs[i].input->name); - } - seq_printf(s, "\n"); - return 0; -} - -static int possible_parents_open(struct inode *inode, struct file *file) -{ - return single_open(file, possible_parents_show, inode->i_private); -} - -static const struct file_operations possible_parents_fops = { - .open = possible_parents_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int clk_debugfs_register_one(struct clk *c) -{ - struct dentry *d; - - d = debugfs_create_dir(c->name, clk_debugfs_root); - if (!d) - return -ENOMEM; - c->dent = d; - - d = debugfs_create_u8("refcnt", S_IRUGO, c->dent, (u8 *)&c->refcnt); - if (!d) - goto err_out; - - d = debugfs_create_u32("rate", S_IRUGO, c->dent, (u32 *)&c->rate); - if (!d) - goto err_out; - - d = debugfs_create_x32("flags", S_IRUGO, c->dent, (u32 *)&c->flags); - if (!d) - goto err_out; - - if (c->inputs) { - d = debugfs_create_file("possible_parents", S_IRUGO, c->dent, - c, &possible_parents_fops); - if (!d) - goto err_out; - } - - return 0; - -err_out: - debugfs_remove_recursive(c->dent); - return -ENOMEM; -} - -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; -} - -int __init tegra_clk_debugfs_init(void) -{ - struct clk *c; - struct dentry *d; - int err = -ENOMEM; - - d = debugfs_create_dir("clock", NULL); - if (!d) - return -ENOMEM; - clk_debugfs_root = d; - - d = debugfs_create_file("clock_tree", S_IRUGO, clk_debugfs_root, NULL, - &clock_tree_fops); - if (!d) - goto err_out; - - list_for_each_entry(c, &clocks, node) { - err = clk_debugfs_register(c); - if (err) - goto err_out; - } - return 0; -err_out: - debugfs_remove_recursive(clk_debugfs_root); - return err; -} - -#endif |