summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/tegra2_clocks.c
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2010-11-01 17:25:30 -0700
committerColin Cross <ccross@android.com>2010-11-03 17:37:56 -0700
commitc95714d0ba915e422ad676f68630fcd513f34295 (patch)
treeb36183c6769f951166bdfe60be4bf3a2f9528159 /arch/arm/mach-tegra/tegra2_clocks.c
parent1f8ca000a05c44e338438c729022d238b167fba4 (diff)
ARM: tegra: clock: Redo clock locking
Give each clock its own lock, and remove all lock traversals from parent to child clocks to prevent AB-BA deadlocks. Signed-off-by: Colin Cross <ccross@android.com> Change-Id: I0afb7d1bca956439b1a4f17bbc6748aaec706b49
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_clocks.c')
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c75
1 files changed, 41 insertions, 34 deletions
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 7f5ed03fa958..8bc46e7e0988 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -23,7 +23,7 @@
#include <linux/spinlock.h>
#include <linux/delay.h>
#include <linux/io.h>
-#include <linux/hrtimer.h>
+#include <linux/clk.h>
#include <asm/clkdev.h>
@@ -337,12 +337,12 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value << shift;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
@@ -389,31 +389,31 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
* Take an extra reference to the main pll so it doesn't turn
* off when we move the cpu off of it
*/
- clk_enable_locked(c->u.cpu.main);
+ clk_enable(c->u.cpu.main);
- ret = clk_set_parent_locked(c->parent, c->u.cpu.backup);
+ ret = clk_set_parent(c->parent, c->u.cpu.backup);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
goto out;
}
- if (rate == c->u.cpu.backup->rate)
+ if (rate == clk_get_rate(c->u.cpu.backup))
goto out;
- ret = clk_set_rate_locked(c->u.cpu.main, rate);
+ ret = clk_set_rate(c->u.cpu.main, rate);
if (ret) {
pr_err("Failed to change cpu pll to %lu\n", rate);
goto out;
}
- ret = clk_set_parent_locked(c->parent, c->u.cpu.main);
+ ret = clk_set_parent(c->parent, c->u.cpu.main);
if (ret) {
pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
goto out;
}
out:
- clk_disable_locked(c->u.cpu.main);
+ clk_disable(c->u.cpu.main);
return ret;
}
@@ -465,7 +465,7 @@ static void tegra2_bus_clk_disable(struct clk *c)
static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
{
u32 val = clk_readl(c->reg);
- unsigned long parent_rate = c->parent->rate;
+ unsigned long parent_rate = clk_get_rate(c->parent);
int i;
for (i = 1; i <= 4; i++) {
if (rate == parent_rate / i) {
@@ -539,14 +539,15 @@ static void tegra2_blink_clk_disable(struct clk *c)
static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
{
- if (rate >= c->parent->rate) {
+ unsigned long parent_rate = clk_get_rate(c->parent);
+ if (rate >= parent_rate) {
c->div = 1;
pmc_writel(0, c->reg);
} else {
unsigned int on_off;
u32 val;
- on_off = DIV_ROUND_UP(c->parent->rate / 8, rate);
+ on_off = DIV_ROUND_UP(parent_rate / 8, rate);
c->div = on_off * 8;
val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
@@ -632,7 +633,7 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
- input_rate = c->parent->rate;
+ input_rate = clk_get_rate(c->parent);
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
if (sel->input_rate == input_rate && sel->output_rate == rate) {
c->mul = sel->n;
@@ -772,9 +773,11 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
u32 val;
u32 new_val;
int divider_u71;
+ unsigned long parent_rate = clk_get_rate(c->parent);
+
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider_u71 = clk_div71_get_divider(c->parent->rate, rate);
+ divider_u71 = clk_div71_get_divider(parent_rate, rate);
if (divider_u71 >= 0) {
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
@@ -792,7 +795,7 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
return 0;
}
} else if (c->flags & DIV_2) {
- if (c->parent->rate == rate * 2)
+ if (parent_rate == rate * 2)
return 0;
}
return -EINVAL;
@@ -801,15 +804,16 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
{
int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate * 2 / (divider + 2);
+ return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_2) {
- return c->parent->rate / 2;
+ return parent_rate / 2;
}
return -EINVAL;
}
@@ -923,12 +927,12 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
@@ -942,9 +946,10 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
{
u32 val;
int divider;
- pr_debug("%s: %lu\n", __func__, rate);
+ unsigned long parent_rate = clk_get_rate(c->parent);
+
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider >= 0) {
val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
@@ -955,7 +960,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
return 0;
}
} else if (c->flags & DIV_U16) {
- divider = clk_div16_get_divider(c->parent->rate, rate);
+ divider = clk_div16_get_divider(parent_rate, rate);
if (divider >= 0) {
val = clk_readl(c->reg);
val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
@@ -965,7 +970,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
c->mul = 1;
return 0;
}
- } else if (c->parent->rate <= rate) {
+ } else if (parent_rate <= rate) {
c->div = 1;
c->mul = 1;
return 0;
@@ -977,19 +982,20 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
unsigned long rate)
{
int divider;
+ unsigned long parent_rate = clk_get_rate(c->parent);
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
- divider = clk_div71_get_divider(c->parent->rate, rate);
+ divider = clk_div71_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate * 2 / (divider + 2);
+ return parent_rate * 2 / (divider + 2);
} else if (c->flags & DIV_U16) {
- divider = clk_div16_get_divider(c->parent->rate, rate);
+ divider = clk_div16_get_divider(parent_rate, rate);
if (divider < 0)
return divider;
- return c->parent->rate / (divider + 1);
+ return parent_rate / (divider + 1);
}
return -EINVAL;
}
@@ -1017,7 +1023,7 @@ static void tegra2_clk_double_init(struct clk *c)
static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate)
{
- if (rate != 2 * c->parent->rate)
+ if (rate != 2 * clk_get_rate(c->parent))
return -EINVAL;
c->mul = 2;
c->div = 1;
@@ -1068,12 +1074,12 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
val |= sel->value;
if (c->refcnt)
- clk_enable_locked(p);
+ clk_enable(p);
clk_writel(val, c->reg);
if (c->refcnt && c->parent)
- clk_disable_locked(c->parent);
+ clk_disable(c->parent);
clk_reparent(c, p);
return 0;
@@ -1134,12 +1140,13 @@ static void tegra_clk_shared_bus_update(struct clk *bus)
struct clk *c;
unsigned long rate = bus->u.shared_bus.min_rate;
- list_for_each_entry(c, &bus->u.shared_bus.list, u.shared_bus_user.node)
+ list_for_each_entry(c, &bus->u.shared_bus.list,
+ u.shared_bus_user.node) {
if (c->u.shared_bus_user.enabled)
rate = max(c->u.shared_bus_user.rate, rate);
+ }
- if (rate != bus->rate)
- clk_set_rate_locked(bus, rate);
+ clk_set_rate(bus, rate);
};
static void tegra_clk_shared_bus_init(struct clk *c)