OSDN Git Service

regulator: Add devm helpers for get and enable
authorMatti Vaittinen <mazziesaccount@gmail.com>
Fri, 12 Aug 2022 10:10:37 +0000 (13:10 +0300)
committerMark Brown <broonie@kernel.org>
Thu, 18 Aug 2022 11:51:48 +0000 (12:51 +0100)
A few regulator consumer drivers seem to be just getting a regulator,
enabling it and registering a devm-action to disable the regulator at
the driver detach and then forget about it.

We can simplify this a bit by adding a devm-helper for this pattern.
Add devm_regulator_get_enable() and devm_regulator_get_enable_optional()

Signed-off-by: Matti Vaittinen <mazziesaccount@gmail.com>
Link: https://lore.kernel.org/r/ed7b8841193bb9749d426f3cb3b199c9460794cd.1660292316.git.mazziesaccount@gmail.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/devres.c
include/linux/regulator/consumer.h

index 32823a8..3265e75 100644 (file)
@@ -70,6 +70,65 @@ struct regulator *devm_regulator_get_exclusive(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive);
 
+static void regulator_action_disable(void *d)
+{
+       struct regulator *r = (struct regulator *)d;
+
+       regulator_disable(r);
+}
+
+static int _devm_regulator_get_enable(struct device *dev, const char *id,
+                                     int get_type)
+{
+       struct regulator *r;
+       int ret;
+
+       r = _devm_regulator_get(dev, id, get_type);
+       if (IS_ERR(r))
+               return PTR_ERR(r);
+
+       ret = regulator_enable(r);
+       if (!ret)
+               ret = devm_add_action_or_reset(dev, &regulator_action_disable, r);
+
+       if (ret)
+               devm_regulator_put(r);
+
+       return ret;
+}
+
+/**
+ * devm_regulator_get_enable_optional - Resource managed regulator get and enable
+ * @dev: device to supply
+ * @id:  supply name or regulator ID.
+ *
+ * Get and enable regulator for duration of the device life-time.
+ * regulator_disable() and regulator_put() are automatically called on driver
+ * detach. See regulator_get_optional() and regulator_enable() for more
+ * information.
+ */
+int devm_regulator_get_enable_optional(struct device *dev, const char *id)
+{
+       return _devm_regulator_get_enable(dev, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_enable_optional);
+
+/**
+ * devm_regulator_get_enable - Resource managed regulator get and enable
+ * @dev: device to supply
+ * @id:  supply name or regulator ID.
+ *
+ * Get and enable regulator for duration of the device life-time.
+ * regulator_disable() and regulator_put() are automatically called on driver
+ * detach. See regulator_get() and regulator_enable() for more
+ * information.
+ */
+int devm_regulator_get_enable(struct device *dev, const char *id)
+{
+       return _devm_regulator_get_enable(dev, id, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_enable);
+
 /**
  * devm_regulator_get_optional - Resource managed regulator_get_optional()
  * @dev: device to supply
@@ -194,6 +253,111 @@ int devm_regulator_bulk_get_const(struct device *dev, int num_consumers,
 }
 EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_const);
 
+static int devm_regulator_bulk_match(struct device *dev, void *res,
+                                    void *data)
+{
+       struct regulator_bulk_devres *match = res;
+       struct regulator_bulk_data *target = data;
+
+       /*
+        * We check the put uses same consumer list as the get did.
+        * We _could_ scan all entries in consumer array and check the
+        * regulators match but ATM I don't see the need. We can change this
+        * later if needed.
+        */
+       return match->consumers == target;
+}
+
+/**
+ * devm_regulator_bulk_put - Resource managed regulator_bulk_put()
+ * @consumers: consumers to free
+ *
+ * Deallocate regulators allocated with devm_regulator_bulk_get(). Normally
+ * this function will not need to be called and the resource management
+ * code will ensure that the resource is freed.
+ */
+void devm_regulator_bulk_put(struct regulator_bulk_data *consumers)
+{
+       int rc;
+       struct regulator *regulator = consumers[0].consumer;
+
+       rc = devres_release(regulator->dev, devm_regulator_bulk_release,
+                           devm_regulator_bulk_match, consumers);
+       if (rc != 0)
+               WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_put);
+
+static void devm_regulator_bulk_disable(void *res)
+{
+       struct regulator_bulk_devres *devres = res;
+       int i;
+
+       for (i = 0; i < devres->num_consumers; i++)
+               regulator_disable(devres->consumers[i].consumer);
+}
+
+/**
+ * devm_regulator_bulk_get_enable - managed get'n enable multiple regulators
+ *
+ * @dev:           device to supply
+ * @num_consumers: number of consumers to register
+ * @id:            list of supply names or regulator IDs
+ *
+ * @return 0 on success, an errno on failure.
+ *
+ * This helper function allows drivers to get several regulator
+ * consumers in one operation with management, the regulators will
+ * automatically be freed when the device is unbound.  If any of the
+ * regulators cannot be acquired then any regulators that were
+ * allocated will be freed before returning to the caller.
+ */
+int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers,
+                                  const char * const *id)
+{
+       struct regulator_bulk_devres *devres;
+       struct regulator_bulk_data *consumers;
+       int i, ret;
+
+       devres = devm_kmalloc(dev, sizeof(*devres), GFP_KERNEL);
+       if (!devres)
+               return -ENOMEM;
+
+       devres->consumers = devm_kcalloc(dev, num_consumers, sizeof(*consumers),
+                                        GFP_KERNEL);
+       consumers = devres->consumers;
+       if (!consumers)
+               return -ENOMEM;
+
+       devres->num_consumers = num_consumers;
+
+       for (i = 0; i < num_consumers; i++)
+               consumers[i].supply = id[i];
+
+       ret = devm_regulator_bulk_get(dev, num_consumers, consumers);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < num_consumers; i++) {
+               ret = regulator_enable(consumers[i].consumer);
+               if (ret)
+                       goto unwind;
+       }
+
+       ret = devm_add_action(dev, devm_regulator_bulk_disable, devres);
+       if (!ret)
+               return 0;
+
+unwind:
+       while (--i >= 0)
+               regulator_disable(consumers[i].consumer);
+
+       devm_regulator_bulk_put(consumers);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_enable);
+
 static void devm_rdev_release(struct device *dev, void *res)
 {
        regulator_unregister(*(struct regulator_dev **)res);
index bc6cda7..ee3b4a0 100644 (file)
@@ -207,6 +207,8 @@ struct regulator *__must_check regulator_get_optional(struct device *dev,
                                                      const char *id);
 struct regulator *__must_check devm_regulator_get_optional(struct device *dev,
                                                           const char *id);
+int devm_regulator_get_enable(struct device *dev, const char *id);
+int devm_regulator_get_enable_optional(struct device *dev, const char *id);
 void regulator_put(struct regulator *regulator);
 void devm_regulator_put(struct regulator *regulator);
 
@@ -244,12 +246,15 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers,
                                    struct regulator_bulk_data *consumers);
 int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers,
                                         struct regulator_bulk_data *consumers);
