diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-24 21:22:33 +0100 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-09-24 21:22:33 +0100 |
commit | baea7b946f00a291b166ccae7fcfed6c01530cc6 (patch) | |
tree | 4aa275fbdbec9c7b9b4629e8bee2bbecd3c6a6af /drivers/regulator/core.c | |
parent | ae19ffbadc1b2100285a5b5b3d0a4e0a11390904 (diff) | |
parent | 94e0fb086fc5663c38bbc0fe86d698be8314f82f (diff) |
Merge branch 'origin' into for-linus
Conflicts:
MAINTAINERS
Diffstat (limited to 'drivers/regulator/core.c')
-rw-r--r-- | drivers/regulator/core.c | 309 |
1 files changed, 242 insertions, 67 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 98c3a74e9949..744ea1d0b59b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -37,7 +37,7 @@ static int has_full_constraints; */ struct regulator_map { struct list_head list; - struct device *dev; + const char *dev_name; /* The dev_name() for the consumer */ const char *supply; struct regulator_dev *regulator; }; @@ -232,7 +232,7 @@ static ssize_t regulator_name_show(struct device *dev, struct regulator_dev *rdev = dev_get_drvdata(dev); const char *name; - if (rdev->constraints->name) + if (rdev->constraints && rdev->constraints->name) name = rdev->constraints->name; else if (rdev->desc->name) name = rdev->desc->name; @@ -280,8 +280,13 @@ static ssize_t regulator_state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct regulator_dev *rdev = dev_get_drvdata(dev); + ssize_t ret; + + mutex_lock(&rdev->mutex); + ret = regulator_print_state(buf, _regulator_is_enabled(rdev)); + mutex_unlock(&rdev->mutex); - return regulator_print_state(buf, _regulator_is_enabled(rdev)); + return ret; } static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); @@ -857,23 +862,39 @@ out: * set_consumer_device_supply: Bind a regulator to a symbolic supply * @rdev: regulator source * @consumer_dev: device the supply applies to + * @consumer_dev_name: dev_name() string for device supply applies to * @supply: symbolic name for supply * * Allows platform initialisation code to map physical regulator * sources to symbolic names for supplies for use by devices. Devices * should use these symbolic names to request regulators, avoiding the * need to provide board-specific regulator names as platform data. + * + * Only one of consumer_dev and consumer_dev_name may be specified. */ static int set_consumer_device_supply(struct regulator_dev *rdev, - struct device *consumer_dev, const char *supply) + struct device *consumer_dev, const char *consumer_dev_name, + const char *supply) { struct regulator_map *node; + int has_dev; + + if (consumer_dev && consumer_dev_name) + return -EINVAL; + + if (!consumer_dev_name && consumer_dev) + consumer_dev_name = dev_name(consumer_dev); if (supply == NULL) return -EINVAL; + if (consumer_dev_name != NULL) + has_dev = 1; + else + has_dev = 0; + list_for_each_entry(node, ®ulator_map_list, list) { - if (consumer_dev != node->dev) + if (consumer_dev_name != node->dev_name) continue; if (strcmp(node->supply, supply) != 0) continue; @@ -886,30 +907,45 @@ static int set_consumer_device_supply(struct regulator_dev *rdev, return -EBUSY; } - node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); + node = kzalloc(sizeof(struct regulator_map), GFP_KERNEL); if (node == NULL) return -ENOMEM; node->regulator = rdev; - node->dev = consumer_dev; node->supply = supply; + if (has_dev) { + node->dev_name = kstrdup(consumer_dev_name, GFP_KERNEL); + if (node->dev_name == NULL) { + kfree(node); + return -ENOMEM; + } + } + list_add(&node->list, ®ulator_map_list); return 0; } static void unset_consumer_device_supply(struct regulator_dev *rdev, - struct device *consumer_dev) + const char *consumer_dev_name, struct device *consumer_dev) { struct regulator_map *node, *n; + if (consumer_dev && !consumer_dev_name) + consumer_dev_name = dev_name(consumer_dev); + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { - if (rdev == node->regulator && - consumer_dev == node->dev) { - list_del(&node->list); - kfree(node); - return; - } + if (rdev != node->regulator) + continue; + + if (consumer_dev_name && node->dev_name && + strcmp(consumer_dev_name, node->dev_name)) + continue; + + list_del(&node->list); + kfree(node->dev_name); + kfree(node); + return; } } @@ -920,6 +956,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev) list_for_each_entry_safe(node, n, ®ulator_map_list, list) { if (rdev == node->regulator) { list_del(&node->list); + kfree(node->dev_name); kfree(node); return; } @@ -1001,35 +1038,33 @@ overflow_err: return NULL; } -/** - * regulator_get - lookup and obtain a reference to a regulator. - * @dev: device for regulator "consumer" - * @id: Supply name or regulator ID. - * - * Returns a struct regulator corresponding to the regulator producer, - * or IS_ERR() condition containing errno. - * - * Use of supply names configured via regulator_set_device_supply() is - * strongly encouraged. It is recommended that the supply name used - * should match the name used for the supply and/or the relevant - * device pins in the datasheet. - */ -struct regulator *regulator_get(struct device *dev, const char *id) +/* Internal regulator request function */ +static struct regulator *_regulator_get(struct device *dev, const char *id, + int exclusive) { struct regulator_dev *rdev; struct regulator_map *map; struct regulator *regulator = ERR_PTR(-ENODEV); + const char *devname = NULL; + int ret; if (id == NULL) { printk(KERN_ERR "regulator: get() with no identifier\n"); return regulator; } + if (dev) + devname = dev_name(dev); + mutex_lock(®ulator_list_mutex); list_for_each_entry(map, ®ulator_map_list, list) { - if (dev == map->dev && - strcmp(map->supply, id) == 0) { + /* 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, id) == 0) { rdev = map->regulator; goto found; } @@ -1038,6 +1073,16 @@ struct regulator *regulator_get(struct device *dev, const char *id) return regulator; found: + if (rdev->exclusive) { + regulator = ERR_PTR(-EPERM); + goto out; + } + + if (exclusive && rdev->open_count) { + regulator = ERR_PTR(-EBUSY); + goto out; + } + if (!try_module_get(rdev->owner)) goto out; @@ -1047,13 +1092,70 @@ found: module_put(rdev->owner); } + rdev->open_count++; + if (exclusive) { + rdev->exclusive = 1; + + ret = _regulator_is_enabled(rdev); + if (ret > 0) + rdev->use_count = 1; + else + rdev->use_count = 0; + } + out: mutex_unlock(®ulator_list_mutex); + return regulator; } + +/** + * regulator_get - lookup and obtain a reference to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 0); +} EXPORT_SYMBOL_GPL(regulator_get); /** + * regulator_get_exclusive - obtain exclusive access to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Other consumers will be + * unable to obtain this reference is held and the use count for the + * regulator will be initialised to reflect the current state of the + * regulator. + * + * This is intended for use by consumers which cannot tolerate shared + * use of the regulator such as those which need to force the + * regulator off for correct operation of the hardware they are + * controlling. + * + * Use of supply names configured via regulator_set_device_supply() is + * strongly encouraged. It is recommended that the supply name used + * should match the name used for the supply and/or the relevant + * device pins in the datasheet. + */ +struct regulator *regulator_get_exclusive(struct device *dev, const char *id) +{ + return _regulator_get(dev, id, 1); +} +EXPORT_SYMBOL_GPL(regulator_get_exclusive); + +/** * regulator_put - "free" the regulator source * @regulator: regulator source * @@ -1081,21 +1183,29 @@ void regulator_put(struct regulator *regulator) list_del(®ulator->list); kfree(regulator); + rdev->open_count--; + rdev->exclusive = 0; + module_put(rdev->owner); mutex_unlock(®ulator_list_mutex); } EXPORT_SYMBOL_GPL(regulator_put); +static int _regulator_can_change_status(struct regulator_dev *rdev) +{ + if (!rdev->constraints) + return 0; + + if (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_STATUS) + return 1; + else + return 0; +} + /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret = -EINVAL; - - if (!rdev->constraints) { - printk(KERN_ERR "%s: %s has no constraints\n", - __func__, rdev->desc->name); - return ret; - } + int ret; /* do we need to enable the supply regulator first */ if (rdev->supply) { @@ -1108,24 +1218,35 @@ static int _regulator_enable(struct regulator_dev *rdev) } /* check voltage and requested load before enabling */ - if (rdev->desc->ops->enable) { - - if (rdev->constraints && - (rdev->constraints->valid_ops_mask & - REGULATOR_CHANGE_DRMS)) - drms_uA_update(rdev); - - ret = rdev->desc->ops->enable(rdev); - if (ret < 0) { - printk(KERN_ERR "%s: failed to enable %s: %d\n", + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + if (rdev->use_count == 0) { + /* The regulator may on if it's not switchable or left on */ + ret = _regulator_is_enabled(rdev); + if (ret == -EINVAL || ret == 0) { + if (!_regulator_can_change_status(rdev)) + return -EPERM; + + if (rdev->desc->ops->enable) { + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + } else { + return -EINVAL; + } + } else if (ret < 0) { + printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", __func__, rdev->desc->name, ret); return ret; } - rdev->use_count++; - return ret; + /* Fallthrough on positive return values - already enabled */ } - return ret; + rdev->use_count++; + + return 0; } /** @@ -1165,7 +1286,8 @@ static int _regulator_disable(struct regulator_dev *rdev) if (rdev->use_count == 1 && !rdev->constraints->always_on) { /* we are last user */ - if (rdev->desc->ops->disable) { + if (_regulator_can_change_status(rdev) && + rdev->desc->ops->disable) { ret = rdev->desc->ops->disable(rdev); if (ret < 0) { printk(KERN_ERR "%s: failed to disable %s\n", @@ -1265,20 +1387,11 @@ EXPORT_SYMBOL_GPL(regulator_force_disable); static int _regulator_is_enabled(struct regulator_dev *rdev) { - int ret; - - mutex_lock(&rdev->mutex); - /* sanity check */ - if (!rdev->desc->ops->is_enabled) { - ret = -EINVAL; - goto out; - } + if (!rdev->desc->ops->is_enabled) + return -EINVAL; - ret = rdev->desc->ops->is_enabled(rdev); -out: - mutex_unlock(&rdev->mutex); - return ret; + return rdev->desc->ops->is_enabled(rdev); } /** @@ -1295,7 +1408,13 @@ out: */ int regulator_is_enabled(struct regulator *regulator) { - return _regulator_is_enabled(regulator->rdev); + int ret; + + mutex_lock(®ulator->rdev->mutex); + ret = _regulator_is_enabled(regulator->rdev); + mutex_unlock(®ulator->rdev->mutex); + + return ret; } EXPORT_SYMBOL_GPL(regulator_is_enabled); @@ -1350,6 +1469,35 @@ int regulator_list_voltage(struct regulator *regulator, unsigned selector) EXPORT_SYMBOL_GPL(regulator_list_voltage); /** + * regulator_is_supported_voltage - check if a voltage range can be supported + * + * @regulator: Regulator to check. + * @min_uV: Minimum required voltage in uV. + * @max_uV: Maximum required voltage in uV. + * + * Returns a boolean or a negative error code. + */ +int regulator_is_supported_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + int i, voltages, ret; + + ret = regulator_count_voltages(regulator); + if (ret < 0) + return ret; + voltages = ret; + + for (i = 0; i < voltages; i++) { + ret = regulator_list_voltage(regulator, i); + + if (ret >= min_uV && ret <= max_uV) + return 1; + } + + return 0; +} + +/** * regulator_set_voltage - set regulator output voltage * @regulator: regulator source * @min_uV: Minimum required voltage in uV @@ -1864,6 +2012,30 @@ int regulator_notifier_call_chain(struct regulator_dev *rdev, } EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); +/** + * regulator_mode_to_status - convert a regulator mode into a status + * + * @mode: Mode to convert + * + * Convert a regulator mode into a status. + */ +int regulator_mode_to_status(unsigned int mode) +{ + switch (mode) { + case REGULATOR_MODE_FAST: + return REGULATOR_STATUS_FAST; + case REGULATOR_MODE_NORMAL: + return REGULATOR_STATUS_NORMAL; + case REGULATOR_MODE_IDLE: + return REGULATOR_STATUS_IDLE; + case REGULATOR_STATUS_STANDBY: + return REGULATOR_STATUS_STANDBY; + default: + return 0; + } +} +EXPORT_SYMBOL_GPL(regulator_mode_to_status); + /* * To avoid cluttering sysfs (and memory) with useless state, only * create attributes that can be meaningfully displayed. @@ -2067,11 +2239,13 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, for (i = 0; i < init_data->num_consumer_supplies; i++) { ret = set_consumer_device_supply(rdev, init_data->consumer_supplies[i].dev, + init_data->consumer_supplies[i].dev_name, init_data->consumer_supplies[i].supply); if (ret < 0) { for (--i; i >= 0; i--) unset_consumer_device_supply(rdev, - init_data->consumer_supplies[i].dev); + init_data->consumer_supplies[i].dev_name, + init_data->consumer_supplies[i].dev); goto scrub; } } @@ -2106,6 +2280,7 @@ void regulator_unregister(struct regulator_dev *rdev) return; mutex_lock(®ulator_list_mutex); + WARN_ON(rdev->open_count); unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) @@ -2253,14 +2428,14 @@ static int __init regulator_init_complete(void) ops = rdev->desc->ops; c = rdev->constraints; - if (c->name) + if (c && c->name) name = c->name; else if (rdev->desc->name) name = rdev->desc->name; else name = "regulator"; - if (!ops->disable || c->always_on) + if (!ops->disable || (c && c->always_on)) continue; mutex_lock(&rdev->mutex); |