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 --- drivers/regulator/virtual.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 5ddb464b1c3..a38d42bc8f8 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 v1.2.3 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 --- drivers/regulator/core.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f511a406fca..0ff95c3ccf5 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) { -- cgit v1.2.3 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 --- drivers/regulator/virtual.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index a38d42bc8f8..45ebbc22f47 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 v1.2.3 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 --- drivers/regulator/core.c | 15 ++++++++++----- drivers/regulator/wm8350-regulator.c | 2 ++ 2 files changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0ff95c3ccf5..96c877dd9da 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 5056e23e441..afad611fbb8 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) -- cgit v1.2.3 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 --- drivers/regulator/bq24022.c | 2 +- drivers/regulator/core.c | 5 +++-- drivers/regulator/da903x.c | 3 ++- drivers/regulator/pcf50633-regulator.c | 3 ++- drivers/regulator/wm8350-regulator.c | 2 +- drivers/regulator/wm8400-regulator.c | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index c175e38a4cd..6804333492e 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 96c877dd9da..f17362ac9c6 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 fe77730a7ed..72b15495183 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 4cc85ec6e12..cd761d85c8f 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 afad611fbb8..93e0ce5a5c2 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 56e23d44ba5..6ed43b0dbdf 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); -- cgit v1.2.3 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 --- drivers/regulator/bq24022.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c index 6804333492e..7ecb820ceeb 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); -- cgit v1.2.3 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 --- drivers/regulator/fixed.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index d31db3e1491..23d554628a7 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; -- cgit v1.2.3 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 --- drivers/regulator/Kconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e7e0cf102d6..85a1f407e75 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 v1.2.3 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 --- drivers/regulator/virtual.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 45ebbc22f47..3d08348584e 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 v1.2.3 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 --- drivers/regulator/core.c | 27 --------------------------- 1 file changed, 27 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f17362ac9c6..0ed13c2a8c3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -29,33 +29,6 @@ static DEFINE_MUTEX(regulator_list_mutex); 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 * -- cgit v1.2.3 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 --- drivers/regulator/core.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 0ed13c2a8c3..b85e4a9dafc 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 v1.2.3 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 --- drivers/regulator/core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b85e4a9dafc..d55a25a6fab 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 v1.2.3 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 --- drivers/regulator/core.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d55a25a6fab..75abcd85e51 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 && -- cgit v1.2.3 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 --- drivers/regulator/wm8350-regulator.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 93e0ce5a5c2..261db94e0e7 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 v1.2.3 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 --- drivers/regulator/core.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75abcd85e51..da357a07c98 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 */ @@ -1250,6 +1313,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 -- cgit v1.2.3 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 --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/twl4030-regulator.c | 511 ++++++++++++++++++++++++++++++++++ 3 files changed, 519 insertions(+) create mode 100644 drivers/regulator/twl4030-regulator.c (limited to 'drivers') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 85a1f407e75..e58c0ce65aa 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 61b30c6ddec..bac133afc06 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 00000000000..23f282670db --- /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"); -- cgit v1.2.3 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 --- drivers/regulator/twl4030-regulator.c | 62 +++++++++++++---------------------- 1 file changed, 23 insertions(+), 39 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index 23f282670db..663a4162b2a 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 v1.2.3 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 --- drivers/mmc/core/core.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index df6ce4a06cf..1445ea8f10a 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 -- cgit v1.2.3 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 --- drivers/regulator/wm8400-regulator.c | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 6ed43b0dbdf..15742602907 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 v1.2.3 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 --- drivers/regulator/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index da357a07c98..2ff76349f39 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 v1.2.3 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 --- drivers/regulator/twl4030-regulator.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index 663a4162b2a..49c41fc60aa 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 v1.2.3 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 --- drivers/regulator/wm8350-regulator.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 261db94e0e7..771eca1066b 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 v1.2.3 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 --- drivers/regulator/core.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 2ff76349f39..08441e24946 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", -- cgit v1.2.3 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 --- drivers/regulator/core.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 08441e24946..019a8a42ac1 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 v1.2.3 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 --- drivers/regulator/core.c | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 019a8a42ac1..944887578d6 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 v1.2.3 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 --- drivers/mfd/twl4030-core.c | 2 -- drivers/regulator/twl4030-regulator.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index 68826f1e36b..ec90e953adc 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 49c41fc60aa..f3ec98c83e9 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 v1.2.3 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 --- drivers/regulator/core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 944887578d6..8588a2490e0 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 v1.2.3 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 --- drivers/regulator/core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) (limited to 'drivers') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 8588a2490e0..01f7702a805 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 @@ -2142,6 +2143,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); -- cgit v1.2.3 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 --- drivers/regulator/twl4030-regulator.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/regulator/twl4030-regulator.c b/drivers/regulator/twl4030-regulator.c index f3ec98c83e9..e2032fb60b5 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 v1.2.3