From fe08be3ec8672ed92b3ed1b85810df9fa0f98931 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 6 Oct 2014 21:33:36 +0200 Subject: [PATCH 01/10] regulator: anatop: Set default voltage selector for vddpu The code reads the default voltage selector from its register. If the bootloader disables the regulator, the default voltage selector will be 0 which results in faulty behaviour of this regulator driver. This patch sets a default voltage selector for vddpu if it is not set in the register. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/regulator/anatop-regulator.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 4f730af70e7c..30e8d7ad5813 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -283,6 +283,14 @@ static int anatop_regulator_probe(struct platform_device *pdev) sreg->sel = 0; sreg->bypass = true; } + + /* + * In case vddpu was disabled by the bootloader, we need to set + * a sane default until imx6-cpufreq was probed and changes the + * voltage to the correct value. In this case we set 1.25V. + */ + if (!sreg->sel && !strcmp(sreg->name, "vddpu")) + sreg->sel = 22; } else { rdesc->ops = &anatop_rops; } From da0607c8df5c6277ff75468f24e055f193b6b336 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 6 Oct 2014 21:33:37 +0200 Subject: [PATCH 02/10] regulator: anatop: Fail on invalid voltage selector A '0' voltage selector is invalid and can't be used with this driver. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 30e8d7ad5813..542d14ed48bb 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -291,6 +291,11 @@ static int anatop_regulator_probe(struct platform_device *pdev) */ if (!sreg->sel && !strcmp(sreg->name, "vddpu")) sreg->sel = 22; + + if (!sreg->sel) { + dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n"); + return -EINVAL; + } } else { rdesc->ops = &anatop_rops; } From 76f439df50aba1838e06dd01e5f20dada7473f57 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Wed, 8 Oct 2014 15:47:05 +0200 Subject: [PATCH 03/10] regulator: Add ena_gpio_initialized to regulator_config Most drivers do not set the ena_gpio field of struct regulator_config before passing it to the regulator core. This is fine as long as the gpio identifier that is passed is a positive integer. But the gpio identifier 0 is also valid. So we are not able to decide wether we got a real gpio identifier or not based on a 0 in ena_gpio. To be able to decide if it is a valid gpio that got passed, this patch adds a ena_gpio_initialized field that should be set if was initialized with a correct value, either a gpio >= 0 or a negative error number. The core then checks if ena_gpio or ena_gpio_initialized before handling it as a gpio. This way we maintain backwards compatibility and fix the behaviour for gpio number 0. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 ++- include/linux/regulator/driver.h | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cd87c0c37034..55a87a2722d8 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3650,7 +3650,8 @@ regulator_register(const struct regulator_desc *regulator_desc, dev_set_drvdata(&rdev->dev, rdev); - if (config->ena_gpio && gpio_is_valid(config->ena_gpio)) { + if ((config->ena_gpio || config->ena_gpio_initialized) && + gpio_is_valid(config->ena_gpio)) { ret = regulator_ena_gpio_request(rdev, config); if (ret != 0) { rdev_err(rdev, "Failed to request enable GPIO%d: %d\n", diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index fc0ee0ce8325..28da08e4671f 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -301,6 +301,9 @@ struct regulator_desc { * NULL). * @regmap: regmap to use for core regmap helpers if dev_get_regulator() is * insufficient. + * @ena_gpio_initialized: GPIO controlling regulator enable was properly + * initialized, meaning that >= 0 is a valid gpio + * identifier and < 0 is a non existent gpio. * @ena_gpio: GPIO controlling regulator enable. * @ena_gpio_invert: Sense for GPIO enable control. * @ena_gpio_flags: Flags to use when calling gpio_request_one() @@ -312,6 +315,7 @@ struct regulator_config { struct device_node *of_node; struct regmap *regmap; + bool ena_gpio_initialized; int ena_gpio; unsigned int ena_gpio_invert:1; unsigned int ena_gpio_flags; From e1326eff80bffc43ef766c0262d1ce9375c2f610 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 27 Oct 2014 16:44:11 +0100 Subject: [PATCH 04/10] regulator: dummy: Make regulator_desc array const The regulator_register() expects array of 'regulator_desc' to be const. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/dummy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index 2436db9e2ca3..7aef9e4c6fbf 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -33,7 +33,7 @@ static struct regulator_init_data dummy_initdata = { static struct regulator_ops dummy_ops; -static struct regulator_desc dummy_desc = { +static const struct regulator_desc dummy_desc = { .name = "regulator-dummy", .id = -1, .type = REGULATOR_VOLTAGE, From 1de3821ace8200432993821bfda043827029de2a Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 3 Nov 2014 19:12:04 +0100 Subject: [PATCH 05/10] regulator: Set ena_gpio_initialized in regulator drivers This patch sets ena_gpio_initialized for all drivers which set a ena_gpio from parsed DT properties. Drivers using pdata may get zero initialized pdata and therefore copy a 0 into the regulator_config ena_gpio field. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 2 ++ drivers/regulator/fixed.c | 5 ++++- drivers/regulator/gpio-regulator.c | 4 +++- drivers/regulator/max8952.c | 2 ++ drivers/regulator/s2mps11.c | 1 + drivers/regulator/s5m8767.c | 1 + drivers/regulator/tps65090-regulator.c | 1 + drivers/regulator/wm8994-regulator.c | 6 ++++-- 8 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 4c9db589f6c1..559e7ea9dcf3 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -260,6 +260,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) ret = arizona_ldo1_of_get_pdata(arizona, &config); if (ret < 0) return ret; + + config.ena_gpio_initialized = true; } } diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 354105eff1f8..17f6a7d331f8 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -157,8 +157,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.fixed_uV = config->microvolts; - if (config->gpio >= 0) + if (config->gpio >= 0) { cfg.ena_gpio = config->gpio; + if (pdev->dev.of_node) + cfg.ena_gpio_initialized = true; + } cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { if (config->enable_high) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 989b23b377c0..5da0125fac6a 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -322,8 +322,10 @@ static int gpio_regulator_probe(struct platform_device *pdev) cfg.driver_data = drvdata; cfg.of_node = np; - if (config->enable_gpio >= 0) + if (config->enable_gpio >= 0) { cfg.ena_gpio = config->enable_gpio; + cfg.ena_gpio_initialized = true; + } cfg.ena_gpio_invert = !config->enable_high; if (config->enabled_at_boot) { if (config->enable_high) diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index f7f9efcfedb7..dec57fb9cd1c 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -225,6 +225,8 @@ static int max8952_pmic_probe(struct i2c_client *client, config.of_node = client->dev.of_node; config.ena_gpio = pdata->gpio_en; + if (client->dev.of_node) + config.ena_gpio_initialized = true; if (pdata->reg_data->constraints.boot_on) config.ena_gpio_flags |= GPIOF_OUT_INIT_HIGH; diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index adab82d5279f..b4ae022091eb 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -886,6 +886,7 @@ common_reg: config.regmap = iodev->regmap_pmic; config.driver_data = s2mps11; config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + config.ena_gpio_initialized = true; for (i = 0; i < s2mps11->rdev_num; i++) { struct regulator_dev *regulator; diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 0ab5cbeeb797..4225df68dd4d 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -950,6 +950,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.of_node = pdata->regulators[i].reg_node; config.ena_gpio = -EINVAL; config.ena_gpio_flags = 0; + config.ena_gpio_initialized = true; if (gpio_is_valid(pdata->regulators[i].ext_control_gpio)) s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index d5df1e9ad1da..2e92aa8718cc 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -312,6 +312,7 @@ static void tps65090_configure_regulator_config( gpio_flag = GPIOF_OUT_INIT_HIGH; config->ena_gpio = tps_pdata->gpio; + config->ena_gpio_initialized = true; config->ena_gpio_flags = gpio_flag; } } diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index c24346db8a71..88f5064e412b 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -145,10 +145,12 @@ static int wm8994_ldo_probe(struct platform_device *pdev) config.driver_data = ldo; config.regmap = wm8994->regmap; config.init_data = &ldo->init_data; - if (pdata) + if (pdata) { config.ena_gpio = pdata->ldo[id].enable; - else if (wm8994->dev->of_node) + } else if (wm8994->dev->of_node) { config.ena_gpio = wm8994->pdata.ldo[id].enable; + config.ena_gpio_initialized = true; + } /* Use default constraints if none set up */ if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) { From 5315fe2f8defc012a5800423be3f6098f95527d7 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 3 Nov 2014 19:12:05 +0100 Subject: [PATCH 06/10] regulator: fixed: Use gpio_is_valid Use gpio_is_valid instead of an explicit comparison with 0. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/fixed.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 17f6a7d331f8..709fba19e596 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -157,7 +157,7 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev) drvdata->desc.fixed_uV = config->microvolts; - if (config->gpio >= 0) { + if (gpio_is_valid(config->gpio)) { cfg.ena_gpio = config->gpio; if (pdev->dev.of_node) cfg.ena_gpio_initialized = true; From 2454f8d15cf75781a2714e99badbfbc2f5a7dacb Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 3 Nov 2014 19:12:06 +0100 Subject: [PATCH 07/10] regulator: gpio: Use gpio_is_valid Use gpio_is_valid instead of an explicit comparison with 0. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 5da0125fac6a..86546c1c32c4 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -322,7 +322,7 @@ static int gpio_regulator_probe(struct platform_device *pdev) cfg.driver_data = drvdata; cfg.of_node = np; - if (config->enable_gpio >= 0) { + if (gpio_is_valid(config->enable_gpio)) { cfg.ena_gpio = config->enable_gpio; cfg.ena_gpio_initialized = true; } From 679c038f544e46803d4fce16636747c3e77af4f1 Mon Sep 17 00:00:00 2001 From: Markus Pargmann Date: Mon, 3 Nov 2014 19:12:07 +0100 Subject: [PATCH 08/10] regulator: tps65090: Fix gpio initialization The config is used for multiple regulators within a for loop. The config field is not cleared before it is used for the next item. To avoid any issues this patch adds a proper initialization for the config->ena_gpio field in case no gpio is available. Signed-off-by: Markus Pargmann Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 2e92aa8718cc..f1df4423d361 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -314,6 +314,9 @@ static void tps65090_configure_regulator_config( config->ena_gpio = tps_pdata->gpio; config->ena_gpio_initialized = true; config->ena_gpio_flags = gpio_flag; + } else { + config->ena_gpio = -EINVAL; + config->ena_gpio_initialized = false; } } From 9f946099fe1927aa746892232c4421e1ae175699 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Wed, 19 Nov 2014 14:13:06 +0000 Subject: [PATCH 09/10] regulator: gpio: fix parsing of gpio list The list of gpios is defined as optional but the code was failing to properly handle the case of no gpios, and also failing to check for errors reading the entry from the devicetree. This patch fixes the handling of optional gpios - this is a useful feature enabling the gpio-regulator to be used as a dummy variable voltage regulator without having to assign any real GPIO lines. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 87 ++++++++++++++++-------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 86546c1c32c4..b42c7ec0017f 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -162,34 +162,41 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); - /* Fetch GPIOs. */ - config->nr_gpios = of_gpio_count(np); + /* Fetch GPIOs. - optional property*/ + ret = of_gpio_count(np); + if ((ret < 0) && (ret != -ENOENT)) + return ERR_PTR(ret); - config->gpios = devm_kzalloc(dev, - sizeof(struct gpio) * config->nr_gpios, - GFP_KERNEL); - if (!config->gpios) - return ERR_PTR(-ENOMEM); + if (ret > 0) { + config->nr_gpios = ret; + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); - proplen = of_property_count_u32_elems(np, "gpios-states"); - /* optional property */ - if (proplen < 0) - proplen = 0; + proplen = of_property_count_u32_elems(np, "gpios-states"); + /* optional property */ + if (proplen < 0) + proplen = 0; - if (proplen > 0 && proplen != config->nr_gpios) { - dev_warn(dev, "gpios <-> gpios-states mismatch\n"); - proplen = 0; - } + if (proplen > 0 && proplen != config->nr_gpios) { + dev_warn(dev, "gpios <-> gpios-states mismatch\n"); + proplen = 0; + } - for (i = 0; i < config->nr_gpios; i++) { - gpio = of_get_named_gpio(np, "gpios", i); - if (gpio < 0) - break; - config->gpios[i].gpio = gpio; - if (proplen > 0) { - of_property_read_u32_index(np, "gpios-states", i, &ret); - if (ret) - config->gpios[i].flags = GPIOF_OUT_INIT_HIGH; + for (i = 0; i < config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + if (proplen > 0) { + of_property_read_u32_index(np, "gpios-states", + i, &ret); + if (ret) + config->gpios[i].flags = + GPIOF_OUT_INIT_HIGH; + } } } @@ -261,13 +268,23 @@ static int gpio_regulator_probe(struct platform_device *pdev) goto err; } - drvdata->gpios = kmemdup(config->gpios, - config->nr_gpios * sizeof(struct gpio), - GFP_KERNEL); - if (drvdata->gpios == NULL) { - dev_err(&pdev->dev, "Failed to allocate gpio data\n"); - ret = -ENOMEM; - goto err_name; + if (config->nr_gpios != 0) { + drvdata->gpios = kmemdup(config->gpios, + config->nr_gpios * sizeof(struct gpio), + GFP_KERNEL); + if (drvdata->gpios == NULL) { + dev_err(&pdev->dev, "Failed to allocate gpio data\n"); + ret = -ENOMEM; + goto err_name; + } + + drvdata->nr_gpios = config->nr_gpios; + ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", ret); + goto err_memstate; + } } drvdata->states = kmemdup(config->states, @@ -301,14 +318,6 @@ static int gpio_regulator_probe(struct platform_device *pdev) goto err_memgpio; } - drvdata->nr_gpios = config->nr_gpios; - ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); - if (ret) { - dev_err(&pdev->dev, - "Could not obtain regulator setting GPIOs: %d\n", ret); - goto err_memstate; - } - /* build initial state from gpio init data. */ state = 0; for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { From a1c8a5512b7cddc81767172f0de37b155cea039f Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Mon, 24 Nov 2014 14:10:52 +0000 Subject: [PATCH 10/10] regulator: core: Add PRE_DISABLE notification Add a PRE_DISABLE notification so that consumers can use a notifier to run any steps required to prepare for the regulator being switched off. Since the regulator disable can fail an abort notification is also added. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/core.c | 16 ++++++++++++++++ include/linux/regulator/consumer.h | 4 ++++ 2 files changed, 20 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cd87c0c37034..53de911a0954 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1976,9 +1976,18 @@ static int _regulator_disable(struct regulator_dev *rdev) /* we are last user */ if (_regulator_can_change_status(rdev)) { + ret = _notifier_call_chain(rdev, + REGULATOR_EVENT_PRE_DISABLE, + NULL); + if (ret & NOTIFY_STOP_MASK) + return -EINVAL; + ret = _regulator_do_disable(rdev); if (ret < 0) { rdev_err(rdev, "failed to disable\n"); + _notifier_call_chain(rdev, + REGULATOR_EVENT_ABORT_DISABLE, + NULL); return ret; } _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, @@ -2035,9 +2044,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; + ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_PRE_DISABLE, NULL); + if (ret & NOTIFY_STOP_MASK) + return -EINVAL; + ret = _regulator_do_disable(rdev); if (ret < 0) { rdev_err(rdev, "failed to force disable\n"); + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_ABORT_DISABLE, NULL); return ret; } diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index d347c805f923..9efddd2a63ee 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -99,6 +99,8 @@ struct regmap; * Data passed is "struct pre_voltage_change_data" * ABORT_VOLTAGE_CHANGE Regulator voltage change failed for some reason. * Data passed is old voltage cast to (void *). + * PRE_DISABLE Regulator is about to be disabled + * ABORT_DISABLE Regulator disable failed for some reason * * NOTE: These events can be OR'ed together when passed into handler. */ @@ -113,6 +115,8 @@ struct regmap; #define REGULATOR_EVENT_DISABLE 0x80 #define REGULATOR_EVENT_PRE_VOLTAGE_CHANGE 0x100 #define REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE 0x200 +#define REGULATOR_EVENT_PRE_DISABLE 0x400 +#define REGULATOR_EVENT_ABORT_DISABLE 0x800 /** * struct pre_voltage_change_data - Data sent with PRE_VOLTAGE_CHANGE event