diff options
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 512 |
1 files changed, 347 insertions, 165 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8a34f6acc801..73b7683355cd 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -51,7 +51,6 @@ pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) 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); @@ -59,6 +58,8 @@ static bool has_full_constraints; static struct dentry *debugfs_root; +static struct class regulator_class; + /* * struct regulator_map * @@ -132,6 +133,45 @@ static bool have_full_constraints(void) } /** + * regulator_lock_supply - lock a regulator and its supplies + * @rdev: regulator source + */ +static void regulator_lock_supply(struct regulator_dev *rdev) +{ + struct regulator *supply; + int i = 0; + + while (1) { + mutex_lock_nested(&rdev->mutex, i++); + supply = rdev->supply; + + if (!rdev->supply) + return; + + rdev = supply->rdev; + } +} + +/** + * regulator_unlock_supply - unlock a regulator and its supplies + * @rdev: regulator source + */ +static void regulator_unlock_supply(struct regulator_dev *rdev) +{ + struct regulator *supply; + + while (1) { + mutex_unlock(&rdev->mutex); + supply = rdev->supply; + + if (!rdev->supply) + return; + + rdev = supply->rdev; + } +} + +/** * of_get_regulator - get a regulator device node based on supply name * @dev: Device pointer for the consumer (of regulator) device * @supply: regulator supply name @@ -180,7 +220,7 @@ static int regulator_check_voltage(struct regulator_dev *rdev, return -ENODEV; } if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { - rdev_err(rdev, "operation not allowed\n"); + rdev_err(rdev, "voltage operation not allowed\n"); return -EPERM; } @@ -240,7 +280,7 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, return -ENODEV; } if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) { - rdev_err(rdev, "operation not allowed\n"); + rdev_err(rdev, "current operation not allowed\n"); return -EPERM; } @@ -277,7 +317,7 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) return -ENODEV; } if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) { - rdev_err(rdev, "operation not allowed\n"); + rdev_err(rdev, "mode operation not allowed\n"); return -EPERM; } @@ -301,7 +341,7 @@ static int regulator_check_drms(struct regulator_dev *rdev) return -ENODEV; } if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { - rdev_dbg(rdev, "operation not allowed\n"); + rdev_dbg(rdev, "drms operation not allowed\n"); return -EPERM; } return 0; @@ -1325,6 +1365,47 @@ static void regulator_supply_alias(struct device **dev, const char **supply) } } +static int of_node_match(struct device *dev, const void *data) +{ + return dev->of_node == data; +} + +static struct regulator_dev *of_find_regulator_by_node(struct device_node *np) +{ + struct device *dev; + + dev = class_find_device(®ulator_class, NULL, np, of_node_match); + + return dev ? dev_to_rdev(dev) : NULL; +} + +static int regulator_match(struct device *dev, const void *data) +{ + struct regulator_dev *r = dev_to_rdev(dev); + + return strcmp(rdev_get_name(r), data) == 0; +} + +static struct regulator_dev *regulator_lookup_by_name(const char *name) +{ + struct device *dev; + + dev = class_find_device(®ulator_class, NULL, name, regulator_match); + + return dev ? dev_to_rdev(dev) : NULL; +} + +/** + * regulator_dev_lookup - lookup a regulator device. + * @dev: device for regulator "consumer". + * @supply: Supply name or regulator ID. + * @ret: 0 on success, -ENODEV if lookup fails permanently, -EPROBE_DEFER if + * lookup could succeed in the future. + * + * If successful, returns a struct regulator_dev that corresponds to the name + * @supply and with the embedded struct device refcount incremented by one, + * or NULL on failure. The refcount must be dropped by calling put_device(). + */ static struct regulator_dev *regulator_dev_lookup(struct device *dev, const char *supply, int *ret) @@ -1340,10 +1421,9 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, if (dev && dev->of_node) { node = of_get_regulator(dev, supply); if (node) { - list_for_each_entry(r, ®ulator_list, list) - if (r->dev.parent && - node == r->dev.of_node) - return r; + r = of_find_regulator_by_node(node); + if (r) + return r; *ret = -EPROBE_DEFER; return NULL; } else { @@ -1361,20 +1441,24 @@ static struct regulator_dev *regulator_dev_lookup(struct device *dev, if (dev) devname = dev_name(dev); - list_for_each_entry(r, ®ulator_list, list) - if (strcmp(rdev_get_name(r), supply) == 0) - return r; + r = regulator_lookup_by_name(supply); + if (r) + return r; + mutex_lock(®ulator_list_mutex); list_for_each_entry(map, ®ulator_map_list, list) { /* If the mapping has a device set up it must match */ if (map->dev_name && (!devname || strcmp(map->dev_name, devname))) continue; - if (strcmp(map->supply, supply) == 0) + if (strcmp(map->supply, supply) == 0 && + get_device(&map->regulator->dev)) { + mutex_unlock(®ulator_list_mutex); return map->regulator; + } } - + mutex_unlock(®ulator_list_mutex); return NULL; } @@ -1409,6 +1493,7 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) if (have_full_constraints()) { r = dummy_regulator_rdev; + get_device(&r->dev); } else { dev_err(dev, "Failed to resolve %s-supply for %s\n", rdev->supply_name, rdev->desc->name); @@ -1418,12 +1503,16 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) /* Recursively resolve the supply of the supply */ ret = regulator_resolve_supply(r); - if (ret < 0) + if (ret < 0) { + put_device(&r->dev); return ret; + } ret = set_supply(rdev, r); - if (ret < 0) + if (ret < 0) { + put_device(&r->dev); return ret; + } /* Cascade always-on state to supply */ if (_regulator_is_enabled(rdev) && rdev->supply) { @@ -1459,8 +1548,6 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, else ret = -EPROBE_DEFER; - mutex_lock(®ulator_list_mutex); - rdev = regulator_dev_lookup(dev, id, &ret); if (rdev) goto found; @@ -1472,7 +1559,7 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, * succeed, so, quit with appropriate error value */ if (ret && ret != -ENODEV) - goto out; + return regulator; if (!devname) devname = "deviceless"; @@ -1486,40 +1573,46 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, devname, id); rdev = dummy_regulator_rdev; + get_device(&rdev->dev); goto found; /* Don't log an error when called from regulator_get_optional() */ } else if (!have_full_constraints() || exclusive) { dev_warn(dev, "dummy supplies not allowed\n"); } - mutex_unlock(®ulator_list_mutex); return regulator; found: if (rdev->exclusive) { regulator = ERR_PTR(-EPERM); - goto out; + put_device(&rdev->dev); + return regulator; } if (exclusive && rdev->open_count) { regulator = ERR_PTR(-EBUSY); - goto out; + put_device(&rdev->dev); + return regulator; } ret = regulator_resolve_supply(rdev); if (ret < 0) { regulator = ERR_PTR(ret); - goto out; + put_device(&rdev->dev); + return regulator; } - if (!try_module_get(rdev->owner)) - goto out; + if (!try_module_get(rdev->owner)) { + put_device(&rdev->dev); + return regulator; + } regulator = create_regulator(rdev, dev, id); if (regulator == NULL) { regulator = ERR_PTR(-ENOMEM); + put_device(&rdev->dev); module_put(rdev->owner); - goto out; + return regulator; } rdev->open_count++; @@ -1533,9 +1626,6 @@ found: rdev->use_count = 0; } -out: - mutex_unlock(®ulator_list_mutex); - return regulator; } @@ -1633,6 +1723,7 @@ static void _regulator_put(struct regulator *regulator) rdev->open_count--; rdev->exclusive = 0; + put_device(&rdev->dev); mutex_unlock(&rdev->mutex); kfree(regulator->supply_name); @@ -2312,6 +2403,40 @@ static int _regulator_is_enabled(struct regulator_dev *rdev) return rdev->desc->ops->is_enabled(rdev); } +static int _regulator_list_voltage(struct regulator *regulator, + unsigned selector, int lock) +{ + struct regulator_dev *rdev = regulator->rdev; + const struct regulator_ops *ops = rdev->desc->ops; + int ret; + + if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector) + return rdev->desc->fixed_uV; + + if (ops->list_voltage) { + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + if (lock) + mutex_lock(&rdev->mutex); + ret = ops->list_voltage(rdev, selector); + if (lock) + mutex_unlock(&rdev->mutex); + } else if (rdev->supply) { + ret = _regulator_list_voltage(rdev->supply, selector, lock); + } else { + return -EINVAL; + } + + if (ret > 0) { + if (ret < rdev->constraints->min_uV) + ret = 0; + else if (ret > rdev->constraints->max_uV) + ret = 0; + } + + return ret; +} + /** * regulator_is_enabled - is the regulator output enabled * @regulator: regulator source @@ -2401,33 +2526,7 @@ EXPORT_SYMBOL_GPL(regulator_count_voltages); */ int regulator_list_voltage(struct regulator *regulator, unsigned selector) { - struct regulator_dev *rdev = regulator->rdev; - const struct regulator_ops *ops = rdev->desc->ops; - int ret; - - if (rdev->desc->fixed_uV && rdev->desc->n_voltages == 1 && !selector) - return rdev->desc->fixed_uV; - - if (ops->list_voltage) { - if (selector >= rdev->desc->n_voltages) - return -EINVAL; - mutex_lock(&rdev->mutex); - ret = ops->list_voltage(rdev, selector); - mutex_unlock(&rdev->mutex); - } else if (rdev->supply) { - ret = regulator_list_voltage(rdev->supply, selector); - } else { - return -EINVAL; - } - - if (ret > 0) { - if (ret < rdev->constraints->min_uV) - ret = 0; - else if (ret > rdev->constraints->max_uV) - ret = 0; - } - - return ret; + return _regulator_list_voltage(regulator, selector, 1); } EXPORT_SYMBOL_GPL(regulator_list_voltage); @@ -2562,6 +2661,23 @@ int regulator_is_supported_voltage(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_is_supported_voltage); +static int regulator_map_voltage(struct regulator_dev *rdev, int min_uV, + int max_uV) +{ + const struct regulator_desc *desc = rdev->desc; + + if (desc->ops->map_voltage) + return desc->ops->map_voltage(rdev, min_uV, max_uV); + + if (desc->ops->list_voltage == regulator_list_voltage_linear) + return regulator_map_voltage_linear(rdev, min_uV, max_uV); + + if (desc->ops->list_voltage == regulator_list_voltage_linear_range) + return regulator_map_voltage_linear_range(rdev, min_uV, max_uV); + + return regulator_map_voltage_iterate(rdev, min_uV, max_uV); +} + static int _regulator_call_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV, unsigned *selector) @@ -2650,23 +2766,7 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, } } else if (rdev->desc->ops->set_voltage_sel) { - if (rdev->desc->ops->map_voltage) { - ret = rdev->desc->ops->map_voltage(rdev, min_uV, - max_uV); - } else { - if (rdev->desc->ops->list_voltage == - regulator_list_voltage_linear) - ret = regulator_map_voltage_linear(rdev, - min_uV, max_uV); - else if (rdev->desc->ops->list_voltage == - regulator_list_voltage_linear_range) - ret = regulator_map_voltage_linear_range(rdev, - min_uV, max_uV); - else - ret = regulator_map_voltage_iterate(rdev, - min_uV, max_uV); - } - + ret = regulator_map_voltage(rdev, min_uV, max_uV); if (ret >= 0) { best_val = rdev->desc->ops->list_voltage(rdev, ret); if (min_uV <= best_val && max_uV >= best_val) { @@ -2717,32 +2817,15 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev, return ret; } -/** - * regulator_set_voltage - set regulator output voltage - * @regulator: regulator source - * @min_uV: Minimum required voltage in uV - * @max_uV: Maximum acceptable voltage in uV - * - * Sets a voltage regulator to the desired output voltage. This can be set - * during any regulator state. IOW, regulator can be disabled or enabled. - * - * If the regulator is enabled then the voltage will change to the new value - * immediately otherwise if the regulator is disabled the regulator will - * output at the new voltage when enabled. - * - * NOTE: If the regulator is shared between several devices then the lowest - * request voltage that meets the system constraints will be used. - * Regulator system constraints must be set for this regulator before - * calling this function otherwise this call will fail. - */ -int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) +static int regulator_set_voltage_unlocked(struct regulator *regulator, + int min_uV, int max_uV) { struct regulator_dev *rdev = regulator->rdev; int ret = 0; int old_min_uV, old_max_uV; int current_uV; - - mutex_lock(&rdev->mutex); + int best_supply_uV = 0; + int supply_change_uV = 0; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -2786,17 +2869,95 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) if (ret < 0) goto out2; + if (rdev->supply && (rdev->desc->min_dropout_uV || + !rdev->desc->ops->get_voltage)) { + int current_supply_uV; + int selector; + + selector = regulator_map_voltage(rdev, min_uV, max_uV); + if (selector < 0) { + ret = selector; + goto out2; + } + + best_supply_uV = _regulator_list_voltage(regulator, selector, 0); + if (best_supply_uV < 0) { + ret = best_supply_uV; + goto out2; + } + + best_supply_uV += rdev->desc->min_dropout_uV; + + current_supply_uV = _regulator_get_voltage(rdev->supply->rdev); + if (current_supply_uV < 0) { + ret = current_supply_uV; + goto out2; + } + + supply_change_uV = best_supply_uV - current_supply_uV; + } + + if (supply_change_uV > 0) { + ret = regulator_set_voltage_unlocked(rdev->supply, + best_supply_uV, INT_MAX); + if (ret) { + dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n", + ret); + goto out2; + } + } + ret = _regulator_do_set_voltage(rdev, min_uV, max_uV); if (ret < 0) goto out2; + if (supply_change_uV < 0) { + ret = regulator_set_voltage_unlocked(rdev->supply, + best_supply_uV, INT_MAX); + if (ret) + dev_warn(&rdev->dev, "Failed to decrease supply voltage: %d\n", + ret); + /* No need to fail here */ + ret = 0; + } + out: - mutex_unlock(&rdev->mutex); return ret; out2: regulator->min_uV = old_min_uV; regulator->max_uV = old_max_uV; - mutex_unlock(&rdev->mutex); + + return ret; +} + +/** + * regulator_set_voltage - set regulator output voltage + * @regulator: regulator source + * @min_uV: Minimum required voltage in uV + * @max_uV: Maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * during any regulator state. IOW, regulator can be disabled or enabled. + * + * If the regulator is enabled then the voltage will change to the new value + * immediately otherwise if the regulator is disabled the regulator will + * output at the new voltage when enabled. + * + * NOTE: If the regulator is shared between several devices then the lowest + * request voltage that meets the system constraints will be used. + * Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) +{ + int ret = 0; + + regulator_lock_supply(regulator->rdev); + + ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV); + + regulator_unlock_supply(regulator->rdev); + return ret; } EXPORT_SYMBOL_GPL(regulator_set_voltage); @@ -2949,7 +3110,7 @@ static int _regulator_get_voltage(struct regulator_dev *rdev) } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) { ret = rdev->desc->fixed_uV; } else if (rdev->supply) { - ret = regulator_get_voltage(rdev->supply); + ret = _regulator_get_voltage(rdev->supply->rdev); } else { return -EINVAL; } @@ -2972,11 +3133,11 @@ int regulator_get_voltage(struct regulator *regulator) { int ret; - mutex_lock(®ulator->rdev->mutex); + regulator_lock_supply(regulator->rdev); ret = _regulator_get_voltage(regulator->rdev); - mutex_unlock(®ulator->rdev->mutex); + regulator_unlock_supply(regulator->rdev); return ret; } @@ -3810,8 +3971,6 @@ regulator_register(const struct regulator_desc *regulator_desc, } } - list_add(&rdev->list, ®ulator_list); - rdev_init_debugfs(rdev); out: mutex_unlock(®ulator_list_mutex); @@ -3865,6 +4024,19 @@ void regulator_unregister(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_unregister); +static int _regulator_suspend_prepare(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + const suspend_state_t *state = data; + int ret; + + mutex_lock(&rdev->mutex); + ret = suspend_prepare(rdev, *state); + mutex_unlock(&rdev->mutex); + + return ret; +} + /** * regulator_suspend_prepare - prepare regulators for system wide suspend * @state: system suspend state @@ -3874,30 +4046,45 @@ EXPORT_SYMBOL_GPL(regulator_unregister); */ int regulator_suspend_prepare(suspend_state_t state) { - struct regulator_dev *rdev; - int ret = 0; - /* ON is handled by regulator active state */ if (state == PM_SUSPEND_ON) return -EINVAL; - mutex_lock(®ulator_list_mutex); - list_for_each_entry(rdev, ®ulator_list, list) { + return class_for_each_device(®ulator_class, NULL, &state, + _regulator_suspend_prepare); +} +EXPORT_SYMBOL_GPL(regulator_suspend_prepare); - mutex_lock(&rdev->mutex); - ret = suspend_prepare(rdev, state); - mutex_unlock(&rdev->mutex); +static int _regulator_suspend_finish(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + int ret; - if (ret < 0) { - rdev_err(rdev, "failed to prepare\n"); - goto out; + mutex_lock(&rdev->mutex); + if (rdev->use_count > 0 || rdev->constraints->always_on) { + if (!_regulator_is_enabled(rdev)) { + ret = _regulator_do_enable(rdev); + if (ret) + dev_err(dev, + "Failed to resume regulator %d\n", + ret); } + } else { + if (!have_full_constraints()) + goto unlock; + if (!_regulator_is_enabled(rdev)) + goto unlock; + + ret = _regulator_do_disable(rdev); + if (ret) + dev_err(dev, "Failed to suspend regulator %d\n", ret); } -out: - mutex_unlock(®ulator_list_mutex); - return ret; +unlock: + mutex_unlock(&rdev->mutex); + + /* Keep processing regulators in spite of any errors */ + return 0; } -EXPORT_SYMBOL_GPL(regulator_suspend_prepare); /** * regulator_suspend_finish - resume regulators from system wide suspend @@ -3907,33 +4094,8 @@ EXPORT_SYMBOL_GPL(regulator_suspend_prepare); */ int regulator_suspend_finish(void) { - struct regulator_dev *rdev; - int ret = 0, error; - - mutex_lock(®ulator_list_mutex); - list_for_each_entry(rdev, ®ulator_list, list) { - mutex_lock(&rdev->mutex); - if (rdev->use_count > 0 || rdev->constraints->always_on) { - if (!_regulator_is_enabled(rdev)) { - error = _regulator_do_enable(rdev); - if (error) - ret = error; - } - } else { - if (!have_full_constraints()) - goto unlock; - if (!_regulator_is_enabled(rdev)) - goto unlock; - - error = _regulator_do_disable(rdev); - if (error) - ret = error; - } -unlock: - mutex_unlock(&rdev->mutex); - } - mutex_unlock(®ulator_list_mutex); - return ret; + return class_for_each_device(®ulator_class, NULL, NULL, + _regulator_suspend_finish); } EXPORT_SYMBOL_GPL(regulator_suspend_finish); @@ -4053,14 +4215,35 @@ static const struct file_operations supply_map_fops = { }; #ifdef CONFIG_DEBUG_FS +struct summary_data { + struct seq_file *s; + struct regulator_dev *parent; + int level; +}; + +static void regulator_summary_show_subtree(struct seq_file *s, + struct regulator_dev *rdev, + int level); + +static int regulator_summary_show_children(struct device *dev, void *data) +{ + struct regulator_dev *rdev = dev_to_rdev(dev); + struct summary_data *summary_data = data; + + if (rdev->supply && rdev->supply->rdev == summary_data->parent) + regulator_summary_show_subtree(summary_data->s, rdev, + summary_data->level + 1); + + return 0; +} + static void regulator_summary_show_subtree(struct seq_file *s, struct regulator_dev *rdev, int level) { - struct list_head *list = s->private; - struct regulator_dev *child; struct regulation_constraints *c; struct regulator *consumer; + struct summary_data summary_data; if (!rdev) return; @@ -4110,33 +4293,32 @@ static void regulator_summary_show_subtree(struct seq_file *s, seq_puts(s, "\n"); } - list_for_each_entry(child, list, list) { - /* handle only non-root regulators supplied by current rdev */ - if (!child->supply || child->supply->rdev != rdev) - continue; + summary_data.s = s; + summary_data.level = level; + summary_data.parent = rdev; - regulator_summary_show_subtree(s, child, level + 1); - } + class_for_each_device(®ulator_class, NULL, &summary_data, + regulator_summary_show_children); } -static int regulator_summary_show(struct seq_file *s, void *data) +static int regulator_summary_show_roots(struct device *dev, void *data) { - struct list_head *list = s->private; - struct regulator_dev *rdev; - - seq_puts(s, " regulator use open bypass voltage current min max\n"); - seq_puts(s, "-------------------------------------------------------------------------------\n"); + struct regulator_dev *rdev = dev_to_rdev(dev); + struct seq_file *s = data; - mutex_lock(®ulator_list_mutex); + if (!rdev->supply) + regulator_summary_show_subtree(s, rdev, 0); - list_for_each_entry(rdev, list, list) { - if (rdev->supply) - continue; + return 0; +} - regulator_summary_show_subtree(s, rdev, 0); - } +static int regulator_summary_show(struct seq_file *s, void *data) +{ + seq_puts(s, " regulator use open bypass voltage current min max\n"); + seq_puts(s, "-------------------------------------------------------------------------------\n"); - mutex_unlock(®ulator_list_mutex); + class_for_each_device(®ulator_class, NULL, s, + regulator_summary_show_roots); return 0; } @@ -4170,7 +4352,7 @@ static int __init regulator_init(void) &supply_map_fops); debugfs_create_file("regulator_summary", 0444, debugfs_root, - ®ulator_list, ®ulator_summary_fops); + NULL, ®ulator_summary_fops); regulator_dummy_init(); |