summaryrefslogtreecommitdiff
path: root/drivers/regulator/devres.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator/devres.c')
-rw-r--r--drivers/regulator/devres.c366
1 files changed, 351 insertions, 15 deletions
diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c
index 9113233f41cd..2cf03042fddf 100644
--- a/drivers/regulator/devres.c
+++ b/drivers/regulator/devres.c
@@ -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
@@ -86,6 +145,65 @@ struct regulator *devm_regulator_get_optional(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_regulator_get_optional);
+/**
+ * devm_regulator_get_enable_read_voltage - Resource managed regulator get and
+ * enable that returns the voltage
+ * @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(), regulator_enable(), and
+ * regulator_get_voltage() for more information.
+ *
+ * This is a convenience function for supplies that provide a reference voltage
+ * where the consumer driver just needs to know the voltage and keep the
+ * regulator enabled.
+ *
+ * In cases where the supply is not strictly required, callers can check for
+ * -ENODEV error and handle it accordingly.
+ *
+ * Returns: voltage in microvolts on success, or an negative error number on failure.
+ */
+int devm_regulator_get_enable_read_voltage(struct device *dev, const char *id)
+{
+ struct regulator *r;
+ int ret;
+
+ /*
+ * Since we need a real voltage, we use devm_regulator_get_optional()
+ * rather than getting a dummy regulator with devm_regulator_get() and
+ * then letting regulator_get_voltage() fail with -EINVAL. This way, the
+ * caller can handle the -ENODEV negative error number if needed instead
+ * of the ambiguous -EINVAL.
+ */
+ r = devm_regulator_get_optional(dev, id);
+ if (IS_ERR(r))
+ return PTR_ERR(r);
+
+ ret = regulator_enable(r);
+ if (ret)
+ goto err_regulator_put;
+
+ ret = devm_add_action_or_reset(dev, regulator_action_disable, r);
+ if (ret)
+ goto err_regulator_put;
+
+ ret = regulator_get_voltage(r);
+ if (ret < 0)
+ goto err_release_action;
+
+ return ret;
+
+err_release_action:
+ devm_release_action(dev, regulator_action_disable, r);
+err_regulator_put:
+ devm_regulator_put(r);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_regulator_get_enable_read_voltage);
+
static int devm_regulator_match(struct device *dev, void *res, void *data)
{
struct regulator **r = res;
@@ -127,6 +245,30 @@ static void devm_regulator_bulk_release(struct device *dev, void *res)
regulator_bulk_free(devres->num_consumers, devres->consumers);
}
+static int _devm_regulator_bulk_get(struct device *dev, int num_consumers,
+ struct regulator_bulk_data *consumers,
+ enum regulator_get_type get_type)
+{
+ struct regulator_bulk_devres *devres;
+ int ret;
+
+ devres = devres_alloc(devm_regulator_bulk_release,
+ sizeof(*devres), GFP_KERNEL);
+ if (!devres)
+ return -ENOMEM;
+
+ ret = _regulator_bulk_get(dev, num_consumers, consumers, get_type);
+ if (!ret) {
+ devres->consumers = consumers;
+ devres->num_consumers = num_consumers;
+ devres_add(dev, devres);
+ } else {
+ devres_free(devres);
+ }
+
+ return ret;
+}
+
/**
* devm_regulator_bulk_get - managed get multiple regulator consumers
*
@@ -134,7 +276,7 @@ static void devm_regulator_bulk_release(struct device *dev, void *res)
* @num_consumers: number of consumers to register
* @consumers: configuration of consumers; clients are stored here.
*
- * @return 0 on success, an errno on failure.
+ * @return 0 on success, a negative error number on failure.
*
* This helper function allows drivers to get several regulator
* consumers in one operation with management, the regulators will
@@ -145,26 +287,164 @@ static void devm_regulator_bulk_release(struct device *dev, void *res)
int devm_regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers)
{
+ return _devm_regulator_bulk_get(dev, num_consumers, consumers, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_get);
+
+/**
+ * devm_regulator_bulk_get_exclusive - managed exclusive get of multiple
+ * regulator consumers
+ *
+ * @dev: device to supply
+ * @num_consumers: number of consumers to register
+ * @consumers: configuration of consumers; clients are stored here.
+ *
+ * @return 0 on success, a negative error number on failure.
+ *
+ * This helper function allows drivers to exclusively 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_exclusive(struct device *dev, int num_consumers,
+ struct regulator_bulk_data *consumers)
+{
+ return _devm_regulator_bulk_get(dev, num_consumers, consumers, EXCLUSIVE_GET);
+}
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_exclusive);
+
+/**
+ * devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data
+ *
+ * @dev: device to supply
+ * @num_consumers: number of consumers to register
+ * @in_consumers: const configuration of consumers
+ * @out_consumers: in_consumers is copied here and this is passed to
+ * devm_regulator_bulk_get().
+ *
+ * This is a convenience function to allow bulk regulator configuration
+ * to be stored "static const" in files.
+ *
+ * Return: 0 on success, a negative error number on failure.
+ */
+int devm_regulator_bulk_get_const(struct device *dev, int num_consumers,
+ const struct regulator_bulk_data *in_consumers,
+ struct regulator_bulk_data **out_consumers)
+{
+ *out_consumers = devm_kmemdup_array(dev, in_consumers, num_consumers,
+ sizeof(*in_consumers), GFP_KERNEL);
+ if (*out_consumers == NULL)
+ return -ENOMEM;
+
+ return devm_regulator_bulk_get(dev, num_consumers, *out_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, a negative error number 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;
- int ret;
+ struct regulator_bulk_data *consumers;
+ int i, ret;
- devres = devres_alloc(devm_regulator_bulk_release,
- sizeof(*devres), GFP_KERNEL);
+ devres = devm_kmalloc(dev, sizeof(*devres), GFP_KERNEL);
if (!devres)
return -ENOMEM;
- ret = regulator_bulk_get(dev, num_consumers, consumers);
- if (!ret) {
- devres->consumers = consumers;
- devres->num_consumers = num_consumers;
- devres_add(dev, devres);
- } else {
- devres_free(devres);
+ 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);
+EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_enable);
static void devm_rdev_release(struct device *dev, void *res)
{
@@ -193,7 +473,7 @@ struct regulator_dev *devm_regulator_register(struct device *dev,
if (!ptr)
return ERR_PTR(-ENOMEM);
- rdev = regulator_register(regulator_desc, config);
+ rdev = regulator_register(dev, regulator_desc, config);
if (!IS_ERR(rdev)) {
*ptr = rdev;
devres_add(dev, ptr);
@@ -293,7 +573,7 @@ static void devm_regulator_unregister_supply_alias(struct device *dev,
* lookup the supply
* @num_id: number of aliases to register
*
- * @return 0 on success, an errno on failure.
+ * @return 0 on success, a negative error number on failure.
*
* This helper function allows drivers to register several supply
* aliases in one operation, the aliases will be automatically
@@ -445,7 +725,7 @@ static void regulator_irq_helper_drop(void *res)
* IRQ.
* @rdev_amount: Amount of regulators associated with this IRQ.
*
- * Return: handle to irq_helper or an ERR_PTR() encoded error code.
+ * Return: handle to irq_helper or an ERR_PTR() encoded negative error number.
*/
void *devm_regulator_irq_helper(struct device *dev,
const struct regulator_irq_desc *d, int irq,
@@ -468,3 +748,59 @@ void *devm_regulator_irq_helper(struct device *dev,
return ptr;
}
EXPORT_SYMBOL_GPL(devm_regulator_irq_helper);
+
+#if IS_ENABLED(CONFIG_OF)
+static struct regulator *_devm_of_regulator_get(struct device *dev, struct device_node *node,
+ const char *id, int get_type)
+{
+ struct regulator **ptr, *regulator;
+
+ ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return ERR_PTR(-ENOMEM);
+
+ regulator = _of_regulator_get(dev, node, id, get_type);
+ if (!IS_ERR(regulator)) {
+ *ptr = regulator;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return regulator;
+}
+
+/**
+ * devm_of_regulator_get - Resource managed of_regulator_get()
+ * @dev: device used for dev_printk() messages and resource lifetime management
+ * @node: device node for regulator "consumer"
+ * @id: supply name or regulator ID.
+ *
+ * Managed of_regulator_get(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * of_regulator_get() for more information.
+ */
+struct regulator *devm_of_regulator_get(struct device *dev, struct device_node *node,
+ const char *id)
+{
+ return _devm_of_regulator_get(dev, node, id, NORMAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_of_regulator_get);
+
+/**
+ * devm_of_regulator_get_optional - Resource managed of_regulator_get_optional()
+ * @dev: device used for dev_printk() messages and resource lifetime management
+ * @node: device node for regulator "consumer"
+ * @id: supply name or regulator ID.
+ *
+ * Managed regulator_get_optional(). Regulators returned from this
+ * function are automatically regulator_put() on driver detach. See
+ * of_regulator_get_optional() for more information.
+ */
+struct regulator *devm_of_regulator_get_optional(struct device *dev, struct device_node *node,
+ const char *id)
+{
+ return _devm_of_regulator_get(dev, node, id, OPTIONAL_GET);
+}
+EXPORT_SYMBOL_GPL(devm_of_regulator_get_optional);
+#endif