summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/regulator/core.c132
-rw-r--r--include/linux/regulator/driver.h2
2 files changed, 93 insertions, 41 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index fe314ff56772..0ca941b53571 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -147,6 +147,56 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
}
/**
+ * regulator_lock_nested - lock a single regulator
+ * @rdev: regulator source
+ * @subclass: mutex subclass used for lockdep
+ *
+ * This function can be called many times by one task on
+ * a single regulator and its mutex will be locked only
+ * once. If a task, which is calling this function is other
+ * than the one, which initially locked the mutex, it will
+ * wait on mutex.
+ */
+static void regulator_lock_nested(struct regulator_dev *rdev,
+ unsigned int subclass)
+{
+ if (!mutex_trylock(&rdev->mutex)) {
+ if (rdev->mutex_owner == current) {
+ rdev->ref_cnt++;
+ return;
+ }
+ mutex_lock_nested(&rdev->mutex, subclass);
+ }
+
+ rdev->ref_cnt = 1;
+ rdev->mutex_owner = current;
+}
+
+static inline void regulator_lock(struct regulator_dev *rdev)
+{
+ regulator_lock_nested(rdev, 0);
+}
+
+/**
+ * regulator_unlock - unlock a single regulator
+ * @rdev: regulator_source
+ *
+ * This function unlocks the mutex when the
+ * reference counter reaches 0.
+ */
+static void regulator_unlock(struct regulator_dev *rdev)
+{
+ if (rdev->ref_cnt != 0) {
+ rdev->ref_cnt--;
+
+ if (!rdev->ref_cnt) {
+ rdev->mutex_owner = NULL;
+ mutex_unlock(&rdev->mutex);
+ }
+ }
+}
+
+/**
* regulator_lock_supply - lock a regulator and its supplies
* @rdev: regulator source
*/
@@ -155,7 +205,7 @@ static void regulator_lock_supply(struct regulator_dev *rdev)
int i;
for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
- mutex_lock_nested(&rdev->mutex, i);
+ regulator_lock_nested(rdev, i);
}
/**
@@ -167,7 +217,7 @@ static void regulator_unlock_supply(struct regulator_dev *rdev)
struct regulator *supply;
while (1) {
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
supply = rdev->supply;
if (!rdev->supply)
@@ -350,9 +400,9 @@ static ssize_t regulator_uV_show(struct device *dev,
struct regulator_dev *rdev = dev_get_drvdata(dev);
ssize_t ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev));
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -416,9 +466,9 @@ static ssize_t regulator_state_show(struct device *dev,
struct regulator_dev *rdev = dev_get_drvdata(dev);
ssize_t ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
ret = regulator_print_state(buf, _regulator_is_enabled(rdev));
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -526,10 +576,10 @@ static ssize_t regulator_total_uA_show(struct device *dev,
struct regulator *regulator;
int uA = 0;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
list_for_each_entry(regulator, &rdev->consumer_list, list)
uA += regulator->uA_load;
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return sprintf(buf, "%d\n", uA);
}
static DEVICE_ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL);
@@ -1333,7 +1383,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
if (regulator == NULL)
return NULL;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
regulator->rdev = rdev;
list_add(&regulator->list, &rdev->consumer_list);
@@ -1388,12 +1438,12 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
_regulator_is_enabled(rdev))
regulator->always_on = true;
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return regulator;
overflow_err:
list_del(&regulator->list);
kfree(regulator);
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return NULL;
}
@@ -1782,13 +1832,13 @@ static void _regulator_put(struct regulator *regulator)
/* remove any sysfs entries */
if (regulator->dev)
sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
list_del(&regulator->list);
rdev->open_count--;
rdev->exclusive = 0;
put_device(&rdev->dev);
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
kfree_const(regulator->supply_name);
kfree(regulator);
@@ -2396,7 +2446,7 @@ static void regulator_disable_work(struct work_struct *work)
disable_work.work);
int count, i, ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
BUG_ON(!rdev->deferred_disables);
@@ -2417,7 +2467,7 @@ static void regulator_disable_work(struct work_struct *work)
rdev_err(rdev, "Deferred disable failed: %d\n", ret);
}
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
if (rdev->supply) {
for (i = 0; i < count; i++) {
@@ -2452,11 +2502,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
if (!ms)
return regulator_disable(regulator);
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
rdev->deferred_disables++;
mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
msecs_to_jiffies(ms));
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return 0;
}
@@ -2488,10 +2538,10 @@ static int _regulator_list_voltage(struct regulator_dev *rdev,
if (selector >= rdev->desc->n_voltages)
return -EINVAL;
if (lock)
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
ret = ops->list_voltage(rdev, selector);
if (lock)
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
} else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply->rdev,
selector, lock);
@@ -3264,7 +3314,7 @@ int regulator_sync_voltage(struct regulator *regulator)
struct regulator_voltage *voltage = &regulator->voltage[PM_SUSPEND_ON];
int ret, min_uV, max_uV;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
if (!rdev->desc->ops->set_voltage &&
!rdev->desc->ops->set_voltage_sel) {
@@ -3293,7 +3343,7 @@ int regulator_sync_voltage(struct regulator *regulator)
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_sync_voltage);
@@ -3386,7 +3436,7 @@ int regulator_set_current_limit(struct regulator *regulator,
struct regulator_dev *rdev = regulator->rdev;
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
/* sanity check */
if (!rdev->desc->ops->set_current_limit) {
@@ -3401,7 +3451,7 @@ int regulator_set_current_limit(struct regulator *regulator,
ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_current_limit);
@@ -3410,7 +3460,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)
{
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
/* sanity check */
if (!rdev->desc->ops->get_current_limit) {
@@ -3420,7 +3470,7 @@ static int _regulator_get_current_limit(struct regulator_dev *rdev)
ret = rdev->desc->ops->get_current_limit(rdev);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -3456,7 +3506,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
int ret;
int regulator_curr_mode;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
/* sanity check */
if (!rdev->desc->ops->set_mode) {
@@ -3480,7 +3530,7 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
ret = rdev->desc->ops->set_mode(rdev, mode);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
EXPORT_SYMBOL_GPL(regulator_set_mode);
@@ -3489,7 +3539,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)
{
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
/* sanity check */
if (!rdev->desc->ops->get_mode) {
@@ -3499,7 +3549,7 @@ static unsigned int _regulator_get_mode(struct regulator_dev *rdev)
ret = rdev->desc->ops->get_mode(rdev);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -3520,7 +3570,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,
{
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
/* sanity check */
if (!rdev->desc->ops->get_error_flags) {
@@ -3530,7 +3580,7 @@ static int _regulator_get_error_flags(struct regulator_dev *rdev,
ret = rdev->desc->ops->get_error_flags(rdev, flags);
out:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -3579,10 +3629,10 @@ int regulator_set_load(struct regulator *regulator, int uA_load)
struct regulator_dev *rdev = regulator->rdev;
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
regulator->uA_load = uA_load;
ret = drms_uA_update(rdev);
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -3610,7 +3660,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS))
return 0;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
if (enable && !regulator->bypass) {
rdev->bypass_count++;
@@ -3634,7 +3684,7 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
if (ret == 0)
regulator->bypass = enable;
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -4300,9 +4350,9 @@ static int _regulator_suspend_late(struct device *dev, void *data)
suspend_state_t *state = data;
int ret;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
ret = suspend_set_state(rdev, *state);
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -4332,14 +4382,14 @@ static int _regulator_resume_early(struct device *dev, void *data)
if (rstate == NULL)
return 0;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
if (rdev->desc->ops->resume_early &&
(rstate->enabled == ENABLE_IN_SUSPEND ||
rstate->enabled == DISABLE_IN_SUSPEND))
ret = rdev->desc->ops->resume_early(rdev);
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return ret;
}
@@ -4641,7 +4691,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)
if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS))
return 0;
- mutex_lock(&rdev->mutex);
+ regulator_lock(rdev);
if (rdev->use_count)
goto unlock;
@@ -4672,7 +4722,7 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)
}
unlock:
- mutex_unlock(&rdev->mutex);
+ regulator_unlock(rdev);
return 0;
}
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 14e512ad6d4f..c2a181fa7287 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -434,6 +434,8 @@ struct regulator_dev {
struct blocking_notifier_head notifier;
struct mutex mutex; /* consumer lock */
+ struct task_struct *mutex_owner;
+ int ref_cnt;
struct module *owner;
struct device dev;
struct regulation_constraints *constraints;