[PATCH] regulator: devres: introduce managed enable and disable operations

From: Dmitry Torokhov
Date: Sun Feb 12 2017 - 19:49:19 EST


While preferred time to power up the device is when there are users of it
present (i.e. when device is "open"ed), there are times when it makes more
sense to power up the device in probe(). One such example is when device is
expected to be always used (such as a touchscreen on a mobile device, or
similar) and powering it in probe() simplifies driver code, when we need to
power up device to inspect it before completing probe, or when
re-initializing device is too slow/costly.

This patch introduces managed versions of regulator_enable() and
regulator_bulk_enable() so that such drivers do not mix managed and regular
resources, which is usually error-prone.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@xxxxxxxxx>
---

Mark, note that there is also patch introducing devm_clk_prepare() and
devm_clk_prepare_enable() that Russell did not hate so I think it will
get applied eventually. I believe lack of CLK methods was cited as a
reason for not having managed enable for regulators.

drivers/regulator/devres.c | 138 +++++++++++++++++++++++++++++++++++++
include/linux/regulator/consumer.h | 32 ++++++++-
2 files changed, 169 insertions(+), 1 deletion(-)

diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 784e3bf32210..ada2081bc24e 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -120,6 +120,62 @@ void devm_regulator_put(struct regulator *regulator)
}
EXPORT_SYMBOL_GPL(devm_regulator_put);

