/* * devres.c -- Voltage/Current Regulator framework devres implementation. * * Copyright 2013 Linaro Ltd * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * */ #include #include #include #include #include #include #include "internal.h" enum { NORMAL_GET, EXCLUSIVE_GET, OPTIONAL_GET, }; static void devm_regulator_release(struct device *dev, void *res) { regulator_put(*(struct regulator **)res); } static struct regulator *_devm_regulator_get(struct device *dev, 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); switch (get_type) { case NORMAL_GET: regulator = regulator_get(dev, id); break; case EXCLUSIVE_GET: regulator = regulator_get_exclusive(dev, id); break; case OPTIONAL_GET: regulator = regulator_get_optional(dev, id); break; default: regulator = ERR_PTR(-EINVAL); } if (!IS_ERR(regulator)) { *ptr = regulator; devres_add(dev, ptr); } else { devres_free(ptr); } return regulator; } /** * 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) { return _devm_regulator_get(dev, id, NORMAL_GET); } EXPORT_SYMBOL_GPL(devm_regulator_get); /** * devm_regulator_get_exclusive - Resource managed regulator_get_exclusive() * @dev: device for regulator "consumer" * @id: Supply name or regulator ID. * * Managed regulator_get_exclusive(). Regulators returned from this function * are automatically regulator_put() on driver detach. See regulator_get() for * more information. */ struct regulator *devm_regulator_get_exclusive(struct device *dev, const char *id) { return _devm_regulator_get(dev, id, EXCLUSIVE_GET); } EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); /** * devm_regulator_get_optional - Resource managed regulator_get_optional() * @dev: device 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 * regulator_get_optional() for more information. */ struct regulator *devm_regulator_get_optional(struct device *dev, const char *id) { return _devm_regulator_get(dev, id, OPTIONAL_GET); } EXPORT_SYMBOL_GPL(devm_regulator_get_optional); 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_release(regulator->dev, devm_regulator_release, devm_regulator_match, regulator); if (rc != 0) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_regulator_put); /** * 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 devm_rdev_release(struct device *dev, void *res) { regulator_unregister(*(struct regulator_dev **)res); } /** * devm_regulator_register - Resource managed regulator_register() * @regulator_desc: regulator to register * @config: runtime configuration for regulator * * Called by regulator drivers to register a regulator. Returns a * valid pointer to struct regulator_dev on success or an ERR_PTR() on * error. The regulator will automatically be released when the device * is unbound. */ struct regulator_dev *devm_regulator_register(struct device *dev, const struct regulator_desc *regulator_desc, const struct regulator_config *config) { struct regulator_dev **ptr, *rdev; ptr = devres_alloc(devm_rdev_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) return ERR_PTR(-ENOMEM); rdev = regulator_register(regulator_desc, config); if (!IS_ERR(rdev)) { *ptr = rdev; devres_add(dev, ptr); } else { devres_free(ptr); } return rdev; } EXPORT_SYMBOL_GPL(devm_regulator_register); static int devm_rdev_match(struct device *dev, void *res, void *data) { struct regulator_dev **r = res; if (!r || !*r) { WARN_ON(!r || !*r); return 0; } return *r == data; } /** * devm_regulator_unregister - Resource managed regulator_unregister() * @regulator: regulator to free * * Unregister a regulator registered with devm_regulator_register(). * Normally this function will not need to be called and the resource * management code will ensure that the resource is freed. */ void devm_regulator_unregister(struct device *dev, struct regulator_dev *rdev) { int rc; rc = devres_release(dev, devm_rdev_release, devm_rdev_match, rdev); if (rc != 0) WARN_ON(rc); } EXPORT_SYMBOL_GPL(devm_regulator_unregister);