From 93e14baa4494607efe81608725f591e3ba31e3c1 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 14 Jan 2009 13:16:27 -0800 Subject: regulator: minor cleanup of virtual consumer Minor cleanup to the regulator set_mode sysfs support: switch to sysfs_streq() in set_mode(), which is also a code shrink. Use the same strings that get_mode() uses, shrinking data too. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 5ddb464..a38d42b 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -226,13 +226,13 @@ static ssize_t set_mode(struct device *dev, struct device_attribute *attr, unsigned int mode; int ret; - if (strncmp(buf, "fast", strlen("fast")) == 0) + if (sysfs_streq(buf, "fast\n") == 0) mode = REGULATOR_MODE_FAST; - else if (strncmp(buf, "normal", strlen("normal")) == 0) + else if (sysfs_streq(buf, "normal\n") == 0) mode = REGULATOR_MODE_NORMAL; - else if (strncmp(buf, "idle", strlen("idle")) == 0) + else if (sysfs_streq(buf, "idle\n") == 0) mode = REGULATOR_MODE_IDLE; - else if (strncmp(buf, "standby", strlen("standby")) == 0) + else if (sysfs_streq(buf, "standby\n") == 0) mode = REGULATOR_MODE_STANDBY; else { dev_err(dev, "Configuring invalid mode\n"); -- cgit v0.10.2 From 853116a10544206b6b2cf42ebc9d78fba2668888 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 14 Jan 2009 23:03:17 -0800 Subject: regulator: add get_status() Based on previous LKML discussions: * Update docs for regulator sysfs class attributes to highlight the fact that all current attributes are intended to be control inputs, including notably "state" and "opmode" which previously implied otherwise. * Define a new regulator driver get_status() method, which is the first method reporting regulator outputs instead of inputs. It can report on/off and error status; or instead of simply "on", report the actual operating mode. For the moment, this is a sysfs-only interface, not accessible to regulator clients. Such clients can use the current notification interfaces to detect errors, if the regulator reports them. Signed-off-by: David Brownell Signed-off-by: Liam Girdwood diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator index 873ef1f..e091fa8 100644 --- a/Documentation/ABI/testing/sysfs-class-regulator +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -4,8 +4,8 @@ KernelVersion: 2.6.26 Contact: Liam Girdwood Description: Some regulator directories will contain a field called - state. This reports the regulator enable status, for - regulators which can report that value. + state. This reports the regulator enable control, for + regulators which can report that input value. This will be one of the following strings: @@ -14,16 +14,54 @@ Description: 'unknown' 'enabled' means the regulator output is ON and is supplying - power to the system. + power to the system (assuming no error prevents it). 'disabled' means the regulator output is OFF and is not - supplying power to the system.. + supplying power to the system (unless some non-Linux + control has enabled it). 'unknown' means software cannot determine the state, or the reported state is invalid. NOTE: this field can be used in conjunction with microvolts - and microamps to determine regulator output levels. + or microamps to determine configured regulator output levels. + + +What: /sys/class/regulator/.../status +Description: + Some regulator directories will contain a field called + "status". This reports the current regulator status, for + regulators which can report that output value. + + This will be one of the following strings: + + off + on + error + fast + normal + idle + standby + + "off" means the regulator is not supplying power to the + system. + + "on" means the regulator is supplying power to the system, + and the regulator can't report a detailed operation mode. + + "error" indicates an out-of-regulation status such as being + disabled due to thermal shutdown, or voltage being unstable + because of problems with the input power supply. + + "fast", "normal", "idle", and "standby" are all detailed + regulator operation modes (described elsewhere). They + imply "on", but provide more detail. + + Note that regulator status is a function of many inputs, + not limited to control inputs from Linux. For example, + the actual load presented may trigger "error" status; or + a regulator may be enabled by another user, even though + Linux did not enable it. What: /sys/class/regulator/.../type @@ -58,7 +96,7 @@ Description: Some regulator directories will contain a field called microvolts. This holds the regulator output voltage setting measured in microvolts (i.e. E-6 Volts), for regulators - which can report that voltage. + which can report the control input for voltage. NOTE: This value should not be used to determine the regulator output voltage level as this value is the same regardless of @@ -73,7 +111,7 @@ Description: Some regulator directories will contain a field called microamps. This holds the regulator output current limit setting measured in microamps (i.e. E-6 Amps), for regulators - which can report that current. + which can report the control input for a current limit. NOTE: This value should not be used to determine the regulator output current level as this value is the same regardless of @@ -87,7 +125,7 @@ Contact: Liam Girdwood Description: Some regulator directories will contain a field called opmode. This holds the current regulator operating mode, - for regulators which can report it. + for regulators which can report that control input value. The opmode value can be one of the following strings: @@ -101,7 +139,8 @@ Description: NOTE: This value should not be used to determine the regulator output operating mode as this value is the same regardless of - whether the regulator is enabled or disabled. + whether the regulator is enabled or disabled. A "status" + attribute may be available to determine the actual mode. What: /sys/class/regulator/.../min_microvolts diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f511a40..0ff95c3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -312,6 +312,47 @@ static ssize_t regulator_state_show(struct device *dev, } static DEVICE_ATTR(state, 0444, regulator_state_show, NULL); +static ssize_t regulator_status_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = dev_get_drvdata(dev); + int status; + char *label; + + status = rdev->desc->ops->get_status(rdev); + if (status < 0) + return status; + + switch (status) { + case REGULATOR_STATUS_OFF: + label = "off"; + break; + case REGULATOR_STATUS_ON: + label = "on"; + break; + case REGULATOR_STATUS_ERROR: + label = "error"; + break; + case REGULATOR_STATUS_FAST: + label = "fast"; + break; + case REGULATOR_STATUS_NORMAL: + label = "normal"; + break; + case REGULATOR_STATUS_IDLE: + label = "idle"; + break; + case REGULATOR_STATUS_STANDBY: + label = "standby"; + break; + default: + return -ERANGE; + } + + return sprintf(buf, "%s\n", label); +} +static DEVICE_ATTR(status, 0444, regulator_status_show, NULL); + static ssize_t regulator_min_uA_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1744,6 +1785,11 @@ static int add_regulator_attributes(struct regulator_dev *rdev) if (status < 0) return status; } + if (ops->get_status) { + status = device_create_file(dev, &dev_attr_status); + if (status < 0) + return status; + } /* some attributes are type-specific */ if (rdev->desc->type == REGULATOR_CURRENT) { diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 2dae057..6e957aa 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -21,6 +21,17 @@ struct regulator_dev; struct regulator_init_data; +enum regulator_status { + REGULATOR_STATUS_OFF, + REGULATOR_STATUS_ON, + REGULATOR_STATUS_ERROR, + /* fast/normal/idle/standby are flavors of "on" */ + REGULATOR_STATUS_FAST, + REGULATOR_STATUS_NORMAL, + REGULATOR_STATUS_IDLE, + REGULATOR_STATUS_STANDBY, +}; + /** * struct regulator_ops - regulator operations. * @@ -72,6 +83,12 @@ struct regulator_ops { int (*set_mode) (struct regulator_dev *, unsigned int mode); unsigned int (*get_mode) (struct regulator_dev *); + /* report regulator status ... most other accessors report + * control inputs, this reports results of combining inputs + * from Linux (and other sources) with the actual load. + */ + int (*get_status)(struct regulator_dev *); + /* get most efficient regulator operating mode for load */ unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, int output_uV, int load_uA); -- cgit v0.10.2 From 9485397aa2195e82da6373586a66689526675ad4 Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Thu, 15 Jan 2009 16:13:01 -0800 Subject: regulator: minor cleanup of virtual consumer On Thu, 15 Jan 2009 16:10:22 -0800 Andrew Morton wrote: > On Wed, 14 Jan 2009 13:16:27 -0800 > David Brownell wrote: > > > From: David Brownell > > > > Minor cleanup to the regulator set_mode sysfs support: > > switch to sysfs_streq() in set_mode(), which is also > > a code shrink. Use the same strings that get_mode() > > uses, shrinking data too. > > > > Signed-off-by: David Brownell > > --- > > drivers/regulator/virtual.c | 8 ++++---- > > 1 file changed, 4 insertions(+), 4 deletions(-) > > > > --- a/drivers/regulator/virtual.c > > +++ b/drivers/regulator/virtual.c > > @@ -226,13 +226,13 @@ static ssize_t set_mode(struct device *d > > unsigned int mode; > > int ret; > > > > - if (strncmp(buf, "fast", strlen("fast")) == 0) > > + if (sysfs_streq(buf, "fast\n") == 0) > > mode = REGULATOR_MODE_FAST; > > - else if (strncmp(buf, "normal", strlen("normal")) == 0) > > + else if (sysfs_streq(buf, "normal\n") == 0) > > mode = REGULATOR_MODE_NORMAL; > > - else if (strncmp(buf, "idle", strlen("idle")) == 0) > > + else if (sysfs_streq(buf, "idle\n") == 0) > > mode = REGULATOR_MODE_IDLE; > > - else if (strncmp(buf, "standby", strlen("standby")) == 0) > > + else if (sysfs_streq(buf, "standby\n") == 0) > > mode = REGULATOR_MODE_STANDBY; > > we don't need the \n's, do we? oh, it's for the string sharing. Sneaky. I wonder how many people will try to fix that up for us? Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index a38d42b..45ebbc2 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -226,6 +226,10 @@ static ssize_t set_mode(struct device *dev, struct device_attribute *attr, unsigned int mode; int ret; + /* + * sysfs_streq() doesn't need the \n's, but we add them so the strings + * will be shared with show_mode(), above. + */ if (sysfs_streq(buf, "fast\n") == 0) mode = REGULATOR_MODE_FAST; else if (sysfs_streq(buf, "normal\n") == 0) -- cgit v0.10.2 From b136fb4463d13eea129bf090a8a465bba6bf0003 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 19 Jan 2009 18:20:58 +0000 Subject: Regulator: Push lock out of _notifier_call_chain + add voltage change event. Regulator: Push lock out of _notifier_call_chain and into caller functions (side effect of fixing deadlock in regulator_force_disable) + Add a voltage changed event. Signed-off-by: Jonathan Cameron Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0ff95c3..96c877d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1284,6 +1284,7 @@ int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV); out: + _notifier_call_chain(rdev, REGULATOR_EVENT_VOLTAGE_CHANGE, NULL); mutex_unlock(&rdev->mutex); return ret; } @@ -1584,20 +1585,23 @@ int regulator_unregister_notifier(struct regulator *regulator, } EXPORT_SYMBOL_GPL(regulator_unregister_notifier); -/* notify regulator consumers and downstream regulator consumers */ +/* notify regulator consumers and downstream regulator consumers. + * Note mutex must be held by caller. + */ static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { struct regulator_dev *_rdev; /* call rdev chain first */ - mutex_lock(&rdev->mutex); blocking_notifier_call_chain(&rdev->notifier, event, NULL); - mutex_unlock(&rdev->mutex); /* now notify regulator we supply */ - list_for_each_entry(_rdev, &rdev->supply_list, slist) - _notifier_call_chain(_rdev, event, data); + list_for_each_entry(_rdev, &rdev->supply_list, slist) { + mutex_lock(&_rdev->mutex); + _notifier_call_chain(_rdev, event, data); + mutex_unlock(&_rdev->mutex); + } } /** @@ -1744,6 +1748,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free); * * Called by regulator drivers to notify clients a regulator event has * occurred. We also notify regulator clients downstream. + * Note lock must be held by caller. */ int regulator_notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 5056e23..afad611 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1293,6 +1293,7 @@ static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data) { struct regulator_dev *rdev = (struct regulator_dev *)data; + mutex_lock(&rdev->mutex); if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_REGULATION_OUT, @@ -1301,6 +1302,7 @@ static void pmic_uv_handler(struct wm8350 *wm8350, int irq, void *data) regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, wm8350); + mutex_unlock(&rdev->mutex); } static int wm8350_regulator_probe(struct platform_device *pdev) diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 801bf77..533f4e2 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -88,6 +88,7 @@ * FAIL Regulator output has failed. * OVER_TEMP Regulator over temp. * FORCE_DISABLE Regulator shut down by software. + * VOLTAGE_CHANGE Regulator voltage changed. * * NOTE: These events can be OR'ed together when passed into handler. */ @@ -98,6 +99,7 @@ #define REGULATOR_EVENT_FAIL 0x08 #define REGULATOR_EVENT_OVER_TEMP 0x10 #define REGULATOR_EVENT_FORCE_DISABLE 0x20 +#define REGULATOR_EVENT_VOLTAGE_CHANGE 0x40 struct regulator; -- cgit v0.10.2 From 0527100fd11d9710c7e153d791da78824b7b46fa Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Jan 2009 13:37:02 +0000 Subject: regulator: Pass regulator init data as explict argument when registering Rather than having the regulator init data read from the platform_data member of the struct device that is registered for the regulator make the init data an explict argument passed in when registering. This allows drivers to use the platform data for their own purposes if they wish. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index c175e38..6804333 100644 --- a/drivers/regulator/bq24022.c +++ b/drivers/regulator/bq24022.c @@ -105,7 +105,7 @@ static int __init bq24022_probe(struct platform_device *pdev) ret = gpio_direction_output(pdata->gpio_iset2, 0); ret = gpio_direction_output(pdata->gpio_nce, 1); - bq24022 = regulator_register(&bq24022_desc, &pdev->dev, pdata); + bq24022 = regulator_register(&bq24022_desc, &pdev->dev, NULL, pdata); if (IS_ERR(bq24022)) { dev_dbg(&pdev->dev, "couldn't register regulator\n"); ret = PTR_ERR(bq24022); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 96c877d..f17362a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1879,17 +1879,18 @@ static int add_regulator_attributes(struct regulator_dev *rdev) * regulator_register - register regulator * @regulator_desc: regulator to register * @dev: struct device for the regulator + * @init_data: platform provided init data, passed through by driver * @driver_data: private regulator data * * Called by regulator drivers to register a regulator. * Returns 0 on success. */ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - struct device *dev, void *driver_data) + struct device *dev, struct regulator_init_data *init_data, + void *driver_data) { static atomic_t regulator_no = ATOMIC_INIT(0); struct regulator_dev *rdev; - struct regulator_init_data *init_data = dev->platform_data; int ret, i; if (regulator_desc == NULL) diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index fe77730..72b1549 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -471,7 +471,8 @@ static int __devinit da903x_regulator_probe(struct platform_device *pdev) if (ri->desc.id == DA9030_ID_LDO1 || ri->desc.id == DA9030_ID_LDO15) ri->desc.ops = &da9030_regulator_ldo1_15_ops; - rdev = regulator_register(&ri->desc, &pdev->dev, ri); + rdev = regulator_register(&ri->desc, &pdev->dev, + pdev->dev.platform_data, ri); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", ri->desc.name); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index 4cc85ec..cd761d8 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -284,7 +284,8 @@ static int __devinit pcf50633_regulator_probe(struct platform_device *pdev) /* Already set by core driver */ pcf = platform_get_drvdata(pdev); - rdev = regulator_register(®ulators[pdev->id], &pdev->dev, pcf); + rdev = regulator_register(®ulators[pdev->id], &pdev->dev, + pdev->dev.platform_data, pcf); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index afad611..93e0ce5 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1335,9 +1335,9 @@ static int wm8350_regulator_probe(struct platform_device *pdev) break; } - /* register regulator */ rdev = regulator_register(&wm8350_reg[pdev->id], &pdev->dev, + pdev->dev.platform_data, dev_get_drvdata(&pdev->dev)); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register %s\n", diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 56e23d4..6ed43b0 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -294,7 +294,7 @@ static int __devinit wm8400_regulator_probe(struct platform_device *pdev) struct regulator_dev *rdev; rdev = regulator_register(®ulators[pdev->id], &pdev->dev, - pdev->dev.driver_data); + pdev->dev.platform_data, pdev->dev.driver_data); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 6e957aa..2254ad9 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -138,7 +138,8 @@ struct regulator_desc { }; struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, - struct device *dev, void *driver_data); + struct device *dev, struct regulator_init_data *init_data, + void *driver_data); void regulator_unregister(struct regulator_dev *rdev); int regulator_notifier_call_chain(struct regulator_dev *rdev, -- cgit v0.10.2 From 93c62da23a717f59933ec799688da42f71d8c6c4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Jan 2009 13:37:03 +0000 Subject: regulator: Allow init data to be supplied for bq24022 Previously it was not possible to do so, making it impossible for machines to configure the driver. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index 6804333..7ecb820 100644 --- a/drivers/regulator/bq24022.c +++ b/drivers/regulator/bq24022.c @@ -105,7 +105,8 @@ static int __init bq24022_probe(struct platform_device *pdev) ret = gpio_direction_output(pdata->gpio_iset2, 0); ret = gpio_direction_output(pdata->gpio_nce, 1); - bq24022 = regulator_register(&bq24022_desc, &pdev->dev, NULL, pdata); + bq24022 = regulator_register(&bq24022_desc, &pdev->dev, + pdata->init_data, pdata); if (IS_ERR(bq24022)) { dev_dbg(&pdev->dev, "couldn't register regulator\n"); ret = PTR_ERR(bq24022); diff --git a/include/linux/regulator/bq24022.h b/include/linux/regulator/bq24022.h index e84b0a9..a6d0140 100644 --- a/include/linux/regulator/bq24022.h +++ b/include/linux/regulator/bq24022.h @@ -10,6 +10,8 @@ * */ +struct regulator_init_data; + /** * bq24022_mach_info - platform data for bq24022 * @gpio_nce: GPIO line connected to the nCE pin, used to enable / disable charging @@ -18,4 +20,5 @@ struct bq24022_mach_info { int gpio_nce; int gpio_iset2; + struct regulator_init_data *init_data; }; -- cgit v0.10.2 From bcf3402c50a48d51462f37f72129d9c4369702b4 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Jan 2009 13:37:04 +0000 Subject: regulator: Allow init_data to be passed to fixed voltage regulators Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index d31db3e..23d5546 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -73,7 +73,8 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) drvdata->microvolts = config->microvolts; - drvdata->dev = regulator_register(&drvdata->desc, drvdata); + drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, + config->init_data, drvdata); if (IS_ERR(drvdata->dev)) { ret = PTR_ERR(drvdata->dev); goto err_name; diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h index 1387a5d..91b4da3 100644 --- a/include/linux/regulator/fixed.h +++ b/include/linux/regulator/fixed.h @@ -14,9 +14,12 @@ #ifndef __REGULATOR_FIXED_H #define __REGULATOR_FIXED_H +struct regulator_init_data; + struct fixed_voltage_config { const char *supply_name; int microvolts; + struct regulator_init_data *init_data; }; #endif -- cgit v0.10.2 From a9cb63b2a1768f40999e09939fd015cd203aa053 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Jan 2009 13:37:05 +0000 Subject: regulator: Make fixed voltage regulators visible in Kconfig This allows users to enable or disable support for these regulators at build time as they can for other regulators rather than having platforms force the regulators to be built in. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e7e0cf1..85a1f40 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -29,8 +29,12 @@ config REGULATOR_DEBUG Say yes here to enable debugging support. config REGULATOR_FIXED_VOLTAGE - tristate + tristate "Fixed voltage regulator support" default n + help + This driver provides support for fixed voltage regulators, + useful for systems which use a combination of software + managed regulators and simple non-configurable regulators. config REGULATOR_VIRTUAL_CONSUMER tristate "Virtual regulator consumer support" -- cgit v0.10.2 From fefdae42465facfa26d41a7f0010f1ade32c3386 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 Jan 2009 13:37:06 +0000 Subject: regulator: Mark attributes table for virtual regulator static It's not exported. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 45ebbc2..3d08348 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -260,7 +260,7 @@ static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA); static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA); static DEVICE_ATTR(mode, 0666, show_mode, set_mode); -struct device_attribute *attributes[] = { +static struct device_attribute *attributes[] = { &dev_attr_min_microvolts, &dev_attr_max_microvolts, &dev_attr_min_microamps, -- cgit v0.10.2 From 1fa9ad52b07811ebf258f3f6907de8dbf020ec2d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 21 Jan 2009 14:08:40 +0000 Subject: regulator: Hoist struct regulator_dev out of core to fix notifiers Commit 872ed3fe176833f7d43748eb88010da4bbd2f983 caused regulator drivers to take the struct regulator_dev lock themselves which requires that the struct be visible to them. Band aid this by making the struct visible. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f17362a..0ed13c2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -30,33 +30,6 @@ static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); /* - * struct regulator_dev - * - * Voltage / Current regulator class device. One for each regulator. - */ -struct regulator_dev { - struct regulator_desc *desc; - int use_count; - - /* lists we belong to */ - struct list_head list; /* list of all regulators */ - struct list_head slist; /* list of supplied regulators */ - - /* lists we own */ - struct list_head consumer_list; /* consumers we supply */ - struct list_head supply_list; /* regulators we supply */ - - struct blocking_notifier_head notifier; - struct mutex mutex; /* consumer lock */ - struct module *owner; - struct device dev; - struct regulation_constraints *constraints; - struct regulator_dev *supply; /* for tree */ - - void *reg_data; /* regulator_dev data */ -}; - -/* * struct regulator_map * * Used to provide symbolic supply names to devices. diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 2254ad9..c263e36 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -137,6 +137,38 @@ struct regulator_desc { struct module *owner; }; +/* + * struct regulator_dev + * + * Voltage / Current regulator class device. One for each + * regulator. + * + * This should *not* be used directly by anything except the regulator + * core and notification injection (which should take the mutex and do + * no other direct access). + */ +struct regulator_dev { + struct regulator_desc *desc; + int use_count; + + /* lists we belong to */ + struct list_head list; /* list of all regulators */ + struct list_head slist; /* list of supplied regulators */ + + /* lists we own */ + struct list_head consumer_list; /* consumers we supply */ + struct list_head supply_list; /* regulators we supply */ + + struct blocking_notifier_head notifier; + struct mutex mutex; /* consumer lock */ + struct module *owner; + struct device dev; + struct regulation_constraints *constraints; + struct regulator_dev *supply; /* for tree */ + + void *reg_data; /* regulator_dev data */ +}; + struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, struct device *dev, struct regulator_init_data *init_data, void *driver_data); -- cgit v0.10.2 From 90ca563b1030bece8a4f15a910e39a46f059ff48 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 20 Jan 2009 16:29:05 -0800 Subject: regulator: fix header file missing kernel-doc Fix regulator/driver.h missing kernel-doc: Warning(linux-next-20090120//include/linux/regulator/driver.h:108): No description found for parameter 'get_status' Signed-off-by: Randy Dunlap cc: Liam Girdwood cc: Mark Brown Signed-off-by: Liam Girdwood diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index c263e36..eb8773b 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -51,6 +51,7 @@ enum regulator_status { * * @set_mode: Set the operating mode for the regulator. * @get_mode: Get the current operating mode for the regulator. + * @get_status: Report the regulator status. * @get_optimum_mode: Get the most efficient operating mode for the regulator * when running with the specified parameters. * -- cgit v0.10.2 From 0f1d747bfa89de4ca52dc1dffdcce35a2b8a1532 Mon Sep 17 00:00:00 2001 From: Mike Rapoport Date: Thu, 22 Jan 2009 16:00:29 +0200 Subject: regulator: add unset_regulator_supplies to fix regulator_unregister Currently regulator_unregister does not clear regulator <--> consumer mapping. This patch introduces unset_regulator_supplies that clear the map. Signed-off-by: Mike Rapoport Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0ed13c2..b85e4a9 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -831,6 +831,19 @@ static void unset_consumer_device_supply(struct regulator_dev *rdev, } } +static void unset_regulator_supplies(struct regulator_dev *rdev) +{ + struct regulator_map *node, *n; + + list_for_each_entry_safe(node, n, ®ulator_map_list, list) { + if (rdev == node->regulator) { + list_del(&node->list); + kfree(node); + return; + } + } +} + #define REG_STR_SIZE 32 static struct regulator *create_regulator(struct regulator_dev *rdev, @@ -1970,6 +1983,7 @@ void regulator_unregister(struct regulator_dev *rdev) return; mutex_lock(®ulator_list_mutex); + unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) sysfs_remove_link(&rdev->dev.kobj, "supply"); -- cgit v0.10.2 From 1dd68f01886a2d5cabbbe90b86e82f70917de89c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 2 Feb 2009 21:43:31 +0000 Subject: regulator: email - update email address and regulator webpage. Remove deceased email address and update to new address. Also update website details in MAINTAINERS with correct page. Signed-off-by: Liam Girdwood diff --git a/MAINTAINERS b/MAINTAINERS index c5f4e9d..15e1b73 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4850,7 +4850,7 @@ M: lrg@slimlogic.co.uk P: Mark Brown M: broonie@opensource.wolfsonmicro.com W: http://opensource.wolfsonmicro.com/node/15 -W: http://www.slimlogic.co.uk/?page_id=5 +W: http://www.slimlogic.co.uk/?p=48 T: git kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6.git S: Supported diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 533f4e2..df6c4bc 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -3,7 +3,7 @@ * * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. * - * Author: Liam Girdwood + * Author: Liam Girdwood * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index eb8773b..0cf37bc 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -3,7 +3,7 @@ * * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. * - * Author: Liam Girdwood + * Author: Liam Girdwood * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 3794773..5aa00ee 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -3,7 +3,7 @@ * * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. * - * Author: Liam Girdwood + * Author: Liam Girdwood * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as -- cgit v0.10.2 From fe203ddfa5451a13589b1c7da9edab80b7fc06d1 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Feb 2009 15:32:06 +0000 Subject: regulator: Suggest use of datasheet supply or pin names for consumers Update the documentation to suggest the use of datasheet names for the supplies requested by regulator consumers. Doing this makes it easier to tie the design for a given platform up with the requirements of the driver for a consumer. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b85e4a9..d55a25a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -925,9 +925,12 @@ overflow_err: * @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. + * 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) { -- cgit v0.10.2 From a308466c24b4f42bab6945026e938874d22cde50 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2009 19:24:19 +0000 Subject: regulator: Allow regulators to set the initial operating mode This is useful when wishing to run in a fixed operating mode that isn't the default. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d55a25a..75abcd8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -724,6 +724,23 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } + if (constraints->initial_mode) { + if (!ops->set_mode) { + printk(KERN_ERR "%s: no set_mode operation for %s\n", + __func__, name); + ret = -EINVAL; + goto out; + } + + ret = ops->set_mode(rdev, constraints->initial_mode); + if (ret < 0) { + printk(KERN_ERR + "%s: failed to set initial mode for %s: %d\n", + __func__, name, ret); + goto out; + } + } + /* if always_on is set then turn the regulator on if it's not * already on. */ if (constraints->always_on && ops->enable && diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 5aa00ee..1eb861c 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -83,6 +83,7 @@ struct regulator_state { * @state_standby: State for regulator when system is suspended in standby * mode. * @initial_state: Suspend state to set by default. + * @initial_mode: Mode to set at startup. */ struct regulation_constraints { @@ -111,6 +112,9 @@ struct regulation_constraints { struct regulator_state state_standby; suspend_state_t initial_state; /* suspend state to set at init */ + /* mode to set on startup */ + unsigned int initial_mode; + /* constriant flags */ unsigned always_on:1; /* regulator never off when system is on */ unsigned boot_on:1; /* bootloader/firmware enabled regulator */ -- cgit v0.10.2 From 33f301af0c56971e3c0f4a4eb4b92f7e80230f49 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 26 Feb 2009 19:24:20 +0000 Subject: regulator: Fix get_mode() for WM835x DCDCs The WM835x regulators need a different register checking for force mode on each DCDC. Previously the force mode status for DCDC1 was checked. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 93e0ce5..261db94 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1031,18 +1031,30 @@ static unsigned int wm8350_dcdc_get_mode(struct regulator_dev *rdev) int dcdc = rdev_get_id(rdev); u16 mask, sleep, active, force; int mode = REGULATOR_MODE_NORMAL; + int reg; - if (dcdc < WM8350_DCDC_1 || dcdc > WM8350_DCDC_6) - return -EINVAL; - - if (dcdc == WM8350_DCDC_2 || dcdc == WM8350_DCDC_5) + switch (dcdc) { + case WM8350_DCDC_1: + reg = WM8350_DCDC1_FORCE_PWM; + break; + case WM8350_DCDC_3: + reg = WM8350_DCDC3_FORCE_PWM; + break; + case WM8350_DCDC_4: + reg = WM8350_DCDC4_FORCE_PWM; + break; + case WM8350_DCDC_6: + reg = WM8350_DCDC6_FORCE_PWM; + break; + default: return -EINVAL; + } mask = 1 << (dcdc - WM8350_DCDC_1); active = wm8350_reg_read(wm8350, WM8350_DCDC_ACTIVE_OPTIONS) & mask; + force = wm8350_reg_read(wm8350, reg) & WM8350_DCDC1_FORCE_PWM_ENA; sleep = wm8350_reg_read(wm8350, WM8350_DCDC_SLEEP_OPTIONS) & mask; - force = wm8350_reg_read(wm8350, WM8350_DCDC1_FORCE_PWM) - & WM8350_DCDC1_FORCE_PWM_ENA; + dev_dbg(wm8350->dev, "mask %x active %x sleep %x force %x", mask, active, sleep, force); -- cgit v0.10.2 From 4367cfdc7c657ad8a797f51b9ffd3c64b31910e7 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 26 Feb 2009 11:48:36 -0800 Subject: regulator: enumerate voltages (v2) Add a basic mechanism for regulators to report the discrete voltages they support: list_voltage() enumerates them using selectors numbered from 0 to an upper bound. Use those methods to force machine-level constraints into bounds. (Example: regulator supports 1.8V, 2.4V, 2.6V, 3.3V, and board constraints for that rail are 2.0V to 3.6V ... so the range of voltages is then 2.4V to 3.3V on this board.) Export those voltages to the regulator consumer interface, so for example regulator hooked up to an MMC/SD/SDIO slot can report the actual voltage options available to cards connected there. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75abcd8..da357a0 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -692,6 +692,69 @@ static int set_machine_constraints(struct regulator_dev *rdev, else name = "regulator"; + /* constrain machine-level voltage specs to fit + * the actual range supported by this regulator. + */ + if (ops->list_voltage && rdev->desc->n_voltages) { + int count = rdev->desc->n_voltages; + int i; + int min_uV = INT_MAX; + int max_uV = INT_MIN; + int cmin = constraints->min_uV; + int cmax = constraints->max_uV; + + /* it's safe to autoconfigure fixed-voltage supplies */ + if (count == 1 && !cmin) { + cmin = INT_MIN; + cmax = INT_MAX; + } + + /* else require explicit machine-level constraints */ + else if (cmin <= 0 || cmax <= 0 || cmax < cmin) { + pr_err("%s: %s '%s' voltage constraints\n", + __func__, "invalid", name); + ret = -EINVAL; + goto out; + } + + /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */ + for (i = 0; i < count; i++) { + int value; + + value = ops->list_voltage(rdev, i); + if (value <= 0) + continue; + + /* maybe adjust [min_uV..max_uV] */ + if (value >= cmin && value < min_uV) + min_uV = value; + if (value <= cmax && value > max_uV) + max_uV = value; + } + + /* final: [min_uV..max_uV] valid iff constraints valid */ + if (max_uV < min_uV) { + pr_err("%s: %s '%s' voltage constraints\n", + __func__, "unsupportable", name); + ret = -EINVAL; + goto out; + } + + /* use regulator's subset of machine constraints */ + if (constraints->min_uV < min_uV) { + pr_debug("%s: override '%s' %s, %d -> %d\n", + __func__, name, "min_uV", + constraints->min_uV, min_uV); + constraints->min_uV = min_uV; + } + if (constraints->max_uV > max_uV) { + pr_debug("%s: override '%s' %s, %d -> %d\n", + __func__, name, "max_uV", + constraints->max_uV, max_uV); + constraints->max_uV = max_uV; + } + } + rdev->constraints = constraints; /* do we need to apply the constraint voltage */ @@ -1251,6 +1314,56 @@ int regulator_is_enabled(struct regulator *regulator) EXPORT_SYMBOL_GPL(regulator_is_enabled); /** + * regulator_count_voltages - count regulator_list_voltage() selectors + * @regulator: regulator source + * + * Returns number of selectors, or negative errno. Selectors are + * numbered starting at zero, and typically correspond to bitfields + * in hardware registers. + */ +int regulator_count_voltages(struct regulator *regulator) +{ + struct regulator_dev *rdev = regulator->rdev; + + return rdev->desc->n_voltages ? : -EINVAL; +} +EXPORT_SYMBOL_GPL(regulator_count_voltages); + +/** + * regulator_list_voltage - enumerate supported voltages + * @regulator: regulator source + * @selector: identify voltage to list + * Context: can sleep + * + * Returns a voltage that can be passed to @regulator_set_voltage(), + * zero if this selector code can't be used on this sytem, or a + * negative errno. + */ +int regulator_list_voltage(struct regulator *regulator, unsigned selector) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator_ops *ops = rdev->desc->ops; + int ret; + + if (!ops->list_voltage || selector >= rdev->desc->n_voltages) + return -EINVAL; + + mutex_lock(&rdev->mutex); + ret = ops->list_voltage(rdev, selector); + mutex_unlock(&rdev->mutex); + + if (ret > 0) { + if (ret < rdev->constraints->min_uV) + ret = 0; + else if (ret > rdev->constraints->max_uV) + ret = 0; + } + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_list_voltage); + +/** * regulator_set_voltage - set regulator output voltage * @regulator: regulator source * @min_uV: Minimum required voltage in uV diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index df6c4bc..277f4b9 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -142,6 +142,8 @@ int regulator_bulk_disable(int num_consumers, void regulator_bulk_free(int num_consumers, struct regulator_bulk_data *consumers); +int regulator_count_voltages(struct regulator *regulator); +int regulator_list_voltage(struct regulator *regulator, unsigned selector); int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); int regulator_get_voltage(struct regulator *regulator); int regulator_set_current_limit(struct regulator *regulator, diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 0cf37bc..2255468 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -45,6 +45,10 @@ enum regulator_status { * @set_voltage: Set the voltage for the regulator within the range specified. * The driver should select the voltage closest to min_uV. * @get_voltage: Return the currently configured voltage for the regulator. + * @list_voltage: Return one of the supported voltages, in microvolts; zero + * if the selector indicates a voltage that is unusable on this system; + * or negative errno. Selectors range from zero to one less than + * regulator_desc.n_voltages. Voltages may be reported in any order. * * @set_current_limit: Configure a limit for a current-limited regulator. * @get_current_limit: Get the limit for a current-limited regulator. @@ -66,6 +70,9 @@ enum regulator_status { */ struct regulator_ops { + /* enumerate supported voltages */ + int (*list_voltage) (struct regulator_dev *, unsigned selector); + /* get/set regulator voltage */ int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV); int (*get_voltage) (struct regulator_dev *); @@ -124,6 +131,7 @@ enum regulator_type { * * @name: Identifying name for the regulator. * @id: Numerical identifier for the regulator. + * @n_voltages: Number of selectors available for ops.list_voltage(). * @ops: Regulator operations table. * @irq: Interrupt number for the regulator. * @type: Indicates if the regulator is a voltage or current regulator. @@ -132,6 +140,7 @@ enum regulator_type { struct regulator_desc { const char *name; int id; + unsigned n_voltages; struct regulator_ops *ops; int irq; enum regulator_type type; -- cgit v0.10.2 From 3b2a6061afe6fcc44437cd5ec641b0aeb2825ee3 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 26 Feb 2009 13:28:41 -0800 Subject: regulator: get_status() grows kerneldoc Add kerneldoc for the new get_status() message. Fix the existing kerneldoc for that struct in two ways: (a) Syntax, making sure parameter descriptions immediately follow the one-line struct description and that the first blank lines is before any more expansive description; (b) Presentation for a few points, to highlight the fact that the previous "get" methods exist only to report the current configuration, not to display actual status. Signed-off-by: David Brownell Signed-off-by: Liam Girdwood diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 2255468..4848d8d 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -35,11 +35,8 @@ enum regulator_status { /** * struct regulator_ops - regulator operations. * - * This struct describes regulator operations which can be implemented by - * regulator chip drivers. - * - * @enable: Enable the regulator. - * @disable: Disable the regulator. + * @enable: Configure the regulator as enabled. + * @disable: Configure the regulator as disabled. * @is_enabled: Return 1 if the regulator is enabled, 0 otherwise. * * @set_voltage: Set the voltage for the regulator within the range specified. @@ -51,11 +48,11 @@ enum regulator_status { * regulator_desc.n_voltages. Voltages may be reported in any order. * * @set_current_limit: Configure a limit for a current-limited regulator. - * @get_current_limit: Get the limit for a current-limited regulator. + * @get_current_limit: Get the configured limit for a current-limited regulator. * - * @set_mode: Set the operating mode for the regulator. - * @get_mode: Get the current operating mode for the regulator. - * @get_status: Report the regulator status. + * @get_mode: Get the configured operating mode for the regulator. + * @get_status: Return actual (not as-configured) status of regulator, as a + * REGULATOR_STATUS value (or negative errno) * @get_optimum_mode: Get the most efficient operating mode for the regulator * when running with the specified parameters. * @@ -67,6 +64,9 @@ enum regulator_status { * suspended. * @set_suspend_mode: Set the operating mode for the regulator when the * system is suspended. + * + * This struct describes regulator operations which can be implemented by + * regulator chip drivers. */ struct regulator_ops { @@ -94,6 +94,7 @@ struct regulator_ops { /* report regulator status ... most other accessors report * control inputs, this reports results of combining inputs * from Linux (and other sources) with the actual load. + * returns REGULATOR_STATUS_* or negative errno. */ int (*get_status)(struct regulator_dev *); -- cgit v0.10.2 From fa16a5c13a2fc1433cfff38a083b4f8c5138d022 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Sun, 8 Feb 2009 10:37:06 -0800 Subject: regulator: twl4030 regulators Support most of the LDO regulators in the twl4030 family chips. In the case of LDOs supporting MMC/SD, the voltage controls are used; but in most other cases, the regulator framework is only used to enable/disable a supplies, conserving power when a given voltage rail is not needed. The drivers/mfd/twl4030-core.c code already sets up the various regulators according to board-specific configuration, and knows that some chips don't provide the full set of voltage rails. The omitted regulators are intended to be under hardware control, such as during the hardware-mediated system powerup, powerdown, and suspend states. Unless/until software hooks are known to be safe, they won't be exported here. These regulators implement the new get_status() operation, but can't realistically implement get_mode(); the status output is effectively the result of a vote, with the relevant hardware inputs not exposed. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 85a1f40..e58c0ce 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -56,6 +56,13 @@ config REGULATOR_BQ24022 charging select between 100 mA and 500 mA charging current limit. +config REGULATOR_TWL4030 + bool "TI TWL4030/TWL5030/TPS695x0 PMIC" + depends on TWL4030_CORE + help + This driver supports the voltage regulators provided by + this family of companion chips. + config REGULATOR_WM8350 tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" depends on MFD_WM8350 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 61b30c6..bac133a 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c new file mode 100644 index 0000000..23f2826 --- /dev/null +++ b/drivers/regulator/twl4030-regulator.c @@ -0,0 +1,511 @@ +/* + * twl4030-regulator.c -- support regulators in twl4030 family chips + * + * Copyright (C) 2008 David Brownell + * + * 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 + + +/* + * The TWL4030/TW5030/TPS659x0 family chips include power management, a + * USB OTG transceiver, an RTC, ADC, PWM, and lots more. Some versions + * include an audio codec, battery charger, and more voltage regulators. + * These chips are often used in OMAP-based systems. + * + * This driver implements software-based resource control for various + * voltage regulators. This is usually augmented with state machine + * based control. + */ + +struct twlreg_info { + /* start of regulator's PM_RECEIVER control register bank */ + u8 base; + + /* twl4030 resource ID, for resource control state machine */ + u8 id; + + /* voltage in mV = table[VSEL]; table_len must be a power-of-two */ + u8 table_len; + const u16 *table; + + /* chip constraints on regulator behavior */ + u16 min_mV; + u16 max_mV; + + /* used by regulator core */ + struct regulator_desc desc; +}; + + +/* LDO control registers ... offset is from the base of its register bank. + * The first three registers of all power resource banks help hardware to + * manage the various resource groups. + */ +#define VREG_GRP 0 +#define VREG_TYPE 1 +#define VREG_REMAP 2 +#define VREG_DEDICATED 3 /* LDO control */ + + +static inline int +twl4030reg_read(struct twlreg_info *info, unsigned offset) +{ + u8 value; + int status; + + status = twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER, + &value, info->base + offset); + return (status < 0) ? status : value; +} + +static inline int +twl4030reg_write(struct twlreg_info *info, unsigned offset, u8 value) +{ + return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, + value, info->base + offset); +} + +/*----------------------------------------------------------------------*/ + +/* generic power resource operations, which work on all regulators */ + +static int twl4030reg_grp(struct regulator_dev *rdev) +{ + return twl4030reg_read(rdev_get_drvdata(rdev), VREG_GRP); +} + +/* + * Enable/disable regulators by joining/leaving the P1 (processor) group. + * We assume nobody else is updating the DEV_GRP registers. + */ + +#define P3_GRP BIT(7) /* "peripherals" */ +#define P2_GRP BIT(6) /* secondary processor, modem, etc */ +#define P1_GRP BIT(5) /* CPU/Linux */ + +static int twl4030reg_is_enabled(struct regulator_dev *rdev) +{ + int state = twl4030reg_grp(rdev); + + if (state < 0) + return state; + + return (state & P1_GRP) != 0; +} + +static int twl4030reg_enable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp; + + grp = twl4030reg_read(info, VREG_GRP); + if (grp < 0) + return grp; + + grp |= P1_GRP; + return twl4030reg_write(info, VREG_GRP, grp); +} + +static int twl4030reg_disable(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int grp; + + grp = twl4030reg_read(info, VREG_GRP); + if (grp < 0) + return grp; + + grp &= ~P1_GRP; + return twl4030reg_write(info, VREG_GRP, grp); +} + +static int twl4030reg_get_status(struct regulator_dev *rdev) +{ + int state = twl4030reg_grp(rdev); + + if (state < 0) + return state; + state &= 0x0f; + + /* assume state != WARM_RESET; we'd not be running... */ + if (!state) + return REGULATOR_STATUS_OFF; + return (state & BIT(3)) + ? REGULATOR_STATUS_NORMAL + : REGULATOR_STATUS_STANDBY; +} + +static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + unsigned message; + int status; + + /* We can only set the mode through state machine commands... */ + switch (mode) { + case REGULATOR_MODE_NORMAL: + message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_ACTIVE); + break; + case REGULATOR_MODE_STANDBY: + message = MSG_SINGULAR(DEV_GRP_P1, info->id, RES_STATE_SLEEP); + break; + default: + return -EINVAL; + } + + /* Ensure the resource is associated with some group */ + status = twl4030reg_grp(rdev); + if (status < 0) + return status; + if (!(status & (P3_GRP | P2_GRP | P1_GRP))) + return -EACCES; + + status = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, + message >> 8, 0x15 /* PB_WORD_MSB */ ); + if (status >= 0) + return status; + + return twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, + message, 0x16 /* PB_WORD_LSB */ ); +} + +/*----------------------------------------------------------------------*/ + +/* + * Support for adjustable-voltage LDOs uses a four bit (or less) voltage + * select field in its control register. We use tables indexed by VSEL + * to record voltages in milliVolts. (Accuracy is about three percent.) + * + * Note that VSEL values for VAUX2 changed in twl5030 and newer silicon; + * currently handled by listing two slightly different VAUX2 regulators, + * only one of which will be configured. + * + * VSEL values documented as "TI cannot support these values" are flagged + * in these tables as UNSUP() values; we normally won't assign them. + */ +#ifdef CONFIG_TWL4030_ALLOW_UNSUPPORTED +#define UNSUP_MASK 0x0000 +#else +#define UNSUP_MASK 0x8000 +#endif + +#define UNSUP(x) (UNSUP_MASK | (x)) +#define IS_UNSUP(x) (UNSUP_MASK & (x)) +#define LDO_MV(x) (~UNSUP_MASK & (x)) + + +static const u16 VAUX1_VSEL_table[] = { + UNSUP(1500), UNSUP(1800), 2500, 2800, + 3000, 3000, 3000, 3000, +}; +static const u16 VAUX2_4030_VSEL_table[] = { + UNSUP(1000), UNSUP(1000), UNSUP(1200), 1300, + 1500, 1800, UNSUP(1850), 2500, + UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), +}; +static const u16 VAUX2_VSEL_table[] = { + 1700, 1700, 1900, 1300, + 1500, 1800, 2000, 2500, + 2100, 2800, 2200, 2300, + 2400, 2400, 2400, 2400, +}; +static const u16 VAUX3_VSEL_table[] = { + 1500, 1800, 2500, 2800, + UNSUP(3000), UNSUP(3000), UNSUP(3000), UNSUP(3000), +}; +static const u16 VAUX4_VSEL_table[] = { + 700, 1000, 1200, UNSUP(1300), + 1500, 1800, UNSUP(1850), 2500, +}; +static const u16 VMMC1_VSEL_table[] = { + 1850, 2850, 3000, 3150, +}; +static const u16 VMMC2_VSEL_table[] = { + UNSUP(1000), UNSUP(1000), UNSUP(1200), UNSUP(1300), + UNSUP(1500), UNSUP(1800), 1850, UNSUP(2500), + 2600, 2800, 2850, 3000, + 3150, 3150, 3150, 3150, +}; +static const u16 VPLL1_VSEL_table[] = { + 1000, 1200, 1300, 1800, + UNSUP(2800), UNSUP(3000), UNSUP(3000), UNSUP(3000), +}; +static const u16 VPLL2_VSEL_table[] = { + 700, 1000, 1200, 1300, + UNSUP(1500), 1800, UNSUP(1850), UNSUP(2500), + UNSUP(2600), UNSUP(2800), UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), +}; +static const u16 VSIM_VSEL_table[] = { + UNSUP(1000), UNSUP(1200), UNSUP(1300), 1800, + 2800, 3000, 3000, 3000, +}; +static const u16 VDAC_VSEL_table[] = { + 1200, 1300, 1800, 1800, +}; + + +static int +twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel; + + for (vsel = 0; vsel < info->table_len; vsel++) { + int mV = info->table[vsel]; + int uV; + + if (IS_UNSUP(mV)) + continue; + uV = LDO_MV(mV) * 1000; + + /* use the first in-range value */ + if (min_uV <= uV && uV <= max_uV) + return twl4030reg_write(info, VREG_DEDICATED, vsel); + } + + return -EDOM; +} + +static int twl4030ldo_get_voltage(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int vsel = twl4030reg_read(info, VREG_DEDICATED); + + if (vsel < 0) + return vsel; + + vsel &= info->table_len - 1; + return LDO_MV(info->table[vsel]) * 1000; +} + +static struct regulator_ops twl4030ldo_ops = { + .set_voltage = twl4030ldo_set_voltage, + .get_voltage = twl4030ldo_get_voltage, + + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, + + .set_mode = twl4030reg_set_mode, + + .get_status = twl4030reg_get_status, +}; + +/*----------------------------------------------------------------------*/ + +/* + * Fixed voltage LDOs don't have a VSEL field to update. + */ +static int twl4030fixed_get_voltage(struct regulator_dev *rdev) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return info->min_mV * 1000; +} + +static struct regulator_ops twl4030fixed_ops = { + .get_voltage = twl4030fixed_get_voltage, + + .enable = twl4030reg_enable, + .disable = twl4030reg_disable, + .is_enabled = twl4030reg_is_enabled, + + .set_mode = twl4030reg_set_mode, + + .get_status = twl4030reg_get_status, +}; + +/*----------------------------------------------------------------------*/ + +#define TWL_ADJUSTABLE_LDO(label, offset, num) { \ + .base = offset, \ + .id = num, \ + .table_len = ARRAY_SIZE(label##_VSEL_table), \ + .table = label##_VSEL_table, \ + .desc = { \ + .name = #label, \ + .id = TWL4030_REG_##label, \ + .ops = &twl4030ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +#define TWL_FIXED_LDO(label, offset, mVolts, num) { \ + .base = offset, \ + .id = num, \ + .min_mV = mVolts, \ + .max_mV = mVolts, \ + .desc = { \ + .name = #label, \ + .id = TWL4030_REG_##label, \ + .ops = &twl4030fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + } + +/* + * We list regulators here if systems need some level of + * software control over them after boot. + */ +static struct twlreg_info twl4030_regs[] = { + TWL_ADJUSTABLE_LDO(VAUX1, 0x17, 1), + TWL_ADJUSTABLE_LDO(VAUX2_4030, 0x1b, 2), + TWL_ADJUSTABLE_LDO(VAUX2, 0x1b, 2), + TWL_ADJUSTABLE_LDO(VAUX3, 0x1f, 3), + TWL_ADJUSTABLE_LDO(VAUX4, 0x23, 4), + TWL_ADJUSTABLE_LDO(VMMC1, 0x27, 5), + TWL_ADJUSTABLE_LDO(VMMC2, 0x2b, 6), + /* + TWL_ADJUSTABLE_LDO(VPLL1, 0x2f, 7), + TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8), + */ + TWL_ADJUSTABLE_LDO(VSIM, 0x37, 9), + TWL_ADJUSTABLE_LDO(VDAC, 0x3b, 10), + /* + TWL_ADJUSTABLE_LDO(VINTANA1, 0x3f, 11), + TWL_ADJUSTABLE_LDO(VINTANA2, 0x43, 12), + TWL_ADJUSTABLE_LDO(VINTDIG, 0x47, 13), + TWL_SMPS(VIO, 0x4b, 14), + TWL_SMPS(VDD1, 0x55, 15), + TWL_SMPS(VDD2, 0x63, 16), + */ + TWL_FIXED_LDO(VUSB1V5, 0x71, 1500, 17), + TWL_FIXED_LDO(VUSB1V8, 0x74, 1800, 18), + TWL_FIXED_LDO(VUSB3V1, 0x77, 3100, 19), + /* VUSBCP is managed *only* by the USB subchip */ +}; + +static int twl4030reg_probe(struct platform_device *pdev) +{ + int i; + struct twlreg_info *info; + struct regulator_init_data *initdata; + struct regulation_constraints *c; + struct regulator_dev *rdev; + int min_uV, max_uV; + + for (i = 0, info = NULL; i < ARRAY_SIZE(twl4030_regs); i++) { + if (twl4030_regs[i].desc.id != pdev->id) + continue; + info = twl4030_regs + i; + min_uV = info->min_mV * 1000; + max_uV = info->max_mV * 1000; + break; + } + if (!info) + return -ENODEV; + + initdata = pdev->dev.platform_data; + if (!initdata) + return -EINVAL; + + /* Constrain board-specific capabilities according to what + * this driver and the chip itself can actually do. + */ + c = &initdata->constraints; + if (!c->min_uV || c->min_uV < min_uV) + c->min_uV = min_uV; + if (!c->max_uV || c->max_uV > max_uV) + c->max_uV = max_uV; + c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; + c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE + | REGULATOR_CHANGE_MODE + | REGULATOR_CHANGE_STATUS; + + rdev = regulator_register(&info->desc, &pdev->dev, initdata, info); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "can't register %s, %ld\n", + info->desc.name, PTR_ERR(rdev)); + return PTR_ERR(rdev); + } + platform_set_drvdata(pdev, rdev); + + /* NOTE: many regulators support short-circuit IRQs (presentable + * as REGULATOR_OVER_CURRENT notifications?) configured via: + * - SC_CONFIG + * - SC_DETECT1 (vintana2, vmmc1/2, vaux1/2/3/4) + * - SC_DETECT2 (vusb, vdac, vio, vdd1/2, vpll2) + * - IT_CONFIG + */ + + return 0; +} + +static int __devexit twl4030reg_remove(struct platform_device *pdev) +{ + regulator_unregister(platform_get_drvdata(pdev)); + return 0; +} + +MODULE_ALIAS("platform:twl4030_reg"); + +static struct platform_driver twl4030reg_driver = { + .probe = twl4030reg_probe, + .remove = __devexit_p(twl4030reg_remove), + /* NOTE: short name, to work around driver model truncation of + * "twl4030_regulator.12" (and friends) to "twl4030_regulator.1". + */ + .driver.name = "twl4030_reg", + .driver.owner = THIS_MODULE, +}; + +static int __init twl4030reg_init(void) +{ + unsigned i, j; + + /* determine min/max voltage constraints, taking into account + * whether set_voltage() will use the "unsupported" settings + */ + for (i = 0; i < ARRAY_SIZE(twl4030_regs); i++) { + struct twlreg_info *info = twl4030_regs + i; + const u16 *table; + + /* fixed-voltage regulators */ + if (!info->table_len) + continue; + + /* LDO regulators: */ + for (j = 0, table = info->table; + j < info->table_len; + j++, table++) { + u16 mV = *table; + + if (IS_UNSUP(mV)) + continue; + mV = LDO_MV(mV); + + if (info->min_mV == 0 || info->min_mV > mV) + info->min_mV = mV; + if (info->max_mV < mV) + info->max_mV = mV; + } + } + + return platform_driver_register(&twl4030reg_driver); +} +subsys_initcall(twl4030reg_init); + +static void __exit twl4030reg_exit(void) +{ + platform_driver_unregister(&twl4030reg_driver); +} +module_exit(twl4030reg_exit) + +MODULE_DESCRIPTION("TWL4030 regulator driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/i2c/twl4030.h b/include/linux/i2c/twl4030.h index 8137f66..0dc80ef 100644 --- a/include/linux/i2c/twl4030.h +++ b/include/linux/i2c/twl4030.h @@ -218,6 +218,53 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes); /*----------------------------------------------------------------------*/ +/* Power bus message definitions */ + +#define DEV_GRP_NULL 0x0 +#define DEV_GRP_P1 0x1 +#define DEV_GRP_P2 0x2 +#define DEV_GRP_P3 0x4 + +#define RES_GRP_RES 0x0 +#define RES_GRP_PP 0x1 +#define RES_GRP_RC 0x2 +#define RES_GRP_PP_RC 0x3 +#define RES_GRP_PR 0x4 +#define RES_GRP_PP_PR 0x5 +#define RES_GRP_RC_PR 0x6 +#define RES_GRP_ALL 0x7 + +#define RES_TYPE2_R0 0x0 + +#define RES_TYPE_ALL 0x7 + +#define RES_STATE_WRST 0xF +#define RES_STATE_ACTIVE 0xE +#define RES_STATE_SLEEP 0x8 +#define RES_STATE_OFF 0x0 + +/* + * Power Bus Message Format ... these can be sent individually by Linux, + * but are usually part of downloaded scripts that are run when various + * power events are triggered. + * + * Broadcast Message (16 Bits): + * DEV_GRP[15:13] MT[12] RES_GRP[11:9] RES_TYPE2[8:7] RES_TYPE[6:4] + * RES_STATE[3:0] + * + * Singular Message (16 Bits): + * DEV_GRP[15:13] MT[12] RES_ID[11:4] RES_STATE[3:0] + */ + +#define MSG_BROADCAST(devgrp, grp, type, type2, state) \ + ( (devgrp) << 13 | 1 << 12 | (grp) << 9 | (type2) << 7 \ + | (type) << 4 | (state)) + +#define MSG_SINGULAR(devgrp, id, state) \ + ((devgrp) << 13 | 0 << 12 | (id) << 4 | (state)) + +/*----------------------------------------------------------------------*/ + struct twl4030_bci_platform_data { int *battery_tmp_tbl; unsigned int tblsize; -- cgit v0.10.2 From 66b659e685bca1f2f6d6102bac74cafbc7eef5c2 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Thu, 26 Feb 2009 11:50:14 -0800 Subject: regulator: twl4030 voltage enumeration (v2) Update previously-posted twl4030 regulator driver to export supported voltages to upper layers using a new mechanism. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index 23f2826..663a416 100644 --- a/drivers/regulator/twl4030-regulator.c +++ b/drivers/regulator/twl4030-regulator.c @@ -42,7 +42,6 @@ struct twlreg_info { /* chip constraints on regulator behavior */ u16 min_mV; - u16 max_mV; /* used by regulator core */ struct regulator_desc desc; @@ -258,6 +257,14 @@ static const u16 VDAC_VSEL_table[] = { }; +static int twl4030ldo_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + int mV = info->table[index]; + + return IS_UNSUP(mV) ? 0 : (LDO_MV(mV) * 1000); +} + static int twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { @@ -272,6 +279,8 @@ twl4030ldo_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) continue; uV = LDO_MV(mV) * 1000; + /* REVISIT for VAUX2, first match may not be best/lowest */ + /* use the first in-range value */ if (min_uV <= uV && uV <= max_uV) return twl4030reg_write(info, VREG_DEDICATED, vsel); @@ -293,6 +302,8 @@ static int twl4030ldo_get_voltage(struct regulator_dev *rdev) } static struct regulator_ops twl4030ldo_ops = { + .list_voltage = twl4030ldo_list_voltage, + .set_voltage = twl4030ldo_set_voltage, .get_voltage = twl4030ldo_get_voltage, @@ -310,6 +321,13 @@ static struct regulator_ops twl4030ldo_ops = { /* * Fixed voltage LDOs don't have a VSEL field to update. */ +static int twl4030fixed_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + struct twlreg_info *info = rdev_get_drvdata(rdev); + + return info->min_mV * 1000; +} + static int twl4030fixed_get_voltage(struct regulator_dev *rdev) { struct twlreg_info *info = rdev_get_drvdata(rdev); @@ -318,6 +336,8 @@ static int twl4030fixed_get_voltage(struct regulator_dev *rdev) } static struct regulator_ops twl4030fixed_ops = { + .list_voltage = twl4030fixed_list_voltage, + .get_voltage = twl4030fixed_get_voltage, .enable = twl4030reg_enable, @@ -339,6 +359,7 @@ static struct regulator_ops twl4030fixed_ops = { .desc = { \ .name = #label, \ .id = TWL4030_REG_##label, \ + .n_voltages = ARRAY_SIZE(label##_VSEL_table), \ .ops = &twl4030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ @@ -349,10 +370,10 @@ static struct regulator_ops twl4030fixed_ops = { .base = offset, \ .id = num, \ .min_mV = mVolts, \ - .max_mV = mVolts, \ .desc = { \ .name = #label, \ .id = TWL4030_REG_##label, \ + .n_voltages = 1, \ .ops = &twl4030fixed_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ @@ -398,14 +419,11 @@ static int twl4030reg_probe(struct platform_device *pdev) struct regulator_init_data *initdata; struct regulation_constraints *c; struct regulator_dev *rdev; - int min_uV, max_uV; for (i = 0, info = NULL; i < ARRAY_SIZE(twl4030_regs); i++) { if (twl4030_regs[i].desc.id != pdev->id) continue; info = twl4030_regs + i; - min_uV = info->min_mV * 1000; - max_uV = info->max_mV * 1000; break; } if (!info) @@ -419,10 +437,6 @@ static int twl4030reg_probe(struct platform_device *pdev) * this driver and the chip itself can actually do. */ c = &initdata->constraints; - if (!c->min_uV || c->min_uV < min_uV) - c->min_uV = min_uV; - if (!c->max_uV || c->max_uV > max_uV) - c->max_uV = max_uV; c->valid_modes_mask &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_STANDBY; c->valid_ops_mask &= REGULATOR_CHANGE_VOLTAGE | REGULATOR_CHANGE_MODE @@ -467,36 +481,6 @@ static struct platform_driver twl4030reg_driver = { static int __init twl4030reg_init(void) { - unsigned i, j; - - /* determine min/max voltage constraints, taking into account - * whether set_voltage() will use the "unsupported" settings - */ - for (i = 0; i < ARRAY_SIZE(twl4030_regs); i++) { - struct twlreg_info *info = twl4030_regs + i; - const u16 *table; - - /* fixed-voltage regulators */ - if (!info->table_len) - continue; - - /* LDO regulators: */ - for (j = 0, table = info->table; - j < info->table_len; - j++, table++) { - u16 mV = *table; - - if (IS_UNSUP(mV)) - continue; - mV = LDO_MV(mV); - - if (info->min_mV == 0 || info->min_mV > mV) - info->min_mV = mV; - if (info->max_mV < mV) - info->max_mV = mV; - } - } - return platform_driver_register(&twl4030reg_driver); } subsys_initcall(twl4030reg_init); -- cgit v0.10.2 From 5c13941acc513669c7d07b28789c3f9ba66ddddf Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 11 Mar 2009 03:30:43 -0800 Subject: MMC: regulator utilities Glue between MMC and regulator stacks ... verified with some OMAP3 boards using adjustable and configured-as-fixed regulators on several MMC controllers. These calls are intended to be used by MMC host adapters using at least one regulator per host. Examples include slots with regulators supporting multiple voltages and ones using multiple voltage rails (e.g. DAT4..DAT7 using a separate supply, or a split rail chip like certain SDIO WLAN or eMMC solutions). Signed-off-by: David Brownell Acked-by: Pierre Ossman Signed-off-by: Liam Girdwood diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index df6ce4a..1445ea8 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -523,6 +524,105 @@ u32 mmc_vddrange_to_ocrmask(int vdd_min, int vdd_max) } EXPORT_SYMBOL(mmc_vddrange_to_ocrmask); +#ifdef CONFIG_REGULATOR + +/** + * mmc_regulator_get_ocrmask - return mask of supported voltages + * @supply: regulator to use + * + * This returns either a negative errno, or a mask of voltages that + * can be provided to MMC/SD/SDIO devices using the specified voltage + * regulator. This would normally be called before registering the + * MMC host adapter. + */ +int mmc_regulator_get_ocrmask(struct regulator *supply) +{ + int result = 0; + int count; + int i; + + count = regulator_count_voltages(supply); + if (count < 0) + return count; + + for (i = 0; i < count; i++) { + int vdd_uV; + int vdd_mV; + + vdd_uV = regulator_list_voltage(supply, i); + if (vdd_uV <= 0) + continue; + + vdd_mV = vdd_uV / 1000; + result |= mmc_vddrange_to_ocrmask(vdd_mV, vdd_mV); + } + + return result; +} +EXPORT_SYMBOL(mmc_regulator_get_ocrmask); + +/** + * mmc_regulator_set_ocr - set regulator to match host->ios voltage + * @vdd_bit: zero for power off, else a bit number (host->ios.vdd) + * @supply: regulator to use + * + * Returns zero on success, else negative errno. + * + * MMC host drivers may use this to enable or disable a regulator using + * a particular supply voltage. This would normally be called from the + * set_ios() method. + */ +int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit) +{ + int result = 0; + int min_uV, max_uV; + int enabled; + + enabled = regulator_is_enabled(supply); + if (enabled < 0) + return enabled; + + if (vdd_bit) { + int tmp; + int voltage; + + /* REVISIT mmc_vddrange_to_ocrmask() may have set some + * bits this regulator doesn't quite support ... don't + * be too picky, most cards and regulators are OK with + * a 0.1V range goof (it's a small error percentage). + */ + tmp = vdd_bit - ilog2(MMC_VDD_165_195); + if (tmp == 0) { + min_uV = 1650 * 1000; + max_uV = 1950 * 1000; + } else { + min_uV = 1900 * 1000 + tmp * 100 * 1000; + max_uV = min_uV + 100 * 1000; + } + + /* avoid needless changes to this voltage; the regulator + * might not allow this operation + */ + voltage = regulator_get_voltage(supply); + if (voltage < 0) + result = voltage; + else if (voltage < min_uV || voltage > max_uV) + result = regulator_set_voltage(supply, min_uV, max_uV); + else + result = 0; + + if (result == 0 && !enabled) + result = regulator_enable(supply); + } else if (enabled) { + result = regulator_disable(supply); + } + + return result; +} +EXPORT_SYMBOL(mmc_regulator_set_ocr); + +#endif + /* * Mask off any voltages we don't support and select * the lowest voltage diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index 4e45725..3e7615e 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -192,5 +192,10 @@ static inline void mmc_signal_sdio_irq(struct mmc_host *host) wake_up_process(host->sdio_irq_thread); } +struct regulator; + +int mmc_regulator_get_ocrmask(struct regulator *supply); +int mmc_regulator_set_ocr(struct regulator *supply, unsigned short vdd_bit); + #endif -- cgit v0.10.2 From 216765d92acaeabdad6561254a5a676325105a37 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 10 Mar 2009 16:28:35 +0000 Subject: regulator: Implement list_voltage() for WM8400 DCDCs and LDOs All DCDCs and LDOs are identical. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 6ed43b0..1574260 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -43,6 +43,18 @@ static int wm8400_ldo_disable(struct regulator_dev *dev) WM8400_LDO1_ENA, 0); } +static int wm8400_ldo_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + if (selector > WM8400_LDO1_VSEL_MASK) + return -EINVAL; + + if (selector < 15) + return 900000 + (selector * 50000); + else + return 1600000 + ((selector - 14) * 100000); +} + static int wm8400_ldo_get_voltage(struct regulator_dev *dev) { struct wm8400 *wm8400 = rdev_get_drvdata(dev); @@ -51,10 +63,7 @@ static int wm8400_ldo_get_voltage(struct regulator_dev *dev) val = wm8400_reg_read(wm8400, WM8400_LDO1_CONTROL + rdev_get_id(dev)); val &= WM8400_LDO1_VSEL_MASK; - if (val < 15) - return 900000 + (val * 50000); - else - return 1600000 + ((val - 14) * 100000); + return wm8400_ldo_list_voltage(dev, val); } static int wm8400_ldo_set_voltage(struct regulator_dev *dev, @@ -92,6 +101,7 @@ static struct regulator_ops wm8400_ldo_ops = { .is_enabled = wm8400_ldo_is_enabled, .enable = wm8400_ldo_enable, .disable = wm8400_ldo_disable, + .list_voltage = wm8400_ldo_list_voltage, .get_voltage = wm8400_ldo_get_voltage, .set_voltage = wm8400_ldo_set_voltage, }; @@ -124,6 +134,15 @@ static int wm8400_dcdc_disable(struct regulator_dev *dev) WM8400_DC1_ENA, 0); } +static int wm8400_dcdc_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + if (selector > WM8400_DC1_VSEL_MASK) + return -EINVAL; + + return 850000 + (selector * 25000); +} + static int wm8400_dcdc_get_voltage(struct regulator_dev *dev) { struct wm8400 *wm8400 = rdev_get_drvdata(dev); @@ -237,6 +256,7 @@ static struct regulator_ops wm8400_dcdc_ops = { .is_enabled = wm8400_dcdc_is_enabled, .enable = wm8400_dcdc_enable, .disable = wm8400_dcdc_disable, + .list_voltage = wm8400_dcdc_list_voltage, .get_voltage = wm8400_dcdc_get_voltage, .set_voltage = wm8400_dcdc_set_voltage, .get_mode = wm8400_dcdc_get_mode, @@ -249,6 +269,7 @@ static struct regulator_desc regulators[] = { .name = "LDO1", .id = WM8400_LDO1, .ops = &wm8400_ldo_ops, + .n_voltages = WM8400_LDO1_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -256,6 +277,7 @@ static struct regulator_desc regulators[] = { .name = "LDO2", .id = WM8400_LDO2, .ops = &wm8400_ldo_ops, + .n_voltages = WM8400_LDO2_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -263,6 +285,7 @@ static struct regulator_desc regulators[] = { .name = "LDO3", .id = WM8400_LDO3, .ops = &wm8400_ldo_ops, + .n_voltages = WM8400_LDO3_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -270,6 +293,7 @@ static struct regulator_desc regulators[] = { .name = "LDO4", .id = WM8400_LDO4, .ops = &wm8400_ldo_ops, + .n_voltages = WM8400_LDO4_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -277,6 +301,7 @@ static struct regulator_desc regulators[] = { .name = "DCDC1", .id = WM8400_DCDC1, .ops = &wm8400_dcdc_ops, + .n_voltages = WM8400_DC1_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, @@ -284,6 +309,7 @@ static struct regulator_desc regulators[] = { .name = "DCDC2", .id = WM8400_DCDC2, .ops = &wm8400_dcdc_ops, + .n_voltages = WM8400_DC2_VSEL_MASK + 1, .type = REGULATOR_VOLTAGE, .owner = THIS_MODULE, }, -- cgit v0.10.2 From 3e2b9abda554e9f6105996dca77cca9ef98de17a Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 10 Mar 2009 16:28:36 +0000 Subject: regulator: Don't warn on omitted voltage constraints Specifying voltage constraints is optional (and only needed if the consumer is allowed to change the voltage) so don't complain unless a voltage has been specified. Also avoid surprises with a dangling else while we're here. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index da357a0..2ff7634 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -709,8 +709,12 @@ static int set_machine_constraints(struct regulator_dev *rdev, cmax = INT_MAX; } + /* voltage constraints are optional */ + if ((cmin == 0) && (cmax == 0)) + goto out; + /* else require explicit machine-level constraints */ - else if (cmin <= 0 || cmax <= 0 || cmax < cmin) { + if (cmin <= 0 || cmax <= 0 || cmax < cmin) { pr_err("%s: %s '%s' voltage constraints\n", __func__, "invalid", name); ret = -EINVAL; -- cgit v0.10.2 From 1897e7423b73ff01db31c24dd20138968e39304c Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 10 Mar 2009 11:51:15 -0800 Subject: twl4030-regulator: list more VAUX4 voltages The VAUX4 voltage table scrolls onto a second page in many versions of the TWL4030 family manuals. This doesn't mean we should ignore those values! Some boards use the (fully supported) 2.8V setting. Signed-off-by: David Brownell Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index 663a416..49c41fc 100644 --- a/drivers/regulator/twl4030-regulator.c +++ b/drivers/regulator/twl4030-regulator.c @@ -228,6 +228,8 @@ static const u16 VAUX3_VSEL_table[] = { static const u16 VAUX4_VSEL_table[] = { 700, 1000, 1200, UNSUP(1300), 1500, 1800, UNSUP(1850), 2500, + UNSUP(2600), 2800, UNSUP(2850), UNSUP(3000), + UNSUP(3150), UNSUP(3150), UNSUP(3150), UNSUP(3150), }; static const u16 VMMC1_VSEL_table[] = { 1850, 2850, 3000, 3150, -- cgit v0.10.2 From 221a7c7c9c88bf9d3ea4e191b35c7da709ca30b7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 2 Mar 2009 16:32:47 +0000 Subject: regulator: Implement list_voltage for WM835x LDOs and DCDCs Implement the recently added voltage step listing API for the WM835x DCDCs and LDOs. DCDCs can use values up to 0x66, LDOs can use the full range of values in the mask. Both masks are the lower bits of the register. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 261db94..771eca1 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -24,6 +24,9 @@ #include #include +/* Maximum value possible for VSEL */ +#define WM8350_DCDC_MAX_VSEL 0x66 + /* Microamps */ static const int isink_cur[] = { 4, @@ -385,6 +388,14 @@ static int wm8350_dcdc_get_voltage(struct regulator_dev *rdev) return wm8350_dcdc_val_to_mvolts(val) * 1000; } +static int wm8350_dcdc_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > WM8350_DCDC_MAX_VSEL) + return -EINVAL; + return wm8350_dcdc_val_to_mvolts(selector) * 1000; +} + static int wm8350_dcdc_set_suspend_voltage(struct regulator_dev *rdev, int uV) { struct wm8350 *wm8350 = rdev_get_drvdata(rdev); @@ -775,6 +786,14 @@ static int wm8350_ldo_get_voltage(struct regulator_dev *rdev) return wm8350_ldo_val_to_mvolts(val) * 1000; } +static int wm8350_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + if (selector > WM8350_LDO1_VSEL_MASK) + return -EINVAL; + return wm8350_ldo_val_to_mvolts(selector) * 1000; +} + int wm8350_dcdc_set_slot(struct wm8350 *wm8350, int dcdc, u16 start, u16 stop, u16 fault) { @@ -1162,6 +1181,7 @@ static int wm8350_ldo_is_enabled(struct regulator_dev *rdev) static struct regulator_ops wm8350_dcdc_ops = { .set_voltage = wm8350_dcdc_set_voltage, .get_voltage = wm8350_dcdc_get_voltage, + .list_voltage = wm8350_dcdc_list_voltage, .enable = wm8350_dcdc_enable, .disable = wm8350_dcdc_disable, .get_mode = wm8350_dcdc_get_mode, @@ -1185,6 +1205,7 @@ static struct regulator_ops wm8350_dcdc2_5_ops = { static struct regulator_ops wm8350_ldo_ops = { .set_voltage = wm8350_ldo_set_voltage, .get_voltage = wm8350_ldo_get_voltage, + .list_voltage = wm8350_ldo_list_voltage, .enable = wm8350_ldo_enable, .disable = wm8350_ldo_disable, .is_enabled = wm8350_ldo_is_enabled, @@ -1209,6 +1230,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc_ops, .irq = WM8350_IRQ_UV_DC1, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .owner = THIS_MODULE, }, { @@ -1225,6 +1247,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc_ops, .irq = WM8350_IRQ_UV_DC3, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .owner = THIS_MODULE, }, { @@ -1233,6 +1256,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc_ops, .irq = WM8350_IRQ_UV_DC4, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .owner = THIS_MODULE, }, { @@ -1249,6 +1273,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_dcdc_ops, .irq = WM8350_IRQ_UV_DC6, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_DCDC_MAX_VSEL + 1, .owner = THIS_MODULE, }, { @@ -1257,6 +1282,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_ldo_ops, .irq = WM8350_IRQ_UV_LDO1, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO1_VSEL_MASK + 1, .owner = THIS_MODULE, }, { @@ -1265,6 +1291,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_ldo_ops, .irq = WM8350_IRQ_UV_LDO2, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO2_VSEL_MASK + 1, .owner = THIS_MODULE, }, { @@ -1273,6 +1300,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_ldo_ops, .irq = WM8350_IRQ_UV_LDO3, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO3_VSEL_MASK + 1, .owner = THIS_MODULE, }, { @@ -1281,6 +1309,7 @@ static struct regulator_desc wm8350_reg[NUM_WM8350_REGULATORS] = { .ops = &wm8350_ldo_ops, .irq = WM8350_IRQ_UV_LDO4, .type = REGULATOR_VOLTAGE, + .n_voltages = WM8350_LDO4_VSEL_MASK + 1, .owner = THIS_MODULE, }, { -- cgit v0.10.2 From cacf90f24e80cec9334f98e0377149f943fe9f16 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 2 Mar 2009 16:32:46 +0000 Subject: regulator: Allow boot_on regulators to be disabled by clients Rather than incrementing the reference count for boot_on regulators (which prevents them being disabled later on) simply force the regulator to be enabled when applying the constraints. Previously boot_on was essentially equivalent to always_on. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2ff7634..08441e2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -776,10 +776,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - /* are we enabled at boot time by firmware / bootloader */ - if (rdev->constraints->boot_on) - rdev->use_count = 1; - /* do we need to setup our suspend state */ if (constraints->initial_state) { ret = suspend_prepare(rdev, constraints->initial_state); @@ -808,11 +804,10 @@ static int set_machine_constraints(struct regulator_dev *rdev, } } - /* if always_on is set then turn the regulator on if it's not - * already on. */ - if (constraints->always_on && ops->enable && - ((ops->is_enabled && !ops->is_enabled(rdev)) || - (!ops->is_enabled && !constraints->boot_on))) { + /* If the constraints say the regulator should be on at this point + * and we have control then make sure it is enabled. + */ + if ((constraints->always_on || constraints->boot_on) && ops->enable) { ret = ops->enable(rdev); if (ret < 0) { printk(KERN_ERR "%s: failed to enable %s\n", diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 1eb861c..5de7aa3 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -73,7 +73,9 @@ struct regulator_state { * * @always_on: Set if the regulator should never be disabled. * @boot_on: Set if the regulator is enabled when the system is initially - * started. + * started. If the regulator is not enabled by the hardware or + * bootloader then it will be enabled when the constraints are + * applied. * @apply_uV: Apply the voltage constraint when initialising. * * @input_uV: Input voltage for regulator when supplied by another regulator. -- cgit v0.10.2 From 1dc60343f874ce4bfbbc2c3d2f7865fc897df479 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Mar 2009 23:35:52 +0000 Subject: regulator: Don't warn if we failed to get a regulator The consumer can print a message if required, some consumers may have optional regulators and wish to downgrade the logging for them or ignore their absence. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 08441e2..019a8a4 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1031,8 +1031,6 @@ struct regulator *regulator_get(struct device *dev, const char *id) goto found; } } - printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n", - id); mutex_unlock(®ulator_list_mutex); return regulator; -- cgit v0.10.2 From cd94b5053081963614f6ad77b9b66a7968056c84 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Wed, 11 Mar 2009 16:43:34 -0800 Subject: regulator: refcount fixes Fix some refcounting issues in the regulator framework, supporting regulator_disable() for regulators that were enabled at boot time via machine constraints: - Update those regulators' usecounts after enabling, so they can cleanly be disabled at that level. - Remove the problematic per-consumer usecount, so there's only one level of enable/disable. Buggy consumers could notice different bug symptoms. The main example would be refcounting bugs; also, any (out-of-tree) users of the experimental regulator_set_optimum_mode() stuff which don't call it when they're done using a regulator. This is a net minor codeshrink. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 019a8a4..9448875 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -52,7 +52,6 @@ struct regulator { int uA_load; int min_uV; int max_uV; - int enabled; /* count of client enables */ char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; @@ -815,6 +814,7 @@ static int set_machine_constraints(struct regulator_dev *rdev, rdev->constraints = NULL; goto out; } + rdev->use_count = 1; } print_constraints(rdev); @@ -1068,10 +1068,6 @@ void regulator_put(struct regulator *regulator) mutex_lock(®ulator_list_mutex); rdev = regulator->rdev; - if (WARN(regulator->enabled, "Releasing supply %s while enabled\n", - regulator->supply_name)) - _regulator_disable(rdev); - /* remove any sysfs entries */ if (regulator->dev) { sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); @@ -1146,12 +1142,7 @@ int regulator_enable(struct regulator *regulator) int ret = 0; mutex_lock(&rdev->mutex); - if (regulator->enabled == 0) - ret = _regulator_enable(rdev); - else if (regulator->enabled < 0) - ret = -EIO; - if (ret == 0) - regulator->enabled++; + ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); return ret; } @@ -1162,6 +1153,11 @@ static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; + if (WARN(rdev->use_count <= 0, + "unbalanced disables for %s\n", + rdev->desc->name)) + return -EIO; + /* are we the last user and permitted to disable ? */ if (rdev->use_count == 1 && !rdev->constraints->always_on) { @@ -1210,16 +1206,7 @@ int regulator_disable(struct regulator *regulator) int ret = 0; mutex_lock(&rdev->mutex); - if (regulator->enabled == 1) { - ret = _regulator_disable(rdev); - if (ret == 0) - regulator->uA_load = 0; - } else if (WARN(regulator->enabled <= 0, - "unbalanced disables for supply %s\n", - regulator->supply_name)) - ret = -EIO; - if (ret == 0) - regulator->enabled--; + ret = _regulator_disable(rdev); mutex_unlock(&rdev->mutex); return ret; } @@ -1266,7 +1253,6 @@ int regulator_force_disable(struct regulator *regulator) int ret; mutex_lock(®ulator->rdev->mutex); - regulator->enabled = 0; regulator->uA_load = 0; ret = _regulator_force_disable(regulator->rdev); mutex_unlock(®ulator->rdev->mutex); -- cgit v0.10.2 From 52914eaa49bf732b091dbf5467ce4c7507c2d32a Mon Sep 17 00:00:00 2001 From: David Brownell Date: Fri, 13 Mar 2009 17:54:54 -0700 Subject: twl4030-regulator: expose VPLL2 Add VPLL2 to the set of twl4030-family regulators exposed for use by various drivers. It's commonly used to power the digital video outputs (e.g. LCD or DVI displays) on OMAP3 systems. Signed-off-by: David Brownell Acked-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index 68826f1..ec90e95 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -592,11 +592,9 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) /* maybe add LDOs that are omitted on cost-reduced parts */ if (twl_has_regulator() && !(features & TPS_SUBSET)) { - /* child = add_regulator(TWL4030_REG_VPLL2, pdata->vpll2); if (IS_ERR(child)) return PTR_ERR(child); - */ child = add_regulator(TWL4030_REG_VMMC2, pdata->vmmc2); if (IS_ERR(child)) diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index 49c41fc..f3ec98c 100644 --- a/drivers/regulator/twl4030-regulator.c +++ b/drivers/regulator/twl4030-regulator.c @@ -396,8 +396,8 @@ static struct twlreg_info twl4030_regs[] = { TWL_ADJUSTABLE_LDO(VMMC2, 0x2b, 6), /* TWL_ADJUSTABLE_LDO(VPLL1, 0x2f, 7), - TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8), */ + TWL_ADJUSTABLE_LDO(VPLL2, 0x33, 8), TWL_ADJUSTABLE_LDO(VSIM, 0x37, 9), TWL_ADJUSTABLE_LDO(VDAC, 0x3b, 10), /* -- cgit v0.10.2 From 50f075963f127d713ff0c30359baefc0f89d9ae2 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 16 Mar 2009 19:36:33 +0000 Subject: regulator: Don't increment use_count for boot_on regulators Don't set use_count for regulators that are enabled at boot since this stops the supply being disabled by well-behaved consumers which do balanced enables and disabled. Any consumers which don't do disables which are not matched by enables are unable to share regulators - shared regulators are the common case so the API should facilitate them. Consumers that want to disable regulators that are enabled when they start have two options: - Do a regulator_enable() prior to the disable to bring the use count in sync with the hardware state; this will ensure that if the regulator was enabled by another driver then this consumer will play nicely with it. - Use regulator_force_disable(); this explicitly bypasses any checks done by the core and documents the inability of the driver to share the supply. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9448875..8588a24 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -814,7 +814,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, rdev->constraints = NULL; goto out; } - rdev->use_count = 1; } print_constraints(rdev); -- cgit v0.10.2 From ca7255614e0861e36480103f4a402a115803d7b5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 16 Mar 2009 19:36:34 +0000 Subject: regulator: Support disabling of unused regulators by machines At present it is not possible for machine constraints to disable regulators which have been left on when the system starts, for example as a result of fixed default configurations in hardware. This means that power may be wasted by these regulators if they are not in use. Provide intial support for this with a late_initcall which will disable any unused regulators if the machine has enabled this feature by calling regulator_has_full_constraints(). If this has not been called then print a warning to encourage users to fully specify their constraints so that we can change this to be the default behaviour in future. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8588a24..01f7702 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -28,6 +28,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_list); static LIST_HEAD(regulator_map_list); +static int has_full_constraints; /* * struct regulator_map @@ -2143,6 +2144,23 @@ out: EXPORT_SYMBOL_GPL(regulator_suspend_prepare); /** + * regulator_has_full_constraints - the system has fully specified constraints + * + * Calling this function will cause the regulator API to disable all + * regulators which have a zero use count and don't have an always_on + * constraint in a late_initcall. + * + * The intention is that this will become the default behaviour in a + * future kernel release so users are encouraged to use this facility + * now. + */ +void regulator_has_full_constraints(void) +{ + has_full_constraints = 1; +} +EXPORT_SYMBOL_GPL(regulator_has_full_constraints); + +/** * rdev_get_drvdata - get rdev regulator driver data * @rdev: regulator * @@ -2209,3 +2227,77 @@ static int __init regulator_init(void) /* init early to allow our consumers to complete system booting */ core_initcall(regulator_init); + +static int __init regulator_init_complete(void) +{ + struct regulator_dev *rdev; + struct regulator_ops *ops; + struct regulation_constraints *c; + int enabled, ret; + const char *name; + + mutex_lock(®ulator_list_mutex); + + /* If we have a full configuration then disable any regulators + * which are not in use or always_on. This will become the + * default behaviour in the future. + */ + list_for_each_entry(rdev, ®ulator_list, list) { + ops = rdev->desc->ops; + c = rdev->constraints; + + if (c->name) + name = c->name; + else if (rdev->desc->name) + name = rdev->desc->name; + else + name = "regulator"; + + if (!ops->disable || c->always_on) + continue; + + mutex_lock(&rdev->mutex); + + if (rdev->use_count) + goto unlock; + + /* If we can't read the status assume it's on. */ + if (ops->is_enabled) + enabled = ops->is_enabled(rdev); + else + enabled = 1; + + if (!enabled) + goto unlock; + + if (has_full_constraints) { + /* We log since this may kill the system if it + * goes wrong. */ + printk(KERN_INFO "%s: disabling %s\n", + __func__, name); + ret = ops->disable(rdev); + if (ret != 0) { + printk(KERN_ERR + "%s: couldn't disable %s: %d\n", + __func__, name, ret); + } + } else { + /* The intention is that in future we will + * assume that full constraints are provided + * so warn even if we aren't going to do + * anything here. + */ + printk(KERN_WARNING + "%s: incomplete constraints, leaving %s on\n", + __func__, name); + } + +unlock: + mutex_unlock(&rdev->mutex); + } + + mutex_unlock(®ulator_list_mutex); + + return 0; +} +late_initcall(regulator_init_complete); diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index 5de7aa3..bac64fa 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -166,4 +166,6 @@ struct regulator_init_data { int regulator_suspend_prepare(suspend_state_t state); +void regulator_has_full_constraints(void); + #endif -- cgit v0.10.2 From d6bb69cfa88b8ac9f952de4fada5b216d5ba8830 Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Fri, 6 Mar 2009 14:51:30 +0200 Subject: regulator: twl4030 VAUX3 supports 3.0V TWL4030 and TWL5030 support 3.0V on VAUX3. Signed-off-by: Adrian Hunter diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index f3ec98c..e2032fb 100644 --- a/drivers/regulator/twl4030-regulator.c +++ b/drivers/regulator/twl4030-regulator.c @@ -193,6 +193,9 @@ static int twl4030reg_set_mode(struct regulator_dev *rdev, unsigned mode) * * VSEL values documented as "TI cannot support these values" are flagged * in these tables as UNSUP() values; we normally won't assign them. + * + * VAUX3 at 3V is incorrectly listed in some TI manuals as unsupported. + * TI are revising the twl5030/tps659x0 specs to support that 3.0V setting. */ #ifdef CONFIG_TWL4030_ALLOW_UNSUPPORTED #define UNSUP_MASK 0x0000 @@ -223,7 +226,7 @@ static const u16 VAUX2_VSEL_table[] = { }; static const u16 VAUX3_VSEL_table[] = { 1500, 1800, 2500, 2800, - UNSUP(3000), UNSUP(3000), UNSUP(3000), UNSUP(3000), + 3000, 3000, 3000, 3000, }; static const u16 VAUX4_VSEL_table[] = { 700, 1000, 1200, UNSUP(1300), -- cgit v0.10.2