+static void __devm_regulator_disable(struct device *dev, void *res)
+{
+ regulator_disable(*(struct regulator **)res);
+}
+
+/**
+ * devm_regulator_enable - Resource managed regulator_enable()
+ * @regulator: regulator to enable
+ *
+ * Managed regulator_enable(). Regulators enabled by this function are
+ * automatically disabled on driver detach. See regulator_enable() for more
+ * information.
+ */
+int devm_regulator_enable(struct regulator *regulator)
+{
+ struct regulator **ptr;
+ int error;
+
+ ptr = devres_alloc(__devm_regulator_disable, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ error = regulator_enable(regulator);
+ if (error) {
+ devres_free(ptr);
+ return error;
+ }
+
+ *ptr = regulator;
+ devres_add(regulator->dev, ptr);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_enable);
+
+/**
+ * devm_regulator_disable - Resource managed regulator_disable()
+ * @regulator: regulator to disable
+ *
+ * Disable a regulator enabled with devm_regulator_enable(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the regulator is disabled.
+ */
+int devm_regulator_disable(struct regulator *regulator)
+{
+ int error;
+
+ error = devres_destroy(regulator->dev, __devm_regulator_disable,
+ devm_regulator_match, regulator);
+ if (WARN_ON(error))
+ return error;
+
+ return regulator_disable(regulator);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_disable);
+
struct regulator_bulk_devres {
struct regulator_bulk_data *consumers;
int num_consumers;
@@ -171,6 +227,88 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers,
}
EXPORT_SYMBOL_GPL(devm_regulator_bulk_get);

+static void __devm_regulator_bulk_disable(struct device *dev, void *res)
+{
+ struct regulator_bulk_devres *devres = res;
+
+ regulator_bulk_disable(devres->num_consumers, devres->consumers);
+}
+
+static int devm_regulator_bulk_match(struct device *dev, void *res, void *data)
+{
+ struct regulator_bulk_devres *r1 = res;
+ struct regulator_bulk_devres *r2 = data;
+
+ if (WARN_ON(!r1 || !r1->consumers || !r1->num_consumers))
+ return 0;
+
+ return r1->consumers == r2->consumers &&
+ r1->num_consumers == r2->num_consumers;
+}
+
+/**
+ * devm_regulator_bulk_enable - Resource managed regulator_bulk_enable()
+ * @dev: device owning this resource
+ * @num_consumers: number of consumers in @consumers array
+ * @consumers: consumers that need be enabled
+ *
+ * Managed regulator_bulk_enable(). Regulators enabled by this function are
+ * automatically disabled on driver detach. See regulator_bulk_enable() for
+ * more information.
+ */
+int devm_regulator_bulk_enable(struct device *dev, int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ struct regulator_bulk_devres *devres;
+ int error;
+
+ devres = devres_alloc(__devm_regulator_bulk_disable,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ error = regulator_bulk_enable(num_consumers, consumers);
+ if (error) {
+ devres_free(devres);
+ return error;
+ }
+
+ devres->consumers = consumers;
+ devres->num_consumers = num_consumers;
+ devres_add(dev, devres);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_enable);
+
+/**
+ * devm_regulator_bulk_disable - Resource managed regulator_bulk_disable()
+ * @dev: device owning this resource
+ * @num_consumers: number of consumers in @consumers array
+ * @consumers: consumers that were enabled with devm_regulator_bulk_enable()
+ *
+ * Disable regulators enabled with devm_regulator_bulk_enable(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the regulator is disabled.
+ */
+int devm_regulator_bulk_disable(struct device *dev, int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ struct regulator_bulk_devres devres = {
+ .consumers = consumers,
+ .num_consumers = num_consumers,
+ };
+ int error;
+
+ error = devres_destroy(dev, __devm_regulator_disable,
+ devm_regulator_bulk_match, &devres);
+ if (WARN_ON(error))
+ return error;
+
+ return regulator_bulk_disable(num_consumers, consumers);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_disable);
+
static void devm_rdev_release(struct device *dev, void *res)
{
regulator_unregister(*(struct regulator_dev **)res);
diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h
index acaeeec279af..553af32e93ba 100644
--- a/include/linux/regulator/consumer.h
+++ b/include/linux/regulator/consumer.h
@@ -225,7 +225,9 @@ void devm_regulator_bulk_unregister_supply_alias(struct device *dev,

/* regulator output control and status */
int __must_check regulator_enable(struct regulator *regulator);
+int __must_check devm_regulator_enable(struct regulator *regulator);
int regulator_disable(struct regulator *regulator);
+int devm_regulator_disable(struct regulator *regulator);
int regulator_force_disable(struct regulator *regulator);
int regulator_is_enabled(struct regulator *regulator);
int regulator_disable_deferred(struct regulator *regulator, int ms);
@@ -236,8 +238,13 @@ int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers);
int __must_check regulator_bulk_enable(int num_consumers,
struct regulator_bulk_data *consumers);
+int __must_check devm_regulator_bulk_enable(struct device *dev,
+ int num_consumers,
+ struct regulator_bulk_data *consumers);
int regulator_bulk_disable(int num_consumers,
struct regulator_bulk_data *consumers);
+int devm_regulator_bulk_disable(struct device *dev, int num_consumers,
+ struct regulator_bulk_data *consumers);
int regulator_bulk_force_disable(int num_consumers,
struct regulator_bulk_data *consumers);
void regulator_bulk_free(int num_consumers,
@@ -399,7 +406,15 @@ static inline int regulator_enable(struct regulator *regulator)
return 0;
}

-static inline int regulator_disable(struct regulator *regulator)
+static inline int devm_regulator_enable(struct device *dev,
+ struct regulator *regulator)
+{
+ return 0;
+}
+
+
+static inline int devm_regulator_disable(struct device *dev,
+ struct regulator *regulator)
{
return 0;
}
@@ -439,12 +454,27 @@ static inline int regulator_bulk_enable(int num_consumers,
return 0;
}

+static inline int devm_regulator_bulk_enable(struct device *dev,
+ int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
+
static inline int regulator_bulk_disable(int num_consumers,
struct regulator_bulk_data *consumers)
{
return 0;
}

+static inline int devm_regulator_bulk_disable(struct device *dev,
+ int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return 0;
+}
+
static inline int regulator_bulk_force_disable(int num_consumers,
struct regulator_bulk_data *consumers)
{
--
2.11.0.483.g087da7b7c-goog


--
Dmitry