From 45a86172452a168f3157e2b0f16cfb113b43fa79 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 21 Feb 2017 11:29:04 -0300 Subject: [PATCH 01/51] regulator: ltc3589: Add OF device ID table The driver doesn't have a struct of_device_id table but supported devices are registered via Device Trees. This is working on the assumption that a I2C device registered via OF will always match a legacy I2C device ID and that the MODALIAS reported will always be of the form i2c:. But this could change in the future so the correct approach is to have an OF device ID table if the devices are registered via OF. Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/regulator/ltc3589.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/ltc3589.c b/drivers/regulator/ltc3589.c index a7a1a0313bbf..853a06ad86d6 100644 --- a/drivers/regulator/ltc3589.c +++ b/drivers/regulator/ltc3589.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -470,7 +471,11 @@ static int ltc3589_probe(struct i2c_client *client, return -ENOMEM; i2c_set_clientdata(client, ltc3589); - ltc3589->variant = id->driver_data; + if (client->dev.of_node) + ltc3589->variant = (enum ltc3589_variant) + of_device_get_match_data(&client->dev); + else + ltc3589->variant = id->driver_data; ltc3589->dev = dev; descs = ltc3589->regulator_descs; @@ -542,9 +547,27 @@ static struct i2c_device_id ltc3589_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id); +static const struct of_device_id ltc3589_of_match[] = { + { + .compatible = "lltc,ltc3589", + .data = (void *)LTC3589, + }, + { + .compatible = "lltc,ltc3589-1", + .data = (void *)LTC3589_1, + }, + { + .compatible = "lltc,ltc3589-2", + .data = (void *)LTC3589_2, + }, + { }, +}; +MODULE_DEVICE_TABLE(of, ltc3589_of_match); + static struct i2c_driver ltc3589_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ltc3589_of_match), }, .probe = ltc3589_probe, .id_table = ltc3589_i2c_id, From c314341557d3e8369b89eabde0b864997cf7f420 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Tue, 21 Feb 2017 11:29:05 -0300 Subject: [PATCH 02/51] regulator: ltc3676: Add OF device ID table The driver doesn't have a struct of_device_id table but supported devices are registered via Device Trees. This is working on the assumption that a I2C device registered via OF will always match a legacy I2C device ID and that the MODALIAS reported will always be of the form i2c:. But this could change in the future so the correct approach is to have an OF device ID table if the devices are registered via OF. Signed-off-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/regulator/ltc3676.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c index 503cd90eba39..662ee05ea44d 100644 --- a/drivers/regulator/ltc3676.c +++ b/drivers/regulator/ltc3676.c @@ -406,9 +406,16 @@ static const struct i2c_device_id ltc3676_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id); +static const struct of_device_id ltc3676_of_match[] = { + { .compatible = "lltc,ltc3676" }, + { }, +}; +MODULE_DEVICE_TABLE(of, ltc3676_of_match); + static struct i2c_driver ltc3676_driver = { .driver = { .name = DRIVER_NAME, + .of_match_table = of_match_ptr(ltc3676_of_match), }, .probe = ltc3676_regulator_probe, .id_table = ltc3676_i2c_id, From b7cd1b1386ff46e60452ad1f16530645761ca7b8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 6 Mar 2017 17:34:48 +0100 Subject: [PATCH 03/51] regulator: core: use snprintf() instead of scnprintf() When creating the link to the device sysfs entry, the regulator core calls scnprintf() and then checks if the returned value is greater or equal than the buffer size. The former can never happen as scnprintf() returns the number of bytes that were actually written to the buffer, not the bytes that *would* have been written. Use the right function in this case: snprintf(). Signed-off-by: Bartosz Golaszewski Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 53d4fc70dbd0..f20ad0a8fc38 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1326,8 +1326,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, regulator->dev = dev; /* Add a link to the device sysfs entry */ - size = scnprintf(buf, REG_STR_SIZE, "%s-%s", - dev->kobj.name, supply_name); + size = snprintf(buf, REG_STR_SIZE, "%s-%s", + dev->kobj.name, supply_name); if (size >= REG_STR_SIZE) goto overflow_err; From 9503b5085fd0e7cfc6e32b1a473a997303d7e26a Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Tue, 28 Feb 2017 16:50:41 +0900 Subject: [PATCH 04/51] regulator: lm363x: Use generic DT property name for external control pins Vpos and Vneg LDOs can be enabled or disabled by external GPIOs. Use general DT property 'enable-gpios' for this usage. Two enable pins are differentiable by selecting the index number. Signed-off-by: Milo Kim Signed-off-by: Mark Brown --- drivers/regulator/lm363x-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c index f53e63301a20..ce5f7d9ad475 100644 --- a/drivers/regulator/lm363x-regulator.c +++ b/drivers/regulator/lm363x-regulator.c @@ -227,9 +227,9 @@ static int lm363x_regulator_of_get_enable_gpio(struct device_node *np, int id) */ switch (id) { case LM3632_LDO_POS: - return of_get_named_gpio(np, "ti,lcm-en1-gpio", 0); + return of_get_named_gpio(np, "enable-gpios", 0); case LM3632_LDO_NEG: - return of_get_named_gpio(np, "ti,lcm-en2-gpio", 0); + return of_get_named_gpio(np, "enable-gpios", 1); default: return -EINVAL; } From 45493684f5953d83cb2621027ad0792d167bedab Mon Sep 17 00:00:00 2001 From: Milo Kim Date: Tue, 28 Feb 2017 16:50:40 +0900 Subject: [PATCH 05/51] regulator: lm363x: Use generic property for hardware enable pins With index usages, device specific properties can be replaced with generic one. Vpos is index 0 and Vneg is index 1. DT examples are added as well. Signed-off-by: Milo Kim Signed-off-by: Mark Brown --- .../bindings/regulator/lm363x-regulator.txt | 78 ++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt b/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt index 8f14df9d1205..cc5a6151d85f 100644 --- a/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/lm363x-regulator.txt @@ -8,8 +8,8 @@ Required property: Optional properties: LM3632 has external enable pins for two LDOs. - - ti,lcm-en1-gpio: A GPIO specifier for Vpos control pin. - - ti,lcm-en2-gpio: A GPIO specifier for Vneg control pin. + - enable-gpios: Two GPIO specifiers for Vpos and Vneg control pins. + The first entry is Vpos, the second is Vneg enable pin. Child nodes: LM3631 @@ -30,5 +30,79 @@ Child nodes: Examples: Please refer to ti-lmu dt-bindings [2]. +lm3631@29 { + compatible = "ti,lm3631"; + reg = <0x29>; + + regulators { + compatible = "ti,lm363x-regulator"; + + vboost { + regulator-name = "lcd_boost"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <6350000>; + regulator-always-on; + }; + + vcont { + regulator-name = "lcd_vcont"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <3300000>; + }; + + voref { + regulator-name = "lcd_voref"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + + vpos { + regulator-name = "lcd_vpos"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + + vneg { + regulator-name = "lcd_vneg"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + regulator-boot-on; + }; + }; +}; + +lm3632@11 { + compatible = "ti,lm3632"; + reg = <0x11>; + + regulators { + compatible = "ti,lm363x-regulator"; + + /* GPIO1_16 for Vpos, GPIO1_28 is for Vneg */ + enable-gpios = <&gpio1 16 GPIO_ACTIVE_HIGH>, + <&gpio1 28 GPIO_ACTIVE_HIGH>; + + vboost { + regulator-name = "lcd_boost"; + regulator-min-microvolt = <4500000>; + regulator-max-microvolt = <6400000>; + regulator-always-on; + }; + + vpos { + regulator-name = "lcd_vpos"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + + vneg { + regulator-name = "lcd_vneg"; + regulator-min-microvolt = <4000000>; + regulator-max-microvolt = <6000000>; + }; + }; +}; + [1] ../regulator/regulator.txt [2] ../mfd/ti-lmu.txt From c6182ac96096f35c7216e4e6a3c64c7374dadeb7 Mon Sep 17 00:00:00 2001 From: George McCollister Date: Thu, 9 Mar 2017 08:14:43 -0600 Subject: [PATCH 06/51] regulator: pfuze100-regulator: add coin support Add support for PF0200 coin cell/super capacitor charger which works as a current limited voltage source via the LICELL pin. When VIN goes below a certain threshold LICELL is used to provide power for VSNVS which is usually used to hold up secure non-volatile storage and the real-time clock on the SoC. Signed-off-by: George McCollister Signed-off-by: Mark Brown --- .../bindings/regulator/pfuze100.txt | 8 ++++++- drivers/regulator/pfuze100-regulator.c | 24 +++++++++++++++++++ include/linux/regulator/pfuze100.h | 1 + 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/pfuze100.txt b/Documentation/devicetree/bindings/regulator/pfuze100.txt index 9b40db88f637..444c47831a40 100644 --- a/Documentation/devicetree/bindings/regulator/pfuze100.txt +++ b/Documentation/devicetree/bindings/regulator/pfuze100.txt @@ -13,7 +13,7 @@ Required child node: --PFUZE100 sw1ab,sw1c,sw2,sw3a,sw3b,sw4,swbst,vsnvs,vrefddr,vgen1~vgen6 --PFUZE200 - sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6 + sw1ab,sw2,sw3a,sw3b,swbst,vsnvs,vrefddr,vgen1~vgen6,coin --PFUZE3000 sw1a,sw1b,sw2,sw3,swbst,vsnvs,vrefddr,vldo1,vldo2,vccsd,v33,vldo3,vldo4 @@ -205,6 +205,12 @@ Example 2: PFUZE200 regulator-max-microvolt = <3300000>; regulator-always-on; }; + + coin_reg: coin { + regulator-min-microvolt = <2500000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + }; }; }; diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c index cb18b5c4f2db..716abcc834c8 100644 --- a/drivers/regulator/pfuze100-regulator.c +++ b/drivers/regulator/pfuze100-regulator.c @@ -40,6 +40,7 @@ #define PFUZE100_REVID 0x3 #define PFUZE100_FABID 0x4 +#define PFUZE100_COINVOL 0x1a #define PFUZE100_SW1ABVOL 0x20 #define PFUZE100_SW1CVOL 0x2e #define PFUZE100_SW2VOL 0x35 @@ -81,6 +82,10 @@ static const int pfuze100_vsnvs[] = { 1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000, }; +static const int pfuze100_coin[] = { + 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000, +}; + static const int pfuze3000_sw2lo[] = { 1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000, }; @@ -230,6 +235,23 @@ static struct regulator_ops pfuze100_swb_regulator_ops = { .stby_mask = 0x20, \ } +#define PFUZE100_COIN_REG(_chip, _name, base, mask, voltages) \ + [_chip ## _ ## _name] = { \ + .desc = { \ + .name = #_name, \ + .n_voltages = ARRAY_SIZE(voltages), \ + .ops = &pfuze100_swb_regulator_ops, \ + .type = REGULATOR_VOLTAGE, \ + .id = _chip ## _ ## _name, \ + .owner = THIS_MODULE, \ + .volt_table = voltages, \ + .vsel_reg = (base), \ + .vsel_mask = (mask), \ + .enable_reg = (base), \ + .enable_mask = 0x8, \ + }, \ + } + #define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \ .desc = { \ .name = #_name, \ @@ -317,6 +339,7 @@ static struct pfuze_regulator pfuze200_regulators[] = { PFUZE100_VGEN_REG(PFUZE200, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE200, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000), PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000), + PFUZE100_COIN_REG(PFUZE200, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin), }; static struct pfuze_regulator pfuze3000_regulators[] = { @@ -371,6 +394,7 @@ static struct of_regulator_match pfuze200_matches[] = { { .name = "vgen4", }, { .name = "vgen5", }, { .name = "vgen6", }, + { .name = "coin", }, }; /* PFUZE3000 */ diff --git a/include/linux/regulator/pfuze100.h b/include/linux/regulator/pfuze100.h index 70c6c66c5bcf..e0ccf46f66cf 100644 --- a/include/linux/regulator/pfuze100.h +++ b/include/linux/regulator/pfuze100.h @@ -48,6 +48,7 @@ #define PFUZE200_VGEN4 10 #define PFUZE200_VGEN5 11 #define PFUZE200_VGEN6 12 +#define PFUZE200_COIN 13 #define PFUZE3000_SW1A 0 #define PFUZE3000_SW1B 1 From 0f6ce809a518d736232de5f496b957733b1b4724 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:18 +0200 Subject: [PATCH 07/51] regulator: max1586: Constify regulator_ops Static struct regulator_ops is not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/max1586.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 2c1228d5796a..6779c2b53674 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -126,14 +126,14 @@ static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev, * The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back * the set up value. */ -static struct regulator_ops max1586_v3_ops = { +static const struct regulator_ops max1586_v3_ops = { .get_voltage_sel = max1586_v3_get_voltage_sel, .set_voltage_sel = max1586_v3_set_voltage_sel, .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, }; -static struct regulator_ops max1586_v6_ops = { +static const struct regulator_ops max1586_v6_ops = { .get_voltage_sel = max1586_v6_get_voltage_sel, .set_voltage_sel = max1586_v6_set_voltage_sel, .list_voltage = regulator_list_voltage_table, From a08904fdb8b5be2334f3873cbeea4bc004528d02 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:19 +0200 Subject: [PATCH 08/51] regulator: max77693: Constify regulator_ops Static struct regulator_ops is not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/max77693-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c index cfbb9512e486..2e7c55058c2b 100644 --- a/drivers/regulator/max77693-regulator.c +++ b/drivers/regulator/max77693-regulator.c @@ -150,7 +150,7 @@ static struct regulator_ops max77693_safeout_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, }; -static struct regulator_ops max77693_charger_ops = { +static const struct regulator_ops max77693_charger_ops = { .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, From 8c2cd4697bfe524639ad3212f8f152c322941889 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:20 +0200 Subject: [PATCH 09/51] regulator: max8660: Constify regulator_ops Static struct regulator_ops (except max8660_dcdc_ops) are not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/max8660.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c index b87f62dd484e..a6183425f27d 100644 --- a/drivers/regulator/max8660.c +++ b/drivers/regulator/max8660.c @@ -194,7 +194,7 @@ static int max8660_ldo5_set_voltage_sel(struct regulator_dev *rdev, return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0); } -static struct regulator_ops max8660_ldo5_ops = { +static const struct regulator_ops max8660_ldo5_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .set_voltage_sel = max8660_ldo5_set_voltage_sel, @@ -252,7 +252,7 @@ static int max8660_ldo67_set_voltage_sel(struct regulator_dev *rdev, selector << 4); } -static struct regulator_ops max8660_ldo67_ops = { +static const struct regulator_ops max8660_ldo67_ops = { .is_enabled = max8660_ldo67_is_enabled, .enable = max8660_ldo67_enable, .disable = max8660_ldo67_disable, From f465bf9b05303154a86fdd74a571733b0f37af7c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:21 +0200 Subject: [PATCH 10/51] regulator: s2mpa01: Constify regulator_ops Static struct regulator_ops is not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/s2mpa01.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index 92f88753bfed..dc2105c619a6 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -212,7 +212,7 @@ ramp_disable: 1 << enable_shift, 0); } -static struct regulator_ops s2mpa01_ldo_ops = { +static const struct regulator_ops s2mpa01_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, @@ -223,7 +223,7 @@ static struct regulator_ops s2mpa01_ldo_ops = { .set_voltage_time_sel = regulator_set_voltage_time_sel, }; -static struct regulator_ops s2mpa01_buck_ops = { +static const struct regulator_ops s2mpa01_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, From 71b4540f61e9cc5597e2a0c0236781efd40af4c6 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:22 +0200 Subject: [PATCH 11/51] regulator: s2mps11: Constify regulator_ops Static struct regulator_ops is not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index d838e77dd947..7726b874e539 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -238,7 +238,7 @@ ramp_disable: 1 << enable_shift, 0); } -static struct regulator_ops s2mps11_ldo_ops = { +static const struct regulator_ops s2mps11_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, @@ -249,7 +249,7 @@ static struct regulator_ops s2mps11_ldo_ops = { .set_voltage_time_sel = regulator_set_voltage_time_sel, }; -static struct regulator_ops s2mps11_buck_ops = { +static const struct regulator_ops s2mps11_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, @@ -392,7 +392,7 @@ static const struct regulator_desc s2mps11_regulators[] = { regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV), }; -static struct regulator_ops s2mps14_reg_ops; +static const struct regulator_ops s2mps14_reg_ops; #define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \ .name = "LDO"#num, \ @@ -599,7 +599,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev) rdev->desc->enable_mask, state); } -static struct regulator_ops s2mps14_reg_ops = { +static const struct regulator_ops s2mps14_reg_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, @@ -681,7 +681,7 @@ static const struct regulator_desc s2mps14_regulators[] = { S2MPS14_BUCK1235_START_SEL), }; -static struct regulator_ops s2mps15_reg_ldo_ops = { +static const struct regulator_ops s2mps15_reg_ldo_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, .is_enabled = regulator_is_enabled_regmap, @@ -691,7 +691,7 @@ static struct regulator_ops s2mps15_reg_ldo_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, }; -static struct regulator_ops s2mps15_reg_buck_ops = { +static const struct regulator_ops s2mps15_reg_buck_ops = { .list_voltage = regulator_list_voltage_linear_range, .map_voltage = regulator_map_voltage_linear_range, .is_enabled = regulator_is_enabled_regmap, @@ -886,7 +886,7 @@ static int s2mpu02_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) ramp_val << ramp_shift); } -static struct regulator_ops s2mpu02_ldo_ops = { +static const struct regulator_ops s2mpu02_ldo_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, @@ -898,7 +898,7 @@ static struct regulator_ops s2mpu02_ldo_ops = { .set_suspend_disable = s2mps14_regulator_set_suspend_disable, }; -static struct regulator_ops s2mpu02_buck_ops = { +static const struct regulator_ops s2mpu02_buck_ops = { .list_voltage = regulator_list_voltage_linear, .map_voltage = regulator_map_voltage_linear, .is_enabled = regulator_is_enabled_regmap, From 8a05eb190238d44a101a7db9addddbe74be0968e Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:23 +0200 Subject: [PATCH 12/51] regulator: s5m8767: Constify regulator_ops Static struct regulator_ops is not modified so can be made const for code safeness. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 27343e1c43ef..383cd7533721 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -357,7 +357,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev, return 0; } -static struct regulator_ops s5m8767_ops = { +static const struct regulator_ops s5m8767_ops = { .list_voltage = regulator_list_voltage_linear, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, @@ -367,7 +367,7 @@ static struct regulator_ops s5m8767_ops = { .set_voltage_time_sel = s5m8767_set_voltage_time_sel, }; -static struct regulator_ops s5m8767_buck78_ops = { +static const struct regulator_ops s5m8767_buck78_ops = { .list_voltage = regulator_list_voltage_linear, .is_enabled = regulator_is_enabled_regmap, .enable = regulator_enable_regmap, From 5339c34f3906b34ddcd8c9614232ae1302d30c7b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sat, 11 Mar 2017 21:01:24 +0200 Subject: [PATCH 13/51] regulator: s2mpa01: Fix inconsistent indenting Broken indenting makes code more difficult to read and brings confusion. Fix warning reported by Smatch: s2mpa01.c:362 s2mpa01_pmic_probe() warn: inconsistent indenting Signed-off-by: Krzysztof Kozlowski Reviewed-by: Chanwoo Choi Signed-off-by: Mark Brown --- drivers/regulator/s2mpa01.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index dc2105c619a6..8ac50bc9d5ff 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -357,11 +357,11 @@ static int s2mpa01_pmic_probe(struct platform_device *pdev) if (iodev->dev->of_node) { reg_np = of_get_child_by_name(iodev->dev->of_node, "regulators"); - if (!reg_np) { - dev_err(&pdev->dev, - "could not find regulators sub-node\n"); - return -EINVAL; - } + if (!reg_np) { + dev_err(&pdev->dev, + "could not find regulators sub-node\n"); + return -EINVAL; + } of_regulator_match(&pdev->dev, reg_np, rdata, S2MPA01_REGULATOR_MAX); From 0630b614391f8cbc35e837b4645ec8faaaa6465e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 16 Mar 2017 18:07:14 -0700 Subject: [PATCH 14/51] regulator: Mark supply_name const and duplicate it as such The supply_name member of struct regulator can be const as we don't change it in the regulator core. Furthermore, when we copy the supply name we can use kstrdup_const() here to avoid a copy if the name is in the ro data section. Signed-off-by: Stephen Boyd Signed-off-by: Mark Brown --- drivers/regulator/core.c | 4 ++-- drivers/regulator/internal.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index f20ad0a8fc38..49a0b6a2e237 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1343,7 +1343,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, /* non-fatal */ } } else { - regulator->supply_name = kstrdup(supply_name, GFP_KERNEL); + regulator->supply_name = kstrdup_const(supply_name, GFP_KERNEL); if (regulator->supply_name == NULL) goto overflow_err; } @@ -1799,7 +1799,7 @@ static void _regulator_put(struct regulator *regulator) put_device(&rdev->dev); mutex_unlock(&rdev->mutex); - kfree(regulator->supply_name); + kfree_const(regulator->supply_name); kfree(regulator); module_put(rdev->owner); diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 1dd575b28564..66a8ea0c8386 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -29,7 +29,7 @@ struct regulator { int uA_load; int min_uV; int max_uV; - char *supply_name; + const char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; struct dentry *debugfs; From c635df496a5c397fd078b4e72a42e1be79632d7d Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 24 Mar 2017 17:13:50 +0000 Subject: [PATCH 15/51] regulator: twl6030: remove redundant range check min_uV > 1300000 && min_uV <= 1350000 It has been pointed out to me that the range for vsel = 58 is actually dead code as this is covered by an earlier check for (min_uV >= 700000) && (min_uV <= 1420000) so remove that check completely. Reported-by: Alban Auzeill Signed-off-by: Colin Ian King Signed-off-by: Mark Brown --- drivers/regulator/twl6030-regulator.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c index 4864b9d742c0..edaf93cc7823 100644 --- a/drivers/regulator/twl6030-regulator.c +++ b/drivers/regulator/twl6030-regulator.c @@ -456,8 +456,6 @@ static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV, vsel = 60; else if ((min_uV > 1350000) && (min_uV <= 1500000)) vsel = 59; - else if ((min_uV > 1300000) && (min_uV <= 1350000)) - vsel = 58; else return -EINVAL; break; From 75f88115391156b3f0fecbbae76bf870c89bcab8 Mon Sep 17 00:00:00 2001 From: Wadim Egorov Date: Wed, 22 Mar 2017 16:50:50 +0100 Subject: [PATCH 16/51] regulator: rk808: Fix RK818 LDO2 Set the correct voltage select register for LDO2. Signed-off-by: Wadim Egorov Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/regulator/rk808-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c index 3314bf299a51..dfa8d50a5d74 100644 --- a/drivers/regulator/rk808-regulator.c +++ b/drivers/regulator/rk808-regulator.c @@ -520,7 +520,7 @@ static const struct regulator_desc rk818_reg[] = { RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, BIT(0), 400), RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100, - RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, + RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG, BIT(1), 400), { .name = "LDO_REG3", From 264b88c9e5c86c92ca1d67689779362760baf651 Mon Sep 17 00:00:00 2001 From: Harald Geyer Date: Thu, 23 Feb 2017 17:06:52 +0000 Subject: [PATCH 17/51] regulator: core: Add new notification for enabling of regulator This is useful for devices, which need some time to start up, to help the drivers track how long the supply has been up already. Ie whether it can safely talk to the HW or needs to wait. Signed-off-by: Harald Geyer Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 ++ include/linux/regulator/consumer.h | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 04baac9a165b..6b9bb1b00226 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2162,6 +2162,8 @@ static int _regulator_enable(struct regulator_dev *rdev) if (ret < 0) return ret; + _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE, + NULL); } else if (ret < 0) { rdev_err(rdev, "is_enabled() failed: %d\n", ret); return ret; diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index ea0fffa5faeb..df176d7c2b87 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -119,6 +119,7 @@ struct regmap; #define REGULATOR_EVENT_ABORT_VOLTAGE_CHANGE 0x200 #define REGULATOR_EVENT_PRE_DISABLE 0x400 #define REGULATOR_EVENT_ABORT_DISABLE 0x800 +#define REGULATOR_EVENT_ENABLE 0x1000 /* * Regulator errors that can be queried using regulator_get_error_flags From fffd1133388857f5b4b8c588b41b2ade16c7891c Mon Sep 17 00:00:00 2001 From: Tamara Diaconita Date: Tue, 28 Mar 2017 21:30:21 +0300 Subject: [PATCH 18/51] regulator: core: Fix kerneldoc comments Remove the description for the non-existing 'ret' to fix the build warning: ./drivers/regulator/core.c:1467: warning: Excess function parameter 'ret' description in 'regulator_dev_lookup'. The description found for the return value is: @ret: 0 on success, -ENODEV if lookup fails permanently, -EPROBE_DEFER if lookup could succeed in the future. Signed-off-by: Tamara Diaconita Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 49a0b6a2e237..c20b28a63d15 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1451,8 +1451,6 @@ static struct regulator_dev *regulator_lookup_by_name(const char *name) * regulator_dev_lookup - lookup a regulator device. * @dev: device for regulator "consumer". * @supply: Supply name or regulator ID. - * @ret: 0 on success, -ENODEV if lookup fails permanently, -EPROBE_DEFER if - * lookup could succeed in the future. * * If successful, returns a struct regulator_dev that corresponds to the name * @supply and with the embedded struct device refcount incremented by one. From fd086045559d90cd7854818b4c60a7119eda6231 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Mon, 27 Mar 2017 16:54:12 -0700 Subject: [PATCH 19/51] regulator: core: Limit propagation of parent voltage count and list Commit 26988efe11b1 ("regulator: core: Allow to get voltage count and list from parent") introduces the propagation of the parent voltage count and list for regulators that don't provide this information themselves. The goal is to support simple switch regulators, however as a side effect normal continuous regulators can leak details of their supplies and provide consumers with inconsistent information. Limit the propagation of the voltage count and list to switch regulators. Fixes: 26988efe11b1 ("regulator: core: Allow to get voltage count and list from parent") Signed-off-by: Matthias Kaehlcke Reviewed-by: Javier Martinez Canillas Tested-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/regulator/core.c | 9 +++++++-- include/linux/regulator/driver.h | 2 ++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index c20b28a63d15..aff302dfab5d 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2484,7 +2484,7 @@ static int _regulator_list_voltage(struct regulator *regulator, ret = ops->list_voltage(rdev, selector); if (lock) mutex_unlock(&rdev->mutex); - } else if (rdev->supply) { + } else if (rdev->is_switch && rdev->supply) { ret = _regulator_list_voltage(rdev->supply, selector, lock); } else { return -EINVAL; @@ -2542,7 +2542,7 @@ int regulator_count_voltages(struct regulator *regulator) if (rdev->desc->n_voltages) return rdev->desc->n_voltages; - if (!rdev->supply) + if (!rdev->is_switch || !rdev->supply) return -EINVAL; return regulator_count_voltages(rdev->supply); @@ -4097,6 +4097,11 @@ regulator_register(const struct regulator_desc *regulator_desc, mutex_unlock(®ulator_list_mutex); } + if (!rdev->desc->ops->get_voltage && + !rdev->desc->ops->list_voltage && + !rdev->desc->fixed_uV) + rdev->is_switch = true; + ret = device_register(&rdev->dev); if (ret != 0) { put_device(&rdev->dev); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index dac8e7b16bc6..4cb1c9be6073 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -429,6 +429,8 @@ struct regulator_dev { struct regulator_enable_gpio *ena_pin; unsigned int ena_gpio_state:1; + unsigned int is_switch:1; + /* time when this regulator was disabled last time */ unsigned long last_off_jiffy; }; From 2543ef3173889373fc07df61520e1bb42a99c85e Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 28 Mar 2017 15:14:37 +0100 Subject: [PATCH 20/51] regulator: arizona-micsupp: Avoid potential memory leak reading init_data The device argument passed to of_get_regulator_init_data is used to do some devres memory allocation. Currently the driver passes the MFD device pointer to this function, this could result in the init_data allocation being leaked if the regulator is unbound but the MFD isn't. Correct this issue by correctly passing the local platform device. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index fcb98dbda837..0ed14e41de11 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -197,7 +197,8 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { .num_consumer_supplies = 1, }; -static int arizona_micsupp_of_get_pdata(struct arizona *arizona, +static int arizona_micsupp_of_get_pdata(struct device *dev, + struct arizona *arizona, struct regulator_config *config, const struct regulator_desc *desc) { @@ -211,7 +212,7 @@ static int arizona_micsupp_of_get_pdata(struct arizona *arizona, if (np) { config->of_node = np; - init_data = of_get_regulator_init_data(arizona->dev, np, desc); + init_data = of_get_regulator_init_data(dev, np, desc); if (init_data) { init_data->consumer_supplies = &micsupp->supply; @@ -266,8 +267,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_micsupp_of_get_pdata(arizona, &config, - desc); + ret = arizona_micsupp_of_get_pdata(&pdev->dev, arizona, + &config, desc); if (ret < 0) return ret; } From 0feb837a42c95fee901e03f76a1266db85ceb6ec Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 28 Mar 2017 15:14:38 +0100 Subject: [PATCH 21/51] regulator: arizona-ldo1: Avoid potential memory leak reading init_data The device argument passed to of_get_regulator_init_data is used to do some devres memory allocation. Currently the driver passes the MFD device pointer to this function, this could result in the init_data allocation being leaked if the regulator is unbound but the MFD isn't. Correct this issue by correctly passing the local platform device. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 302b57cb89c6..cf558168664d 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -186,7 +186,8 @@ static const struct regulator_init_data arizona_ldo1_wm5110 = { .num_consumer_supplies = 1, }; -static int arizona_ldo1_of_get_pdata(struct arizona *arizona, +static int arizona_ldo1_of_get_pdata(struct device *dev, + struct arizona *arizona, struct regulator_config *config, const struct regulator_desc *desc) { @@ -212,8 +213,7 @@ static int arizona_ldo1_of_get_pdata(struct arizona *arizona, if (init_node) { config->of_node = init_node; - init_data = of_get_regulator_init_data(arizona->dev, init_node, - desc); + init_data = of_get_regulator_init_data(dev, init_node, desc); if (init_data) { init_data->consumer_supplies = &ldo1->supply; @@ -283,7 +283,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_ldo1_of_get_pdata(arizona, &config, desc); + ret = arizona_ldo1_of_get_pdata(&pdev->dev, arizona, + &config, desc); if (ret < 0) return ret; } From cdf4275e957c6bad3756e98942341667f1d7de7d Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 28 Mar 2017 15:14:39 +0100 Subject: [PATCH 22/51] MAINTAINERS: Add missing regulator regex for Wolfson Arizona parts The maintainers entry for the Wolfson parts seems to be missing an entry that covers the Arizona regulator drivers, correct this by adding one. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 9bf5aa617cf2..ef0c801aff45 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13119,6 +13119,7 @@ F: drivers/mfd/cs47l24* F: drivers/power/supply/wm83*.c F: drivers/rtc/rtc-wm83*.c F: drivers/regulator/wm8*.c +F: drivers/regulator/arizona* F: drivers/video/backlight/wm83*_bl.c F: drivers/watchdog/wm83*_wdt.c F: include/linux/mfd/arizona/ From 2f3c578fe2edf1de7ab981d1d7121e2eeee5b466 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 3 Apr 2017 00:28:42 -0500 Subject: [PATCH 23/51] regulator: hi655x: Describe consumed platform device The hi655x-regulator driver consumes a similarly named platform device. Adding that to the module device table, allows modprobe to locate this driver once the device is created. Signed-off-by: Jeremy Linton Signed-off-by: Mark Brown --- drivers/regulator/hi655x-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c index aca18466f522..11a6d27ec82f 100644 --- a/drivers/regulator/hi655x-regulator.c +++ b/drivers/regulator/hi655x-regulator.c @@ -214,7 +214,14 @@ static int hi655x_regulator_probe(struct platform_device *pdev) return 0; } +static const struct platform_device_id hi655x_regulator_table[] = { + { .name = "hi655x-regulator" }, + {}, +}; +MODULE_DEVICE_TABLE(platform, hi655x_regulator_table); + static struct platform_driver hi655x_regulator_driver = { + .id_table = hi655x_regulator_table, .driver = { .name = "hi655x-regulator", }, From cfd2cedb482ae29ce13a3db46cc24f0c082ca9cf Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 4 Apr 2017 18:59:49 +0530 Subject: [PATCH 24/51] regulator: DT: Add settling time property for non-linear voltage change Some regulators (some PWM regulators) have the voltage transition exponentially. On such cases, the settling time for voltage change is treated as constant time. Add DT property for providing the settling time for any level of voltage change for non-linear voltage change. signed-off-by: Laxman Dewangan Acked-by: Rob Herring Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index 6ab5aef619d9..d18edb075e1c 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -21,6 +21,9 @@ Optional properties: design requires. This property describes the total system ramp time required due to the combination of internal ramping of the regulator itself, and board design issues such as trace capacitance and load on the supply. +- regulator-settling-time-us: Settling time, in microseconds, for voltage + change if regulator have the constant time for any level voltage change. + This is useful when regulator have exponential voltage change. - regulator-soft-start: Enable soft start so that voltage ramps slowly - regulator-state-mem sub-root node for Suspend-to-RAM mode : suspend to memory, the device goes to sleep, but all data stored in memory, From d6c1dc3f52e3a65f35c58433ba57d14c0bad902f Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 4 Apr 2017 18:59:50 +0530 Subject: [PATCH 25/51] regulator: Add settling time for non-linear voltage transition Some regulators (some PWM regulators) have the voltage transition non-linear i.e. exponentially. On such cases, the settling time for voltage transition can not be presented in the voltage-ramp-delay. Add new property for non-linear voltage transition and handle this in getting the voltage settling time. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/core.c | 2 ++ drivers/regulator/of_regulator.c | 4 ++++ include/linux/regulator/machine.h | 3 +++ 3 files changed, 9 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 04baac9a165b..3a641d64f8e1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2753,6 +2753,8 @@ static int _regulator_set_voltage_time(struct regulator_dev *rdev, ramp_delay = rdev->constraints->ramp_delay; else if (rdev->desc->ramp_delay) ramp_delay = rdev->desc->ramp_delay; + else if (rdev->constraints->settling_time) + return rdev->constraints->settling_time; if (ramp_delay == 0) { rdev_dbg(rdev, "ramp_delay not set\n"); diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 4f613ec99500..09d677d5d3f0 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -86,6 +86,10 @@ static void of_get_regulation_constraints(struct device_node *np, constraints->ramp_disable = true; } + ret = of_property_read_u32(np, "regulator-settling-time-us", &pval); + if (!ret) + constraints->settling_time = pval; + ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval); if (!ret) constraints->enable_time = pval; diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index ad3e5158e586..598a493b3927 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -108,6 +108,8 @@ struct regulator_state { * @initial_state: Suspend state to set by default. * @initial_mode: Mode to set at startup. * @ramp_delay: Time to settle down after voltage change (unit: uV/us) + * @settling_time: Time to settle down after voltage change when voltage + * change is non-linear (unit: microseconds). * @active_discharge: Enable/disable active discharge. The enum * regulator_active_discharge values are used for * initialisation. @@ -149,6 +151,7 @@ struct regulation_constraints { unsigned int initial_mode; unsigned int ramp_delay; + unsigned int settling_time; unsigned int enable_time; unsigned int active_discharge; From a7a453f56a1a116027f84ac53b365eb045a0e279 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 28 Mar 2017 15:14:40 +0100 Subject: [PATCH 26/51] regulator: helpers: Add regmap set_soft_start helper Add a helper function regulator_set_soft_start_regmap to allow regmap based regulators to easily enable soft start. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/helpers.c | 18 ++++++++++++++++++ include/linux/regulator/driver.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index 379cdacc05d8..a75e7da49af8 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -445,6 +445,24 @@ int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable) } EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap); +/** + * regulator_set_soft_start_regmap - Default set_soft_start() using regmap + * + * @rdev: device to operate on. + */ +int regulator_set_soft_start_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + val = rdev->desc->soft_start_val_on; + if (!val) + val = rdev->desc->soft_start_mask; + + return regmap_update_bits(rdev->regmap, rdev->desc->soft_start_reg, + rdev->desc->soft_start_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_soft_start_regmap); + /** * regulator_get_bypass_regmap - Default get_bypass() using regmap * diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index dac8e7b16bc6..1054c033e783 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -292,6 +292,10 @@ enum regulator_type { * set_active_discharge * @active_discharge_reg: Register for control when using regmap * set_active_discharge + * @soft_start_reg: Register for control when using regmap set_soft_start + * @soft_start_mask: Mask for control when using regmap set_soft_start + * @soft_start_val_on: Enabling value for control when using regmap + * set_soft_start * * @enable_time: Time taken for initial enable of regulator (in uS). * @off_on_delay: guard time (in uS), before re-enabling a regulator @@ -345,6 +349,9 @@ struct regulator_desc { unsigned int active_discharge_off; unsigned int active_discharge_mask; unsigned int active_discharge_reg; + unsigned int soft_start_reg; + unsigned int soft_start_mask; + unsigned int soft_start_val_on; unsigned int enable_time; @@ -476,6 +483,7 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, unsigned int new_selector); int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable); int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable); +int regulator_set_soft_start_regmap(struct regulator_dev *rdev); int regulator_set_active_discharge_regmap(struct regulator_dev *rdev, bool enable); From f7d37bc3cb20828ac43b22cbd40222877ee2c46a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 28 Mar 2017 15:14:41 +0100 Subject: [PATCH 27/51] regulator: helpers: Add regmap set_pull_down helper Add a helper function regulator_set_pull_down_regmap to allow regmap based regulators to easily enable pull down. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/helpers.c | 18 ++++++++++++++++++ include/linux/regulator/driver.h | 8 ++++++++ 2 files changed, 26 insertions(+) diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c index a75e7da49af8..2ae7c3ac5940 100644 --- a/drivers/regulator/helpers.c +++ b/drivers/regulator/helpers.c @@ -463,6 +463,24 @@ int regulator_set_soft_start_regmap(struct regulator_dev *rdev) } EXPORT_SYMBOL_GPL(regulator_set_soft_start_regmap); +/** + * regulator_set_pull_down_regmap - Default set_pull_down() using regmap + * + * @rdev: device to operate on. + */ +int regulator_set_pull_down_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + + val = rdev->desc->pull_down_val_on; + if (!val) + val = rdev->desc->pull_down_mask; + + return regmap_update_bits(rdev->regmap, rdev->desc->pull_down_reg, + rdev->desc->pull_down_mask, val); +} +EXPORT_SYMBOL_GPL(regulator_set_pull_down_regmap); + /** * regulator_get_bypass_regmap - Default get_bypass() using regmap * diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 1054c033e783..8a9078dd2a5f 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -296,6 +296,10 @@ enum regulator_type { * @soft_start_mask: Mask for control when using regmap set_soft_start * @soft_start_val_on: Enabling value for control when using regmap * set_soft_start + * @pull_down_reg: Register for control when using regmap set_pull_down + * @pull_down_mask: Mask for control when using regmap set_pull_down + * @pull_down_val_on: Enabling value for control when using regmap + * set_pull_down * * @enable_time: Time taken for initial enable of regulator (in uS). * @off_on_delay: guard time (in uS), before re-enabling a regulator @@ -352,6 +356,9 @@ struct regulator_desc { unsigned int soft_start_reg; unsigned int soft_start_mask; unsigned int soft_start_val_on; + unsigned int pull_down_reg; + unsigned int pull_down_mask; + unsigned int pull_down_val_on; unsigned int enable_time; @@ -484,6 +491,7 @@ int regulator_set_voltage_time_sel(struct regulator_dev *rdev, int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable); int regulator_get_bypass_regmap(struct regulator_dev *rdev, bool *enable); int regulator_set_soft_start_regmap(struct regulator_dev *rdev); +int regulator_set_pull_down_regmap(struct regulator_dev *rdev); int regulator_set_active_discharge_regmap(struct regulator_dev *rdev, bool enable); From 9dee7a72d0c7cdfa2573c48b1e5f928c721d54d5 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Fri, 7 Apr 2017 12:51:58 -0700 Subject: [PATCH 28/51] regulator: Add driver for voltage controlled regulators The output voltage of a voltage controlled regulator can be controlled through the voltage of another regulator. The current version of this driver assumes that the output voltage is a linear function of the control voltage. Signed-off-by: Matthias Kaehlcke Acked-by: Rob Herring Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/vctrl.txt | 49 ++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/vctrl-regulator.c | 546 ++++++++++++++++++ 4 files changed, 603 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/vctrl.txt create mode 100644 drivers/regulator/vctrl-regulator.c diff --git a/Documentation/devicetree/bindings/regulator/vctrl.txt b/Documentation/devicetree/bindings/regulator/vctrl.txt new file mode 100644 index 000000000000..601328d7fdbb --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/vctrl.txt @@ -0,0 +1,49 @@ +Bindings for Voltage controlled regulators +========================================== + +Required properties: +-------------------- +- compatible : must be "vctrl-regulator". +- regulator-min-microvolt : smallest voltage consumers may set +- regulator-max-microvolt : largest voltage consumers may set +- ctrl-supply : The regulator supplying the control voltage. +- ctrl-voltage-range : an array of two integer values describing the range + (min/max) of the control voltage. The values specify + the control voltage needed to generate the corresponding + regulator-min/max-microvolt output voltage. + +Optional properties: +-------------------- +- ovp-threshold-percent : overvoltage protection (OVP) threshold of the + regulator in percent. Some regulators have an OVP + circuitry which shuts down the regulator when the + actual output voltage deviates beyond a certain + margin from the expected value for a given control + voltage. On larger voltage decreases this can occur + undesiredly since the output voltage does not adjust + inmediately to changes in the control voltage. To + avoid this situation the vctrl driver breaks down + larger voltage decreases into multiple steps, where + each step is within the OVP threshold. +- min-slew-down-rate : Describes how slowly the regulator voltage will decay + down in the worst case (lightest expected load). + Specified in uV / us (like main regulator ramp rate). + This value is required when ovp-threshold-percent is + specified. + +Example: + + vctrl-reg { + compatible = "vctrl-regulator"; + regulator-name = "vctrl_reg"; + + ctrl-supply = <&ctrl_reg>; + + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1500000>; + + ctrl-voltage-range = <200000 500000>; + + min-slew-down-rate = <225>; + ovp-threshold-percent = <16>; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 936f7ccc9736..da83a3abe288 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -843,6 +843,13 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_VCTRL + tristate "Voltage controlled regulators" + depends on OF + help + This driver provides support for voltage regulators whose output + voltage is controlled by the voltage of another regulator. + config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 14294692beb9..e246e148a7f9 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o +obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c new file mode 100644 index 000000000000..6baadef0ed74 --- /dev/null +++ b/drivers/regulator/vctrl-regulator.c @@ -0,0 +1,546 @@ +/* + * Driver for voltage controller regulators + * + * Copyright (C) 2017 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct vctrl_voltage_range { + int min_uV; + int max_uV; +}; + +struct vctrl_voltage_ranges { + struct vctrl_voltage_range ctrl; + struct vctrl_voltage_range out; +}; + +struct vctrl_voltage_table { + int ctrl; + int out; + int ovp_min_sel; +}; + +struct vctrl_data { + struct regulator_dev *rdev; + struct regulator_desc desc; + struct regulator *ctrl_reg; + bool enabled; + unsigned int min_slew_down_rate; + unsigned int ovp_threshold; + struct vctrl_voltage_ranges vrange; + struct vctrl_voltage_table *vtable; + unsigned int sel; +}; + +static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV) +{ + struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; + struct vctrl_voltage_range *out = &vctrl->vrange.out; + + return ctrl->min_uV + + DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) * + (ctrl->max_uV - ctrl->min_uV), + out->max_uV - out->min_uV); +} + +static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV) +{ + struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl; + struct vctrl_voltage_range *out = &vctrl->vrange.out; + + if (ctrl_uV < 0) { + pr_err("vctrl: failed to get control voltage\n"); + return ctrl_uV; + } + + if (ctrl_uV < ctrl->min_uV) + return out->min_uV; + + if (ctrl_uV > ctrl->max_uV) + return out->max_uV; + + return out->min_uV + + DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) * + (out->max_uV - out->min_uV), + ctrl->max_uV - ctrl->min_uV); +} + +static int vctrl_get_voltage(struct regulator_dev *rdev) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg); + + return vctrl_calc_output_voltage(vctrl, ctrl_uV); +} + +static int vctrl_set_voltage(struct regulator_dev *rdev, + int req_min_uV, int req_max_uV, + unsigned int *selector) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + struct regulator *ctrl_reg = vctrl->ctrl_reg; + int orig_ctrl_uV = regulator_get_voltage(ctrl_reg); + int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV); + int ret; + + if (req_min_uV >= uV || !vctrl->ovp_threshold) + /* voltage rising or no OVP */ + return regulator_set_voltage( + ctrl_reg, + vctrl_calc_ctrl_voltage(vctrl, req_min_uV), + vctrl_calc_ctrl_voltage(vctrl, req_max_uV)); + + while (uV > req_min_uV) { + int max_drop_uV = (uV * vctrl->ovp_threshold) / 100; + int next_uV; + int next_ctrl_uV; + int delay; + + /* Make sure no infinite loop even in crazy cases */ + if (max_drop_uV == 0) + max_drop_uV = 1; + + next_uV = max_t(int, req_min_uV, uV - max_drop_uV); + next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV); + + ret = regulator_set_voltage(ctrl_reg, + next_ctrl_uV, + next_ctrl_uV); + if (ret) + goto err; + + delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate); + usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); + + uV = next_uV; + } + + return 0; + +err: + /* Try to go back to original voltage */ + regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV); + + return ret; +} + +static int vctrl_get_voltage_sel(struct regulator_dev *rdev) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + + return vctrl->sel; +} + +static int vctrl_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + struct regulator *ctrl_reg = vctrl->ctrl_reg; + unsigned int orig_sel = vctrl->sel; + int ret; + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + if (selector >= vctrl->sel || !vctrl->ovp_threshold) { + /* voltage rising or no OVP */ + ret = regulator_set_voltage(ctrl_reg, + vctrl->vtable[selector].ctrl, + vctrl->vtable[selector].ctrl); + if (!ret) + vctrl->sel = selector; + + return ret; + } + + while (vctrl->sel != selector) { + unsigned int next_sel; + int delay; + + if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel) + next_sel = selector; + else + next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel; + + ret = regulator_set_voltage(ctrl_reg, + vctrl->vtable[next_sel].ctrl, + vctrl->vtable[next_sel].ctrl); + if (ret) { + dev_err(&rdev->dev, + "failed to set control voltage to %duV\n", + vctrl->vtable[next_sel].ctrl); + goto err; + } + vctrl->sel = next_sel; + + delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out - + vctrl->vtable[next_sel].out, + vctrl->min_slew_down_rate); + usleep_range(delay, delay + DIV_ROUND_UP(delay, 10)); + } + + return 0; + +err: + if (vctrl->sel != orig_sel) { + /* Try to go back to original voltage */ + if (!regulator_set_voltage(ctrl_reg, + vctrl->vtable[orig_sel].ctrl, + vctrl->vtable[orig_sel].ctrl)) + vctrl->sel = orig_sel; + else + dev_warn(&rdev->dev, + "failed to restore original voltage\n"); + } + + return ret; +} + +static int vctrl_list_voltage(struct regulator_dev *rdev, + unsigned int selector) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + + if (selector >= rdev->desc->n_voltages) + return -EINVAL; + + return vctrl->vtable[selector].out; +} + +static int vctrl_parse_dt(struct platform_device *pdev, + struct vctrl_data *vctrl) +{ + int ret; + struct device_node *np = pdev->dev.of_node; + u32 pval; + u32 vrange_ctrl[2]; + + vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl"); + if (IS_ERR(vctrl->ctrl_reg)) + return PTR_ERR(vctrl->ctrl_reg); + + ret = of_property_read_u32(np, "ovp-threshold-percent", &pval); + if (!ret) { + vctrl->ovp_threshold = pval; + if (vctrl->ovp_threshold > 100) { + dev_err(&pdev->dev, + "ovp-threshold-percent (%u) > 100\n", + vctrl->ovp_threshold); + return -EINVAL; + } + } + + ret = of_property_read_u32(np, "min-slew-down-rate", &pval); + if (!ret) { + vctrl->min_slew_down_rate = pval; + + /* We use the value as int and as divider; sanity check */ + if (vctrl->min_slew_down_rate == 0) { + dev_err(&pdev->dev, + "min-slew-down-rate must not be 0\n"); + return -EINVAL; + } else if (vctrl->min_slew_down_rate > INT_MAX) { + dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n", + vctrl->min_slew_down_rate); + return -EINVAL; + } + } + + if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) { + dev_err(&pdev->dev, + "ovp-threshold-percent requires min-slew-down-rate\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "regulator-min-microvolt", &pval); + if (ret) { + dev_err(&pdev->dev, + "failed to read regulator-min-microvolt: %d\n", ret); + return ret; + } + vctrl->vrange.out.min_uV = pval; + + ret = of_property_read_u32(np, "regulator-max-microvolt", &pval); + if (ret) { + dev_err(&pdev->dev, + "failed to read regulator-max-microvolt: %d\n", ret); + return ret; + } + vctrl->vrange.out.max_uV = pval; + + ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl, + 2); + if (ret) { + dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n", + ret); + return ret; + } + + if (vrange_ctrl[0] >= vrange_ctrl[1]) { + dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n", + vrange_ctrl[0], vrange_ctrl[1]); + return -EINVAL; + } + + vctrl->vrange.ctrl.min_uV = vrange_ctrl[0]; + vctrl->vrange.ctrl.max_uV = vrange_ctrl[1]; + + return 0; +} + +static int vctrl_cmp_ctrl_uV(const void *a, const void *b) +{ + const struct vctrl_voltage_table *at = a; + const struct vctrl_voltage_table *bt = b; + + return at->ctrl - bt->ctrl; +} + +static int vctrl_init_vtable(struct platform_device *pdev) +{ + struct vctrl_data *vctrl = platform_get_drvdata(pdev); + struct regulator_desc *rdesc = &vctrl->desc; + struct regulator *ctrl_reg = vctrl->ctrl_reg; + struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl; + int n_voltages; + int ctrl_uV; + int i, idx_vt; + + n_voltages = regulator_count_voltages(ctrl_reg); + + rdesc->n_voltages = n_voltages; + + /* determine number of steps within the range of the vctrl regulator */ + for (i = 0; i < n_voltages; i++) { + ctrl_uV = regulator_list_voltage(ctrl_reg, i); + + if (ctrl_uV < vrange_ctrl->min_uV || + ctrl_uV > vrange_ctrl->max_uV) { + rdesc->n_voltages--; + continue; + } + } + + if (rdesc->n_voltages == 0) { + dev_err(&pdev->dev, "invalid configuration\n"); + return -EINVAL; + } + + vctrl->vtable = devm_kmalloc_array( + &pdev->dev, sizeof(struct vctrl_voltage_table), + rdesc->n_voltages, GFP_KERNEL | __GFP_ZERO); + if (!vctrl->vtable) + return -ENOMEM; + + /* create mapping control <=> output voltage */ + for (i = 0, idx_vt = 0; i < n_voltages; i++) { + ctrl_uV = regulator_list_voltage(ctrl_reg, i); + + if (ctrl_uV < vrange_ctrl->min_uV || + ctrl_uV > vrange_ctrl->max_uV) + continue; + + vctrl->vtable[idx_vt].ctrl = ctrl_uV; + vctrl->vtable[idx_vt].out = + vctrl_calc_output_voltage(vctrl, ctrl_uV); + idx_vt++; + } + + /* we rely on the table to be ordered by ascending voltage */ + sort(vctrl->vtable, rdesc->n_voltages, + sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV, + NULL); + + /* pre-calculate OVP-safe downward transitions */ + for (i = n_voltages - 1; i > 0; i--) { + int j; + int ovp_min_uV = (vctrl->vtable[i].out * + (100 - vctrl->ovp_threshold)) / 100; + + for (j = 0; j < i; j++) { + if (vctrl->vtable[j].out >= ovp_min_uV) { + vctrl->vtable[i].ovp_min_sel = j; + break; + } + } + + if (j == i) { + dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n", + vctrl->vtable[i].out); + /* use next lowest voltage */ + vctrl->vtable[i].ovp_min_sel = i - 1; + } + } + + return 0; +} + +static int vctrl_enable(struct regulator_dev *rdev) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + int ret = regulator_enable(vctrl->ctrl_reg); + + if (!ret) + vctrl->enabled = true; + + return ret; +} + +static int vctrl_disable(struct regulator_dev *rdev) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + int ret = regulator_disable(vctrl->ctrl_reg); + + if (!ret) + vctrl->enabled = false; + + return ret; +} + +static int vctrl_is_enabled(struct regulator_dev *rdev) +{ + struct vctrl_data *vctrl = rdev_get_drvdata(rdev); + + return vctrl->enabled; +} + +static const struct regulator_ops vctrl_ops_cont = { + .enable = vctrl_enable, + .disable = vctrl_disable, + .is_enabled = vctrl_is_enabled, + .get_voltage = vctrl_get_voltage, + .set_voltage = vctrl_set_voltage, +}; + +static const struct regulator_ops vctrl_ops_non_cont = { + .enable = vctrl_enable, + .disable = vctrl_disable, + .is_enabled = vctrl_is_enabled, + .set_voltage_sel = vctrl_set_voltage_sel, + .get_voltage_sel = vctrl_get_voltage_sel, + .list_voltage = vctrl_list_voltage, + .map_voltage = regulator_map_voltage_iterate, +}; + +static int vctrl_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct vctrl_data *vctrl; + const struct regulator_init_data *init_data; + struct regulator_desc *rdesc; + struct regulator_config cfg = { }; + struct vctrl_voltage_range *vrange_ctrl; + int ctrl_uV; + int ret; + + vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data), + GFP_KERNEL); + if (!vctrl) + return -ENOMEM; + + platform_set_drvdata(pdev, vctrl); + + ret = vctrl_parse_dt(pdev, vctrl); + if (ret) + return ret; + + vrange_ctrl = &vctrl->vrange.ctrl; + + rdesc = &vctrl->desc; + rdesc->name = "vctrl"; + rdesc->type = REGULATOR_VOLTAGE; + rdesc->owner = THIS_MODULE; + + if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) || + (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) { + rdesc->continuous_voltage_range = true; + rdesc->ops = &vctrl_ops_cont; + } else { + rdesc->ops = &vctrl_ops_non_cont; + } + + init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc); + if (!init_data) + return -ENOMEM; + + cfg.of_node = np; + cfg.dev = &pdev->dev; + cfg.driver_data = vctrl; + cfg.init_data = init_data; + + if (!rdesc->continuous_voltage_range) { + ret = vctrl_init_vtable(pdev); + if (ret) + return ret; + + ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg); + if (ctrl_uV < 0) { + dev_err(&pdev->dev, "failed to get control voltage\n"); + return ctrl_uV; + } + + /* determine current voltage selector from control voltage */ + if (ctrl_uV < vrange_ctrl->min_uV) { + vctrl->sel = 0; + } else if (ctrl_uV > vrange_ctrl->max_uV) { + vctrl->sel = rdesc->n_voltages - 1; + } else { + int i; + + for (i = 0; i < rdesc->n_voltages; i++) { + if (ctrl_uV == vctrl->vtable[i].ctrl) { + vctrl->sel = i; + break; + } + } + } + } + + vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg); + if (IS_ERR(vctrl->rdev)) { + ret = PTR_ERR(vctrl->rdev); + dev_err(&pdev->dev, "failed to register regulator: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct of_device_id vctrl_of_match[] = { + { .compatible = "vctrl-regulator", }, + {}, +}; +MODULE_DEVICE_TABLE(of, vctrl_of_match); + +static struct platform_driver vctrl_driver = { + .probe = vctrl_probe, + .driver = { + .name = "vctrl-regulator", + .of_match_table = of_match_ptr(vctrl_of_match), + }, +}; + +module_platform_driver(vctrl_driver); + +MODULE_DESCRIPTION("Voltage Controlled Regulator Driver"); +MODULE_AUTHOR("Matthias Kaehlcke "); +MODULE_LICENSE("GPL v2"); From 0c08aaf873174c95e674cf21ffcd041c589d2e5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vincent=20Stehl=C3=A9?= Date: Sun, 9 Apr 2017 22:05:05 +0200 Subject: [PATCH 29/51] regulator: isl9305: fix array size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ISL9305_MAX_REGULATOR is the last index used to access the init_data[] array, so we need to add one to this last index to obtain the necessary array size. This fixes the following smatch error: drivers/regulator/isl9305.c:160 isl9305_i2c_probe() error: buffer overflow 'pdata->init_data' 3 <= 3 Fixes: dec38b5ce6a9edb4 ("regulator: isl9305: Add Intersil ISL9305/H driver") Signed-off-by: Vincent Stehlé Cc: Mark Brown Signed-off-by: Mark Brown --- include/linux/platform_data/isl9305.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/platform_data/isl9305.h b/include/linux/platform_data/isl9305.h index 1419133fa69e..4ac1a070af0a 100644 --- a/include/linux/platform_data/isl9305.h +++ b/include/linux/platform_data/isl9305.h @@ -24,7 +24,7 @@ struct regulator_init_data; struct isl9305_pdata { - struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR]; + struct regulator_init_data *init_data[ISL9305_MAX_REGULATOR + 1]; }; #endif From 7f51cf2ea7186e3f217e616a5522f1156678356f Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 12 Apr 2017 09:58:42 +0800 Subject: [PATCH 30/51] regulator: anatop: check return value of of_get_regulator_init_data Should check the return value of of_get_regulator_init_data before using it. Signed-off-by: Dong Aisheng Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 3a6d0290c54c..aa93f462ac6e 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -200,6 +200,9 @@ static int anatop_regulator_probe(struct platform_device *pdev) rdesc->owner = THIS_MODULE; initdata = of_get_regulator_init_data(dev, np, rdesc); + if (!initdata) + return -ENOMEM; + initdata->supply_regulator = "vin"; sreg->initdata = initdata; From 5062e04711dbc4f67b24ffd926cc67060267792d Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 12 Apr 2017 09:58:44 +0800 Subject: [PATCH 31/51] regulator: anatop: use of_property_read_string to read the name sreg->name is a string, so use a more proper api to read back the string instead of of_get_property. Signed-off-by: Dong Aisheng Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index aa93f462ac6e..58141cbdf257 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -193,7 +193,8 @@ static int anatop_regulator_probe(struct platform_device *pdev) sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL); if (!sreg) return -ENOMEM; - sreg->name = of_get_property(np, "regulator-name", NULL); + + of_property_read_string(np, "regulator-name", &sreg->name); rdesc = &sreg->rdesc; rdesc->name = sreg->name; rdesc->type = REGULATOR_VOLTAGE; From aeb1404d68df62b0a1d277a4138dbd92a4330304 Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 12 Apr 2017 09:58:45 +0800 Subject: [PATCH 32/51] regulator: anatop: remove unneeded name field of struct anatop_regulator sreg->name is only used as an intermediate assign of rdesc->name, plus another strcmp. Since we already have rdesc->name, no need it anymore. Signed-off-by: Dong Aisheng Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index 58141cbdf257..c6ce9745ffc8 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -39,7 +39,6 @@ #define LDO_FET_FULL_ON 0x1f struct anatop_regulator { - const char *name; u32 control_reg; struct regmap *anatop; int vol_bit_shift; @@ -194,12 +193,12 @@ static int anatop_regulator_probe(struct platform_device *pdev) if (!sreg) return -ENOMEM; - of_property_read_string(np, "regulator-name", &sreg->name); rdesc = &sreg->rdesc; - rdesc->name = sreg->name; rdesc->type = REGULATOR_VOLTAGE; rdesc->owner = THIS_MODULE; + of_property_read_string(np, "regulator-name", &rdesc->name); + initdata = of_get_regulator_init_data(dev, np, rdesc); if (!initdata) return -ENOMEM; @@ -297,7 +296,7 @@ static int anatop_regulator_probe(struct platform_device *pdev) * 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")) + if (!sreg->sel && !strcmp(rdesc->name, "vddpu")) sreg->sel = 22; if (!sreg->bypass && !sreg->sel) { From 77c129bfefc85bae4dbaa655a5d9b75c9c665da9 Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Wed, 12 Apr 2017 15:44:36 +0530 Subject: [PATCH 33/51] regulator: tps65132: add regulator driver for TI TPS65132 Add regulator driver for the device TI TPS65132 which is single inductor - dual output power supply device. TPS65132 device is designed to support general positive/negative driven applications like TFT display panels. TPS65132 regulator driver supports to enable/disable and set voltage on its output. Signed-off-by: Venkat Reddy Talla Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/tps65132-regulator.c | 285 +++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 drivers/regulator/tps65132-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 936f7ccc9736..00150c21166d 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -781,6 +781,14 @@ config REGULATOR_TPS65090 This driver provides support for the voltage regulators on the TI TPS65090 PMIC. +config REGULATOR_TPS65132 + tristate "TI TPS65132 Dual Output Power regulators" + depends on I2C && GPIOLIB + select REGMAP_I2C + help + This driver supports TPS65132 single inductor - dual output + power supply specifcally designed for display panels. + config REGULATOR_TPS65217 tristate "TI TPS65217 Power regulators" depends on MFD_TPS65217 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 14294692beb9..0e9275e78271 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -104,6 +104,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o +obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c new file mode 100644 index 000000000000..a949206065d4 --- /dev/null +++ b/drivers/regulator/tps65132-regulator.c @@ -0,0 +1,285 @@ +/* + * TI TPS65132 Regulator driver + * + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * Author: Venkat Reddy Talla + * Laxman Dewangan + * + * 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. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, + * whether express or implied; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TPS65132_REG_VPOS 0x00 +#define TPS65132_REG_VNEG 0x01 +#define TPS65132_REG_APPS_DISP_DISN 0x03 +#define TPS65132_REG_CONTROL 0x0FF + +#define TPS65132_VOUT_MASK 0x1F +#define TPS65132_VOUT_N_VOLTAGE 0x15 +#define TPS65132_VOUT_VMIN 4000000 +#define TPS65132_VOUT_VMAX 6000000 +#define TPS65132_VOUT_STEP 100000 + +#define TPS65132_REG_APPS_DIS_VPOS BIT(0) +#define TPS65132_REG_APPS_DIS_VNEG BIT(1) + +#define TPS65132_REGULATOR_ID_VPOS 0 +#define TPS65132_REGULATOR_ID_VNEG 1 +#define TPS65132_MAX_REGULATORS 2 + +#define TPS65132_ACT_DIS_TIME_SLACK 1000 + +struct tps65132_reg_pdata { + struct gpio_desc *en_gpiod; + struct gpio_desc *act_dis_gpiod; + unsigned int act_dis_time_us; + int ena_gpio_state; +}; + +struct tps65132_regulator { + struct device *dev; + struct regmap *rmap; + struct regulator_desc *rdesc[TPS65132_MAX_REGULATORS]; + struct tps65132_reg_pdata reg_pdata[TPS65132_MAX_REGULATORS]; + struct regulator_dev *rdev[TPS65132_MAX_REGULATORS]; +}; + +static int tps65132_regulator_enable(struct regulator_dev *rdev) +{ + struct tps65132_regulator *tps = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id]; + int ret; + + if (!IS_ERR(rpdata->en_gpiod)) { + gpiod_set_value_cansleep(rpdata->en_gpiod, 1); + rpdata->ena_gpio_state = 1; + } + + /* Hardware automatically enable discharge bit in enable */ + if (rdev->constraints->active_discharge == + REGULATOR_ACTIVE_DISCHARGE_DISABLE) { + ret = regulator_set_active_discharge_regmap(rdev, false); + if (ret < 0) { + dev_err(tps->dev, "Failed to disable active discharge: %d\n", + ret); + return ret; + } + } + + return 0; +} + +static int tps65132_regulator_disable(struct regulator_dev *rdev) +{ + struct tps65132_regulator *tps = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id]; + + if (!IS_ERR(rpdata->en_gpiod)) { + gpiod_set_value_cansleep(rpdata->en_gpiod, 0); + rpdata->ena_gpio_state = 0; + } + + if (!IS_ERR(rpdata->act_dis_gpiod)) { + gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 1); + usleep_range(rpdata->act_dis_time_us, rpdata->act_dis_time_us + + TPS65132_ACT_DIS_TIME_SLACK); + gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 0); + } + + return 0; +} + +static int tps65132_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct tps65132_regulator *tps = rdev_get_drvdata(rdev); + int id = rdev_get_id(rdev); + struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id]; + + if (!IS_ERR(rpdata->en_gpiod)) + return rpdata->ena_gpio_state; + + return 1; +} + +static struct regulator_ops tps65132_regulator_ops = { + .enable = tps65132_regulator_enable, + .disable = tps65132_regulator_disable, + .is_enabled = tps65132_regulator_is_enabled, + .list_voltage = regulator_list_voltage_linear, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, +}; + +static int tps65132_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct tps65132_regulator *tps = config->driver_data; + struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[desc->id]; + int ret; + + rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(tps->dev, + "enable", 0, &np->fwnode, 0, "enable"); + if (IS_ERR(rpdata->en_gpiod)) { + ret = PTR_ERR(rpdata->en_gpiod); + + /* Ignore the error other than probe defer */ + if (ret == -EPROBE_DEFER) + return ret; + return 0; + } + + rpdata->act_dis_gpiod = devm_fwnode_get_index_gpiod_from_child( + tps->dev, "active-discharge", 0, + &np->fwnode, 0, "active-discharge"); + if (IS_ERR(rpdata->act_dis_gpiod)) { + ret = PTR_ERR(rpdata->act_dis_gpiod); + + /* Ignore the error other than probe defer */ + if (ret == -EPROBE_DEFER) + return ret; + + return 0; + } + + ret = of_property_read_u32(np, "ti,active-discharge-time-us", + &rpdata->act_dis_time_us); + if (ret < 0) { + dev_err(tps->dev, "Failed to read active discharge time:%d\n", + ret); + return ret; + } + + return 0; +} + +#define TPS65132_REGULATOR_DESC(_id, _name) \ + [TPS65132_REGULATOR_ID_##_id] = { \ + .name = "tps65132-"#_name, \ + .supply_name = "vin", \ + .id = TPS65132_REGULATOR_ID_##_id, \ + .of_match = of_match_ptr(#_name), \ + .of_parse_cb = tps65132_of_parse_cb, \ + .ops = &tps65132_regulator_ops, \ + .n_voltages = TPS65132_VOUT_N_VOLTAGE, \ + .min_uV = TPS65132_VOUT_VMIN, \ + .uV_step = TPS65132_VOUT_STEP, \ + .enable_time = 500, \ + .vsel_mask = TPS65132_VOUT_MASK, \ + .vsel_reg = TPS65132_REG_##_id, \ + .active_discharge_off = 0, \ + .active_discharge_on = TPS65132_REG_APPS_DIS_##_id, \ + .active_discharge_mask = TPS65132_REG_APPS_DIS_##_id, \ + .active_discharge_reg = TPS65132_REG_APPS_DISP_DISN, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + } + +static struct regulator_desc tps_regs_desc[TPS65132_MAX_REGULATORS] = { + TPS65132_REGULATOR_DESC(VPOS, outp), + TPS65132_REGULATOR_DESC(VNEG, outn), +}; + +static const struct regmap_range tps65132_no_reg_ranges[] = { + regmap_reg_range(TPS65132_REG_APPS_DISP_DISN + 1, + TPS65132_REG_CONTROL - 1), +}; + +static const struct regmap_access_table tps65132_no_reg_table = { + .no_ranges = tps65132_no_reg_ranges, + .n_no_ranges = ARRAY_SIZE(tps65132_no_reg_ranges), +}; + +static const struct regmap_config tps65132_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TPS65132_REG_CONTROL + 1, + .cache_type = REGCACHE_NONE, + .rd_table = &tps65132_no_reg_table, + .wr_table = &tps65132_no_reg_table, +}; + +static int tps65132_probe(struct i2c_client *client, + const struct i2c_device_id *client_id) +{ + struct device *dev = &client->dev; + struct tps65132_regulator *tps; + struct regulator_config config = { }; + int id; + int ret; + + tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL); + if (!tps) + return -ENOMEM; + + tps->rmap = devm_regmap_init_i2c(client, &tps65132_regmap_config); + if (IS_ERR(tps->rmap)) { + ret = PTR_ERR(tps->rmap); + dev_err(dev, "regmap init failed: %d\n", ret); + return ret; + } + + i2c_set_clientdata(client, tps); + tps->dev = dev; + + for (id = 0; id < TPS65132_MAX_REGULATORS; ++id) { + tps->rdesc[id] = &tps_regs_desc[id]; + + config.regmap = tps->rmap; + config.dev = dev; + config.driver_data = tps; + + tps->rdev[id] = devm_regulator_register(dev, + tps->rdesc[id], &config); + if (IS_ERR(tps->rdev[id])) { + ret = PTR_ERR(tps->rdev[id]); + dev_err(dev, "regulator %s register failed: %d\n", + tps->rdesc[id]->name, ret); + return ret; + } + } + return 0; +} + +static const struct i2c_device_id tps65132_id[] = { + {.name = "tps65132",}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, tps65132_id); + +static struct i2c_driver tps65132_i2c_driver = { + .driver = { + .name = "tps65132", + .owner = THIS_MODULE, + }, + .probe = tps65132_probe, + .id_table = tps65132_id, +}; + +module_i2c_driver(tps65132_i2c_driver); + +MODULE_DESCRIPTION("tps65132 regulator driver"); +MODULE_AUTHOR("Venkat Reddy Talla "); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); From 682d33c65c28bca28238e84dc3f3b629008d1079 Mon Sep 17 00:00:00 2001 From: Venkat Reddy Talla Date: Wed, 12 Apr 2017 15:45:04 +0530 Subject: [PATCH 34/51] regulator: tps65132: add device-tree binding Add tps65132 regulator device-tree binding documentation Signed-off-by: Venkat Reddy Talla Signed-off-by: Mark Brown --- .../bindings/regulator/tps65132-regulator.txt | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/tps65132-regulator.txt diff --git a/Documentation/devicetree/bindings/regulator/tps65132-regulator.txt b/Documentation/devicetree/bindings/regulator/tps65132-regulator.txt new file mode 100644 index 000000000000..3a3505520c69 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/tps65132-regulator.txt @@ -0,0 +1,46 @@ +TPS65132 regulators + +Required properties: +- compatible: "ti,tps65132" +- reg: I2C slave address + +Optional Subnode: +Device supports two regulators OUTP and OUTN. A sub node within the + device node describe the properties of these regulators. The sub-node + names must be as follows: + -For regulator outp, the sub node name should be "outp". + -For regulator outn, the sub node name should be "outn". + +-enable-gpios:(active high, output) Regulators are controlled by the input pins. + If it is connected to GPIO through host system then provide the + gpio number as per gpio.txt. +-active-discharge-gpios: (active high, output) Some configurations use delay mechanisms + on the enable pin, to keep the regulator enabled for some time after + the enable signal goes low. This GPIO is used to actively discharge + the delay mechanism. Requires specification of ti,active-discharge-time-us +-ti,active-discharge-time-us: how long the active discharge gpio should be + asserted for during active discharge, in microseconds. + +Each regulator is defined using the standard binding for regulators. + +Example: + + tps65132@3e { + compatible = "ti,tps65132"; + reg = <0x3e>; + + outp { + regulator-name = "outp"; + regulator-boot-on; + regulator-always-on; + enable-gpios = <&gpio 23 0>; + }; + + outn { + regulator-name = "outn"; + regulator-boot-on; + regulator-always-on; + regulator-active-discharge = <0>; + enable-gpios = <&gpio 40 0>; + }; + }; From 9bf944548169f6153c3d3778cf983cb5db251a0e Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Wed, 12 Apr 2017 09:58:47 +0800 Subject: [PATCH 35/51] regulator: anatop: set default voltage selector for pcie Set the initial voltage selector for vddpcie in case it's disabled by default. This fixes the below warning: 20c8000.anatop:regulator-vddpcie: Failed to read a valid default voltage selector. anatop_regulator: probe of 20c8000.anatop:regulator-vddpcie failed with error -22 Cc: Liam Girdwood Cc: Mark Brown Cc: Shawn Guo Cc: Sascha Hauer Cc: Robin Gong Cc: Richard Zhu Signed-off-by: Richard Zhu Signed-off-by: Dong Aisheng 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 c6ce9745ffc8..fa27c9de6744 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -299,6 +299,11 @@ static int anatop_regulator_probe(struct platform_device *pdev) if (!sreg->sel && !strcmp(rdesc->name, "vddpu")) sreg->sel = 22; + /* set the default voltage of the pcie phy to be 1.100v */ + if (!sreg->sel && rdesc->name && + !strcmp(rdesc->name, "vddpcie")) + sreg->sel = 0x10; + if (!sreg->bypass && !sreg->sel) { dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n"); return -EINVAL; From 5abca06c21bce9b65c1a9bf8b26d8f1711aca94a Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 13 Apr 2017 20:55:50 +0800 Subject: [PATCH 36/51] regulator: tps65132: Fix off-by-one for .max_register setting TPS65132_REG_CONTROL(0xFF) is the latest valid register. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65132-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c index a949206065d4..a2fc7322f434 100644 --- a/drivers/regulator/tps65132-regulator.c +++ b/drivers/regulator/tps65132-regulator.c @@ -214,7 +214,7 @@ static const struct regmap_access_table tps65132_no_reg_table = { static const struct regmap_config tps65132_regmap_config = { .reg_bits = 8, .val_bits = 8, - .max_register = TPS65132_REG_CONTROL + 1, + .max_register = TPS65132_REG_CONTROL, .cache_type = REGCACHE_NONE, .rd_table = &tps65132_no_reg_table, .wr_table = &tps65132_no_reg_table, From 43594dd453f082d36336ea8338cd9c2d28c1691a Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Fri, 14 Apr 2017 04:57:35 +0800 Subject: [PATCH 37/51] regulator: tps65132: fix platform_no_drv_owner.cocci warnings drivers/regulator/tps65132-regulator.c:274:3-8: No need to set .owner here. The core will do it. Remove .owner field if calls are used which set it automatically Generated by: scripts/coccinelle/api/platform_no_drv_owner.cocci Signed-off-by: Fengguang Wu Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65132-regulator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c index a2fc7322f434..73978dd440f7 100644 --- a/drivers/regulator/tps65132-regulator.c +++ b/drivers/regulator/tps65132-regulator.c @@ -271,7 +271,6 @@ MODULE_DEVICE_TABLE(i2c, tps65132_id); static struct i2c_driver tps65132_i2c_driver = { .driver = { .name = "tps65132", - .owner = THIS_MODULE, }, .probe = tps65132_probe, .id_table = tps65132_id, From a9bbb453b50c91295ab362e4832eb37fd4e6785d Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Fri, 14 Apr 2017 10:50:43 +0800 Subject: [PATCH 38/51] regulator: vctrl: Fix out of bounds array access for vctrl->vtable Current code only allocates rdesc->n_voltages entries for vctrl->vtable. Thus use rdesc->n_voltages instead of n_voltages in the for loop. While at it, also switch to use devm_kcalloc instead of devm_kmalloc_array + __GFP_ZERO flag and fix the argument order. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/vctrl-regulator.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c index 6baadef0ed74..78de002037c7 100644 --- a/drivers/regulator/vctrl-regulator.c +++ b/drivers/regulator/vctrl-regulator.c @@ -345,9 +345,9 @@ static int vctrl_init_vtable(struct platform_device *pdev) return -EINVAL; } - vctrl->vtable = devm_kmalloc_array( - &pdev->dev, sizeof(struct vctrl_voltage_table), - rdesc->n_voltages, GFP_KERNEL | __GFP_ZERO); + vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages, + sizeof(struct vctrl_voltage_table), + GFP_KERNEL); if (!vctrl->vtable) return -ENOMEM; @@ -371,7 +371,7 @@ static int vctrl_init_vtable(struct platform_device *pdev) NULL); /* pre-calculate OVP-safe downward transitions */ - for (i = n_voltages - 1; i > 0; i--) { + for (i = rdesc->n_voltages - 1; i > 0; i--) { int j; int ovp_min_uV = (vctrl->vtable[i].out * (100 - vctrl->ovp_threshold)) / 100; From 43fc99f293cc802866bea904ca2f1f8573f236f7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 13 Apr 2017 18:36:59 +0100 Subject: [PATCH 39/51] regulator: core: Only propagate voltage changes to if it can change voltages When we are propagating voltage changes to parent regulators don't bother if the parent does not have permission to change voltages. This simplifies error checking in the function for cases where the regulator lacks some of the voltage operations. Reported-by: Dong Aisheng Tested-by: Dong Aisheng Reviewed-by: Dong Aisheng Signed-off-by: Mark Brown --- drivers/regulator/core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index aff302dfab5d..3f424ec4fc56 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -2939,8 +2939,10 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, if (ret < 0) goto out2; - if (rdev->supply && (rdev->desc->min_dropout_uV || - !rdev->desc->ops->get_voltage)) { + if (rdev->supply && + regulator_ops_is_valid(rdev->supply->rdev, + REGULATOR_CHANGE_VOLTAGE) && + (rdev->desc->min_dropout_uV || !rdev->desc->ops->get_voltage)) { int current_supply_uV; int selector; From c93609ab3924cc974fc90001fb6aa250a8900a3c Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 11 Apr 2017 21:31:40 +0100 Subject: [PATCH 40/51] regulator: core: Allow dummy regulators for supplies Rather than just not resolving the supply when there is explicitly no supply mapping fall through and allow a dummy supply to be substituted. This fixes issues with constant retries reported by Dong Aisheng. Signed-off-by: Mark Brown Tested-by: Dong Aisheng Reviewed-by: Dong Aisheng --- drivers/regulator/core.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 3f424ec4fc56..462e6e679ce1 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1532,14 +1532,6 @@ static int regulator_resolve_supply(struct regulator_dev *rdev) if (IS_ERR(r)) { ret = PTR_ERR(r); - if (ret == -ENODEV) { - /* - * No supply was specified for this regulator and - * there will never be one. - */ - return 0; - } - /* Did the lookup explicitly defer for us? */ if (ret == -EPROBE_DEFER) return ret; From 4af5924c02818ca170bb6902dbb883687b969aeb Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 14 Apr 2017 22:32:43 +0800 Subject: [PATCH 41/51] regulator: anatop: make sure regulator name is properly defined For anatop regulator we must have a name accordingly. Make sure the name is properly checked before using it to avoid a possible kernel NULL point crash. Signed-off-by: Dong Aisheng Signed-off-by: Mark Brown --- drivers/regulator/anatop-regulator.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c index fa27c9de6744..606f3fa6de62 100644 --- a/drivers/regulator/anatop-regulator.c +++ b/drivers/regulator/anatop-regulator.c @@ -198,6 +198,10 @@ static int anatop_regulator_probe(struct platform_device *pdev) rdesc->owner = THIS_MODULE; of_property_read_string(np, "regulator-name", &rdesc->name); + if (!rdesc->name) { + dev_err(dev, "failed to get a regulator-name\n"); + return -EINVAL; + } initdata = of_get_regulator_init_data(dev, np, rdesc); if (!initdata) @@ -300,8 +304,7 @@ static int anatop_regulator_probe(struct platform_device *pdev) sreg->sel = 22; /* set the default voltage of the pcie phy to be 1.100v */ - if (!sreg->sel && rdesc->name && - !strcmp(rdesc->name, "vddpcie")) + if (!sreg->sel && !strcmp(rdesc->name, "vddpcie")) sreg->sel = 0x10; if (!sreg->bypass && !sreg->sel) { From c90722b54a4f5e21ac59301ed9a6dbaa439bdb16 Mon Sep 17 00:00:00 2001 From: Richard Cochran Date: Mon, 17 Apr 2017 10:23:36 +0200 Subject: [PATCH 42/51] regulator: tps65023: Fix inverted core enable logic. Commit 43530b69d758328d3ffe6ab98fd640463e8e3667 ("regulator: Use regmap_read/write(), regmap_update_bits functions directly") intended to replace working inline helper functions with standard regmap calls. However, it also inverted the set/clear logic of the "CORE ADJ Allowed" bit. That patch was clearly never tested, since without that bit cleared, the core VDCDC1 voltage output does not react to I2C configuration changes. This patch fixes the issue by clearing the bit as in the original, correct implementation. Note for stable back porting that, due to subsequent driver churn, this patch will not apply on every kernel version. Fixes: 43530b69d758 ("regulator: Use regmap_read/write(), regmap_update_bits functions directly") Signed-off-by: Richard Cochran Signed-off-by: Mark Brown Cc: stable@vger.kernel.org --- drivers/regulator/tps65023-regulator.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index d2c3d7cc35f5..5ca6d2130593 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -311,8 +311,7 @@ static int tps_65023_probe(struct i2c_client *client, /* Enable setting output voltage by I2C */ regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2, - TPS65023_REG_CTRL2_CORE_ADJ, - TPS65023_REG_CTRL2_CORE_ADJ); + TPS65023_REG_CTRL2_CORE_ADJ, 0); return 0; } From 0e69c2eceb7c71327086c6f48db42a0ba2378cbb Mon Sep 17 00:00:00 2001 From: Dong Aisheng Date: Fri, 21 Apr 2017 10:53:52 +0800 Subject: [PATCH 43/51] regulator: anatop: make regulator name property required We actually can't allow the missing of the regualor name, thus update the binding doc to make regulator-name property to be required. Acked-by: Rob Herring Signed-off-by: Dong Aisheng Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/anatop-regulator.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt index 37c4ea076f88..312060658a53 100644 --- a/Documentation/devicetree/bindings/regulator/anatop-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/anatop-regulator.txt @@ -2,6 +2,7 @@ Anatop Voltage regulators Required properties: - compatible: Must be "fsl,anatop-regulator" +- regulator-name: A string used as a descriptive name for regulator outputs - anatop-reg-offset: Anatop MFD register offset - anatop-vol-bit-shift: Bit shift for the register - anatop-vol-bit-width: Number of bits used in the register From 7e6425968bf742b9772aa5bae1250158c9312e31 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:48 +0100 Subject: [PATCH 44/51] regulator: arizona: Split KConfig options for LDO1 and MICSUPP regulators The CS47L24 Arizona codec and most Madera codecs do not have a LDO1 regulator. Split the LDO1 and MICSUPP regulators into separate KConfig options so the LDO1 is only built into the kernel if needed. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 14 +++++++++++--- drivers/regulator/Makefile | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index be06eb29c681..c026b09c479c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -125,12 +125,20 @@ config REGULATOR_AB8500 This driver supports the regulators found on the ST-Ericsson mixed signal AB8500 PMIC -config REGULATOR_ARIZONA - tristate "Wolfson Arizona class devices" +config REGULATOR_ARIZONA_LDO1 + tristate "Wolfson Arizona class devices LDO1" depends on MFD_ARIZONA depends on SND_SOC help - Support for the regulators found on Wolfson Arizona class + Support for the LDO1 regulators found on Wolfson Arizona class + devices. + +config REGULATOR_ARIZONA_MICSUPP + tristate "Wolfson Arizona class devices MICSUPP" + depends on MFD_ARIZONA + depends on SND_SOC + help + Support for the MICSUPP regulators found on Wolfson Arizona class devices. config REGULATOR_AS3711 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ef7725e2592a..313a7ca97b4d 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -19,7 +19,8 @@ obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o -obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o +obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o +obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o From 22161f3eb65dc29434325736c4d780908fe3bf6a Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:49 +0100 Subject: [PATCH 45/51] regulator: arizona-micsupp: Move pdata into a separate structure In preparation for sharing this driver with Madera, move the pdata for the micsupp regulator out of struct arizona_pdata into a dedicated pdata struct for this driver. As a result the code in arizona_micsupp_of_get_pdata() can be made independent of struct arizona. This patch also updates the definition of struct arizona_pdata and the use of this pdata in mach-crag6410-module.c Signed-off-by: Richard Fitzgerald Acked-by: Lee Jones Signed-off-by: Mark Brown --- MAINTAINERS | 1 + drivers/regulator/arizona-micsupp.c | 21 +++++++++++---------- include/linux/mfd/arizona/pdata.h | 3 ++- include/linux/regulator/arizona-micsupp.h | 21 +++++++++++++++++++++ 4 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 include/linux/regulator/arizona-micsupp.h diff --git a/MAINTAINERS b/MAINTAINERS index c35e0cea7831..6ed8ef18e7b6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13623,6 +13623,7 @@ F: include/linux/mfd/arizona/ F: include/linux/mfd/wm831x/ F: include/linux/mfd/wm8350/ F: include/linux/mfd/wm8400* +F: include/linux/regulator/arizona* F: include/linux/wm97xx.h F: include/sound/wm????.h F: sound/soc/codecs/arizona.? diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 5e38861e71d8..5f8b5a713311 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -30,6 +30,8 @@ #include #include +#include + struct arizona_micsupp { struct regulator_dev *regulator; struct arizona *arizona; @@ -199,28 +201,26 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { .num_consumer_supplies = 1, }; -static int arizona_micsupp_of_get_pdata(struct device *dev, - struct arizona *arizona, +static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata, struct regulator_config *config, const struct regulator_desc *desc) { - struct arizona_pdata *pdata = &arizona->pdata; struct arizona_micsupp *micsupp = config->driver_data; struct device_node *np; struct regulator_init_data *init_data; - np = of_get_child_by_name(arizona->dev->of_node, "micvdd"); + np = of_get_child_by_name(config->dev->of_node, "micvdd"); if (np) { config->of_node = np; - init_data = of_get_regulator_init_data(dev, np, desc); + init_data = of_get_regulator_init_data(config->dev, np, desc); if (init_data) { init_data->consumer_supplies = &micsupp->supply; init_data->num_consumer_supplies = 1; - pdata->micvdd = init_data; + pdata->init_data = init_data; } } @@ -232,6 +232,7 @@ static int arizona_micsupp_probe(struct platform_device *pdev) struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); const struct regulator_desc *desc; struct regulator_config config = { }; + struct arizona_micsupp_pdata *pdata = &arizona->pdata.micvdd; struct arizona_micsupp *micsupp; int ret; @@ -269,15 +270,15 @@ static int arizona_micsupp_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_micsupp_of_get_pdata(&pdev->dev, arizona, - &config, desc); + ret = arizona_micsupp_of_get_pdata(pdata, &config, + desc); if (ret < 0) return ret; } } - if (arizona->pdata.micvdd) - config.init_data = arizona->pdata.micvdd; + if (pdata->init_data) + config.init_data = pdata->init_data; else config.init_data = &micsupp->init_data; diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 64faeeff698c..43e875f9850c 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -12,6 +12,7 @@ #define _ARIZONA_PDATA_H #include +#include #define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ #define ARIZONA_GPN_DIR_SHIFT 15 /* GPN_DIR */ @@ -79,7 +80,7 @@ struct arizona_pdata { int ldoena; /** GPIO controlling LODENA, if any */ /** Regulator configuration for MICVDD */ - struct regulator_init_data *micvdd; + struct arizona_micsupp_pdata micvdd; /** Regulator configuration for LDO1 */ struct regulator_init_data *ldo1; diff --git a/include/linux/regulator/arizona-micsupp.h b/include/linux/regulator/arizona-micsupp.h new file mode 100644 index 000000000000..616842619c00 --- /dev/null +++ b/include/linux/regulator/arizona-micsupp.h @@ -0,0 +1,21 @@ +/* + * Platform data for Arizona micsupp regulator + * + * Copyright 2017 Cirrus Logic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARIZONA_MICSUPP_H +#define ARIZONA_MICSUPP_H + +struct regulator_init_data; + +struct arizona_micsupp_pdata { + /** Regulator configuration for micsupp */ + const struct regulator_init_data *init_data; +}; + +#endif From e165983e5102c953d68bd935048e95567564e438 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:50 +0100 Subject: [PATCH 46/51] regulator: arizona-micsupp: Make arizona_micsupp independent of struct arizona In preparation for supporting Madera codecs, remove the dependency on struct arizona in the regulator callbacks and struct arizona_micsupp. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 5f8b5a713311..db4fecf228b7 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -34,7 +34,10 @@ struct arizona_micsupp { struct regulator_dev *regulator; - struct arizona *arizona; + struct regmap *regmap; + struct snd_soc_dapm_context **dapm; + unsigned int enable_reg; + struct device *dev; struct regulator_consumer_supply supply; struct regulator_init_data init_data; @@ -46,21 +49,22 @@ static void arizona_micsupp_check_cp(struct work_struct *work) { struct arizona_micsupp *micsupp = container_of(work, struct arizona_micsupp, check_cp_work); - struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm; - struct snd_soc_component *component = snd_soc_dapm_to_component(dapm); - struct arizona *arizona = micsupp->arizona; - struct regmap *regmap = arizona->regmap; - unsigned int reg; + struct snd_soc_dapm_context *dapm = *micsupp->dapm; + struct snd_soc_component *component; + unsigned int val; int ret; - ret = regmap_read(regmap, ARIZONA_MIC_CHARGE_PUMP_1, ®); + ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val); if (ret != 0) { - dev_err(arizona->dev, "Failed to read CP state: %d\n", ret); + dev_err(micsupp->dev, + "Failed to read CP state: %d\n", ret); return; } if (dapm) { - if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) == + component = snd_soc_dapm_to_component(dapm); + + if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) == ARIZONA_CPMIC_ENA) snd_soc_component_force_enable_pin(component, "MICSUPP"); @@ -240,7 +244,9 @@ static int arizona_micsupp_probe(struct platform_device *pdev) if (!micsupp) return -ENOMEM; - micsupp->arizona = arizona; + micsupp->regmap = arizona->regmap; + micsupp->dapm = &arizona->dapm; + micsupp->dev = arizona->dev; INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); /* @@ -263,6 +269,7 @@ static int arizona_micsupp_probe(struct platform_device *pdev) micsupp->init_data.consumer_supplies = &micsupp->supply; micsupp->supply.supply = "MICVDD"; micsupp->supply.dev_name = dev_name(arizona->dev); + micsupp->enable_reg = desc->enable_reg; config.dev = arizona->dev; config.driver_data = micsupp; From 7d8d14b51921cbfe082a796e55c22d0c1dd8fc26 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:51 +0100 Subject: [PATCH 47/51] regulator: arizona-micsupp: Factor out generic initialization In preparation for sharing this driver with Madera codecs, factor out the parts of initialization that aren't dependent on struct arizona. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 104 +++++++++++++++------------- 1 file changed, 57 insertions(+), 47 deletions(-) diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index db4fecf228b7..120de94caf02 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -231,14 +231,66 @@ static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata, return 0; } +static int arizona_micsupp_common_init(struct platform_device *pdev, + struct arizona_micsupp *micsupp, + const struct regulator_desc *desc, + struct arizona_micsupp_pdata *pdata) +{ + struct regulator_config config = { }; + int ret; + + INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); + + micsupp->init_data.consumer_supplies = &micsupp->supply; + micsupp->supply.supply = "MICVDD"; + micsupp->supply.dev_name = dev_name(micsupp->dev); + micsupp->enable_reg = desc->enable_reg; + + config.dev = micsupp->dev; + config.driver_data = micsupp; + config.regmap = micsupp->regmap; + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(micsupp->dev)) { + ret = arizona_micsupp_of_get_pdata(pdata, &config, + desc); + if (ret < 0) + return ret; + } + } + + if (pdata->init_data) + config.init_data = pdata->init_data; + else + config.init_data = &micsupp->init_data; + + /* Default to regulated mode */ + regmap_update_bits(micsupp->regmap, micsupp->enable_reg, + ARIZONA_CPMIC_BYPASS, 0); + + micsupp->regulator = devm_regulator_register(&pdev->dev, + desc, + &config); + + of_node_put(config.of_node); + + if (IS_ERR(micsupp->regulator)) { + ret = PTR_ERR(micsupp->regulator); + dev_err(micsupp->dev, "Failed to register mic supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, micsupp); + + return 0; +} + static int arizona_micsupp_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); const struct regulator_desc *desc; - struct regulator_config config = { }; - struct arizona_micsupp_pdata *pdata = &arizona->pdata.micvdd; struct arizona_micsupp *micsupp; - int ret; micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL); if (!micsupp) @@ -247,7 +299,6 @@ static int arizona_micsupp_probe(struct platform_device *pdev) micsupp->regmap = arizona->regmap; micsupp->dapm = &arizona->dapm; micsupp->dev = arizona->dev; - INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp); /* * Since the chip usually supplies itself we provide some @@ -266,49 +317,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) break; } - micsupp->init_data.consumer_supplies = &micsupp->supply; - micsupp->supply.supply = "MICVDD"; - micsupp->supply.dev_name = dev_name(arizona->dev); - micsupp->enable_reg = desc->enable_reg; - - config.dev = arizona->dev; - config.driver_data = micsupp; - config.regmap = arizona->regmap; - - if (IS_ENABLED(CONFIG_OF)) { - if (!dev_get_platdata(arizona->dev)) { - ret = arizona_micsupp_of_get_pdata(pdata, &config, - desc); - if (ret < 0) - return ret; - } - } - - if (pdata->init_data) - config.init_data = pdata->init_data; - else - config.init_data = &micsupp->init_data; - - /* Default to regulated mode until the API supports bypass */ - regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1, - ARIZONA_CPMIC_BYPASS, 0); - - micsupp->regulator = devm_regulator_register(&pdev->dev, - desc, - &config); - - of_node_put(config.of_node); - - if (IS_ERR(micsupp->regulator)) { - ret = PTR_ERR(micsupp->regulator); - dev_err(arizona->dev, "Failed to register mic supply: %d\n", - ret); - return ret; - } - - platform_set_drvdata(pdev, micsupp); - - return 0; + return arizona_micsupp_common_init(pdev, micsupp, desc, + &arizona->pdata.micvdd); } static struct platform_driver arizona_micsupp_driver = { From aaa84e6a0399df374634c42590e644a698fcc3ff Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:52 +0100 Subject: [PATCH 48/51] regulator: arizona-ldo1: Move pdata into a separate structure In preparation for sharing this driver with Madera, move the pdata for the LDO1 regulator out of struct arizona_pdata into a dedicated pdata struct for this driver. As a result the code in arizona_ldo1_of_get_pdata() can be made independent of struct arizona. This patch also updates the definition of struct arizona_pdata and the use of this pdata in mach-crag6410-module.c Signed-off-by: Richard Fitzgerald Acked-by: Krzysztof Kozlowski Acked-by: Lee Jones Signed-off-by: Mark Brown --- arch/arm/mach-s3c64xx/mach-crag6410-module.c | 8 +++- drivers/regulator/arizona-ldo1.c | 39 +++++++++++--------- include/linux/mfd/arizona/pdata.h | 4 +- include/linux/regulator/arizona-ldo1.h | 24 ++++++++++++ 4 files changed, 53 insertions(+), 22 deletions(-) create mode 100644 include/linux/regulator/arizona-ldo1.h diff --git a/arch/arm/mach-s3c64xx/mach-crag6410-module.c b/arch/arm/mach-s3c64xx/mach-crag6410-module.c index ccc3ab8d58e7..ea5f2169c850 100644 --- a/arch/arm/mach-s3c64xx/mach-crag6410-module.c +++ b/arch/arm/mach-s3c64xx/mach-crag6410-module.c @@ -209,7 +209,9 @@ static const struct i2c_board_info wm1277_devs[] = { }; static struct arizona_pdata wm5102_reva_pdata = { - .ldoena = S3C64XX_GPN(7), + .ldo1 = { + .ldoena = S3C64XX_GPN(7), + }, .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, .micd_pol_gpio = CODEC_GPIO_BASE + 4, @@ -239,7 +241,9 @@ static struct spi_board_info wm5102_reva_spi_devs[] = { }; static struct arizona_pdata wm5102_pdata = { - .ldoena = S3C64XX_GPN(7), + .ldo1 = { + .ldoena = S3C64XX_GPN(7), + }, .gpio_base = CODEC_GPIO_BASE, .irq_flags = IRQF_TRIGGER_HIGH, .micd_pol_gpio = CODEC_GPIO_BASE + 2, diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index b726fa17f7b2..f5bc75ab85fa 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -25,6 +25,8 @@ #include #include +#include + #include #include #include @@ -186,20 +188,19 @@ static const struct regulator_init_data arizona_ldo1_wm5110 = { .num_consumer_supplies = 1, }; -static int arizona_ldo1_of_get_pdata(struct device *dev, - struct arizona *arizona, +static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata, struct regulator_config *config, - const struct regulator_desc *desc) + const struct regulator_desc *desc, + bool *external_dcvdd) { - struct arizona_pdata *pdata = &arizona->pdata; struct arizona_ldo1 *ldo1 = config->driver_data; - struct device_node *np = arizona->dev->of_node; + struct device_node *np = config->dev->of_node; struct device_node *init_node, *dcvdd_node; struct regulator_init_data *init_data; pdata->ldoena = of_get_named_gpio(np, "wlf,ldoena", 0); if (pdata->ldoena < 0) { - dev_warn(arizona->dev, + dev_warn(config->dev, "LDOENA GPIO property missing/malformed: %d\n", pdata->ldoena); pdata->ldoena = 0; @@ -213,19 +214,19 @@ static int arizona_ldo1_of_get_pdata(struct device *dev, if (init_node) { config->of_node = init_node; - init_data = of_get_regulator_init_data(dev, init_node, desc); - + init_data = of_get_regulator_init_data(config->dev, init_node, + desc); if (init_data) { init_data->consumer_supplies = &ldo1->supply; init_data->num_consumer_supplies = 1; if (dcvdd_node && dcvdd_node != init_node) - arizona->external_dcvdd = true; + *external_dcvdd = true; - pdata->ldo1 = init_data; + pdata->init_data = init_data; } } else if (dcvdd_node) { - arizona->external_dcvdd = true; + *external_dcvdd = true; } of_node_put(dcvdd_node); @@ -239,10 +240,9 @@ static int arizona_ldo1_probe(struct platform_device *pdev) const struct regulator_desc *desc; struct regulator_config config = { }; struct arizona_ldo1 *ldo1; + bool external_dcvdd = false; int ret; - arizona->external_dcvdd = false; - ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); if (!ldo1) return -ENOMEM; @@ -283,17 +283,18 @@ static int arizona_ldo1_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_ldo1_of_get_pdata(&pdev->dev, arizona, - &config, desc); + ret = arizona_ldo1_of_get_pdata(&arizona->pdata.ldo1, + &config, desc, + &external_dcvdd); if (ret < 0) return ret; } } - config.ena_gpio = arizona->pdata.ldoena; + config.ena_gpio = arizona->pdata.ldo1.ldoena; - if (arizona->pdata.ldo1) - config.init_data = arizona->pdata.ldo1; + if (arizona->pdata.ldo1.init_data) + config.init_data = arizona->pdata.ldo1.init_data; else config.init_data = &ldo1->init_data; @@ -303,6 +304,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) */ if (config.init_data->num_consumer_supplies == 0) arizona->external_dcvdd = true; + else + arizona->external_dcvdd = external_dcvdd; ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); diff --git a/include/linux/mfd/arizona/pdata.h b/include/linux/mfd/arizona/pdata.h index 43e875f9850c..bfeecf179895 100644 --- a/include/linux/mfd/arizona/pdata.h +++ b/include/linux/mfd/arizona/pdata.h @@ -12,6 +12,7 @@ #define _ARIZONA_PDATA_H #include +#include #include #define ARIZONA_GPN_DIR_MASK 0x8000 /* GPN_DIR */ @@ -77,13 +78,12 @@ struct arizona_micd_range { struct arizona_pdata { int reset; /** GPIO controlling /RESET, if any */ - int ldoena; /** GPIO controlling LODENA, if any */ /** Regulator configuration for MICVDD */ struct arizona_micsupp_pdata micvdd; /** Regulator configuration for LDO1 */ - struct regulator_init_data *ldo1; + struct arizona_ldo1_pdata ldo1; /** If a direct 32kHz clock is provided on an MCLK specify it here */ int clk32k_src; diff --git a/include/linux/regulator/arizona-ldo1.h b/include/linux/regulator/arizona-ldo1.h new file mode 100644 index 000000000000..c685f1277c63 --- /dev/null +++ b/include/linux/regulator/arizona-ldo1.h @@ -0,0 +1,24 @@ +/* + * Platform data for Arizona LDO1 regulator + * + * Copyright 2017 Cirrus Logic + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef ARIZONA_LDO1_H +#define ARIZONA_LDO1_H + +struct regulator_init_data; + +struct arizona_ldo1_pdata { + /** GPIO controlling LDOENA, if any */ + int ldoena; + + /** Regulator configuration for LDO1 */ + const struct regulator_init_data *init_data; +}; + +#endif From 80a55f41aef4ee808f53f1a356491d7eaeefdd3c Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:53 +0100 Subject: [PATCH 49/51] regulator: arizona-ldo1: Make arizona_ldo1 independent of struct arizona In preparation for supporting Madera codecs, remove the dependency on struct arizona in the regulator callbacks and struct arizona_ldo1. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index f5bc75ab85fa..678f81fda22a 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -33,7 +33,7 @@ struct arizona_ldo1 { struct regulator_dev *regulator; - struct arizona *arizona; + struct regmap *regmap; struct regulator_consumer_supply supply; struct regulator_init_data init_data; @@ -67,7 +67,7 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) { struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev); - struct regmap *regmap = ldo->arizona->regmap; + struct regmap *regmap = ldo->regmap; unsigned int val; int ret; @@ -93,7 +93,7 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev, static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev) { struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev); - struct regmap *regmap = ldo->arizona->regmap; + struct regmap *regmap = ldo->regmap; unsigned int val; int ret; @@ -247,7 +247,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev) if (!ldo1) return -ENOMEM; - ldo1->arizona = arizona; + ldo1->regmap = arizona->regmap; /* * Since the chip usually supplies itself we provide some From af367afafb5ba7ae26defd35e4ba42cfe157ef72 Mon Sep 17 00:00:00 2001 From: Richard Fitzgerald Date: Tue, 18 Apr 2017 11:43:54 +0100 Subject: [PATCH 50/51] regulator: arizona-ldo1: Factor out generic initialization In preparation for sharing this driver with Madera codecs, factor out the parts of initialization that aren't dependent on struct arizona. Signed-off-by: Richard Fitzgerald Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 116 ++++++++++++++++++------------- 1 file changed, 67 insertions(+), 49 deletions(-) diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 678f81fda22a..96fddfff5dc4 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -234,13 +234,72 @@ static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata, return 0; } +static int arizona_ldo1_common_init(struct platform_device *pdev, + struct arizona_ldo1 *ldo1, + const struct regulator_desc *desc, + struct arizona_ldo1_pdata *pdata, + bool *external_dcvdd) +{ + struct device *parent_dev = pdev->dev.parent; + struct regulator_config config = { }; + int ret; + + *external_dcvdd = false; + + ldo1->supply.supply = "DCVDD"; + ldo1->init_data.consumer_supplies = &ldo1->supply; + ldo1->supply.dev_name = dev_name(parent_dev); + + config.dev = parent_dev; + config.driver_data = ldo1; + config.regmap = ldo1->regmap; + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(parent_dev)) { + ret = arizona_ldo1_of_get_pdata(pdata, + &config, desc, + external_dcvdd); + if (ret < 0) + return ret; + } + } + + config.ena_gpio = pdata->ldoena; + + if (pdata->init_data) + config.init_data = pdata->init_data; + else + config.init_data = &ldo1->init_data; + + /* + * LDO1 can only be used to supply DCVDD so if it has no + * consumers then DCVDD is supplied externally. + */ + if (config.init_data->num_consumer_supplies == 0) + *external_dcvdd = true; + + ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); + + of_node_put(config.of_node); + + if (IS_ERR(ldo1->regulator)) { + ret = PTR_ERR(ldo1->regulator); + dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n", + ret); + return ret; + } + + platform_set_drvdata(pdev, ldo1); + + return 0; +} + static int arizona_ldo1_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); - const struct regulator_desc *desc; - struct regulator_config config = { }; struct arizona_ldo1 *ldo1; - bool external_dcvdd = false; + const struct regulator_desc *desc; + bool external_dcvdd; int ret; ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); @@ -273,54 +332,13 @@ static int arizona_ldo1_probe(struct platform_device *pdev) break; } - ldo1->init_data.consumer_supplies = &ldo1->supply; - ldo1->supply.supply = "DCVDD"; - ldo1->supply.dev_name = dev_name(arizona->dev); - - config.dev = arizona->dev; - config.driver_data = ldo1; - config.regmap = arizona->regmap; - - if (IS_ENABLED(CONFIG_OF)) { - if (!dev_get_platdata(arizona->dev)) { - ret = arizona_ldo1_of_get_pdata(&arizona->pdata.ldo1, - &config, desc, - &external_dcvdd); - if (ret < 0) - return ret; - } - } - - config.ena_gpio = arizona->pdata.ldo1.ldoena; - - if (arizona->pdata.ldo1.init_data) - config.init_data = arizona->pdata.ldo1.init_data; - else - config.init_data = &ldo1->init_data; - - /* - * LDO1 can only be used to supply DCVDD so if it has no - * consumers then DCVDD is supplied externally. - */ - if (config.init_data->num_consumer_supplies == 0) - arizona->external_dcvdd = true; - else + ret = arizona_ldo1_common_init(pdev, ldo1, desc, + &arizona->pdata.ldo1, + &external_dcvdd); + if (ret == 0) arizona->external_dcvdd = external_dcvdd; - ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); - - of_node_put(config.of_node); - - if (IS_ERR(ldo1->regulator)) { - ret = PTR_ERR(ldo1->regulator); - dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n", - ret); - return ret; - } - - platform_set_drvdata(pdev, ldo1); - - return 0; + return ret; } static struct platform_driver arizona_ldo1_driver = { From e85c5a153fe237f261838fc9638c28f19e0f27c1 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 24 Apr 2017 17:21:30 +0200 Subject: [PATCH 51/51] regulator: Add ROHM BD9571MWV-M PMIC regulator driver Add driver for the regulator block in the ROHM BD9571MWV-W MFD PMIC. This block supports three voltage monitors, VD18, VD25, VD33 for the 1V8, 2V5, 3V3 voltage rails and a single voltage regulator for the DVFS rail. Signed-off-by: Marek Vasut Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 11 ++ drivers/regulator/Makefile | 1 + drivers/regulator/bd9571mwv-regulator.c | 178 ++++++++++++++++++++++++ 3 files changed, 190 insertions(+) create mode 100644 drivers/regulator/bd9571mwv-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index be06eb29c681..a27b97c5695b 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -163,6 +163,17 @@ config REGULATOR_BCM590XX BCM590xx PMUs. This will enable support for the software controllable LDO/Switching regulators. +config REGULATOR_BD9571MWV + tristate "ROHM BD9571MWV Regulators" + depends on MFD_BD9571MWV + help + This driver provides support for the voltage regulators on the + ROHM BD9571MWV PMIC. This will enable support for the software + controllable regulator and voltage sampling units. + + This driver can also be built as a module. If so, the module + will be called bd9571mwv-regulator. + config REGULATOR_CPCAP tristate "Motorola CPCAP regulator" depends on MFD_CPCAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index ef7725e2592a..439b9b21f2fe 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o +obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c new file mode 100644 index 000000000000..8ba206fec31e --- /dev/null +++ b/drivers/regulator/bd9571mwv-regulator.c @@ -0,0 +1,178 @@ +/* + * ROHM BD9571MWV-M regulator driver + * + * Copyright (C) 2017 Marek Vasut + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether expressed or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License version 2 for more details. + * + * Based on the TPS65086 driver + * + * NOTE: VD09 is missing + */ + +#include +#include +#include +#include + +#include + +enum bd9571mwv_regulators { VD09, VD18, VD25, VD33, DVFS }; + +#define BD9571MWV_REG(_name, _of, _id, _ops, _vr, _vm, _nv, _min, _step, _lmin)\ + { \ + .name = _name, \ + .of_match = of_match_ptr(_of), \ + .regulators_node = "regulators", \ + .id = _id, \ + .ops = &_ops, \ + .n_voltages = _nv, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .vsel_reg = _vr, \ + .vsel_mask = _vm, \ + .min_uV = _min, \ + .uV_step = _step, \ + .linear_min_sel = _lmin, \ + } + +int bd9571mwv_avs_get_moni_state(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, BD9571MWV_AVS_SET_MONI, &val); + if (ret != 0) + return ret; + + return val & BD9571MWV_AVS_SET_MONI_MASK; +} + +int bd9571mwv_avs_set_voltage_sel_regmap(struct regulator_dev *rdev, + unsigned int sel) +{ + int ret; + + ret = bd9571mwv_avs_get_moni_state(rdev); + if (ret < 0) + return ret; + + return regmap_write_bits(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), + rdev->desc->vsel_mask, sel); +} + +int bd9571mwv_avs_get_voltage_sel_regmap(struct regulator_dev *rdev) +{ + unsigned int val; + int ret; + + ret = bd9571mwv_avs_get_moni_state(rdev); + if (ret < 0) + return ret; + + ret = regmap_read(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), &val); + if (ret != 0) + return ret; + + val &= rdev->desc->vsel_mask; + val >>= ffs(rdev->desc->vsel_mask) - 1; + + return val; +} + +int bd9571mwv_reg_set_voltage_sel_regmap(struct regulator_dev *rdev, + unsigned int sel) +{ + return regmap_write_bits(rdev->regmap, BD9571MWV_DVFS_SETVID, + rdev->desc->vsel_mask, sel); +} + +/* Operations permitted on AVS voltage regulator */ +static struct regulator_ops avs_ops = { + .set_voltage_sel = bd9571mwv_avs_set_voltage_sel_regmap, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = bd9571mwv_avs_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +/* Operations permitted on voltage regulators */ +static struct regulator_ops reg_ops = { + .set_voltage_sel = bd9571mwv_reg_set_voltage_sel_regmap, + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +/* Operations permitted on voltage monitors */ +static struct regulator_ops vid_ops = { + .map_voltage = regulator_map_voltage_linear, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_desc regulators[] = { + BD9571MWV_REG("VD09", "vd09", VD09, avs_ops, 0, 0x7f, + 0x80, 600000, 10000, 0x3c), + BD9571MWV_REG("VD18", "vd18", VD18, vid_ops, BD9571MWV_VD18_VID, 0xf, + 16, 1625000, 25000, 0), + BD9571MWV_REG("VD25", "vd25", VD25, vid_ops, BD9571MWV_VD25_VID, 0xf, + 16, 2150000, 50000, 0), + BD9571MWV_REG("VD33", "vd33", VD33, vid_ops, BD9571MWV_VD33_VID, 0xf, + 11, 2800000, 100000, 0), + BD9571MWV_REG("DVFS", "dvfs", DVFS, reg_ops, + BD9571MWV_DVFS_MONIVDAC, 0x7f, + 0x80, 600000, 10000, 0x3c), +}; + +static int bd9571mwv_regulator_probe(struct platform_device *pdev) +{ + struct bd9571mwv *bd = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_dev *rdev; + int i; + + platform_set_drvdata(pdev, bd); + + config.dev = &pdev->dev; + config.dev->of_node = bd->dev->of_node; + config.driver_data = bd; + config.regmap = bd->regmap; + + for (i = 0; i < ARRAY_SIZE(regulators); i++) { + rdev = devm_regulator_register(&pdev->dev, ®ulators[i], + &config); + if (IS_ERR(rdev)) { + dev_err(bd->dev, "failed to register %s regulator\n", + pdev->name); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id bd9571mwv_regulator_id_table[] = { + { "bd9571mwv-regulator", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table); + +static struct platform_driver bd9571mwv_regulator_driver = { + .driver = { + .name = "bd9571mwv-regulator", + }, + .probe = bd9571mwv_regulator_probe, + .id_table = bd9571mwv_regulator_id_table, +}; +module_platform_driver(bd9571mwv_regulator_driver); + +MODULE_AUTHOR("Marek Vasut "); +MODULE_DESCRIPTION("BD9571MWV Regulator driver"); +MODULE_LICENSE("GPL v2");