msm: rpm-regulator: Replace spinlocks with mutex locks
All calls of rpm_vreg_set_voltage() now take place in process
context. Clock driver code on all targets has been written so
that atomic regulator control is no longer needed.
Refactor the rpm-regulator driver so that a mutex locks are used
in place of a spinlocks for sleep-selectable regulators which are
controlled via the rpm_vreg_set_voltage() function.
At the same time, move clk_prepare() and clk_unprepare calls in
the TCXO workaround so that these functions are correctly called
during each workaround invocation. This is needed so that TCXO
isn't kept on forever after a single clk_prepare() call.
Change-Id: I07caed041dac6da073a0644271df2227b6c5a96e
Signed-off-by: David Collins <collinsd@codeaurora.org>
diff --git a/arch/arm/mach-msm/rpm-regulator.c b/arch/arm/mach-msm/rpm-regulator.c
index 01543a2..4e5281d 100644
--- a/arch/arm/mach-msm/rpm-regulator.c
+++ b/arch/arm/mach-msm/rpm-regulator.c
@@ -19,7 +19,6 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
-#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/wakelock.h>
@@ -299,12 +298,9 @@
}
static bool requires_tcxo_workaround;
-static bool tcxo_workaround_noirq;
static struct clk *tcxo_handle;
static struct wake_lock tcxo_wake_lock;
static DEFINE_MUTEX(tcxo_mutex);
-/* Spin lock needed for sleep-selectable regulators. */
-static DEFINE_SPINLOCK(tcxo_noirq_lock);
static bool tcxo_is_enabled;
/*
* TCXO must be kept on for at least the duration of its warmup (4 ms);
@@ -314,19 +310,10 @@
static void tcxo_get_handle(void)
{
- int rc;
-
if (!tcxo_handle) {
tcxo_handle = clk_get_sys("rpm-regulator", "vref_buff");
- if (IS_ERR(tcxo_handle)) {
+ if (IS_ERR(tcxo_handle))
tcxo_handle = NULL;
- } else {
- rc = clk_prepare(tcxo_handle);
- if (rc) {
- clk_put(tcxo_handle);
- tcxo_handle = NULL;
- }
- }
}
}
@@ -342,7 +329,7 @@
int rc;
if (tcxo_handle && !tcxo_is_enabled) {
- rc = clk_enable(tcxo_handle);
+ rc = clk_prepare_enable(tcxo_handle);
if (!rc) {
tcxo_is_enabled = true;
wake_lock(&tcxo_wake_lock);
@@ -355,21 +342,13 @@
static void tcxo_delayed_disable_work(struct work_struct *work)
{
- unsigned long flags = 0;
+ mutex_lock(&tcxo_mutex);
- if (tcxo_workaround_noirq)
- spin_lock_irqsave(&tcxo_noirq_lock, flags);
- else
- mutex_lock(&tcxo_mutex);
-
- clk_disable(tcxo_handle);
+ clk_disable_unprepare(tcxo_handle);
tcxo_is_enabled = false;
wake_unlock(&tcxo_wake_lock);
- if (tcxo_workaround_noirq)
- spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
- else
- mutex_unlock(&tcxo_mutex);
+ mutex_unlock(&tcxo_mutex);
}
static DECLARE_DELAYED_WORK(tcxo_disable_work, tcxo_delayed_disable_work);
@@ -387,8 +366,8 @@
msecs_to_jiffies(TCXO_WARMUP_TIME_MS) + 1);
}
-/* Spin lock needed for sleep-selectable regulators. */
-static DEFINE_SPINLOCK(rpm_noirq_lock);
+/* Mutex lock needed for sleep-selectable regulators. */
+static DEFINE_MUTEX(rpm_sleep_sel_lock);
static int voltage_from_req(struct vreg *vreg)
{
@@ -421,7 +400,6 @@
{
struct msm_rpm_iv_pair *prev_req;
int rc = 0, max_uV_vote = 0;
- unsigned long flags = 0;
bool tcxo_enabled = false;
bool voltage_increased = false;
unsigned prev0, prev1;
@@ -470,17 +448,19 @@
if (requires_tcxo_workaround && vreg->requires_cxo
&& (set == MSM_RPM_CTX_SET_0)
&& (GET_PART(vreg, uV) > GET_PART_PREV_ACT(vreg, uV))) {
+ mutex_lock(&tcxo_mutex);
+ if (!tcxo_handle)
+ tcxo_get_handle();
voltage_increased = true;
- spin_lock_irqsave(&tcxo_noirq_lock, flags);
tcxo_enabled = tcxo_enable();
}
- rc = msm_rpmrs_set_noirq(set, vreg->req, cnt);
+ rc = msm_rpmrs_set(set, vreg->req, cnt);
if (rc) {
vreg->req[0].value = prev0;
vreg->req[1].value = prev1;
- vreg_err(vreg, "msm_rpmrs_set_noirq failed - "
+ vreg_err(vreg, "msm_rpmrs_set failed - "
"set=%s, id=%d, rc=%d\n",
(set == MSM_RPM_CTX_SET_0 ? "active" : "sleep"),
vreg->req[0].id, rc);
@@ -502,7 +482,7 @@
if (voltage_increased) {
if (tcxo_enabled)
tcxo_delayed_disable();
- spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
+ mutex_unlock(&tcxo_mutex);
}
} else if (msm_rpm_vreg_debug_mask & MSM_RPM_VREG_DEBUG_DUPLICATE) {
rpm_regulator_duplicate(vreg, set, cnt);
@@ -511,19 +491,18 @@
return rc;
}
-static int vreg_set_noirq(struct vreg *vreg, enum rpm_vreg_voter voter,
+static int vreg_set_sleep_sel(struct vreg *vreg, enum rpm_vreg_voter voter,
int sleep, unsigned mask0, unsigned val0,
unsigned mask1, unsigned val1, unsigned cnt,
int update_voltage)
{
unsigned int s_mask[2] = {mask0, mask1}, s_val[2] = {val0, val1};
- unsigned long flags;
int rc;
if (voter < 0 || voter >= RPM_VREG_VOTER_COUNT)
return -EINVAL;
- spin_lock_irqsave(&rpm_noirq_lock, flags);
+ mutex_lock(&rpm_sleep_sel_lock);
/*
* Send sleep set request first so that subsequent set_mode, etc calls
@@ -559,7 +538,7 @@
rc = vreg_send_request(vreg, voter, MSM_RPM_CTX_SET_0, mask0, val0,
mask1, val1, cnt, update_voltage);
- spin_unlock_irqrestore(&rpm_noirq_lock, flags);
+ mutex_unlock(&rpm_sleep_sel_lock);
return rc;
}
@@ -575,10 +554,8 @@
* Returns 0 on success or errno.
*
* This function is used to vote for the voltage of a regulator without
- * using the regulator framework. It is needed by consumers which hold spin
- * locks or have interrupts disabled because the regulator framework can sleep.
- * It is also needed by consumers which wish to only vote for active set
- * regulator voltage.
+ * using the regulator framework. It is needed for consumers which wish to only
+ * vote for active set regulator voltage.
*
* If sleep_also == 0, then a sleep-set value of 0V will be voted for.
*
@@ -693,10 +670,10 @@
= vreg->part->enable_state.mask;
}
- rc = vreg_set_noirq(vreg, voter, sleep_also, mask[0], val[0], mask[1],
- val[1], vreg->part->request_len, 1);
+ rc = vreg_set_sleep_sel(vreg, voter, sleep_also, mask[0], val[0],
+ mask[1], val[1], vreg->part->request_len, 1);
if (rc)
- vreg_err(vreg, "vreg_set_noirq failed, rc=%d\n", rc);
+ vreg_err(vreg, "vreg_set_sleep_sel failed, rc=%d\n", rc);
return rc;
}
@@ -743,10 +720,10 @@
val[vreg->part->freq.word] = freq << vreg->part->freq.shift;
mask[vreg->part->freq.word] = vreg->part->freq.mask;
- rc = vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0],
+ rc = vreg_set_sleep_sel(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1, mask[0],
val[0], mask[1], val[1], vreg->part->request_len, 0);
if (rc)
- vreg_err(vreg, "vreg_set failed, rc=%d\n", rc);
+ vreg_err(vreg, "vreg_set_sleep_sel failed, rc=%d\n", rc);
return rc;
}
@@ -1018,10 +995,8 @@
static int vreg_store(struct vreg *vreg, unsigned mask0, unsigned val0,
unsigned mask1, unsigned val1)
{
- unsigned long flags = 0;
-
if (vreg->pdata.sleep_selectable)
- spin_lock_irqsave(&rpm_noirq_lock, flags);
+ mutex_lock(&rpm_sleep_sel_lock);
vreg->req[0].value &= ~mask0;
vreg->req[0].value |= val0 & mask0;
@@ -1030,7 +1005,7 @@
vreg->req[1].value |= val1 & mask1;
if (vreg->pdata.sleep_selectable)
- spin_unlock_irqrestore(&rpm_noirq_lock, flags);
+ mutex_unlock(&rpm_sleep_sel_lock);
return 0;
}
@@ -1039,7 +1014,6 @@
unsigned mask1, unsigned val1, unsigned cnt)
{
unsigned prev0 = 0, prev1 = 0;
- unsigned long flags = 0;
bool tcxo_enabled = false;
bool voltage_increased = false;
int rc;
@@ -1049,7 +1023,7 @@
* just the active set values.
*/
if (vreg->pdata.sleep_selectable)
- return vreg_set_noirq(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1,
+ return vreg_set_sleep_sel(vreg, RPM_VREG_VOTER_REG_FRAMEWORK, 1,
mask0, val0, mask1, val1, cnt, 1);
prev0 = vreg->req[0].value;
@@ -1071,21 +1045,14 @@
/* Enable CXO clock if necessary for TCXO workaround. */
if (requires_tcxo_workaround && vreg->requires_cxo
&& (GET_PART(vreg, uV) > GET_PART_PREV_ACT(vreg, uV))) {
+ mutex_lock(&tcxo_mutex);
if (!tcxo_handle)
tcxo_get_handle();
- if (tcxo_workaround_noirq)
- spin_lock_irqsave(&tcxo_noirq_lock, flags);
- else
- mutex_lock(&tcxo_mutex);
-
voltage_increased = true;
tcxo_enabled = tcxo_enable();
}
- if (voltage_increased && tcxo_workaround_noirq)
- rc = msm_rpmrs_set_noirq(MSM_RPM_CTX_SET_0, vreg->req, cnt);
- else
- rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt);
+ rc = msm_rpm_set(MSM_RPM_CTX_SET_0, vreg->req, cnt);
if (rc) {
vreg->req[0].value = prev0;
@@ -1107,11 +1074,7 @@
if (voltage_increased) {
if (tcxo_enabled)
tcxo_delayed_disable();
-
- if (tcxo_workaround_noirq)
- spin_unlock_irqrestore(&tcxo_noirq_lock, flags);
- else
- mutex_unlock(&tcxo_mutex);
+ mutex_unlock(&tcxo_mutex);
}
return rc;
@@ -1794,7 +1757,6 @@
struct rpm_regulator_platform_data *platform_data;
static struct rpm_regulator_consumer_mapping *prev_consumer_map;
static int prev_consumer_map_len;
- struct vreg *vreg;
int rc = 0;
int i, id;
@@ -1880,18 +1842,6 @@
"rpm_regulator_tcxo");
}
- if (requires_tcxo_workaround && !tcxo_workaround_noirq) {
- for (i = 0; i < platform_data->num_regulators; i++) {
- vreg = rpm_vreg_get_vreg(
- platform_data->init_data[i].id);
- if (vreg && vreg->requires_cxo
- && platform_data->init_data[i].sleep_selectable) {
- tcxo_workaround_noirq = true;
- break;
- }
- }
- }
-
/* Initialize all of the regulators listed in the platform data. */
for (i = 0; i < platform_data->num_regulators; i++) {
rc = rpm_vreg_init_regulator(&platform_data->init_data[i],