diff options
author | Scott Williams <scwilliams@nvidia.com> | 2011-08-05 18:16:48 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:47:03 -0800 |
commit | 4b848adaedc9e0c335b992fefefc16545a0ca7b1 (patch) | |
tree | 5cb52fb00add02a4fa666da3555c4e47ab765602 /arch/arm/mach-tegra/tegra2_clocks.c | |
parent | 4182b75abce8204b13a783dd2f14d4aa63e58aaf (diff) |
ARM: tegra: Fix mutex in atomic context when updating TWD freq
The CPU frequency change notifer runs in an atomic context but
obtaining the current CPU frequency requires taking a mutex because
updating the CPU frequency involves the regulator. Instead of
directly parenting the TWD clock on the CPU clock, make the TWD
a "detached child" of the CPU clock whose rate is updated whenever
the CPU frequency changes.
Change-Id: I49e15f85f269fb3ed0bcaee36ff739b4f064d6b8
Signed-off-by: Scott Williams <scwilliams@nvidia.com>
Rebase-Id: R7aa10f2576752390464586bc629c972802beb989
Diffstat (limited to 'arch/arm/mach-tegra/tegra2_clocks.c')
-rw-r--r-- | arch/arm/mach-tegra/tegra2_clocks.c | 40 |
1 files changed, 30 insertions, 10 deletions
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c index 4a511987c8a3..3c5561e02e11 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -384,6 +384,29 @@ static struct clk_ops tegra_super_ops = { .set_rate = tegra2_super_clk_set_rate, }; +static int tegra2_twd_clk_set_rate(struct clk *c, unsigned long rate) +{ + /* The input value 'rate' is the clock rate of the CPU complex. */ + c->rate = (rate * c->mul) / c->div; + return 0; +} + +static struct clk_ops tegra2_twd_ops = { + .set_rate = tegra2_twd_clk_set_rate, +}; + +static struct clk tegra2_clk_twd = { + /* NOTE: The twd clock must have *NO* parent. It's rate is directly + updated by tegra3_cpu_cmplx_clk_set_rate() because the + frequency change notifer for the twd is called in an + atomic context which cannot take a mutex. */ + .name = "twd", + .ops = &tegra2_twd_ops, + .max_rate = 1000000000, /* Same as tegra_clk_virtual_cpu.max_rate */ + .mul = 1, + .div = 4, +}; + /* virtual cpu clock functions */ /* some clocks can not be stopped (cpu, memory bus) while the SoC is running. To change the frequency of these clocks, the parent pll may need to be @@ -438,6 +461,12 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate) goto out; } + /* We can't parent the twd to directly to the CPU complex because + the TWD frequency update notifier is called in an atomic context + and the CPU frequency update requires a mutex. Update the twd + clock rate with the new CPU complex rate. */ + clk_set_rate(&tegra2_clk_twd, clk_get_rate_locked(c)); + out: clk_disable(c->u.cpu.main); return ret; @@ -2069,15 +2098,6 @@ static struct clk tegra_clk_virtual_cpu = { }, }; -static struct clk tegra_clk_twd = { - .name = "twd", - .parent = &tegra_clk_cclk, - .ops = NULL, - .max_rate = 250000000, - .mul = 1, - .div = 4, -}; - static struct clk tegra_clk_cop = { .name = "cop", .parent = &tegra_clk_sclk, @@ -2452,7 +2472,7 @@ struct clk *tegra_ptr_clks[] = { &tegra_clk_blink, &tegra_clk_cop, &tegra_clk_emc, - &tegra_clk_twd, + &tegra2_clk_twd, }; /* For some clocks maximum rate limits depend on tegra2 SKU */ |