In some cases the regulators may be organized in a chain like:
-regA-regB-regC
where regA is supplier for regB and regB is supplier for regC.
Currently it would be possible to reconfigure regA and regC at same time
form different contexts, because each regulator has it own mutex.
But in some cases, the only the whole chain is allowed be reconfigured
because of dependencies between regulators - to change regB
configuration the regA need to be updated first.
Hence, introduce regulator chain locking scheme to lock whole Regulator
chain in case if any part of it has been accessed from outside.
To achieve this goal:
- abstract regulator locking out into helper functions;
- use the root Regulator (which has no supply defined, like regA) in chain to
protect the whole chain;
- implement regulator chain locking scheme as proposed by Thomas Gleixner for
CCF
re-entrance in https://lkml.org/lkml/2013/3/27/171 and in the similar way as
it is done for CCF by Mike Turquette in https://lkml.org/lkml/2013/3/28/512
In addition, such locking scheme allows to have access to the supplier
regulator API from inside child's (consumer) regulator API.
Cc: linux-ker...@vger.kernel.org
Cc: Mike Turquette mturque...@linaro.org
Cc: Nishanth Menon n...@ti.com
Cc: Tero Kristo t-kri...@ti.com
Cc: linux-omap linux-omap@vger.kernel.org
Cc: linux-arm linux-arm-ker...@lists.infradead.org
Signed-off-by: Grygorii Strashko grygorii.stras...@ti.com
---
drivers/regulator/core.c | 134 --
include/linux/regulator/driver.h |2 +
2 files changed, 88 insertions(+), 48 deletions(-)
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e3661c2..089cc63 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -110,6 +110,44 @@ static const char *rdev_get_name(struct regulator_dev
*rdev)
return ;
}
+static void regulator_lock(struct regulator_dev *rdev)
+{
+ struct regulator_dev *locking_rdev = rdev;
+
+ while (locking_rdev-supply)
+ locking_rdev = locking_rdev-supply-rdev;
+
+ if (!mutex_trylock(locking_rdev-mutex)) {
+ if (locking_rdev-lock_owner == current) {
+ locking_rdev-lock_count++;
+ return;
+ }
+ mutex_lock(locking_rdev-mutex);
+ }
+
+ WARN_ON_ONCE(locking_rdev-lock_owner != NULL);
+ WARN_ON_ONCE(locking_rdev-lock_count != 0);
+
+ locking_rdev-lock_count = 1;
+ locking_rdev-lock_owner = current;
+ dev_dbg(locking_rdev-dev, Is locked. locking %s\n,
+ rdev_get_name(rdev));
+}
+
+static void regulator_unlock(struct regulator_dev *rdev)
+{
+ struct regulator_dev *locking_rdev = rdev;
+
+ while (locking_rdev-supply)
+ locking_rdev = locking_rdev-supply-rdev;
+
+ if (--locking_rdev-lock_count)
+ return;
+
+ locking_rdev-lock_owner = NULL;
+ mutex_unlock(locking_rdev-mutex);
+}
+
/**
* of_get_regulator - get a regulator device node based on supply name
* @dev: Device pointer for the consumer (of regulator) device
@@ -292,9 +330,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;
}
@@ -357,9 +395,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;
}
@@ -467,10 +505,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);
@@ -1104,7 +1142,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);
@@ -1156,12 +1194,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;