From 070b9079226d4f3e3e7c9f4eb81f2e02e7d99572 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 16 Jan 2012 19:39:58 -0800 Subject: [PATCH 1/3] regulator: Add devm_regulator_get() Add a resource managed regulator_get() to simplify regulator usage in drivers. This allows driver authors to "get and forget" about their regulators by automatically calling regulator_put() when the driver is detached. [Fixed up a couple of coding style issues -- broonie] Signed-off-by: Stephen Boyd Signed-off-by: Mark Brown --- Documentation/driver-model/devres.txt | 3 +++ drivers/regulator/core.c | 34 +++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 9 +++++++ 3 files changed, 46 insertions(+) diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 10c64c8a13d4..016fd2b06a57 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -267,3 +267,6 @@ IOMAP pcim_iounmap() pcim_iomap_table() : array of mapped addresses indexed by BAR pcim_iomap_regions() : do request_region() and iomap() on multiple BARs + +REGULATOR + devm_regulator_get() diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca86f39a0fdc..214640db084b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1320,6 +1320,40 @@ struct regulator *regulator_get(struct device *dev, const char *id) } EXPORT_SYMBOL_GPL(regulator_get); +static void devm_regulator_release(struct device *dev, void *res) +{ + regulator_put(*(struct regulator **)res); +} + +/** + * devm_regulator_get - Resource managed regulator_get() + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Managed regulator_get(). Regulators returned from this function are + * automatically regulator_put() on driver detach. See regulator_get() for more + * information. + */ +struct regulator *devm_regulator_get(struct device *dev, const char *id) +{ + struct regulator **ptr, *regulator; + + ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + regulator = regulator_get(dev, id); + if (!IS_ERR(regulator)) { + *ptr = regulator; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return regulator; +} +EXPORT_SYMBOL_GPL(devm_regulator_get); + /** * regulator_get_exclusive - obtain exclusive access to a regulator. * @dev: device for regulator "consumer" diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index f2698a0edfc4..bcfe10658763 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -132,6 +132,8 @@ struct regulator_bulk_data { /* regulator get and put */ struct regulator *__must_check regulator_get(struct device *dev, const char *id); +struct regulator *__must_check devm_regulator_get(struct device *dev, + const char *id); struct regulator *__must_check regulator_get_exclusive(struct device *dev, const char *id); void regulator_put(struct regulator *regulator); @@ -200,6 +202,13 @@ static inline struct regulator *__must_check regulator_get(struct device *dev, */ return NULL; } + +static inline struct regulator *__must_check +devm_regulator_get(struct device *dev, const char *id) +{ + return NULL; +} + static inline void regulator_put(struct regulator *regulator) { } From d5ad34f7cb8b23ab165cabef69577a2a20d53195 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Jan 2012 20:09:18 +0000 Subject: [PATCH 2/3] regulator: Implement devm_regulator_free() Allow consumers to free regulators allocated using devm_regulator_get() if they need to. This will not normally be required. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 28 ++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 1 + 2 files changed, 29 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 214640db084b..88bcb111ca68 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1421,6 +1421,34 @@ void regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_put); +static int devm_regulator_match(struct device *dev, void *res, void *data) +{ + struct regulator **r = res; + if (!r || !*r) { + WARN_ON(!r || !*r); + return 0; + } + return *r == data; +} + +/** + * devm_regulator_put - Resource managed regulator_put() + * @regulator: regulator to free + * + * Deallocate a regulator allocated with devm_regulator_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_put(struct regulator *regulator) +{ + int rc; + + rc = devres_destroy(regulator->dev, devm_regulator_release, + devm_regulator_match, regulator); + WARN_ON(rc); +} +EXPORT_SYMBOL_GPL(devm_regulator_put); + static int _regulator_can_change_status(struct regulator_dev *rdev) { if (!rdev->constraints) diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index bcfe10658763..60c2f996d895 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -137,6 +137,7 @@ struct regulator *__must_check devm_regulator_get(struct device *dev, struct regulator *__must_check regulator_get_exclusive(struct device *dev, const char *id); void regulator_put(struct regulator *regulator); +void devm_regulator_free(struct regulator *regulator); /* regulator output control and status */ int regulator_enable(struct regulator *regulator); From e6e740304aa2a49ef09497e6c0bb906ed7987f6b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 20 Jan 2012 20:10:08 +0000 Subject: [PATCH 3/3] regulator: Provide devm_regulator_bulk_get() Allow drivers to benefit from both the bulk APIs and managed resources simultaneously. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 46 ++++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 2 ++ 2 files changed, 48 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 88bcb111ca68..1432c22926b5 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2463,6 +2463,52 @@ err: } EXPORT_SYMBOL_GPL(regulator_bulk_get); +/** + * devm_regulator_bulk_get - managed get 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, 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(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = devm_regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + ret = PTR_ERR(consumers[i].consumer); + dev_err(dev, "Failed to get supply '%s': %d\n", + consumers[i].supply, ret); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < num_consumers && consumers[i].consumer; i++) + devm_regulator_put(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); + static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) { struct regulator_bulk_data *bulk = data; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 60c2f996d895..35c42834ba3d 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -148,6 +148,8 @@ int regulator_disable_deferred(struct regulator *regulator, int ms); int regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); +int devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); int regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers); int regulator_bulk_disable(int num_consumers,