+void devm_regulator_bulk_put(struct regulator_bulk_data *consumers);
 int __must_check devm_regulator_bulk_get_const(
        struct device *dev, int num_consumers,
        const struct regulator_bulk_data *in_consumers,
        struct regulator_bulk_data **out_consumers);
 int __must_check regulator_bulk_enable(int num_consumers,
                                       struct regulator_bulk_data *consumers);
+int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers,
+                                  const char * const *id);
 int regulator_bulk_disable(int num_consumers,
                           struct regulator_bulk_data *consumers);
 int regulator_bulk_force_disable(int num_consumers,
@@ -354,6 +359,17 @@ devm_regulator_get_exclusive(struct device *dev, const char *id)
        return ERR_PTR(-ENODEV);
 }
 
+static inline int devm_regulator_get_enable(struct device *dev, const char *id)
+{
+       return -ENODEV;
+}
+
+static inline int devm_regulator_get_enable_optional(struct device *dev,
+                                                    const char *id)
+{
+       return -ENODEV;
+}
+
 static inline struct regulator *__must_check
 regulator_get_optional(struct device *dev, const char *id)
 {
@@ -375,6 +391,10 @@ static inline void devm_regulator_put(struct regulator *regulator)
 {
 }
 
+static inline void devm_regulator_bulk_put(struct regulator_bulk_data *consumers)
+{
+}
+
 static inline int regulator_register_supply_alias(struct device *dev,
                                                  const char *id,
                                                  struct device *alias_dev,
@@ -465,6 +485,13 @@ static inline int regulator_bulk_enable(int num_consumers,
        return 0;
 }
 
+static inline int devm_regulator_bulk_get_enable(struct device *dev,
+                                                int num_consumers,
+                                                const char * const *id)
+{
+       return 0;
+}
+
 static inline int regulator_bulk_disable(int num_consumers,
                                         struct regulator_bulk_data *consumers)
 {