diff options
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 455 |
1 files changed, 243 insertions, 212 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index a01b8b3b70ca..6382f0af353b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -36,6 +36,7 @@ #include <trace/events/regulator.h> #include "dummy.h" +#include "internal.h" #define rdev_crit(rdev, fmt, ...) \ pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) @@ -52,8 +53,8 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); +static LIST_HEAD(regulator_supply_alias_list); static bool has_full_constraints; -static bool board_wants_dummy_regulator; static struct dentry *debugfs_root; @@ -83,22 +84,16 @@ struct regulator_enable_gpio { }; /* - * struct regulator + * struct regulator_supply_alias * - * One for each consumer device. + * Used to map lookups for a supply onto an alternative device. */ -struct regulator { - struct device *dev; +struct regulator_supply_alias { struct list_head list; - unsigned int always_on:1; - unsigned int bypass:1; - int uA_load; - int min_uV; - int max_uV; - char *supply_name; - struct device_attribute dev_attr; - struct regulator_dev *rdev; - struct dentry *debugfs; + struct device *src_dev; + const char *src_supply; + struct device *alias_dev; + const char *alias_supply; }; static int _regulator_is_enabled(struct regulator_dev *rdev); @@ -923,6 +918,36 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, return 0; } +static int machine_constraints_current(struct regulator_dev *rdev, + struct regulation_constraints *constraints) +{ + struct regulator_ops *ops = rdev->desc->ops; + int ret; + + if (!constraints->min_uA && !constraints->max_uA) + return 0; + + if (constraints->min_uA > constraints->max_uA) { + rdev_err(rdev, "Invalid current constraints\n"); + return -EINVAL; + } + + if (!ops->set_current_limit || !ops->get_current_limit) { + rdev_warn(rdev, "Operation of current configuration missing\n"); + return 0; + } + + /* Set regulator current in constraints range */ + ret = ops->set_current_limit(rdev, constraints->min_uA, + constraints->max_uA); + if (ret < 0) { + rdev_err(rdev, "Failed to set current constraint, %d\n", ret); + return ret; + } + + return 0; +} + /** * set_machine_constraints - sets regulator constraints * @rdev: regulator source @@ -953,6 +978,10 @@ static int set_machine_constraints(struct regulator_dev *rdev, if (ret != 0) goto out; + ret = machine_constraints_current(rdev, rdev->constraints); + if (ret != 0) + goto out; + /* do we need to setup our suspend state */ if (rdev->constraints->initial_state) { ret = suspend_prepare(rdev, rdev->constraints->initial_state); @@ -1186,11 +1215,39 @@ overflow_err: static int _regulator_get_enable_time(struct regulator_dev *rdev) { + if (rdev->constraints && rdev->constraints->enable_time) + return rdev->constraints->enable_time; if (!rdev->desc->ops->enable_time) return rdev->desc->enable_time; return rdev->desc->ops->enable_time(rdev); } +static struct regulator_supply_alias *regulator_find_supply_alias( + struct device *dev, const char *supply) +{ + struct regulator_supply_alias *map; + + list_for_each_entry(map, ®ulator_supply_alias_list, list) + if (map->src_dev == dev && strcmp(map->src_supply, supply) == 0) + return map; + + return NULL; +} + +static void regulator_supply_alias(struct device **dev, const char **supply) +{ + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(*dev, *supply); + if (map) { + dev_dbg(*dev, "Mapping supply %s to %s,%s\n", + *supply, map->alias_supply, + dev_name(map->alias_dev)); + *dev = map->alias_dev; + *supply = map->alias_supply; + } +} + static struct regulator_dev *regulator_dev_lookup(struct device *dev, const char *supply, int *ret) @@ -1200,6 +1257,8 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, struct regulator_map *map; const char *devname = NULL; + regulator_supply_alias(&dev, &supply); + /* first do a dt based lookup */ if (dev && dev->of_node) { node = of_get_regulator(dev, supply); @@ -1243,16 +1302,16 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, - bool exclusive) + bool exclusive, bool allow_dummy) { struct regulator_dev *rdev; struct regulator *regulator = ERR_PTR(-EPROBE_DEFER); const char *devname = NULL; - int ret = 0; + int ret = -EPROBE_DEFER; if (id == NULL) { pr_err("get() with no identifier\n"); - return regulator; + return ERR_PTR(-EINVAL); } if (dev) @@ -1264,34 +1323,32 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, if (rdev) goto found; + regulator = ERR_PTR(ret); + /* * If we have return value from dev_lookup fail, we do not expect to * succeed, so, quit with appropriate error value */ - if (ret) { - regulator = ERR_PTR(ret); + if (ret && ret != -ENODEV) { goto out; } - if (board_wants_dummy_regulator) { - rdev = dummy_regulator_rdev; - goto found; - } - -#ifdef CONFIG_REGULATOR_DUMMY if (!devname) devname = "deviceless"; - /* If the board didn't flag that it was fully constrained then - * substitute in a dummy regulator so consumers can continue. + /* + * Assume that a regulator is physically present and enabled + * even if it isn't hooked up and just provide a dummy. */ - if (!has_full_constraints) { + if (has_full_constraints && allow_dummy) { pr_warn("%s supply %s not found, using dummy regulator\n", devname, id); + rdev = dummy_regulator_rdev; goto found; + } else { + dev_err(dev, "dummy supplies not allowed\n"); } -#endif mutex_unlock(®ulator_list_mutex); return regulator; @@ -1349,44 +1406,10 @@ out: */ struct regulator *regulator_get(struct device *dev, const char *id) { - return _regulator_get(dev, id, false); + return _regulator_get(dev, id, false, true); } 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" @@ -1410,7 +1433,7 @@ EXPORT_SYMBOL_GPL(devm_regulator_get); */ struct regulator *regulator_get_exclusive(struct device *dev, const char *id) { - return _regulator_get(dev, id, true); + return _regulator_get(dev, id, true, false); } EXPORT_SYMBOL_GPL(regulator_get_exclusive); @@ -1439,40 +1462,10 @@ EXPORT_SYMBOL_GPL(regulator_get_exclusive); */ struct regulator *regulator_get_optional(struct device *dev, const char *id) { - return _regulator_get(dev, id, 0); + return _regulator_get(dev, id, false, false); } EXPORT_SYMBOL_GPL(regulator_get_optional); -/** - * 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) -{ - struct regulator **ptr, *regulator; - - ptr = devres_alloc(devm_regulator_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - regulator = regulator_get_optional(dev, id); - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; -} -EXPORT_SYMBOL_GPL(devm_regulator_get_optional); - /* Locks held by regulator_put() */ static void _regulator_put(struct regulator *regulator) { @@ -1499,36 +1492,6 @@ static void _regulator_put(struct regulator *regulator) } /** - * 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) -{ - 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, 1); - if (!IS_ERR(regulator)) { - *ptr = regulator; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return regulator; -} -EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); - -/** * regulator_put - "free" the regulator source * @regulator: regulator source * @@ -1544,34 +1507,133 @@ void regulator_put(struct regulator *regulator) } EXPORT_SYMBOL_GPL(regulator_put); -static int devm_regulator_match(struct device *dev, void *res, void *data) +/** + * regulator_register_supply_alias - Provide device alias for supply lookup + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * @alias_dev: device that should be used to lookup the supply + * @alias_id: Supply name or regulator ID that should be used to lookup the + * supply + * + * All lookups for id on dev will instead be conducted for alias_id on + * alias_dev. + */ +int regulator_register_supply_alias(struct device *dev, const char *id, + struct device *alias_dev, + const char *alias_id) +{ + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(dev, id); + if (map) + return -EEXIST; + + map = kzalloc(sizeof(struct regulator_supply_alias), GFP_KERNEL); + if (!map) + return -ENOMEM; + + map->src_dev = dev; + map->src_supply = id; + map->alias_dev = alias_dev; + map->alias_supply = alias_id; + + list_add(&map->list, ®ulator_supply_alias_list); + + pr_info("Adding alias for supply %s,%s -> %s,%s\n", + id, dev_name(dev), alias_id, dev_name(alias_dev)); + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_register_supply_alias); + +/** + * regulator_unregister_supply_alias - Remove device alias + * + * @dev: device that will be given as the regulator "consumer" + * @id: Supply name or regulator ID + * + * Remove a lookup alias if one exists for id on dev. + */ +void regulator_unregister_supply_alias(struct device *dev, const char *id) { - struct regulator **r = res; - if (!r || !*r) { - WARN_ON(!r || !*r); - return 0; + struct regulator_supply_alias *map; + + map = regulator_find_supply_alias(dev, id); + if (map) { + list_del(&map->list); + kfree(map); } - return *r == data; } +EXPORT_SYMBOL_GPL(regulator_unregister_supply_alias); /** - * devm_regulator_put - Resource managed regulator_put() - * @regulator: regulator to free + * regulator_bulk_register_supply_alias - register multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @alias_dev: device that should be used to lookup the supply + * @alias_id: List of supply names or regulator IDs that should be used to + * lookup the supply + * @num_id: Number of aliases to register + * + * @return 0 on success, an errno on failure. * - * 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. + * This helper function allows drivers to register several supply + * aliases in one operation. If any of the aliases cannot be + * registered any aliases that were registered will be removed + * before returning to the caller. */ -void devm_regulator_put(struct regulator *regulator) +int regulator_bulk_register_supply_alias(struct device *dev, const char **id, + struct device *alias_dev, + const char **alias_id, + int num_id) { - int rc; + int i; + int ret; + + for (i = 0; i < num_id; ++i) { + ret = regulator_register_supply_alias(dev, id[i], alias_dev, + alias_id[i]); + if (ret < 0) + goto err; + } + + return 0; + +err: + dev_err(dev, + "Failed to create supply alias %s,%s -> %s,%s\n", + id[i], dev_name(dev), alias_id[i], dev_name(alias_dev)); + + while (--i >= 0) + regulator_unregister_supply_alias(dev, id[i]); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_register_supply_alias); + +/** + * regulator_bulk_unregister_supply_alias - unregister multiple aliases + * + * @dev: device that will be given as the regulator "consumer" + * @id: List of supply names or regulator IDs + * @num_id: Number of aliases to unregister + * + * This helper function allows drivers to unregister several supply + * aliases in one operation. + */ +void regulator_bulk_unregister_supply_alias(struct device *dev, + const char **id, + int num_id) +{ + int i; - rc = devres_release(regulator->dev, devm_regulator_release, - devm_regulator_match, regulator); - if (rc != 0) - WARN_ON(rc); + for (i = 0; i < num_id; ++i) + regulator_unregister_supply_alias(dev, id[i]); } -EXPORT_SYMBOL_GPL(devm_regulator_put); +EXPORT_SYMBOL_GPL(regulator_bulk_unregister_supply_alias); + /* Manage enable GPIO list. Same GPIO pin can be shared among regulators */ static int regulator_ena_gpio_request(struct regulator_dev *rdev, @@ -1704,11 +1766,39 @@ static int _regulator_do_enable(struct regulator_dev *rdev) * together. */ trace_regulator_enable_delay(rdev_get_name(rdev)); - if (delay >= 1000) { - mdelay(delay / 1000); - udelay(delay % 1000); - } else if (delay) { - udelay(delay); + /* + * Delay for the requested amount of time as per the guidelines in: + * + * Documentation/timers/timers-howto.txt + * + * The assumption here is that regulators will never be enabled in + * atomic context and therefore sleeping functions can be used. + */ + if (delay) { + unsigned int ms = delay / 1000; + unsigned int us = delay % 1000; + + if (ms > 0) { + /* + * For small enough values, handle super-millisecond + * delays in the usleep_range() call below. + */ + if (ms < 20) + us += ms * 1000; + else + msleep(ms); + } + + /* + * Give the scheduler some room to coalesce with any other + * wakeup sources. For delays shorter than 10 us, don't even + * bother setting up high-resolution timers and just busy- + * loop. + */ + if (us >= 10) + usleep_range(us, us + 100); + else + udelay(us); } trace_regulator_enable_complete(rdev_get_name(rdev)); @@ -2489,6 +2579,8 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) ret = rdev->desc->ops->get_voltage(rdev); } else if (rdev->desc->ops->list_voltage) { ret = rdev->desc->ops->list_voltage(rdev, 0); + } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { + ret = rdev->desc->fixed_uV; } else { return -EINVAL; } @@ -2912,52 +3004,6 @@ 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; @@ -3170,7 +3216,8 @@ static int add_regulator_attributes(struct regulator_dev *rdev) /* some attributes need specific methods to be displayed */ if ((ops->get_voltage && ops->get_voltage(rdev) >= 0) || (ops->get_voltage_sel && ops->get_voltage_sel(rdev) >= 0) || - (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0)) { + (ops->list_voltage && ops->list_voltage(rdev, 0) >= 0) || + (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1))) { status = device_create_file(dev, &dev_attr_microvolts); if (status < 0) return status; @@ -3614,22 +3661,6 @@ void regulator_has_full_constraints(void) EXPORT_SYMBOL_GPL(regulator_has_full_constraints); /** - * regulator_use_dummy_regulator - Provide a dummy regulator when none is found - * - * Calling this function will cause the regulator API to provide a - * dummy regulator to consumers if no physical regulator is found, - * allowing most consumers to proceed as though a regulator were - * configured. This allows systems such as those with software - * controllable regulators for the CPU core only to be brought up more - * readily. - */ -void regulator_use_dummy_regulator(void) -{ - board_wants_dummy_regulator = true; -} -EXPORT_SYMBOL_GPL(regulator_use_dummy_regulator); - -/** * rdev_get_drvdata - get rdev regulator driver data * @rdev: regulator * |