msm: clock: Move voltage scaling to prepare/unprepare
Move the voltage calls to a sleepable context so we can avoid
spinning for hundreds of microseconds with interrupts disabled
waiting for a regulator to increase. This change also paves the
way to making the clock driver into a real regulator consumer
that uses the proper regulator APIs instead of the MSM specific
rpm_vreg API.
Doing this also requires us to make clk_set_rate() a sleeping
call so that concurrent clk_prepare() and clk_set_rate() calls
are synchronized. Making clk_set_rate() sleepable also
necessitates moving the clk_set_rate() call in clock-voter.c to a
sleepable context. Do that by having clock-voter aggregate and
update the rate in prepare/unprepare instead of enable/disable
(note that enable/disable of voter clocks implicitly calls
enable/disable on the parent via generic clock code).
Now that clk_set_rate() is sleeping, call clk_prepare() and
clk_unprepare() in the clock-local code when a rate change
requires reparenting the clock. This cleans up a problem we have
where sources may be left unprepared by a clock rate switch
(hence the default true warned flag for PLLs and crystals).
Change-Id: I493a00261055001a68fa884daa52e3d281ec6a2d
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
diff --git a/arch/arm/mach-msm/clock-8960.c b/arch/arm/mach-msm/clock-8960.c
index 373cf47..2ec4e38 100644
--- a/arch/arm/mach-msm/clock-8960.c
+++ b/arch/arm/mach-msm/clock-8960.c
@@ -2883,6 +2883,7 @@
};
struct pix_rdi_clk {
+ bool prepared;
bool enabled;
unsigned long cur_rate;
@@ -2908,6 +2909,7 @@
unsigned long flags;
struct pix_rdi_clk *rdi = to_pix_rdi_clk(c);
struct clk **mux_map = pix_rdi_mux_map;
+ unsigned long old_rate = rdi->cur_rate;
/*
* These clocks select three inputs via two muxes. One mux selects
@@ -2918,7 +2920,7 @@
* needs to be on at what time.
*/
for (i = 0; mux_map[i]; i++) {
- ret = clk_enable(mux_map[i]);
+ ret = clk_prepare_enable(mux_map[i]);
if (ret)
goto err;
}
@@ -2927,11 +2929,21 @@
goto err;
}
/* Keep the new source on when switching inputs of an enabled clock */
- if (rdi->enabled) {
- clk_disable(mux_map[rdi->cur_rate]);
- clk_enable(mux_map[rate]);
+ if (rdi->prepared) {
+ ret = clk_prepare(mux_map[rate]);
+ if (ret)
+ goto err;
}
- spin_lock_irqsave(&local_clock_reg_lock, flags);
+ spin_lock_irqsave(&c->lock, flags);
+ if (rdi->enabled) {
+ ret = clk_enable(mux_map[rate]);
+ if (ret) {
+ spin_unlock_irqrestore(&c->lock, flags);
+ clk_unprepare(mux_map[rate]);
+ goto err;
+ }
+ }
+ spin_lock(&local_clock_reg_lock);
reg = readl_relaxed(rdi->s2_reg);
reg &= ~rdi->s2_mask;
reg |= rate == 2 ? rdi->s2_mask : 0;
@@ -2953,10 +2965,16 @@
mb();
udelay(1);
rdi->cur_rate = rate;
- spin_unlock_irqrestore(&local_clock_reg_lock, flags);
+ spin_unlock(&local_clock_reg_lock);
+
+ if (rdi->enabled)
+ clk_disable(mux_map[old_rate]);
+ spin_unlock_irqrestore(&c->lock, flags);
+ if (rdi->prepared)
+ clk_unprepare(mux_map[old_rate]);
err:
for (i--; i >= 0; i--)
- clk_disable(mux_map[i]);
+ clk_disable_unprepare(mux_map[i]);
return 0;
}
@@ -2966,6 +2984,13 @@
return to_pix_rdi_clk(c)->cur_rate;
}
+static int pix_rdi_clk_prepare(struct clk *c)
+{
+ struct pix_rdi_clk *rdi = to_pix_rdi_clk(c);
+ rdi->prepared = true;
+ return 0;
+}
+
static int pix_rdi_clk_enable(struct clk *c)
{
unsigned long flags;
@@ -2990,6 +3015,12 @@
rdi->enabled = false;
}
+static void pix_rdi_clk_unprepare(struct clk *c)
+{
+ struct pix_rdi_clk *rdi = to_pix_rdi_clk(c);
+ rdi->prepared = false;
+}
+
static int pix_rdi_clk_reset(struct clk *c, enum clk_reset_action action)
{
return branch_reset(&to_pix_rdi_clk(c)->b, action);
@@ -3026,8 +3057,10 @@
}
static struct clk_ops clk_ops_pix_rdi_8960 = {
+ .prepare = pix_rdi_clk_prepare,
.enable = pix_rdi_clk_enable,
.disable = pix_rdi_clk_disable,
+ .unprepare = pix_rdi_clk_unprepare,
.handoff = pix_rdi_clk_handoff,
.set_rate = pix_rdi_clk_set_rate,
.get_rate = pix_rdi_clk_get_rate,
diff --git a/arch/arm/mach-msm/clock-local.c b/arch/arm/mach-msm/clock-local.c
index 2df1cd1..b952f2f 100644
--- a/arch/arm/mach-msm/clock-local.c
+++ b/arch/arm/mach-msm/clock-local.c
@@ -320,10 +320,6 @@
u32 reg_val;
void __iomem *const reg = rcg->b.ctl_reg;
- WARN(rcg->current_freq == &rcg_dummy_freq,
- "Attempting to enable %s before setting its rate. "
- "Set the rate first!\n", rcg->c.dbg_name);
-
/*
* Program the NS register, if applicable. NS registers are not
* set in the set_rate path because power can be saved by deferring
@@ -419,6 +415,18 @@
}
}
+static int rcg_clk_prepare(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+
+ WARN(rcg->current_freq == &rcg_dummy_freq,
+ "Attempting to prepare %s before setting its rate. "
+ "Set the rate first!\n", rcg->c.dbg_name);
+ rcg->prepared = true;
+
+ return 0;
+}
+
/* Enable a rate-settable clock. */
static int rcg_clk_enable(struct clk *c)
{
@@ -445,6 +453,12 @@
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
}
+static void rcg_clk_unprepare(struct clk *c)
+{
+ struct rcg_clk *rcg = to_rcg_clk(c);
+ rcg->prepared = false;
+}
+
/*
* Frequency-related functions
*/
@@ -456,6 +470,7 @@
struct clk_freq_tbl *nf, *cf;
struct clk *chld;
int rc = 0;
+ unsigned long flags;
for (nf = rcg->freq_tbl; nf->freq_hz != FREQ_END
&& nf->freq_hz != rate; nf++)
@@ -466,11 +481,22 @@
cf = rcg->current_freq;
- if (rcg->enabled) {
- /* Enable source clock dependency for the new freq. */
- rc = clk_enable(nf->src_clk);
+ /* Enable source clock dependency for the new frequency */
+ if (rcg->prepared) {
+ rc = clk_prepare(nf->src_clk);
if (rc)
return rc;
+
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ if (rcg->enabled) {
+ rc = clk_enable(nf->src_clk);
+ if (rc) {
+ spin_unlock_irqrestore(&c->lock, flags);
+ clk_unprepare(nf->src_clk);
+ return rc;
+ }
}
spin_lock(&local_clock_reg_lock);
@@ -519,6 +545,10 @@
/* Release source requirements of the old freq. */
if (rcg->enabled)
clk_disable(cf->src_clk);
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ if (rcg->prepared)
+ clk_unprepare(cf->src_clk);
return rc;
}
@@ -819,8 +849,10 @@
}
struct clk_ops clk_ops_rcg = {
+ .prepare = rcg_clk_prepare,
.enable = rcg_clk_enable,
.disable = rcg_clk_disable,
+ .unprepare = rcg_clk_unprepare,
.enable_hwcg = rcg_clk_enable_hwcg,
.disable_hwcg = rcg_clk_disable_hwcg,
.in_hwcg_mode = rcg_clk_in_hwcg_mode,
diff --git a/arch/arm/mach-msm/clock-local.h b/arch/arm/mach-msm/clock-local.h
index 034e09c..81085ef 100644
--- a/arch/arm/mach-msm/clock-local.h
+++ b/arch/arm/mach-msm/clock-local.h
@@ -164,6 +164,7 @@
* Generic clock-definition struct and macros
*/
struct rcg_clk {
+ bool prepared;
bool enabled;
void *const ns_reg;
void *const md_reg;
diff --git a/arch/arm/mach-msm/clock-local2.c b/arch/arm/mach-msm/clock-local2.c
index 42b36f6..3f19b2a 100644
--- a/arch/arm/mach-msm/clock-local2.c
+++ b/arch/arm/mach-msm/clock-local2.c
@@ -148,12 +148,12 @@
spin_unlock_irqrestore(&local_clock_reg_lock, flags);
}
-static int rcg_clk_enable(struct clk *c)
+static int rcg_clk_prepare(struct clk *c)
{
struct rcg_clk *rcg = to_rcg_clk(c);
WARN(rcg->current_freq == &rcg_dummy_freq,
- "Attempting to enable %s before setting its rate. "
+ "Attempting to prepare %s before setting its rate. "
"Set the rate first!\n", rcg->c.dbg_name);
return 0;
@@ -163,7 +163,8 @@
{
struct clk_freq_tbl *cf, *nf;
struct rcg_clk *rcg = to_rcg_clk(c);
- int rc = 0;
+ int rc;
+ unsigned long flags;
for (nf = rcg->freq_tbl; nf->freq_hz != FREQ_END
&& nf->freq_hz != rate; nf++)
@@ -174,12 +175,21 @@
cf = rcg->current_freq;
- if (rcg->c.count) {
- /* TODO: Modify to use the prepare API */
- /* Enable source clock dependency for the new freq. */
- rc = clk_enable(nf->src_clk);
+ /* Enable source clock dependency for the new freq. */
+ if (c->prepare_count) {
+ rc = clk_prepare(nf->src_clk);
if (rc)
- goto out;
+ return rc;
+ }
+
+ spin_lock_irqsave(&c->lock, flags);
+ if (c->count) {
+ rc = clk_enable(nf->src_clk);
+ if (rc) {
+ spin_unlock_irqrestore(&c->lock, flags);
+ clk_unprepare(nf->src_clk);
+ return rc;
+ }
}
BUG_ON(!rcg->set_rate);
@@ -188,12 +198,16 @@
rcg->set_rate(rcg, nf);
/* Release source requirements of the old freq. */
- if (rcg->c.count)
+ if (c->count)
clk_disable(cf->src_clk);
+ spin_unlock_irqrestore(&c->lock, flags);
+
+ if (c->prepare_count)
+ clk_unprepare(cf->src_clk);
rcg->current_freq = nf;
-out:
- return rc;
+
+ return 0;
}
/* Return a supported rate that's at least the specified rate. */
@@ -585,7 +599,7 @@
struct clk_ops clk_ops_empty;
struct clk_ops clk_ops_rcg = {
- .enable = rcg_clk_enable,
+ .enable = rcg_clk_prepare,
.set_rate = rcg_clk_set_rate,
.list_rate = rcg_clk_list_rate,
.round_rate = rcg_clk_round_rate,
@@ -594,7 +608,7 @@
};
struct clk_ops clk_ops_rcg_mnd = {
- .enable = rcg_clk_enable,
+ .enable = rcg_clk_prepare,
.set_rate = rcg_clk_set_rate,
.list_rate = rcg_clk_list_rate,
.round_rate = rcg_clk_round_rate,
diff --git a/arch/arm/mach-msm/clock-voter.c b/arch/arm/mach-msm/clock-voter.c
index 4cd9b1c..3e1cbb9 100644
--- a/arch/arm/mach-msm/clock-voter.c
+++ b/arch/arm/mach-msm/clock-voter.c
@@ -12,13 +12,13 @@
*/
#include <linux/err.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/clk.h>
#include "clock.h"
#include "clock-voter.h"
-static DEFINE_SPINLOCK(voter_clk_lock);
+static DEFINE_MUTEX(voter_clk_lock);
/* Aggregate the rate of clocks that are currently on. */
static unsigned long voter_clk_aggregate_rate(const struct clk *parent)
@@ -37,12 +37,11 @@
static int voter_clk_set_rate(struct clk *clk, unsigned long rate)
{
int ret = 0;
- unsigned long flags;
struct clk *clkp;
struct clk_voter *clkh, *v = to_clk_voter(clk);
unsigned long cur_rate, new_rate, other_rate = 0;
- spin_lock_irqsave(&voter_clk_lock, flags);
+ mutex_lock(&voter_clk_lock);
if (v->enabled) {
struct clk *parent = v->parent;
@@ -68,20 +67,19 @@
}
clk->rate = rate;
unlock:
- spin_unlock_irqrestore(&voter_clk_lock, flags);
+ mutex_unlock(&voter_clk_lock);
return ret;
}
-static int voter_clk_enable(struct clk *clk)
+static int voter_clk_prepare(struct clk *clk)
{
int ret = 0;
- unsigned long flags;
unsigned long cur_rate;
struct clk *parent;
struct clk_voter *v = to_clk_voter(clk);
- spin_lock_irqsave(&voter_clk_lock, flags);
+ mutex_lock(&voter_clk_lock);
parent = v->parent;
/*
@@ -96,18 +94,18 @@
}
v->enabled = true;
out:
- spin_unlock_irqrestore(&voter_clk_lock, flags);
+ mutex_unlock(&voter_clk_lock);
return ret;
}
-static void voter_clk_disable(struct clk *clk)
+static void voter_clk_unprepare(struct clk *clk)
{
- unsigned long flags, cur_rate, new_rate;
+ unsigned long cur_rate, new_rate;
struct clk *parent;
struct clk_voter *v = to_clk_voter(clk);
- spin_lock_irqsave(&voter_clk_lock, flags);
+ mutex_lock(&voter_clk_lock);
parent = v->parent;
/*
@@ -121,7 +119,7 @@
if (new_rate < cur_rate)
clk_set_rate(parent, new_rate);
- spin_unlock_irqrestore(&voter_clk_lock, flags);
+ mutex_unlock(&voter_clk_lock);
}
static int voter_clk_is_enabled(struct clk *clk)
@@ -157,8 +155,8 @@
}
struct clk_ops clk_ops_voter = {
- .enable = voter_clk_enable,
- .disable = voter_clk_disable,
+ .prepare = voter_clk_prepare,
+ .unprepare = voter_clk_unprepare,
.set_rate = voter_clk_set_rate,
.is_enabled = voter_clk_is_enabled,
.round_rate = voter_clk_round_rate,
diff --git a/arch/arm/mach-msm/clock.c b/arch/arm/mach-msm/clock.c
index f605c1f..a0367b0 100644
--- a/arch/arm/mach-msm/clock.c
+++ b/arch/arm/mach-msm/clock.c
@@ -156,6 +156,9 @@
if (ret)
goto err_prepare_depends;
+ ret = vote_rate_vdd(clk, clk->rate);
+ if (ret)
+ goto err_vote_vdd;
if (clk->ops->prepare)
ret = clk->ops->prepare(clk);
if (ret)
@@ -166,6 +169,8 @@
mutex_unlock(&clk->prepare_lock);
return ret;
err_prepare_clock:
+ unvote_rate_vdd(clk, clk->rate);
+err_vote_vdd:
clk_unprepare(clk->depends);
err_prepare_depends:
clk_unprepare(parent);
@@ -202,9 +207,6 @@
if (ret)
goto err_enable_depends;
- ret = vote_rate_vdd(clk, clk->rate);
- if (ret)
- goto err_vote_vdd;
trace_clock_enable(clk->dbg_name, 1, smp_processor_id());
if (clk->ops->enable)
ret = clk->ops->enable(clk);
@@ -217,8 +219,6 @@
return 0;
err_enable_clock:
- unvote_rate_vdd(clk, clk->rate);
-err_vote_vdd:
clk_disable(clk->depends);
err_enable_depends:
clk_disable(parent);
@@ -249,7 +249,6 @@
trace_clock_disable(clk->dbg_name, 0, smp_processor_id());
if (clk->ops->disable)
clk->ops->disable(clk);
- unvote_rate_vdd(clk, clk->rate);
clk_disable(clk->depends);
clk_disable(parent);
}
@@ -281,6 +280,7 @@
if (clk->ops->unprepare)
clk->ops->unprepare(clk);
+ unvote_rate_vdd(clk, clk->rate);
clk_unprepare(clk->depends);
clk_unprepare(parent);
}
@@ -316,7 +316,7 @@
int clk_set_rate(struct clk *clk, unsigned long rate)
{
- unsigned long start_rate, flags;
+ unsigned long start_rate;
int rc = 0;
if (IS_ERR_OR_NULL(clk))
@@ -325,14 +325,14 @@
if (!clk->ops->set_rate)
return -ENOSYS;
- spin_lock_irqsave(&clk->lock, flags);
+ mutex_lock(&clk->prepare_lock);
/* Return early if the rate isn't going to change */
if (clk->rate == rate)
goto out;
- trace_clock_set_rate(clk->dbg_name, rate, smp_processor_id());
- if (clk->count) {
+ trace_clock_set_rate(clk->dbg_name, rate, raw_smp_processor_id());
+ if (clk->prepare_count) {
start_rate = clk->rate;
/* Enforce vdd requirements for target frequency. */
rc = vote_rate_vdd(clk, rate);
@@ -350,14 +350,13 @@
if (!rc)
clk->rate = rate;
out:
- spin_unlock_irqrestore(&clk->lock, flags);
+ mutex_unlock(&clk->prepare_lock);
return rc;
err_set_rate:
unvote_rate_vdd(clk, rate);
err_vote_vdd:
- spin_unlock_irqrestore(&clk->lock, flags);
- return rc;
+ goto out;
}
EXPORT_SYMBOL(clk_set_rate);