From 423156b3d37ba051e72e87a8b4791a2a0ea40592 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 4 Jun 2022 16:58:13 +0200 Subject: [PATCH 01/35] regulator: dt-bindings: mps,mp5416: add MP5496 compatible MP5496 is the updated version of MP5416 with the only difference being that now all Buck regulators have the same 0.6-2.1875V range with a 12.5mV step. Since there is no way to differentiate them other than using compatibles, add compatible for the MP5496. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220604145816.47576-1-robimarko@gmail.com Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/mps,mp5416.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml b/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml index 90727fdc1283..7023c597c3ed 100644 --- a/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml +++ b/Documentation/devicetree/bindings/regulator/mps,mp5416.yaml @@ -15,6 +15,7 @@ properties: compatible: enum: - mps,mp5416 + - mps,mp5496 reg: maxItems: 1 From b9dea0184b2641fb3937162a617289b23d52a587 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 4 Jun 2022 16:58:14 +0200 Subject: [PATCH 02/35] regulator: mp5416: alphabetically sort header includes Sort the header include list alphabetically. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220604145816.47576-2-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mp5416.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c index 39cebec0edb6..d8245080df03 100644 --- a/drivers/regulator/mp5416.c +++ b/drivers/regulator/mp5416.c @@ -6,14 +6,14 @@ // // Author: Saravanan Sekar +#include +#include +#include #include #include -#include -#include #include #include #include -#include #define MP5416_REG_CTL0 0x00 #define MP5416_REG_CTL1 0x01 From df43c245dd0535f6e2256e0261d43a4dd72b8b28 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 4 Jun 2022 16:58:15 +0200 Subject: [PATCH 03/35] regulator: mp5416: use OF match data In preparation for adding support for MP5496 which slightly differs from MP5416 convert the driver to use OF match data instead of always using the MP5416 regulator_desc for regulator registration. Signed-off-by: Robert Marko Acked-by: Saravanan Sekar Link: https://lore.kernel.org/r/20220604145816.47576-3-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mp5416.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c index d8245080df03..011a825570ea 100644 --- a/drivers/regulator/mp5416.c +++ b/drivers/regulator/mp5416.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,7 @@ static int mp5416_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; struct regulator_config config = { NULL, }; + static const struct regulator_desc *desc; struct regulator_dev *rdev; struct regmap *regmap; int i; @@ -188,12 +190,16 @@ static int mp5416_i2c_probe(struct i2c_client *client) return PTR_ERR(regmap); } + desc = of_device_get_match_data(dev); + if (!desc) + return -ENODEV; + config.dev = dev; config.regmap = regmap; for (i = 0; i < MP5416_MAX_REGULATORS; i++) { rdev = devm_regulator_register(dev, - &mp5416_regulators_desc[i], + &desc[i], &config); if (IS_ERR(rdev)) { dev_err(dev, "Failed to register regulator!\n"); @@ -205,7 +211,7 @@ static int mp5416_i2c_probe(struct i2c_client *client) } static const struct of_device_id mp5416_of_match[] = { - { .compatible = "mps,mp5416" }, + { .compatible = "mps,mp5416", .data = &mp5416_regulators_desc }, {}, }; MODULE_DEVICE_TABLE(of, mp5416_of_match); From fcdaf74a0abb6a4410b69dd80b525562457daafd Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 4 Jun 2022 16:58:16 +0200 Subject: [PATCH 04/35] regulator: mp5416: add support for MP5496 MP5496 is the updated version of MP5416 with the only difference being that now all Buck regulators have the same 0.6-2.1875V range with a 12.5mV step. Signed-off-by: Robert Marko Acked-by: Saravanan Sekar Link: https://lore.kernel.org/r/20220604145816.47576-4-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mp5416.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c index 011a825570ea..71e20e8d78ac 100644 --- a/drivers/regulator/mp5416.c +++ b/drivers/regulator/mp5416.c @@ -175,6 +175,17 @@ static struct regulator_desc mp5416_regulators_desc[MP5416_MAX_REGULATORS] = { MP5416LDO("ldo4", 4, BIT(1)), }; +static struct regulator_desc mp5496_regulators_desc[MP5416_MAX_REGULATORS] = { + MP5416BUCK("buck1", 1, mp5416_I_limits1, MP5416_REG_CTL1, BIT(0), 1), + MP5416BUCK("buck2", 2, mp5416_I_limits2, MP5416_REG_CTL1, BIT(1), 1), + MP5416BUCK("buck3", 3, mp5416_I_limits1, MP5416_REG_CTL1, BIT(2), 1), + MP5416BUCK("buck4", 4, mp5416_I_limits2, MP5416_REG_CTL2, BIT(5), 1), + MP5416LDO("ldo1", 1, BIT(4)), + MP5416LDO("ldo2", 2, BIT(3)), + MP5416LDO("ldo3", 3, BIT(2)), + MP5416LDO("ldo4", 4, BIT(1)), +}; + static int mp5416_i2c_probe(struct i2c_client *client) { struct device *dev = &client->dev; @@ -212,12 +223,14 @@ static int mp5416_i2c_probe(struct i2c_client *client) static const struct of_device_id mp5416_of_match[] = { { .compatible = "mps,mp5416", .data = &mp5416_regulators_desc }, + { .compatible = "mps,mp5496", .data = &mp5496_regulators_desc }, {}, }; MODULE_DEVICE_TABLE(of, mp5416_of_match); static const struct i2c_device_id mp5416_id[] = { { "mp5416", }, + { "mp5496", }, { }, }; MODULE_DEVICE_TABLE(i2c, mp5416_id); From 5f7202d827b8d39146b27c2f813849b1ad437beb Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Thu, 2 Jun 2022 13:44:06 +0800 Subject: [PATCH 05/35] regulator: rt5190a: check if init_data is NULL, bypass rt5190a_of_parse_cb If the node for the match name cannot be found, 'of_regulator_match' will returns init_data as NULL for this regulator. Add the check for the init_data. If it's NULL, make 'rt5190a_of_parse_cb' function directly return. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1654148646-12182-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/rt5190a-regulator.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/regulator/rt5190a-regulator.c b/drivers/regulator/rt5190a-regulator.c index 155d4afd00b1..4a3397b32582 100644 --- a/drivers/regulator/rt5190a-regulator.c +++ b/drivers/regulator/rt5190a-regulator.c @@ -224,6 +224,9 @@ static int rt5190a_of_parse_cb(struct rt5190a_priv *priv, int rid, bool latchup_enable; unsigned int mask = RT5190A_RID_BITMASK(rid), val; + if (!init_data) + return 0; + switch (rid) { case RT5190A_IDX_BUCK1: case RT5190A_IDX_BUCK4: From a9369fd316cf3002642295250b6a52c4554aa74c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Mon, 6 Jun 2022 13:43:09 -0500 Subject: [PATCH 06/35] regulator: dt-bindings: Convert pwm-regulator to DT schema Convert the pwm-regulator binding to DT schema format. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20220606184310.1057797-1-robh@kernel.org Signed-off-by: Mark Brown --- .../bindings/regulator/pwm-regulator.txt | 92 ------------- .../bindings/regulator/pwm-regulator.yaml | 126 ++++++++++++++++++ 2 files changed, 126 insertions(+), 92 deletions(-) delete mode 100644 Documentation/devicetree/bindings/regulator/pwm-regulator.txt create mode 100644 Documentation/devicetree/bindings/regulator/pwm-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt b/Documentation/devicetree/bindings/regulator/pwm-regulator.txt deleted file mode 100644 index 3d78d507e29f..000000000000 --- a/Documentation/devicetree/bindings/regulator/pwm-regulator.txt +++ /dev/null @@ -1,92 +0,0 @@ -Bindings for the Generic PWM Regulator -====================================== - -Currently supports 2 modes of operation: - -Voltage Table: When in this mode, a voltage table (See below) of - predefined voltage <=> duty-cycle values must be - provided via DT. Limitations are that the regulator can - only operate at the voltages supplied in the table. - Intermediary duty-cycle values which would normally - allow finer grained voltage selection are ignored and - rendered useless. Although more control is given to - the user if the assumptions made in continuous-voltage - mode do not reign true. - -Continuous Voltage: This mode uses the regulator's maximum and minimum - supplied voltages specified in the - regulator-{min,max}-microvolt properties to calculate - appropriate duty-cycle values. This allows for a much - more fine grained solution when compared with - voltage-table mode above. This solution does make an - assumption that a %50 duty-cycle value will cause the - regulator voltage to run at half way between the - supplied max_uV and min_uV values. - -Required properties: --------------------- -- compatible: Should be "pwm-regulator" - -- pwms: PWM specification (See: ../pwm/pwm.txt) - -Only required for Voltage Table Mode: -- voltage-table: Voltage and Duty-Cycle table consisting of 2 cells - First cell is voltage in microvolts (uV) - Second cell is duty-cycle in percent (%) - -Optional properties for Continuous mode: -- pwm-dutycycle-unit: Integer value encoding the duty cycle unit. If not - defined, <100> is assumed, meaning that - pwm-dutycycle-range contains values expressed in - percent. - -- pwm-dutycycle-range: Should contain 2 entries. The first entry is encoding - the dutycycle for regulator-min-microvolt and the - second one the dutycycle for regulator-max-microvolt. - Duty cycle values are expressed in pwm-dutycycle-unit. - If not defined, <0 100> is assumed. - -NB: To be clear, if voltage-table is provided, then the device will be used -in Voltage Table Mode. If no voltage-table is provided, then the device will -be used in Continuous Voltage Mode. - -Optional properties: --------------------- -- enable-gpios: GPIO to use to enable/disable the regulator - -Any property defined as part of the core regulator binding can also be used. -(See: ../regulator/regulator.txt) - -Continuous Voltage With Enable GPIO Example: - pwm_regulator { - compatible = "pwm-regulator"; - pwms = <&pwm1 0 8448 0>; - enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; - regulator-min-microvolt = <1016000>; - regulator-max-microvolt = <1114000>; - regulator-name = "vdd_logic"; - /* unit == per-mille */ - pwm-dutycycle-unit = <1000>; - /* - * Inverted PWM logic, and the duty cycle range is limited - * to 30%-70%. - */ - pwm-dutycycle-range = <700 300>; /* */ - }; - -Voltage Table Example: - pwm_regulator { - compatible = "pwm-regulator"; - pwms = <&pwm1 0 8448 0>; - regulator-min-microvolt = <1016000>; - regulator-max-microvolt = <1114000>; - regulator-name = "vdd_logic"; - - /* Voltage Duty-Cycle */ - voltage-table = <1114000 0>, - <1095000 10>, - <1076000 20>, - <1056000 30>, - <1036000 40>, - <1016000 50>; - }; diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml b/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml new file mode 100644 index 000000000000..0332fec48a88 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml @@ -0,0 +1,126 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/pwm-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bindings for the Generic PWM Regulator + +maintainers: + - Brian Norris + - Lee Jones + - Alexandre Courbot + +description: | + Currently supports 2 modes of operation: + + Voltage Table: + When in this mode, a voltage table (See below) of predefined voltage <=> + duty-cycle values must be provided via DT. Limitations are that the + regulator can only operate at the voltages supplied in the table. + Intermediary duty-cycle values which would normally allow finer grained + voltage selection are ignored and rendered useless. Although more control + is given to the user if the assumptions made in continuous-voltage mode do + not reign true. + + Continuous Voltage: + This mode uses the regulator's maximum and minimum supplied voltages + specified in the regulator-{min,max}-microvolt properties to calculate + appropriate duty-cycle values. This allows for a much more fine grained + solution when compared with voltage-table mode above. This solution does + make an assumption that a %50 duty-cycle value will cause the regulator + voltage to run at half way between the supplied max_uV and min_uV values. + + If voltage-table is provided, then the device will be used in Voltage Table + Mode. If no voltage-table is provided, then the device will be used in + Continuous Voltage Mode. + +allOf: + - $ref: regulator.yaml# + +properties: + compatible: + const: pwm-regulator + + pwms: + maxItems: 1 + + voltage-table: + description: Voltage and Duty-Cycle table. + $ref: /schemas/types.yaml#/definitions/uint32-matrix + items: + items: + - description: voltage in microvolts (uV) + - description: duty-cycle in percent (%) + + enable-gpios: + description: Regulator enable GPIO + maxItems: 1 + + # Optional properties for Continuous mode: + pwm-dutycycle-unit: + description: + Integer value encoding the duty cycle unit. If not + defined, <100> is assumed, meaning that + pwm-dutycycle-range contains values expressed in + percent. + default: 100 + + pwm-dutycycle-range: + description: + Should contain 2 entries. The first entry is encoding + the dutycycle for regulator-min-microvolt and the + second one the dutycycle for regulator-max-microvolt. + Duty cycle values are expressed in pwm-dutycycle-unit. + If not defined, <0 100> is assumed. + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: the dutycycle for regulator-min-microvolt + - description: the dutycycle for regulator-max-microvolt + default: [ 0 100 ] + +required: + - compatible + - pwms + +unevaluatedProperties: false + +examples: + - | + #include + + // Continuous Voltage With Enable GPIO Example: + regulator { + compatible = "pwm-regulator"; + pwms = <&pwm1 0 8448 0>; + enable-gpios = <&gpio0 23 GPIO_ACTIVE_HIGH>; + regulator-min-microvolt = <1016000>; + regulator-max-microvolt = <1114000>; + regulator-name = "vdd_logic"; + /* unit == per-mille */ + pwm-dutycycle-unit = <1000>; + /* + * Inverted PWM logic, and the duty cycle range is limited + * to 30%-70%. + */ + pwm-dutycycle-range = <700 300>; /* */ + }; + + - | + // Voltage Table Example: + regulator { + compatible = "pwm-regulator"; + pwms = <&pwm1 0 8448 0>; + regulator-min-microvolt = <1016000>; + regulator-max-microvolt = <1114000>; + regulator-name = "vdd_logic"; + + /* Voltage Duty-Cycle */ + voltage-table = <1114000 0>, + <1095000 10>, + <1076000 20>, + <1056000 30>, + <1036000 40>, + <1016000 50>; + }; +... From a8280a5dd5a425bd33d858ac787defdbad47362d Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Tue, 7 Jun 2022 14:47:59 +0200 Subject: [PATCH 07/35] regulator: mp5416: remove kernel.h include Driver does not seem to utilize anything from the kernel.h, compiles and works fine for me without it. So remove kernel.h include as it pulls in a lot of unused stuff. Signed-off-by: Robert Marko Cc: christophe.jaillet@wanadoo.fr Link: https://lore.kernel.org/r/20220607124759.775133-1-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mp5416.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/regulator/mp5416.c b/drivers/regulator/mp5416.c index 71e20e8d78ac..82892d71c2c9 100644 --- a/drivers/regulator/mp5416.c +++ b/drivers/regulator/mp5416.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include From f210f387c8c05ae0bd5312b8b6b85398c20b94f9 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Sat, 4 Jun 2022 21:32:59 +0200 Subject: [PATCH 08/35] regulator: qcom_smd: add get_voltage to MP5496 Add the get_voltage OP to MP5496 ops using the generic rpm_reg_get_voltage. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220604193300.125758-1-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_smd-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 7dff94a2eb7e..a73fbbf2daa8 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -205,6 +205,7 @@ static const struct regulator_ops rpm_mp5496_ops = { .is_enabled = rpm_reg_is_enabled, .list_voltage = regulator_list_voltage_linear_range, + .get_voltage = rpm_reg_get_voltage, .set_voltage = rpm_reg_set_voltage, }; From 8c92d9221d47ea97534888e2975ecd54896718d8 Mon Sep 17 00:00:00 2001 From: Stephen Kitt Date: Tue, 7 Jun 2022 20:53:04 +0200 Subject: [PATCH 09/35] regulator: rpi-panel-attiny: Use backlight helper backlight_properties.fb_blank is deprecated. The states it represents are handled by other properties; but instead of accessing those properties directly, drivers should use the helpers provided by backlight.h. Instead of retrieving the backlight brightness in struct backlight_properties manually, and then checking whether the backlight should be on at all, use backlight_get_brightness() which does all this and insulates this from future changes. Signed-off-by: Stephen Kitt Cc: Liam Girdwood Cc: Mark Brown Link: https://lore.kernel.org/r/20220607185304.1128962-1-steve@sk2.org Signed-off-by: Mark Brown --- drivers/regulator/rpi-panel-attiny-regulator.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/regulator/rpi-panel-attiny-regulator.c b/drivers/regulator/rpi-panel-attiny-regulator.c index fa8706a352ce..105f694a67e6 100644 --- a/drivers/regulator/rpi-panel-attiny-regulator.c +++ b/drivers/regulator/rpi-panel-attiny-regulator.c @@ -187,15 +187,11 @@ static int attiny_update_status(struct backlight_device *bl) { struct attiny_lcd *state = bl_get_data(bl); struct regmap *regmap = state->regmap; - int brightness = bl->props.brightness; + int brightness = backlight_get_brightness(bl); int ret, i; mutex_lock(&state->lock); - if (bl->props.power != FB_BLANK_UNBLANK || - bl->props.fb_blank != FB_BLANK_UNBLANK) - brightness = 0; - for (i = 0; i < 10; i++) { ret = regmap_write(regmap, REG_PWM, brightness); if (!ret) From c0216c0e68f736d20ac02bbaaaf4a216c10e53af Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 22 Jun 2022 18:01:43 +0800 Subject: [PATCH 10/35] regulator: rt5120: Add PMIC regulator support Add RT5120 PMIC regulator support. It integrates 4 buck convertes, 1 LDO voltage regulator, 1 external enable signal to control the external power source. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1655892104-10874-4-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/rt5120-regulator.c | 420 +++++++++++++++++++++++++++ 3 files changed, 431 insertions(+) create mode 100644 drivers/regulator/rt5120-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index cbe0f96ca342..d777f713d522 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1047,6 +1047,16 @@ config REGULATOR_RT5033 RT5033 PMIC. The device supports multiple regulators like current source, LDO and Buck. +config REGULATOR_RT5120 + tristate "Richtek RT5120 PMIC Regulators" + depends on MFD_RT5120 + help + This adds support for voltage regulator in Richtek RT5120 PMIC. + It integrates 4 channels buck controller, 1 channel LDO, 1 EXTEN + to control external power source. Only BUCK1 is adjustable from + 600mV to 1395mV, per step 6.250mV. The others are all fixed voltage + by external hardware circuit. + config REGULATOR_RT5190A tristate "Richtek RT5190A PMIC" depends on I2C diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 8d3ee8b6d41d..b74ca8ff9f71 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_REGULATOR_ROHM) += rohm-regulator.o obj-$(CONFIG_REGULATOR_RT4801) += rt4801-regulator.o obj-$(CONFIG_REGULATOR_RT4831) += rt4831-regulator.o obj-$(CONFIG_REGULATOR_RT5033) += rt5033-regulator.o +obj-$(CONFIG_REGULATOR_RT5120) += rt5120-regulator.o obj-$(CONFIG_REGULATOR_RT5190A) += rt5190a-regulator.o obj-$(CONFIG_REGULATOR_RT5759) += rt5759-regulator.o obj-$(CONFIG_REGULATOR_RT6160) += rt6160-regulator.o diff --git a/drivers/regulator/rt5120-regulator.c b/drivers/regulator/rt5120-regulator.c new file mode 100644 index 000000000000..8173ede09414 --- /dev/null +++ b/drivers/regulator/rt5120-regulator.c @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define RT5120_REG_PGSTAT 0x03 +#define RT5120_REG_CH1VID 0x06 +#define RT5120_REG_CH1SLPVID 0x07 +#define RT5120_REG_ENABLE 0x08 +#define RT5120_REG_MODECTL 0x09 +#define RT5120_REG_UVOVPROT 0x0A +#define RT5120_REG_SLPCTL 0x0C +#define RT5120_REG_INTSTAT 0x1E +#define RT5120_REG_DISCHG 0x1F + +#define RT5120_OUTPG_MASK(rid) BIT(rid + 1) +#define RT5120_OUTUV_MASK(rid) BIT(rid + 9) +#define RT5120_OUTOV_MASK(rid) BIT(rid + 16) +#define RT5120_CH1VID_MASK GENMASK(6, 0) +#define RT5120_RIDEN_MASK(rid) BIT(rid + 1) +#define RT5120_RADEN_MASK(rid) BIT(rid) +#define RT5120_FPWM_MASK(rid) BIT(rid + 1) +#define RT5120_UVHICCUP_MASK BIT(1) +#define RT5120_OVHICCUP_MASK BIT(0) +#define RT5120_HOTDIE_MASK BIT(1) + +#define RT5120_BUCK1_MINUV 600000 +#define RT5120_BUCK1_MAXUV 1393750 +#define RT5120_BUCK1_STEPUV 6250 +#define RT5120_BUCK1_NUM_VOLT 0x80 + +#define RT5120_AUTO_MODE 0 +#define RT5120_FPWM_MODE 1 + +enum { + RT5120_REGULATOR_BUCK1 = 0, + RT5120_REGULATOR_BUCK2, + RT5120_REGULATOR_BUCK3, + RT5120_REGULATOR_BUCK4, + RT5120_REGULATOR_LDO, + RT5120_REGULATOR_EXTEN, + RT5120_MAX_REGULATOR +}; + +struct rt5120_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_desc rdesc[RT5120_MAX_REGULATOR]; +}; + +static int rt5120_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int mask = RT5120_FPWM_MASK(rid), val; + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val = 0; + break; + case REGULATOR_MODE_FAST: + val = RT5120_FPWM_MASK(rid); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(regmap, RT5120_REG_MODECTL, mask, val); +} + +static unsigned int rt5120_buck_get_mode(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int ret, rid = rdev_get_id(rdev); + unsigned int val; + + ret = regmap_read(regmap, RT5120_REG_MODECTL, &val); + if (ret) + return REGULATOR_MODE_INVALID; + + if (val & RT5120_FPWM_MASK(rid)) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static int rt5120_regulator_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int stat, hd_stat, cur_flags = 0; + int rid = rdev_get_id(rdev), ret; + + /* + * reg 0x03/0x04/0x05 to indicate PG/UV/OV + * use block read to descrease I/O xfer time + */ + ret = regmap_raw_read(regmap, RT5120_REG_PGSTAT, &stat, 3); + if (ret) + return ret; + + ret = regmap_read(regmap, RT5120_REG_INTSTAT, &hd_stat); + if (ret) + return ret; + + if (!(stat & RT5120_OUTPG_MASK(rid))) { + if (stat & RT5120_OUTUV_MASK(rid)) + cur_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (stat & RT5120_OUTOV_MASK(rid)) + cur_flags |= REGULATOR_ERROR_REGULATION_OUT; + } + + if (hd_stat & RT5120_HOTDIE_MASK) + cur_flags |= REGULATOR_ERROR_OVER_TEMP; + + *flags = cur_flags; + return 0; +} + +static int rt5120_buck1_set_suspend_voltage(struct regulator_dev *rdev, int uV) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int sel; + + if (uV < RT5120_BUCK1_MINUV || uV > RT5120_BUCK1_MAXUV) + return -EINVAL; + + sel = (uV - RT5120_BUCK1_MINUV) / RT5120_BUCK1_STEPUV; + return regmap_write(regmap, RT5120_REG_CH1SLPVID, sel); +} + +static int rt5120_regulator_set_suspend_enable(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int mask = RT5120_RIDEN_MASK(rid); + + return regmap_update_bits(regmap, RT5120_REG_SLPCTL, mask, mask); +} + +static int rt5120_regulator_set_suspend_disable(struct regulator_dev *rdev) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + int rid = rdev_get_id(rdev); + unsigned int mask = RT5120_RIDEN_MASK(rid); + + return regmap_update_bits(regmap, RT5120_REG_SLPCTL, mask, 0); +} + +static const struct regulator_ops rt5120_buck1_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_mode = rt5120_buck_set_mode, + .get_mode = rt5120_buck_get_mode, + .get_error_flags = rt5120_regulator_get_error_flags, + .set_suspend_voltage = rt5120_buck1_set_suspend_voltage, + .set_suspend_enable = rt5120_regulator_set_suspend_enable, + .set_suspend_disable = rt5120_regulator_set_suspend_disable, +}; + +static const struct regulator_ops rt5120_buck234_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_mode = rt5120_buck_set_mode, + .get_mode = rt5120_buck_get_mode, + .get_error_flags = rt5120_regulator_get_error_flags, + .set_suspend_enable = rt5120_regulator_set_suspend_enable, + .set_suspend_disable = rt5120_regulator_set_suspend_disable, +}; + +static const struct regulator_ops rt5120_ldo_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = rt5120_regulator_get_error_flags, + .set_suspend_enable = rt5120_regulator_set_suspend_enable, + .set_suspend_disable = rt5120_regulator_set_suspend_disable, +}; + +static const struct regulator_ops rt5120_exten_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .set_suspend_enable = rt5120_regulator_set_suspend_enable, + .set_suspend_disable = rt5120_regulator_set_suspend_disable, +}; + +static unsigned int rt5120_buck_of_map_mode(unsigned int mode) +{ + switch (mode) { + case RT5120_AUTO_MODE: + return REGULATOR_MODE_NORMAL; + case RT5120_FPWM_MODE: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static void rt5120_fillin_regulator_desc(struct regulator_desc *desc, int rid) +{ + static const char * const name[] = { + "buck1", "buck2", "buck3", "buck4", "ldo", "exten" }; + static const char * const sname[] = { + "vin1", "vin2", "vin3", "vin4", "vinldo", NULL }; + + /* Common regulator property */ + desc->name = name[rid]; + desc->supply_name = sname[rid]; + desc->owner = THIS_MODULE; + desc->type = REGULATOR_VOLTAGE; + desc->id = rid; + desc->enable_reg = RT5120_REG_ENABLE; + desc->enable_mask = RT5120_RIDEN_MASK(rid); + desc->active_discharge_reg = RT5120_REG_DISCHG; + desc->active_discharge_mask = RT5120_RADEN_MASK(rid); + desc->active_discharge_on = RT5120_RADEN_MASK(rid); + /* Config n_voltages to 1 for all*/ + desc->n_voltages = 1; + + /* Only buck support mode change */ + if (rid >= RT5120_REGULATOR_BUCK1 && rid <= RT5120_REGULATOR_BUCK4) + desc->of_map_mode = rt5120_buck_of_map_mode; + + /* RID specific property init */ + switch (rid) { + case RT5120_REGULATOR_BUCK1: + /* Only buck1 support voltage change by I2C */ + desc->n_voltages = RT5120_BUCK1_NUM_VOLT; + desc->min_uV = RT5120_BUCK1_MINUV; + desc->uV_step = RT5120_BUCK1_STEPUV; + desc->vsel_reg = RT5120_REG_CH1VID, + desc->vsel_mask = RT5120_CH1VID_MASK, + desc->ops = &rt5120_buck1_ops; + break; + case RT5120_REGULATOR_BUCK2 ... RT5120_REGULATOR_BUCK4: + desc->ops = &rt5120_buck234_ops; + break; + case RT5120_REGULATOR_LDO: + desc->ops = &rt5120_ldo_ops; + break; + default: + desc->ops = &rt5120_exten_ops; + } +} + +static int rt5120_of_parse_cb(struct rt5120_priv *priv, int rid, + struct of_regulator_match *match) +{ + struct regulator_desc *desc = priv->rdesc + rid; + struct regulator_init_data *init_data = match->init_data; + + if (!init_data || rid == RT5120_REGULATOR_BUCK1) + return 0; + + if (init_data->constraints.min_uV != init_data->constraints.max_uV) { + dev_err(priv->dev, "Variable voltage for fixed regulator\n"); + return -EINVAL; + } + + desc->fixed_uV = init_data->constraints.min_uV; + return 0; +} + +static struct of_regulator_match rt5120_regu_match[RT5120_MAX_REGULATOR] = { + [RT5120_REGULATOR_BUCK1] = { .name = "buck1", }, + [RT5120_REGULATOR_BUCK2] = { .name = "buck2", }, + [RT5120_REGULATOR_BUCK3] = { .name = "buck3", }, + [RT5120_REGULATOR_BUCK4] = { .name = "buck4", }, + [RT5120_REGULATOR_LDO] = { .name = "ldo", }, + [RT5120_REGULATOR_EXTEN] = { .name = "exten", } +}; + +static int rt5120_parse_regulator_dt_data(struct rt5120_priv *priv) +{ + struct device *dev = priv->dev->parent; + struct device_node *reg_node; + int i, ret; + + for (i = 0; i < RT5120_MAX_REGULATOR; i++) { + rt5120_fillin_regulator_desc(priv->rdesc + i, i); + + rt5120_regu_match[i].desc = priv->rdesc + i; + } + + reg_node = of_get_child_by_name(dev->of_node, "regulators"); + if (!reg_node) { + dev_err(priv->dev, "Couldn't find 'regulators' node\n"); + return -ENODEV; + } + + ret = of_regulator_match(priv->dev, reg_node, rt5120_regu_match, + ARRAY_SIZE(rt5120_regu_match)); + + of_node_put(reg_node); + + if (ret < 0) { + dev_err(priv->dev, + "Error parsing regulator init data (%d)\n", ret); + return ret; + } + + for (i = 0; i < RT5120_MAX_REGULATOR; i++) { + ret = rt5120_of_parse_cb(priv, i, rt5120_regu_match + i); + if (ret) { + dev_err(priv->dev, "Failed in [%d] of_passe_cb\n", i); + return ret; + } + } + + return 0; +} + +static int rt5120_device_property_init(struct rt5120_priv *priv) +{ + struct device *dev = priv->dev->parent; + struct device_node *np = dev->of_node; + bool prot_enable; + unsigned int prot_enable_val = 0; + + /* Assign UV/OV HW protection behavior */ + prot_enable = of_property_read_bool(np, + "richtek,enable-undervolt-hiccup"); + if (prot_enable) + prot_enable_val |= RT5120_UVHICCUP_MASK; + + prot_enable = of_property_read_bool(np, + "richtek,enable-overvolt-hiccup"); + if (prot_enable) + prot_enable_val |= RT5120_OVHICCUP_MASK; + + return regmap_update_bits(priv->regmap, RT5120_REG_UVOVPROT, + RT5120_UVHICCUP_MASK | RT5120_OVHICCUP_MASK, + prot_enable_val); +} + +static int rt5120_regulator_probe(struct platform_device *pdev) +{ + struct rt5120_priv *priv; + struct regulator_dev *rdev; + struct regulator_config config = {}; + int i, ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return -ENODEV; + } + + ret = rt5120_device_property_init(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to do property init\n"); + return ret; + } + + ret = rt5120_parse_regulator_dt_data(priv); + if (ret) { + dev_err(&pdev->dev, "Failed to parse dt data\n"); + return ret; + } + + config.dev = &pdev->dev; + config.regmap = priv->regmap; + + for (i = 0; i < RT5120_MAX_REGULATOR; i++) { + config.of_node = rt5120_regu_match[i].of_node; + config.init_data = rt5120_regu_match[i].init_data; + + rdev = devm_regulator_register(&pdev->dev, priv->rdesc + i, + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, + "Failed to register regulator [%d]\n", i); + return PTR_ERR(rdev); + } + } + + return 0; +} + +static const struct platform_device_id rt5120_regulator_dev_table[] = { + { "rt5120-regulator", 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, rt5120_regulator_dev_table); + +static struct platform_driver rt5120_regulator_driver = { + .driver = { + .name = "rt5120-regulator", + }, + .id_table = rt5120_regulator_dev_table, + .probe = rt5120_regulator_probe, +}; +module_platform_driver(rt5120_regulator_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Richtek RT5120 regulator driver"); +MODULE_LICENSE("GPL v2"); From 8171c93bac1bf9e98269b2efb19ef4e6c4e55ed7 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Thu, 23 Jun 2022 19:56:26 +0800 Subject: [PATCH 11/35] regulator: mt6370: Add mt6370 DisplayBias and VibLDO support Add mt6370 DisplayBias and VibLDO support. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/20220623115631.22209-10-peterwu.pub@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 8 + drivers/regulator/Makefile | 1 + drivers/regulator/mt6370-regulator.c | 388 +++++++++++++++++++++++++++ 3 files changed, 397 insertions(+) create mode 100644 drivers/regulator/mt6370-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index d777f713d522..03e8a4ef2ed6 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -804,6 +804,14 @@ config REGULATOR_MT6360 2-channel buck with Thermal Shutdown and Overload Protection 6-channel High PSRR and Low Dropout LDO. +config REGULATOR_MT6370 + tristate "MT6370 SubPMIC Regulator" + depends on MFD_MT6370 + help + Say Y here to enable MT6370 regulator support. + This driver supports the control for DisplayBias voltages and one + general purpose LDO which is commonly used to drive the vibrator. + config REGULATOR_MT6380 tristate "MediaTek MT6380 PMIC" depends on MTK_PMIC_WRAP diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b74ca8ff9f71..a9c392587120 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -97,6 +97,7 @@ obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o obj-$(CONFIG_REGULATOR_MT6358) += mt6358-regulator.o obj-$(CONFIG_REGULATOR_MT6359) += mt6359-regulator.o obj-$(CONFIG_REGULATOR_MT6360) += mt6360-regulator.o +obj-$(CONFIG_REGULATOR_MT6370) += mt6370-regulator.o obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o obj-$(CONFIG_REGULATOR_MTK_DVFSRC) += mtk-dvfsrc-regulator.o diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c new file mode 100644 index 000000000000..bc356b4bbf22 --- /dev/null +++ b/drivers/regulator/mt6370-regulator.c @@ -0,0 +1,388 @@ +// SPDX-License-Identifier: GPL-2.0+ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +enum { + MT6370_IDX_DSVBOOST = 0, + MT6370_IDX_DSVPOS, + MT6370_IDX_DSVNEG, + MT6370_IDX_VIBLDO, + MT6370_MAX_IDX +}; + +#define MT6370_REG_LDO_CFG 0x180 +#define MT6370_REG_LDO_VOUT 0x181 +#define MT6370_REG_DB_CTRL1 0x1B0 +#define MT6370_REG_DB_CTRL2 0x1B1 +#define MT6370_REG_DB_VBST 0x1B2 +#define MT6370_REG_DB_VPOS 0x1B3 +#define MT6370_REG_DB_VNEG 0x1B4 +#define MT6370_REG_LDO_STAT 0x1DC +#define MT6370_REG_DB_STAT 0x1DF + +#define MT6370_LDOOMS_MASK BIT(7) +#define MT6370_LDOEN_MASK BIT(7) +#define MT6370_LDOVOUT_MASK GENMASK(3, 0) +#define MT6370_DBPERD_MASK (BIT(7) | BIT(4)) +#define MT6370_DBEXTEN_MASK BIT(0) +#define MT6370_DBVPOSEN_MASK BIT(6) +#define MT6370_DBVPOSDISG_MASK BIT(5) +#define MT6370_DBVNEGEN_MASK BIT(3) +#define MT6370_DBVNEGDISG_MASK BIT(2) +#define MT6370_DBALLON_MASK (MT6370_DBVPOSEN_MASK | MT6370_DBVNEGEN_MASK) +#define MT6370_DBSLEW_MASK GENMASK(7, 6) +#define MT6370_DBVOUT_MASK GENMASK(5, 0) +#define MT6370_LDOOC_EVT_MASK BIT(7) +#define MT6370_POSSCP_EVT_MASK BIT(7) +#define MT6370_NEGSCP_EVT_MASK BIT(6) +#define MT6370_BSTOCP_EVT_MASK BIT(5) +#define MT6370_POSOCP_EVT_MASK BIT(4) +#define MT6370_NEGOCP_EVT_MASK BIT(3) + +#define MT6370_LDO_MINUV 1600000 +#define MT6370_LDO_STPUV 200000 +#define MT6370_LDO_N_VOLT 13 +#define MT6370_DBVBOOST_MINUV 4000000 +#define MT6370_DBVBOOST_STPUV 50000 +#define MT6370_DBVBOOST_N_VOLT 45 +#define MT6370_DBVOUT_MINUV 4000000 +#define MT6370_DBVOUT_STPUV 50000 +#define MT6370_DBVOUT_N_VOLT 41 + +struct mt6370_priv { + struct device *dev; + struct regmap *regmap; + struct regulator_dev *rdev[MT6370_MAX_IDX]; + bool use_external_ctrl; +}; + +static const unsigned int mt6370_vpos_ramp_tbl[] = { 8540, 5840, 4830, 3000 }; +static const unsigned int mt6370_vneg_ramp_tbl[] = { 10090, 6310, 5050, 3150 }; + +static int mt6370_get_error_flags(struct regulator_dev *rdev, + unsigned int *flags) +{ + struct regmap *regmap = rdev_get_regmap(rdev); + unsigned int stat_reg, stat, rpt_flags = 0; + int rid = rdev_get_id(rdev), ret; + + if (rid == MT6370_IDX_VIBLDO) + stat_reg = MT6370_REG_LDO_STAT; + else + stat_reg = MT6370_REG_DB_STAT; + + ret = regmap_read(regmap, stat_reg, &stat); + if (ret) + return ret; + + switch (rid) { + case MT6370_IDX_DSVBOOST: + if (stat & MT6370_BSTOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + case MT6370_IDX_DSVPOS: + if (stat & MT6370_POSSCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (stat & MT6370_POSOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + case MT6370_IDX_DSVNEG: + if (stat & MT6370_NEGSCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_UNDER_VOLTAGE; + + if (stat & MT6370_NEGOCP_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + default: + if (stat & MT6370_LDOOC_EVT_MASK) + rpt_flags |= REGULATOR_ERROR_OVER_CURRENT; + break; + } + + *flags = rpt_flags; + return 0; +} + +static const struct regulator_ops mt6370_dbvboost_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .get_bypass = regulator_get_bypass_regmap, + .set_bypass = regulator_set_bypass_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static const struct regulator_ops mt6370_dbvout_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .set_ramp_delay = regulator_set_ramp_delay_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static const struct regulator_ops mt6370_ldo_ops = { + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .is_enabled = regulator_is_enabled_regmap, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .set_active_discharge = regulator_set_active_discharge_regmap, + .get_error_flags = mt6370_get_error_flags, +}; + +static int mt6370_of_parse_cb(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *config) +{ + struct mt6370_priv *priv = config->driver_data; + struct gpio_desc *enable_gpio; + int ret; + + enable_gpio = gpiod_get_from_of_node(np, "enable", 0, GPIOD_OUT_HIGH | + GPIOD_FLAGS_BIT_NONEXCLUSIVE, + desc->name); + if (IS_ERR(enable_gpio)) { + config->ena_gpiod = NULL; + return 0; + } + + /* + * RG control by default + * Only if all are using external pin, change all by external control + */ + if (priv->use_external_ctrl) { + ret = regmap_update_bits(priv->regmap, MT6370_REG_DB_CTRL1, + MT6370_DBEXTEN_MASK, + MT6370_DBEXTEN_MASK); + if (ret) + return ret; + } + + config->ena_gpiod = enable_gpio; + priv->use_external_ctrl = true; + return 0; +} + +static const struct regulator_desc mt6370_regulator_descs[] = { + { + .name = "mt6370-dsv-vbst", + .of_match = of_match_ptr("dsvbst"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVBOOST, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &mt6370_dbvboost_ops, + .min_uV = MT6370_DBVBOOST_MINUV, + .uV_step = MT6370_DBVBOOST_STPUV, + .n_voltages = MT6370_DBVBOOST_N_VOLT, + .vsel_reg = MT6370_REG_DB_VBST, + .vsel_mask = MT6370_DBVOUT_MASK, + .bypass_reg = MT6370_REG_DB_CTRL1, + .bypass_mask = MT6370_DBPERD_MASK, + .bypass_val_on = MT6370_DBPERD_MASK, + }, + { + .name = "mt6370-dsv-vpos", + .of_match = of_match_ptr("dsvpos"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVPOS, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .of_parse_cb = mt6370_of_parse_cb, + .ops = &mt6370_dbvout_ops, + .min_uV = MT6370_DBVOUT_MINUV, + .uV_step = MT6370_DBVOUT_STPUV, + .n_voltages = MT6370_DBVOUT_N_VOLT, + .vsel_reg = MT6370_REG_DB_VPOS, + .vsel_mask = MT6370_DBVOUT_MASK, + .enable_reg = MT6370_REG_DB_CTRL2, + .enable_mask = MT6370_DBVPOSEN_MASK, + .ramp_reg = MT6370_REG_DB_VPOS, + .ramp_mask = MT6370_DBSLEW_MASK, + .ramp_delay_table = mt6370_vpos_ramp_tbl, + .n_ramp_values = ARRAY_SIZE(mt6370_vpos_ramp_tbl), + .active_discharge_reg = MT6370_REG_DB_CTRL2, + .active_discharge_mask = MT6370_DBVPOSDISG_MASK, + .active_discharge_on = MT6370_DBVPOSDISG_MASK, + }, + { + .name = "mt6370-dsv-vneg", + .of_match = of_match_ptr("dsvneg"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_DSVNEG, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .of_parse_cb = mt6370_of_parse_cb, + .ops = &mt6370_dbvout_ops, + .min_uV = MT6370_DBVOUT_MINUV, + .uV_step = MT6370_DBVOUT_STPUV, + .n_voltages = MT6370_DBVOUT_N_VOLT, + .vsel_reg = MT6370_REG_DB_VNEG, + .vsel_mask = MT6370_DBVOUT_MASK, + .enable_reg = MT6370_REG_DB_CTRL2, + .enable_mask = MT6370_DBVNEGEN_MASK, + .ramp_reg = MT6370_REG_DB_VNEG, + .ramp_mask = MT6370_DBSLEW_MASK, + .ramp_delay_table = mt6370_vneg_ramp_tbl, + .n_ramp_values = ARRAY_SIZE(mt6370_vneg_ramp_tbl), + .active_discharge_reg = MT6370_REG_DB_CTRL2, + .active_discharge_mask = MT6370_DBVNEGDISG_MASK, + .active_discharge_on = MT6370_DBVNEGDISG_MASK, + }, + { + .name = "mt6370-vib-ldo", + .of_match = of_match_ptr("vibldo"), + .regulators_node = of_match_ptr("regulators"), + .id = MT6370_IDX_VIBLDO, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &mt6370_ldo_ops, + .min_uV = MT6370_LDO_MINUV, + .uV_step = MT6370_LDO_STPUV, + .n_voltages = MT6370_LDO_N_VOLT, + .vsel_reg = MT6370_REG_LDO_VOUT, + .vsel_mask = MT6370_LDOVOUT_MASK, + .enable_reg = MT6370_REG_LDO_VOUT, + .enable_mask = MT6370_LDOEN_MASK, + .active_discharge_reg = MT6370_REG_LDO_CFG, + .active_discharge_mask = MT6370_LDOOMS_MASK, + .active_discharge_on = MT6370_LDOOMS_MASK, + } +}; + +static irqreturn_t mt6370_scp_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_UNDER_VOLTAGE, + NULL); + return IRQ_HANDLED; +} + +static irqreturn_t mt6370_ocp_handler(int irq, void *data) +{ + struct regulator_dev *rdev = data; + + regulator_notifier_call_chain(rdev, REGULATOR_EVENT_OVER_CURRENT, NULL); + return IRQ_HANDLED; +} + +static int mt6370_regulator_irq_register(struct mt6370_priv *priv) +{ + struct platform_device *pdev = to_platform_device(priv->dev); + static const struct { + const char *name; + int rid; + irq_handler_t handler; + } mt6370_irqs[] = { + { "db_vpos_scp", MT6370_IDX_DSVPOS, mt6370_scp_handler }, + { "db_vneg_scp", MT6370_IDX_DSVNEG, mt6370_scp_handler }, + { "db_vbst_ocp", MT6370_IDX_DSVBOOST, mt6370_ocp_handler }, + { "db_vpos_ocp", MT6370_IDX_DSVPOS, mt6370_ocp_handler }, + { "db_vneg_ocp", MT6370_IDX_DSVNEG, mt6370_ocp_handler }, + { "ldo_oc", MT6370_IDX_VIBLDO, mt6370_ocp_handler } + }; + struct regulator_dev *rdev; + int i, irq, ret; + + for (i = 0; i < ARRAY_SIZE(mt6370_irqs); i++) { + irq = platform_get_irq_byname(pdev, mt6370_irqs[i].name); + + rdev = priv->rdev[mt6370_irqs[i].rid]; + + ret = devm_request_threaded_irq(priv->dev, irq, NULL, + mt6370_irqs[i].handler, 0, + mt6370_irqs[i].name, rdev); + if (ret) { + dev_err(priv->dev, + "Failed to register (%d) interrupt\n", i); + return ret; + } + } + + return 0; +} + +static int mt6370_regualtor_register(struct mt6370_priv *priv) +{ + struct regulator_dev *rdev; + struct regulator_config cfg = {}; + struct device *parent = priv->dev->parent; + int i; + + cfg.dev = parent; + cfg.driver_data = priv; + + for (i = 0; i < MT6370_MAX_IDX; i++) { + rdev = devm_regulator_register(priv->dev, + mt6370_regulator_descs + i, + &cfg); + if (IS_ERR(rdev)) { + dev_err(priv->dev, + "Failed to register (%d) regulator\n", i); + return PTR_ERR(rdev); + } + + priv->rdev[i] = rdev; + } + + return 0; +} + +static int mt6370_regulator_probe(struct platform_device *pdev) +{ + struct mt6370_priv *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + + priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!priv->regmap) { + dev_err(&pdev->dev, "Failed to init regmap\n"); + return -ENODEV; + } + + ret = mt6370_regualtor_register(priv); + if (ret) + return ret; + + return mt6370_regulator_irq_register(priv); +} + +static const struct platform_device_id mt6370_devid_table[] = { + { "mt6370-regulator", 0}, + {} +}; +MODULE_DEVICE_TABLE(platform, mt6370_devid_table); + +static struct platform_driver mt6370_regulator_driver = { + .driver = { + .name = "mt6370-regulator", + }, + .id_table = mt6370_devid_table, + .probe = mt6370_regulator_probe, +}; +module_platform_driver(mt6370_regulator_driver); + +MODULE_AUTHOR("ChiYuan Huang "); +MODULE_DESCRIPTION("Mediatek MT6370 Regulator Driver"); +MODULE_LICENSE("GPL v2"); From 0e584d46218e3b9dc12a98e18e81a0cd3e0d5419 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 28 Jun 2022 10:46:22 +0100 Subject: [PATCH 12/35] regulator: fix a kernel-doc warning document n_ramp_values field at struct regulator_desc, in order to solve this warning: include/linux/regulator/driver.h:434: warning: Function parameter or member 'n_ramp_values' not described in 'regulator_desc' Signed-off-by: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/15efc16e878aa327aa2769023bcdf959a795f41d.1656409369.git.mchehab@kernel.org Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 0228caaa6741..f9a7461e72b8 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -348,6 +348,7 @@ enum regulator_type { * @ramp_delay_table: Table for mapping the regulator ramp-rate values. Values * should be given in units of V/S (uV/uS). See the * regulator_set_ramp_delay_regmap(). + * @n_ramp_values: number of elements at @ramp_delay_table. * * @enable_time: Time taken for initial enable of regulator (in uS). * @off_on_delay: guard time (in uS), before re-enabling a regulator From 4806c991bf4ff959acf921ea72f931a945a968ae Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 29 Jun 2022 09:41:00 +0800 Subject: [PATCH 13/35] regulator: mt6370: Use 'fwnode_gpiod_get_index' to fix gpio parsing From the common binding, 'enable-gpio' or 'enable-gpios' are all well for external 'enable' gpio. 'gpiod_get_from_of_node' only parse the 'enable' property, it need to add the gpio suffix. It's more convenient to use fwnode_gpiod_get_index. Although fwnode parsing is not preferred, but 'of_parse_cb' already can guarantee the callback will only be used by regulator of_node parsing. Signed-off-by: ChiYuan Huang Link: https://lore.kernel.org/r/1656466861-7737-1-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mt6370-regulator.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index bc356b4bbf22..949b2c7b5556 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -153,7 +153,8 @@ static int mt6370_of_parse_cb(struct device_node *np, struct gpio_desc *enable_gpio; int ret; - enable_gpio = gpiod_get_from_of_node(np, "enable", 0, GPIOD_OUT_HIGH | + enable_gpio = fwnode_gpiod_get_index(of_fwnode_handle(np), "enable", 0, + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, desc->name); if (IS_ERR(enable_gpio)) { From 46ae6fb58fd49c4cb2d1ca34e87d603b7c96ba97 Mon Sep 17 00:00:00 2001 From: ChiYuan Huang Date: Wed, 29 Jun 2022 09:41:01 +0800 Subject: [PATCH 14/35] regulator: mt6370: Use the correct header for platform_device_id 'platform_device_id' struct is defined in 'mod_devicetable.h'. Even 'of.h' also includes this header usage. The 'of.h' cannot be removed due to 'of_match_ptr' function. Signed-off-by: ChiYuan Huang Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/1656466861-7737-2-git-send-email-u0084500@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mt6370-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index 949b2c7b5556..e73f5a46cb9a 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include From e8977917e116d1571dacb8e9864474551c1c12bd Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 23 Jun 2022 11:46:12 +0200 Subject: [PATCH 15/35] regulator: qcom_smd: Fix pm8916_pldo range The PM8916 device specification [1] documents a programmable range of 1.75V to 3.337V with 12.5mV steps for the PMOS LDOs in PM8916. This range is also used when controlling the regulator directly using the qcom_spmi-regulator driver ("ult_pldo" there). However, for some reason the qcom_smd-regulator driver allows a much larger range for the same hardware component. This could be simply a typo, since the start of the range is essentially just missing a '1'. In practice this does not cause any major problems, since the driver just sends the actual voltage to the RPM firmware instead of making use of the incorrect voltage selector. Still, having the wrong range there is confusing and prevents the regulator core from validating requests correctly. [1]: https://developer.qualcomm.com/download/sd410/pm8916pm8916-1-power-management-ic-device-specification.pdf Fixes: 57d6567680ed ("regulator: qcom-smd: Add PM8916 support") Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20220623094614.1410180-2-stephan.gerhold@kernkonzept.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_smd-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index ef6e47d025ca..60f3513f7038 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -357,10 +357,10 @@ static const struct regulator_desc pm8941_switch = { static const struct regulator_desc pm8916_pldo = { .linear_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(750000, 0, 208, 12500), + REGULATOR_LINEAR_RANGE(1750000, 0, 127, 12500), }, .n_linear_ranges = 1, - .n_voltages = 209, + .n_voltages = 128, .ops = &rpm_smps_ldo_ops, }; From 8cbb948a7cc2875d09234e2ce0424bc501c370b9 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 23 Jun 2022 11:46:13 +0200 Subject: [PATCH 16/35] regulator: dt-bindings: qcom,smd-rpm: Add PM8909 Document the "qcom,rpm-pm8909-regulators" compatible for describing the regulators available in the PM8909 PMIC (controlled via the RPM firmware). PM8909 is very similar to the existing PM8916 but lacks 3 of the regulators (s3, s4 and l16). Signed-off-by: Stephan Gerhold Acked-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220623094614.1410180-3-stephan.gerhold@kernkonzept.com Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml index 6a9a7eed466f..c233461cc980 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,smd-rpm-regulator.yaml @@ -30,6 +30,9 @@ description: For pm8841, s1, s2, s3, s4, s5, s6, s7, s8 + For pm8909, s1, s2, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, + l14, l15, l17, l18 + For pm8916, s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18 @@ -78,6 +81,7 @@ properties: - qcom,rpm-mp5496-regulators - qcom,rpm-pm8226-regulators - qcom,rpm-pm8841-regulators + - qcom,rpm-pm8909-regulators - qcom,rpm-pm8916-regulators - qcom,rpm-pm8941-regulators - qcom,rpm-pm8950-regulators From bc4d193238be4ef8ecee1ba0e0371169ad448c31 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Thu, 23 Jun 2022 11:46:14 +0200 Subject: [PATCH 17/35] regulator: qcom_smd: Add PM8909 RPM regulators The set of regulators available in the PM8909 PMIC is similar to PM8916 which is already supported by the driver. s3, s4 and l16 are missing. However, probing the SPMI hardware identification registers using the qcom_spmi-regulator driver reveals that the regulators in PM8909 are actually some kind of mixture between PM8916 and PM8226: - ult_lo_smps (= pm8916_buck_lvo_smps): s1 - ult_ho_smps (= pm8916_buck_hvo_smps): s2 - ult_nldo (= pm8916_nldo): l1, l2, l3, l10 - ult_pldo (= pm8916_pldo): l4, l8, l9, l12-l15, l17, l18 - pldo (= pm8226_pldo): l5, l6, l7, l11 Use this mapping to add the rpm_regulator_data for PM8909 by reusing the existing regulator definitions. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20220623094614.1410180-4-stephan.gerhold@kernkonzept.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_smd-regulator.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/regulator/qcom_smd-regulator.c b/drivers/regulator/qcom_smd-regulator.c index 60f3513f7038..6b8cf7073e9b 100644 --- a/drivers/regulator/qcom_smd-regulator.c +++ b/drivers/regulator/qcom_smd-regulator.c @@ -783,6 +783,29 @@ static const struct rpm_regulator_data rpm_pm8841_regulators[] = { {} }; +static const struct rpm_regulator_data rpm_pm8909_regulators[] = { + { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8916_buck_lvo_smps, "vdd_s1" }, + { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8916_buck_hvo_smps, "vdd_s2" }, + { "l1", QCOM_SMD_RPM_LDOA, 1, &pm8916_nldo, "vdd_l1" }, + { "l2", QCOM_SMD_RPM_LDOA, 2, &pm8916_nldo, "vdd_l2_l5" }, + { "l3", QCOM_SMD_RPM_LDOA, 3, &pm8916_nldo, "vdd_l3_l6_l10" }, + { "l4", QCOM_SMD_RPM_LDOA, 4, &pm8916_pldo, "vdd_l4_l7" }, + { "l5", QCOM_SMD_RPM_LDOA, 5, &pm8226_pldo, "vdd_l2_l5" }, + { "l6", QCOM_SMD_RPM_LDOA, 6, &pm8226_pldo, "vdd_l3_l6_l10" }, + { "l7", QCOM_SMD_RPM_LDOA, 7, &pm8226_pldo, "vdd_l4_l7" }, + { "l8", QCOM_SMD_RPM_LDOA, 8, &pm8916_pldo, "vdd_l8_l11_l15_l18" }, + { "l9", QCOM_SMD_RPM_LDOA, 9, &pm8916_pldo, "vdd_l9_l12_l14_l17" }, + { "l10", QCOM_SMD_RPM_LDOA, 10, &pm8916_nldo, "vdd_l3_l6_l10" }, + { "l11", QCOM_SMD_RPM_LDOA, 11, &pm8226_pldo, "vdd_l8_l11_l15_l18" }, + { "l12", QCOM_SMD_RPM_LDOA, 12, &pm8916_pldo, "vdd_l9_l12_l14_l17" }, + { "l13", QCOM_SMD_RPM_LDOA, 13, &pm8916_pldo, "vdd_l13" }, + { "l14", QCOM_SMD_RPM_LDOA, 14, &pm8916_pldo, "vdd_l9_l12_l14_l17" }, + { "l15", QCOM_SMD_RPM_LDOA, 15, &pm8916_pldo, "vdd_l8_l11_l15_l18" }, + { "l17", QCOM_SMD_RPM_LDOA, 17, &pm8916_pldo, "vdd_l9_l12_l14_l17" }, + { "l18", QCOM_SMD_RPM_LDOA, 18, &pm8916_pldo, "vdd_l8_l11_l15_l18" }, + {} +}; + static const struct rpm_regulator_data rpm_pm8916_regulators[] = { { "s1", QCOM_SMD_RPM_SMPA, 1, &pm8916_buck_lvo_smps, "vdd_s1" }, { "s2", QCOM_SMD_RPM_SMPA, 2, &pm8916_buck_lvo_smps, "vdd_s2" }, @@ -1221,6 +1244,7 @@ static const struct rpm_regulator_data rpm_pm2250_regulators[] = { static const struct of_device_id rpm_of_match[] = { { .compatible = "qcom,rpm-mp5496-regulators", .data = &rpm_mp5496_regulators }, { .compatible = "qcom,rpm-pm8841-regulators", .data = &rpm_pm8841_regulators }, + { .compatible = "qcom,rpm-pm8909-regulators", .data = &rpm_pm8909_regulators }, { .compatible = "qcom,rpm-pm8916-regulators", .data = &rpm_pm8916_regulators }, { .compatible = "qcom,rpm-pm8226-regulators", .data = &rpm_pm8226_regulators }, { .compatible = "qcom,rpm-pm8941-regulators", .data = &rpm_pm8941_regulators }, From a9e37a828ca069802b2f817e3cbf791b3806c97c Mon Sep 17 00:00:00 2001 From: Liang He Date: Wed, 22 Jun 2022 11:48:16 +0800 Subject: [PATCH 18/35] regulator: scmi: Add missing of_node_get() In scmi_regulator_probe(), of_find_node_by_name() will decrease the refcount of its first argument and we need a of_node_get() to keep reference balance. Signed-off-by: Liang He Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20220622034816.4094043-1-windhl@126.com Signed-off-by: Mark Brown --- drivers/regulator/scmi-regulator.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/scmi-regulator.c b/drivers/regulator/scmi-regulator.c index 41ae7ac27ff6..b9918f4fd241 100644 --- a/drivers/regulator/scmi-regulator.c +++ b/drivers/regulator/scmi-regulator.c @@ -343,6 +343,7 @@ static int scmi_regulator_probe(struct scmi_device *sdev) * plausible SCMI Voltage Domain number, all belonging to this SCMI * platform instance node (handle->dev->of_node). */ + of_node_get(handle->dev->of_node); np = of_find_node_by_name(handle->dev->of_node, "regulators"); for_each_child_of_node(np, child) { ret = process_scmi_regulator_of_node(sdev, ph, child, rinfo); From 38493f008deb435577361d4c4cdd69f7bb30f4b9 Mon Sep 17 00:00:00 2001 From: Patrick Rudolph Date: Tue, 5 Jul 2022 14:22:41 +0200 Subject: [PATCH 19/35] regulator: max597x: Add support for max597x regulator max597x is hot swap controller. This regulator driver controls the same & also configures fault protection features supported by the chip. Signed-off-by: Patrick Rudolph Signed-off-by: Marcello Sylvester Bauer Signed-off-by: Naresh Solanki Link: https://lore.kernel.org/r/20220705122244.472894-4-Naresh.Solanki@9elements.com Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/max597x-regulator.c | 506 ++++++++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 drivers/regulator/max597x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 03e8a4ef2ed6..aedc4cba9240 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -546,6 +546,16 @@ config REGULATOR_MAX1586 regulator via I2C bus. The provided regulator is suitable for PXA27x chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX597X + tristate "Maxim 597x power switch and monitor" + depends on I2C + depends on OF + select MFD_MAX597X + help + This driver controls a Maxim 5970/5978 switch via I2C bus. + The MAX5970/5978 is a smart switch with no output regulation, but + fault protection and voltage and current monitoring capabilities. + config REGULATOR_MAX77620 tristate "Maxim 77620/MAX20024 voltage regulator" depends on MFD_MAX77620 || COMPILE_TEST diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index a9c392587120..fa49bb6cc544 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_REGULATOR_LTC3589) += ltc3589.o obj-$(CONFIG_REGULATOR_LTC3676) += ltc3676.o obj-$(CONFIG_REGULATOR_MAX14577) += max14577-regulator.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o +obj-$(CONFIG_REGULATOR_MAX597X) += max597x-regulator.o obj-$(CONFIG_REGULATOR_MAX77620) += max77620-regulator.o obj-$(CONFIG_REGULATOR_MAX77650) += max77650-regulator.o obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o diff --git a/drivers/regulator/max597x-regulator.c b/drivers/regulator/max597x-regulator.c new file mode 100644 index 000000000000..f95acd1c5e3a --- /dev/null +++ b/drivers/regulator/max597x-regulator.c @@ -0,0 +1,506 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Device driver for regulators in MAX5970 and MAX5978 IC + * + * Copyright (c) 2022 9elements GmbH + * + * Author: Patrick Rudolph + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +struct max597x_regulator { + int num_switches, mon_rng, irng, shunt_micro_ohms, lim_uA; + struct regmap *regmap; +}; + +enum max597x_regulator_id { + MAX597X_SW0, + MAX597X_SW1, +}; + +static int max597x_uvp_ovp_check_mode(struct regulator_dev *rdev, int severity) +{ + int ret, reg; + + /* Status1 register contains the soft strap values sampled at POR */ + ret = regmap_read(rdev->regmap, MAX5970_REG_STATUS1, ®); + if (ret) + return ret; + + /* Check soft straps match requested mode */ + if (severity == REGULATOR_SEVERITY_PROT) { + if (STATUS1_PROT(reg) != STATUS1_PROT_SHUTDOWN) + return -EOPNOTSUPP; + + return 0; + } + if (STATUS1_PROT(reg) == STATUS1_PROT_SHUTDOWN) + return -EOPNOTSUPP; + + return 0; +} + +static int max597x_set_vp(struct regulator_dev *rdev, int lim_uV, int severity, + bool enable, bool overvoltage) +{ + int off_h, off_l, reg, ret; + struct max597x_regulator *data = rdev_get_drvdata(rdev); + int channel = rdev_get_id(rdev); + + if (overvoltage) { + if (severity == REGULATOR_SEVERITY_WARN) { + off_h = MAX5970_REG_CH_OV_WARN_H(channel); + off_l = MAX5970_REG_CH_OV_WARN_L(channel); + } else { + off_h = MAX5970_REG_CH_OV_CRIT_H(channel); + off_l = MAX5970_REG_CH_OV_CRIT_L(channel); + } + } else { + if (severity == REGULATOR_SEVERITY_WARN) { + off_h = MAX5970_REG_CH_UV_WARN_H(channel); + off_l = MAX5970_REG_CH_UV_WARN_L(channel); + } else { + off_h = MAX5970_REG_CH_UV_CRIT_H(channel); + off_l = MAX5970_REG_CH_UV_CRIT_L(channel); + } + } + + if (enable) + /* reg = ADC_MASK * (lim_uV / 1000000) / (data->mon_rng / 1000000) */ + reg = ADC_MASK * lim_uV / data->mon_rng; + else + reg = 0; + + ret = regmap_write(rdev->regmap, off_h, MAX5970_VAL2REG_H(reg)); + if (ret) + return ret; + + ret = regmap_write(rdev->regmap, off_l, MAX5970_VAL2REG_L(reg)); + if (ret) + return ret; + + return 0; +} + +static int max597x_set_uvp(struct regulator_dev *rdev, int lim_uV, int severity, + bool enable) +{ + int ret; + + /* + * MAX5970 has enable control as a special value in limit reg. Can't + * set limit but keep feature disabled or enable W/O given limit. + */ + if ((lim_uV && !enable) || (!lim_uV && enable)) + return -EINVAL; + + ret = max597x_uvp_ovp_check_mode(rdev, severity); + if (ret) + return ret; + + return max597x_set_vp(rdev, lim_uV, severity, enable, false); +} + +static int max597x_set_ovp(struct regulator_dev *rdev, int lim_uV, int severity, + bool enable) +{ + int ret; + + /* + * MAX5970 has enable control as a special value in limit reg. Can't + * set limit but keep feature disabled or enable W/O given limit. + */ + if ((lim_uV && !enable) || (!lim_uV && enable)) + return -EINVAL; + + ret = max597x_uvp_ovp_check_mode(rdev, severity); + if (ret) + return ret; + + return max597x_set_vp(rdev, lim_uV, severity, enable, true); +} + +static int max597x_set_ocp(struct regulator_dev *rdev, int lim_uA, + int severity, bool enable) +{ + int ret, val, reg; + unsigned int vthst, vthfst; + + struct max597x_regulator *data = rdev_get_drvdata(rdev); + int rdev_id = rdev_get_id(rdev); + /* + * MAX5970 doesn't has enable control for ocp. + * If limit is specified but enable is not set then hold the value in + * variable & later use it when ocp needs to be enabled. + */ + if (lim_uA != 0 && lim_uA != data->lim_uA) + data->lim_uA = lim_uA; + + if (severity != REGULATOR_SEVERITY_PROT) + return -EINVAL; + + if (enable) { + + /* Calc Vtrip threshold in uV. */ + vthst = + div_u64(mul_u32_u32(data->shunt_micro_ohms, data->lim_uA), + 1000000); + + /* + * As recommended in datasheed, add 20% margin to avoid + * spurious event & passive component tolerance. + */ + vthst = div_u64(mul_u32_u32(vthst, 120), 100); + + /* Calc fast Vtrip threshold in uV */ + vthfst = vthst * (MAX5970_FAST2SLOW_RATIO / 100); + + if (vthfst > data->irng) { + dev_err(&rdev->dev, "Current limit out of range\n"); + return -EINVAL; + } + /* Fast trip threshold to be programmed */ + val = div_u64(mul_u32_u32(0xFF, vthfst), data->irng); + } else + /* + * Since there is no option to disable ocp, set limit to max + * value + */ + val = 0xFF; + + reg = MAX5970_REG_DAC_FAST(rdev_id); + ret = regmap_write(rdev->regmap, reg, val); + + return ret; +} + +static int max597x_get_status(struct regulator_dev *rdev) +{ + int val, ret; + + ret = regmap_read(rdev->regmap, MAX5970_REG_STATUS3, &val); + if (ret) + return REGULATOR_FAILED_RETRY; + + if (val & MAX5970_STATUS3_ALERT) + return REGULATOR_STATUS_ERROR; + + ret = regulator_is_enabled_regmap(rdev); + if (ret < 0) + return ret; + + if (ret) + return REGULATOR_STATUS_ON; + + return REGULATOR_STATUS_OFF; +} + +static const struct regulator_ops max597x_switch_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, + .get_status = max597x_get_status, + .set_over_voltage_protection = max597x_set_ovp, + .set_under_voltage_protection = max597x_set_uvp, + .set_over_current_protection = max597x_set_ocp, +}; + +static int max597x_dt_parse(struct device_node *np, + const struct regulator_desc *desc, + struct regulator_config *cfg) +{ + struct max597x_regulator *data = cfg->driver_data; + int ret = 0; + + ret = + of_property_read_u32(np, "shunt-resistor-micro-ohms", + &data->shunt_micro_ohms); + if (ret < 0) + dev_err(cfg->dev, + "property 'shunt-resistor-micro-ohms' not found, err %d\n", + ret); + return ret; + +} + +#define MAX597X_SWITCH(_ID, _ereg, _chan, _supply) { \ + .name = #_ID, \ + .of_match = of_match_ptr(#_ID), \ + .ops = &max597x_switch_ops, \ + .regulators_node = of_match_ptr("regulators"), \ + .type = REGULATOR_VOLTAGE, \ + .id = MAX597X_##_ID, \ + .owner = THIS_MODULE, \ + .supply_name = _supply, \ + .enable_reg = _ereg, \ + .enable_mask = CHXEN((_chan)), \ + .of_parse_cb = max597x_dt_parse, \ +} + +static const struct regulator_desc regulators[] = { + MAX597X_SWITCH(SW0, MAX5970_REG_CHXEN, 0, "vss1"), + MAX597X_SWITCH(SW1, MAX5970_REG_CHXEN, 1, "vss2"), +}; + +static int max597x_regmap_read_clear(struct regmap *map, unsigned int reg, + unsigned int *val) +{ + int ret; + + ret = regmap_read(map, reg, val); + if (ret) + return ret; + + if (*val) + return regmap_write(map, reg, *val); + + return 0; +} + +static int max597x_irq_handler(int irq, struct regulator_irq_data *rid, + unsigned long *dev_mask) +{ + struct regulator_err_state *stat; + struct max597x_regulator *d = (struct max597x_regulator *)rid->data; + int val, ret, i; + + ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT0, &val); + if (ret) + return REGULATOR_FAILED_RETRY; + + *dev_mask = 0; + for (i = 0; i < d->num_switches; i++) { + stat = &rid->states[i]; + stat->notifs = 0; + stat->errors = 0; + } + + for (i = 0; i < d->num_switches; i++) { + stat = &rid->states[i]; + + if (val & UV_STATUS_CRIT(i)) { + *dev_mask |= 1 << i; + stat->notifs |= REGULATOR_EVENT_UNDER_VOLTAGE; + stat->errors |= REGULATOR_ERROR_UNDER_VOLTAGE; + } else if (val & UV_STATUS_WARN(i)) { + *dev_mask |= 1 << i; + stat->notifs |= REGULATOR_EVENT_UNDER_VOLTAGE_WARN; + stat->errors |= REGULATOR_ERROR_UNDER_VOLTAGE_WARN; + } + } + + ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT1, &val); + if (ret) + return REGULATOR_FAILED_RETRY; + + for (i = 0; i < d->num_switches; i++) { + stat = &rid->states[i]; + + if (val & OV_STATUS_CRIT(i)) { + *dev_mask |= 1 << i; + stat->notifs |= REGULATOR_EVENT_REGULATION_OUT; + stat->errors |= REGULATOR_ERROR_REGULATION_OUT; + } else if (val & OV_STATUS_WARN(i)) { + *dev_mask |= 1 << i; + stat->notifs |= REGULATOR_EVENT_OVER_VOLTAGE_WARN; + stat->errors |= REGULATOR_ERROR_OVER_VOLTAGE_WARN; + } + } + + ret = max597x_regmap_read_clear(d->regmap, MAX5970_REG_FAULT2, &val); + if (ret) + return REGULATOR_FAILED_RETRY; + + for (i = 0; i < d->num_switches; i++) { + stat = &rid->states[i]; + + if (val & OC_STATUS_WARN(i)) { + *dev_mask |= 1 << i; + stat->notifs |= REGULATOR_EVENT_OVER_CURRENT_WARN; + stat->errors |= REGULATOR_ERROR_OVER_CURRENT_WARN; + } + } + + ret = regmap_read(d->regmap, MAX5970_REG_STATUS0, &val); + if (ret) + return REGULATOR_FAILED_RETRY; + + for (i = 0; i < d->num_switches; i++) { + stat = &rid->states[i]; + + if ((val & MAX5970_CB_IFAULTF(i)) + || (val & MAX5970_CB_IFAULTS(i))) { + *dev_mask |= 1 << i; + stat->notifs |= + REGULATOR_EVENT_OVER_CURRENT | + REGULATOR_EVENT_DISABLE; + stat->errors |= + REGULATOR_ERROR_OVER_CURRENT | REGULATOR_ERROR_FAIL; + + /* Clear the sub-IRQ status */ + regulator_disable_regmap(stat->rdev); + } + } + return 0; +} + +static const struct regmap_config max597x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX_REGISTERS, +}; + +static int max597x_adc_range(struct regmap *regmap, const int ch, + u32 *irng, u32 *mon_rng) +{ + unsigned int reg; + int ret; + + /* Decode current ADC range */ + ret = regmap_read(regmap, MAX5970_REG_STATUS2, ®); + if (ret) + return ret; + switch (MAX5970_IRNG(reg, ch)) { + case 0: + *irng = 100000; /* 100 mV */ + break; + case 1: + *irng = 50000; /* 50 mV */ + break; + case 2: + *irng = 25000; /* 25 mV */ + break; + default: + return -EINVAL; + } + + /* Decode current voltage monitor range */ + ret = regmap_read(regmap, MAX5970_REG_MON_RANGE, ®); + if (ret) + return ret; + + *mon_rng = MAX5970_MON_MAX_RANGE_UV >> MAX5970_MON(reg, ch); + + return 0; +} + +static int max597x_setup_irq(struct device *dev, + int irq, + struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES], + int num_switches, struct max597x_regulator *data) +{ + struct regulator_irq_desc max597x_notif = { + .name = "max597x-irq", + .map_event = max597x_irq_handler, + .data = data, + }; + int errs = REGULATOR_ERROR_UNDER_VOLTAGE | + REGULATOR_ERROR_UNDER_VOLTAGE_WARN | + REGULATOR_ERROR_OVER_VOLTAGE_WARN | + REGULATOR_ERROR_REGULATION_OUT | + REGULATOR_ERROR_OVER_CURRENT | + REGULATOR_ERROR_OVER_CURRENT_WARN | REGULATOR_ERROR_FAIL; + void *irq_helper; + + /* Register notifiers - can fail if IRQ is not given */ + irq_helper = devm_regulator_irq_helper(dev, &max597x_notif, + irq, 0, errs, NULL, + &rdevs[0], num_switches); + if (IS_ERR(irq_helper)) { + if (PTR_ERR(irq_helper) == -EPROBE_DEFER) + return -EPROBE_DEFER; + + dev_warn(dev, "IRQ disabled %pe\n", irq_helper); + } + + return 0; +} + +static int max597x_regulator_probe(struct platform_device *pdev) +{ + + + struct max597x_data *max597x = dev_get_drvdata(pdev->dev.parent); + struct max597x_regulator *data; + + struct regulator_config config = { }; + struct regulator_dev *rdev; + struct regulator_dev *rdevs[MAX5970_NUM_SWITCHES]; + int num_switches = max597x->num_switches; + int ret, i; + + for (i = 0; i < num_switches; i++) { + data = + devm_kzalloc(max597x->dev, sizeof(struct max597x_regulator), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->num_switches = num_switches; + data->regmap = max597x->regmap; + + if (ret < 0) + return ret; + + ret = max597x_adc_range(data->regmap, i, &max597x->irng[i], &max597x->mon_rng[i]); + if (ret < 0) + return ret; + + data->irng = max597x->irng[i]; + data->mon_rng = max597x->mon_rng[i]; + + config.dev = max597x->dev; + config.driver_data = (void *)data; + config.regmap = data->regmap; + rdev = devm_regulator_register(max597x->dev, + ®ulators[i], &config); + if (IS_ERR(rdev)) { + dev_err(max597x->dev, "failed to register regulator %s\n", + regulators[i].name); + return PTR_ERR(rdev); + } + rdevs[i] = rdev; + max597x->shunt_micro_ohms[i] = data->shunt_micro_ohms; + } + + if (max597x->irq) { + ret = + max597x_setup_irq(max597x->dev, max597x->irq, rdevs, num_switches, + data); + if (ret) { + dev_err(max597x->dev, "IRQ setup failed"); + return ret; + } + } + + return ret; +} + +static struct platform_driver max597x_regulator_driver = { + .driver = { + .name = "max597x-regulator", + }, + .probe = max597x_regulator_probe, +}; + +module_platform_driver(max597x_regulator_driver); + + +MODULE_AUTHOR("Patrick Rudolph "); +MODULE_DESCRIPTION("MAX5970_hot-swap controller driver"); +MODULE_LICENSE("GPL v2"); From d08412328e4ff4928a60438e62e34e67f687a740 Mon Sep 17 00:00:00 2001 From: Zhang Jiaming Date: Tue, 5 Jul 2022 15:14:45 +0800 Subject: [PATCH 20/35] regulator: Fix parameter declaration and spelling mistake. Use Complete data type declaration of 'sel' in ti_abb_set_voltage_sel(). Fix spelling of 'are'nt' in comments. Signed-off-by: Zhang Jiaming Link: https://lore.kernel.org/r/20220705071445.21124-1-jiaming@nfschina.com Signed-off-by: Mark Brown --- drivers/regulator/ti-abb-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index bd7b2f287250..afa336be1cc9 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -309,7 +309,7 @@ out: * * Return: 0 on success or appropriate error value when fails */ -static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) +static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) { const struct regulator_desc *desc = rdev->desc; struct ti_abb *abb = rdev_get_drvdata(rdev); @@ -344,7 +344,7 @@ static int ti_abb_set_voltage_sel(struct regulator_dev *rdev, unsigned sel) info = &abb->info[sel]; /* - * When Linux kernel is starting up, we are'nt sure of the + * When Linux kernel is starting up, we aren't sure of the * Bias configuration that bootloader has configured. * So, we get to know the actual setting the first time * we are asked to transition. From 79152fc74ff664e2912abe17f21c42d7c49c90c9 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 7 Jul 2022 12:17:53 +0100 Subject: [PATCH 21/35] regulator: Fix MFD_MAX597X dependency Drivers should depend on rather than select their MFDs. Reported-by: Stephen Rothwell Signed-off-by: Mark Brown Link: https://lore.kernel.org/r/20220707111753.16581-1-broonie@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index aedc4cba9240..23e3e4a35cc9 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -550,7 +550,7 @@ config REGULATOR_MAX597X tristate "Maxim 597x power switch and monitor" depends on I2C depends on OF - select MFD_MAX597X + depends on MFD_MAX597X help This driver controls a Maxim 5970/5978 switch via I2C bus. The MAX5970/5978 is a smart switch with no output regulation, but From 3fec90048d3757fa4cedb598ff93d1cae23860e4 Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Mon, 11 Jul 2022 11:40:11 +0800 Subject: [PATCH 22/35] regulator: max597x: Remove unused including The patch makes sense but these are not compile warnings. They come from scripts/checkversion.pl, which can be called by 'make versioncheck', so I suppose that something in your build system is running 'make versioncheck'. Eliminate the follow versioncheck warning: ./drivers/regulator/max597x-regulator.c: 21 linux/version.h not needed. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Link: https://lore.kernel.org/r/20220711034011.46096-1-jiapeng.chong@linux.alibaba.com Signed-off-by: Mark Brown --- drivers/regulator/max597x-regulator.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/regulator/max597x-regulator.c b/drivers/regulator/max597x-regulator.c index f95acd1c5e3a..5e09aa4213be 100644 --- a/drivers/regulator/max597x-regulator.c +++ b/drivers/regulator/max597x-regulator.c @@ -18,7 +18,6 @@ #include #include #include -#include #include #include From 00f6ebbd0177a4cb15b353bbd4eaee6372fdbbc2 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 4 Jul 2022 23:23:54 +0200 Subject: [PATCH 23/35] regulator: qcom_spmi: add support for HT_P150 HT_P150 is a LDO PMOS regulator based on LV P150 using HFS430 layout found in PMP8074 and PMS405 PMIC-s. Both PMP8074 and PMS405 define the programmable range as 1.616V to 3.304V but the actual MAX output voltage depends on the exact LDO in each of the PMIC-s. It has a max current of 150mA, voltage step of 8mV. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220704212402.1715182-4-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_spmi-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 02bfce981150..38bbc70241ae 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -164,6 +164,7 @@ enum spmi_regulator_subtype { SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3 = 0x0f, SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10, SPMI_REGULATOR_SUBTYPE_HFS430 = 0x0a, + SPMI_REGULATOR_SUBTYPE_HT_P150 = 0x35, }; enum spmi_common_regulator_registers { @@ -544,6 +545,10 @@ static struct spmi_voltage_range hfs430_ranges[] = { SPMI_VOLTAGE_RANGE(0, 320000, 320000, 2040000, 2040000, 8000), }; +static struct spmi_voltage_range ht_p150_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 1616000, 1616000, 3304000, 3304000, 8000), +}; + static DEFINE_SPMI_SET_POINTS(pldo); static DEFINE_SPMI_SET_POINTS(nldo1); static DEFINE_SPMI_SET_POINTS(nldo2); @@ -564,6 +569,7 @@ static DEFINE_SPMI_SET_POINTS(nldo660); static DEFINE_SPMI_SET_POINTS(ht_lvpldo); static DEFINE_SPMI_SET_POINTS(ht_nldo); static DEFINE_SPMI_SET_POINTS(hfs430); +static DEFINE_SPMI_SET_POINTS(ht_p150); static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf, int len) @@ -1458,6 +1464,7 @@ static const struct regulator_ops spmi_hfs430_ops = { static const struct spmi_regulator_mapping supported_regulators[] = { /* type subtype dig_min dig_max ltype ops setpoints hpm_min */ + SPMI_VREG(LDO, HT_P150, 0, INF, HFS430, hfs430, ht_p150, 10000), SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000), SPMI_VREG(BUCK, HFS430, 0, INF, HFS430, hfs430, hfs430, 10000), SPMI_VREG(LDO, N300, 0, INF, LDO, ldo, nldo1, 10000), From 3d04ae8e3e916bc298b674613565d5b26cf1054a Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 4 Jul 2022 23:23:55 +0200 Subject: [PATCH 24/35] regulator: qcom_spmi: add support for HT_P600 HT_P600 is a LDO PMOS regulator based on LV P600 using HFS430 layout found in PMP8074 and PMS405 PMIC-s. Both PMP8074 and PMS405 define the programmable range as 1.704 to 1.896V but the actual MAX output voltage depends on the exact LDO in each of the PMIC-s. Their usual voltage that they are used is 1.8V. It has a max current of 600mA, voltage step of 8mV. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220704212402.1715182-5-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_spmi-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index 38bbc70241ae..ad9ad9f4be8e 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -165,6 +165,7 @@ enum spmi_regulator_subtype { SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10, SPMI_REGULATOR_SUBTYPE_HFS430 = 0x0a, SPMI_REGULATOR_SUBTYPE_HT_P150 = 0x35, + SPMI_REGULATOR_SUBTYPE_HT_P600 = 0x3d, }; enum spmi_common_regulator_registers { @@ -549,6 +550,10 @@ static struct spmi_voltage_range ht_p150_ranges[] = { SPMI_VOLTAGE_RANGE(0, 1616000, 1616000, 3304000, 3304000, 8000), }; +static struct spmi_voltage_range ht_p600_ranges[] = { + SPMI_VOLTAGE_RANGE(0, 1704000, 1704000, 1896000, 1896000, 8000), +}; + static DEFINE_SPMI_SET_POINTS(pldo); static DEFINE_SPMI_SET_POINTS(nldo1); static DEFINE_SPMI_SET_POINTS(nldo2); @@ -570,6 +575,7 @@ static DEFINE_SPMI_SET_POINTS(ht_lvpldo); static DEFINE_SPMI_SET_POINTS(ht_nldo); static DEFINE_SPMI_SET_POINTS(hfs430); static DEFINE_SPMI_SET_POINTS(ht_p150); +static DEFINE_SPMI_SET_POINTS(ht_p600); static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf, int len) @@ -1464,6 +1470,7 @@ static const struct regulator_ops spmi_hfs430_ops = { static const struct spmi_regulator_mapping supported_regulators[] = { /* type subtype dig_min dig_max ltype ops setpoints hpm_min */ + SPMI_VREG(LDO, HT_P600, 0, INF, HFS430, hfs430, ht_p600, 10000), SPMI_VREG(LDO, HT_P150, 0, INF, HFS430, hfs430, ht_p150, 10000), SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000), SPMI_VREG(BUCK, HFS430, 0, INF, HFS430, hfs430, hfs430, 10000), From 34ceb6a6ef87cda7629fd4ebe0074d9b5c7613d9 Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 4 Jul 2022 23:23:57 +0200 Subject: [PATCH 25/35] regulator: qcom_spmi: add support for PMP8074 regulators PMP8074 is a companion PMIC for the Qualcomm IPQ8074 WiSoC-s. It features 5 HF-SMPS and 13 LDO regulators. HF-SMPS regulators are Buck HFS430 regulators. L1, L2 and L3 are HT_N1200_ST subtype LDO regulators. L4 is HT_N300_ST subtype LDO regulator. L5 and L6 are HT_P600 subtype LDO regulators. L7, L11, L12 and L13 are HT_P150 subtype LDO regulators. L10 is HT_P50 subtype LDO regulator. This commit adds support for all of the buck regulators and LDO-s except for L10 as I dont have documentation on its output voltage range. S3 is the CPU cluster voltage supply, S4 supplies the UBI32 NPU cores and L11 is the SDIO/eMMC I/O voltage regulator required for high speeds. Signed-off-by: Robert Marko Link: https://lore.kernel.org/r/20220704212402.1715182-7-robimarko@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/qcom_spmi-regulator.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index ad9ad9f4be8e..a2d0292a92fd 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -2139,6 +2139,28 @@ static const struct spmi_regulator_data pm8005_regulators[] = { { } }; +static const struct spmi_regulator_data pmp8074_regulators[] = { + { "s1", 0x1400, "vdd_s1"}, + { "s2", 0x1700, "vdd_s2"}, + { "s3", 0x1a00, "vdd_s3"}, + { "s4", 0x1d00, "vdd_s4"}, + { "s5", 0x2000, "vdd_s5"}, + { "l1", 0x4000, "vdd_l1_l2"}, + { "l2", 0x4100, "vdd_l1_l2"}, + { "l3", 0x4200, "vdd_l3_l8"}, + { "l4", 0x4300, "vdd_l4"}, + { "l5", 0x4400, "vdd_l5_l6_l15"}, + { "l6", 0x4500, "vdd_l5_l6_l15"}, + { "l7", 0x4600, "vdd_l7"}, + { "l8", 0x4700, "vdd_l3_l8"}, + { "l9", 0x4800, "vdd_l9"}, + /* l10 is currently unsupported HT_P50 */ + { "l11", 0x4a00, "vdd_l10_l11_l12_l13"}, + { "l12", 0x4b00, "vdd_l10_l11_l12_l13"}, + { "l13", 0x4c00, "vdd_l10_l11_l12_l13"}, + { } +}; + static const struct spmi_regulator_data pms405_regulators[] = { { "s3", 0x1a00, "vdd_s3"}, { } @@ -2156,6 +2178,7 @@ static const struct of_device_id qcom_spmi_regulator_match[] = { { .compatible = "qcom,pmi8994-regulators", .data = &pmi8994_regulators }, { .compatible = "qcom,pm660-regulators", .data = &pm660_regulators }, { .compatible = "qcom,pm660l-regulators", .data = &pm660l_regulators }, + { .compatible = "qcom,pmp8074-regulators", .data = &pmp8074_regulators }, { .compatible = "qcom,pms405-regulators", .data = &pms405_regulators }, { } }; From 0b3bbd7646b03920e81efa376dee08f5b288c05e Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 4 Jul 2022 23:23:53 +0200 Subject: [PATCH 26/35] regulator: qcom,spmi-regulator: Convert to dtschema Convert the bindings of Qualcomm SPMI regulators to DT schema. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220704212402.1715182-3-robimarko@gmail.com Signed-off-by: Mark Brown --- .../regulator/qcom,spmi-regulator.txt | 347 ------------------ .../regulator/qcom,spmi-regulator.yaml | 307 ++++++++++++++++ 2 files changed, 307 insertions(+), 347 deletions(-) delete mode 100644 Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt create mode 100644 Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt deleted file mode 100644 index c2a39b121b1b..000000000000 --- a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt +++ /dev/null @@ -1,347 +0,0 @@ -Qualcomm SPMI Regulators - -- compatible: - Usage: required - Value type: - Definition: must be one of: - "qcom,pm8004-regulators" - "qcom,pm8005-regulators" - "qcom,pm8226-regulators" - "qcom,pm8841-regulators" - "qcom,pm8916-regulators" - "qcom,pm8941-regulators" - "qcom,pm8950-regulators" - "qcom,pm8994-regulators" - "qcom,pmi8994-regulators" - "qcom,pm660-regulators" - "qcom,pm660l-regulators" - "qcom,pms405-regulators" - -- interrupts: - Usage: optional - Value type: - Definition: List of OCP interrupts. - -- interrupt-names: - Usage: required if 'interrupts' property present - Value type: - Definition: List of strings defining the names of the - interrupts in the 'interrupts' property 1-to-1. - Supported values are "ocp-", where - corresponds to a voltage switch - type regulator. - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_s5-supply: -- vdd_s6-supply: -- vdd_s7-supply: -- vdd_s8-supply: - Usage: optional (pm8841 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_l1_l3-supply: -- vdd_l2-supply: -- vdd_l4_l5_l6-supply: -- vdd_l7-supply: -- vdd_l8_l11_l14_l15_l16-supply: -- vdd_l9_l10_l12_l13_l17_l18-supply: - Usage: optional (pm8916 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_l1_l3-supply: -- vdd_l2_lvs_1_2_3-supply: -- vdd_l4_l11-supply: -- vdd_l5_l7-supply: -- vdd_l6_l12_l14_l15-supply: -- vdd_l8_l16_l18_19-supply: -- vdd_l9_l10_l17_l22-supply: -- vdd_l13_l20_l23_l24-supply: -- vdd_l21-supply: -- vin_5vs-supply: - Usage: optional (pm8941 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_s4-supply: -- vdd_s5-supply: -- vdd_s6-supply: -- vdd_l1_l19-supply: -- vdd_l2_l23-supply: -- vdd_l3-supply: -- vdd_l4_l5_l6_l7_l16-supply: -- vdd_l8_l11_l12_l17_l22-supply: -- vdd_l9_l10_l13_l14_l15_l18-supply: -- vdd_l20-supply: -- vdd_l21-supply: - Usage: optional (pm8950 only) - Value type: - Definition: reference to regulator supplying the input pin, as - described in the data sheet - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_s5-supply: -- vdd_s6-supply: -- vdd_s7-supply: -- vdd_s8-supply: -- vdd_s9-supply: -- vdd_s10-supply: -- vdd_s11-supply: -- vdd_s12-supply: -- vdd_l1-supply: -- vdd_l2_l26_l28-supply: -- vdd_l3_l11-supply: -- vdd_l4_l27_l31-supply: -- vdd_l5_l7-supply: -- vdd_l6_l12_l32-supply: -- vdd_l8_l16_l30-supply: -- vdd_l9_l10_l18_l22-supply: -- vdd_l13_l19_l23_l24-supply: -- vdd_l14_l15-supply: -- vdd_l17_l29-supply: -- vdd_l20_l21-supply: -- vdd_l25-supply: -- vdd_lvs_1_2-supply: - Usage: optional (pm8994 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_l1-supply: - Usage: optional (pmi8994 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_l1_l6_l7-supply: -- vdd_l2_l3-supply: -- vdd_l5-supply: -- vdd_l8_l9_l10_l11_l12_l13_l14-supply: -- vdd_l15_l16_l17_l18_l19-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s5-supply: -- vdd_s6-supply: - Usage: optional (pm660 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_l1_l9_l10-supply: -- vdd_l2-supply: -- vdd_l3_l5_l7_l8-supply: -- vdd_l4_l6-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_s5-supply: - Usage: optional (pm660l only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- vdd_l1_l2-supply: -- vdd_l3_l8-supply: -- vdd_l4-supply: -- vdd_l5_l6-supply: -- vdd_l10_l11_l12_l13-supply: -- vdd_l7-supply: -- vdd_l9-supply: -- vdd_s1-supply: -- vdd_s2-supply: -- vdd_s3-supply: -- vdd_s4-supply: -- vdd_s5-supply - Usage: optional (pms405 only) - Value type: - Definition: Reference to regulator supplying the input pin, as - described in the data sheet. - -- qcom,saw-reg: - Usage: optional - Value type: - Description: Reference to syscon node defining the SAW registers. - - -The regulator node houses sub-nodes for each regulator within the device. Each -sub-node is identified using the node's name, with valid values listed for each -of the PMICs below. - -pm8004: - s2, s5 - -pm8005: - s1, s2, s3, s4 - -pm8841: - s1, s2, s3, s4, s5, s6, s7, s8 - -pm8916: - s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, - l14, l15, l16, l17, l18 - -pm8941: - s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, - l14, l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3, - 5vs1, 5vs2 - -pm8994: - s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, l1, l2, l3, l4, l5, - l6, l7, l8, l9, l10, l11, l12, l13, l14, l15, l16, l17, l18, l19, l20, - l21, l22, l23, l24, l25, l26, l27, l28, l29, l30, l31, l32, lvs1, lvs2 - -pmi8994: - s1, s2, s3, l1 - -The content of each sub-node is defined by the standard binding for regulators - -see regulator.txt - with additional custom properties described below: - -- regulator-initial-mode: - Usage: optional - Value type: - Description: 2 = Set initial mode to auto mode (automatically select - between HPM and LPM); not available on boost type - regulators. - - 1 = Set initial mode to high power mode (HPM), also referred - to as NPM. HPM consumes more ground current than LPM, but - it can source significantly higher load current. HPM is not - available on boost type regulators. For voltage switch type - regulators, HPM implies that over current protection and - soft start are active all the time. - - 0 = Set initial mode to low power mode (LPM). - -- qcom,ocp-max-retries: - Usage: optional - Value type: - Description: Maximum number of times to try toggling a voltage switch - off and back on as a result of consecutive over current - events. - -- qcom,ocp-retry-delay: - Usage: optional - Value type: - Description: Time to delay in milliseconds between each voltage switch - toggle after an over current event takes place. - -- qcom,pin-ctrl-enable: - Usage: optional - Value type: - Description: Bit mask specifying which hardware pins should be used to - enable the regulator, if any; supported bits are: - 0 = ignore all hardware enable signals - BIT(0) = follow HW0_EN signal - BIT(1) = follow HW1_EN signal - BIT(2) = follow HW2_EN signal - BIT(3) = follow HW3_EN signal - -- qcom,pin-ctrl-hpm: - Usage: optional - Value type: - Description: Bit mask specifying which hardware pins should be used to - force the regulator into high power mode, if any; - supported bits are: - 0 = ignore all hardware enable signals - BIT(0) = follow HW0_EN signal - BIT(1) = follow HW1_EN signal - BIT(2) = follow HW2_EN signal - BIT(3) = follow HW3_EN signal - BIT(4) = follow PMIC awake state - -- qcom,vs-soft-start-strength: - Usage: optional - Value type: - Description: This property sets the soft start strength for voltage - switch type regulators; supported values are: - 0 = 0.05 uA - 1 = 0.25 uA - 2 = 0.55 uA - 3 = 0.75 uA - -- qcom,saw-slave: - Usage: optional - Value type: - Description: SAW controlled gang slave. Will not be configured. - -- qcom,saw-leader: - Usage: optional - Value type: - Description: SAW controlled gang leader. Will be configured as - SAW regulator. - -Example: - - regulators { - compatible = "qcom,pm8941-regulators"; - vdd_l1_l3-supply = <&s1>; - - s1: s1 { - regulator-min-microvolt = <1300000>; - regulator-max-microvolt = <1400000>; - }; - - ... - - l1: l1 { - regulator-min-microvolt = <1225000>; - regulator-max-microvolt = <1300000>; - }; - - .... - }; - -Example 2: - - saw3: syscon@9A10000 { - compatible = "syscon"; - reg = <0x9A10000 0x1000>; - }; - - ... - - spm-regulators { - compatible = "qcom,pm8994-regulators"; - qcom,saw-reg = <&saw3>; - s8 { - qcom,saw-slave; - }; - s9 { - qcom,saw-slave; - }; - s10 { - qcom,saw-slave; - }; - pm8994_s11_saw: s11 { - qcom,saw-leader; - regulator-always-on; - regulator-min-microvolt = <900000>; - regulator-max-microvolt = <1140000>; - }; - }; diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml new file mode 100644 index 000000000000..06dc99b942e5 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml @@ -0,0 +1,307 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/regulator/qcom,spmi-regulator.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm SPMI Regulators + +maintainers: + - Robert Marko + +properties: + compatible: + enum: + - qcom,pm660-regulators + - qcom,pm660l-regulators + - qcom,pm8004-regulators + - qcom,pm8005-regulators + - qcom,pm8226-regulators + - qcom,pm8841-regulators + - qcom,pm8916-regulators + - qcom,pm8941-regulators + - qcom,pm8950-regulators + - qcom,pm8994-regulators + - qcom,pmi8994-regulators + - qcom,pms405-regulators + + qcom,saw-reg: + description: Reference to syscon node defining the SAW registers + $ref: /schemas/types.yaml#/definitions/phandle + +patternProperties: + "^(5vs[1-2]|(l|s)[1-9][0-9]?|lvs[1-3])$": + description: List of regulators and its properties + type: object + $ref: regulator.yaml# + + properties: + qcom,ocp-max-retries: + description: + Maximum number of times to try toggling a voltage switch off and + back on as a result of consecutive over current events + $ref: /schemas/types.yaml#/definitions/uint32 + + qcom,ocp-retry-delay: + description: + Time to delay in milliseconds between each voltage switch toggle + after an over current event takes place + $ref: /schemas/types.yaml#/definitions/uint32 + + qcom,pin-ctrl-enable: + description: + Bit mask specifying which hardware pins should be used to enable the + regulator, if any. + Supported bits are + 0 = ignore all hardware enable signals + BIT(0) = follow HW0_EN signal + BIT(1) = follow HW1_EN signal + BIT(2) = follow HW2_EN signal + BIT(3) = follow HW3_EN signal + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + + qcom,pin-ctrl-hpm: + description: + Bit mask specifying which hardware pins should be used to force the + regulator into high power mode, if any. + Supported bits are + 0 = ignore all hardware enable signals + BIT(0) = follow HW0_EN signal + BIT(1) = follow HW1_EN signal + BIT(2) = follow HW2_EN signal + BIT(3) = follow HW3_EN signal + BIT(4) = follow PMIC awake state + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 31 + + qcom,vs-soft-start-strength: + description: + This property sets the soft start strength for voltage switch type + regulators. + Supported values are + 0 = 0.05 uA + 1 = 0.25 uA + 2 = 0.55 uA + 3 = 0.75 uA + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 3 + + qcom,saw-slave: + description: SAW controlled gang slave. Will not be configured. + type: boolean + + qcom,saw-leader: + description: + SAW controlled gang leader. Will be configured as SAW regulator. + type: boolean + + unevaluatedProperties: false + +required: + - compatible + +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,pm660-regulators + then: + properties: + vdd_l15_l16_l17_l18_l19-supply: true + vdd_l1_l6_l7-supply: true + vdd_l2_l3-supply: true + vdd_l5-supply: true + vdd_l8_l9_l10_l11_l12_l13_l14-supply: true + patternProperties: + "^vdd_s[1-6]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm660l-regulators + then: + properties: + vdd_l1_l9_l10-supply: true + vdd_l2-supply: true + vdd_l3_l5_l7_l8-supply: true + vdd_l4_l6-supply: true + patternProperties: + "^vdd_s[1-5]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8004-regulators + then: + patternProperties: + "^vdd_s[25]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8005-regulators + then: + patternProperties: + "^vdd_s[1-4]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8226-regulators + then: + properties: + vdd_l10_l11_l13-supply: true + vdd_l12_l14-supply: true + vdd_l15_l16_l17_l18-supply: true + vdd_l19_l20_l21_l22_l23_l28-supply: true + vdd_l1_l2_l4_l5-supply: true + vdd_l25-supply: true + vdd_l3_l24_l26-supply: true + vdd_l6_l7_l8_l9_l27-supply: true + vdd_lvs1-supply: true + patternProperties: + "^vdd_s[1-5]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8841-regulators + then: + patternProperties: + "^vdd_s[1-8]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8916-regulators + then: + properties: + vdd_l1_l3-supply: true + vdd_l4_l5_l6-supply: true + vdd_l8_l11_l14_l15_l16-supply: true + vdd_l9_l10_l12_l13_l17_l18-supply: true + patternProperties: + "^vdd_l[27]-supply$": true + "^vdd_s[1-4]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8941-regulators + then: + properties: + interrupts: + items: + - description: Over-current protection interrupt for 5V S1 + - description: Over-current protection interrupt for 5V S2 + interrupt-names: + items: + - const: ocp-5vs1 + - const: ocp-5vs2 + vdd_l13_l20_l23_l24-supply: true + vdd_l1_l3-supply: true + vdd_l21-supply: true + vdd_l2_lvs_1_2_3-supply: true + vdd_l4_l11-supply: true + vdd_l5_l7-supply: true + vdd_l6_l12_l14_l15-supply: true + vdd_l8_l16_l18_19-supply: true + vdd_l9_l10_l17_l22-supply: true + vin_5vs-supply: true + patternProperties: + "^vdd_s[1-3]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8950-regulators + then: + properties: + vdd_l1_l19-supply: true + vdd_l20-supply: true + vdd_l21-supply: true + vdd_l2_l23-supply: true + vdd_l3-supply: true + vdd_l4_l5_l6_l7_l16-supply: true + vdd_l8_l11_l12_l17_l22-supply: true + vdd_l9_l10_l13_l14_l15_l18-supply: true + patternProperties: + "^vdd_s[1-6]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pm8994-regulators + then: + properties: + vdd_l1-supply: true + vdd_l13_l19_l23_l24-supply: true + vdd_l14_l15-supply: true + vdd_l17_l29-supply: true + vdd_l20_l21-supply: true + vdd_l25-supply: true + vdd_l2_l26_l28-supply: true + vdd_l3_l11-supply: true + vdd_l4_l27_l31-supply: true + vdd_l5_l7-supply: true + vdd_l6_l12_l32-supply: true + vdd_l8_l16_l30-supply: true + vdd_l9_l10_l18_l22-supply: true + vdd_lvs_1_2-supply: true + patternProperties: + "^vdd_s[1-9][0-2]?-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pmi8994-regulators + then: + properties: + vdd_l1-supply: true + patternProperties: + "^vdd_s[1-3]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pms405-regulators + then: + properties: + vdd_s3-supply: true + +unevaluatedProperties: false + +examples: + - | + regulators { + compatible = "qcom,pm8941-regulators"; + vdd_l1_l3-supply = <&s1>; + + s1: s1 { + regulator-min-microvolt = <1300000>; + regulator-max-microvolt = <1400000>; + }; + + l1: l1 { + regulator-min-microvolt = <1225000>; + regulator-max-microvolt = <1300000>; + }; + }; +... From 044750573903595765fa52ba9e1aadc397d591df Mon Sep 17 00:00:00 2001 From: Robert Marko Date: Mon, 4 Jul 2022 23:23:56 +0200 Subject: [PATCH 27/35] regulator: qcom,spmi-regulator: add PMP8074 PMIC Document the PMP8074 PMIC compatible. Signed-off-by: Robert Marko Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20220704212402.1715182-6-robimarko@gmail.com Signed-off-by: Mark Brown --- .../bindings/regulator/qcom,spmi-regulator.yaml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml index 06dc99b942e5..8b7c4af4b551 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.yaml @@ -23,6 +23,7 @@ properties: - qcom,pm8950-regulators - qcom,pm8994-regulators - qcom,pmi8994-regulators + - qcom,pmp8074-regulators - qcom,pms405-regulators qcom,saw-reg: @@ -276,6 +277,21 @@ allOf: vdd_l1-supply: true patternProperties: "^vdd_s[1-3]-supply$": true + - if: + properties: + compatible: + contains: + enum: + - qcom,pmp8074-regulators + then: + properties: + vdd_l10_l11_l12_l13-supply: true + vdd_l1_l2-supply: true + vdd_l3_l8-supply: true + vdd_l5_l6_l15-supply: true + patternProperties: + "^vdd_l[479]-supply$": true + "^vdd_s[1-5]-supply$": true - if: properties: compatible: From be6bd82351e8019eae1e289537529ff59b41b955 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 14 Jul 2022 18:12:12 +0800 Subject: [PATCH 28/35] regulator: max597x: Don't return uninitialized variable in .probe Remove the code checking and returning uninitialized variable. Signed-off-by: Axel Lin Link: https://lore.kernel.org/r/20220714101212.502824-1-axel.lin@ingics.com Signed-off-by: Mark Brown --- drivers/regulator/max597x-regulator.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/regulator/max597x-regulator.c b/drivers/regulator/max597x-regulator.c index 5e09aa4213be..03c6027682d8 100644 --- a/drivers/regulator/max597x-regulator.c +++ b/drivers/regulator/max597x-regulator.c @@ -453,9 +453,6 @@ static int max597x_regulator_probe(struct platform_device *pdev) data->num_switches = num_switches; data->regmap = max597x->regmap; - if (ret < 0) - return ret; - ret = max597x_adc_range(data->regmap, i, &max597x->irng[i], &max597x->mon_rng[i]); if (ret < 0) return ret; From 91de5eb17c100b3ab9192c36d99761e9da8536da Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 14 Jul 2022 12:25:33 +0100 Subject: [PATCH 29/35] regulator: pwm: Update Lee Jones' email address Going forward, I'll be using my kernel.org for upstream work. Signed-off-by: Lee Jones Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20220714112533.539910-9-lee@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/pwm-regulator.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml b/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml index 0332fec48a88..82b6f2fde422 100644 --- a/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/pwm-regulator.yaml @@ -8,7 +8,7 @@ title: Bindings for the Generic PWM Regulator maintainers: - Brian Norris - - Lee Jones + - Lee Jones - Alexandre Courbot description: | From 66efb665cd5ad69b27dca8571bf89fc6b9c628a4 Mon Sep 17 00:00:00 2001 From: Liang He Date: Fri, 15 Jul 2022 19:10:27 +0800 Subject: [PATCH 30/35] regulator: of: Fix refcount leak bug in of_get_regulation_constraints() We should call the of_node_put() for the reference returned by of_get_child_by_name() which has increased the refcount. Fixes: 40e20d68bb3f ("regulator: of: Add support for parsing regulator_state for suspend state") Signed-off-by: Liang He Link: https://lore.kernel.org/r/20220715111027.391032-1-windhl@126.com Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f54d4f176882..e12b681c72e5 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -264,8 +264,12 @@ static int of_get_regulation_constraints(struct device *dev, } suspend_np = of_get_child_by_name(np, regulator_states[i]); - if (!suspend_np || !suspend_state) + if (!suspend_np) continue; + if (!suspend_state) { + of_node_put(suspend_np); + continue; + } if (!of_property_read_u32(suspend_np, "regulator-mode", &pval)) { From 218320fec29430438016f88dd4fbebfa1b95ad8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kohlsch=C3=BCtter?= Date: Tue, 19 Jul 2022 16:02:00 +0200 Subject: [PATCH 31/35] regulator: core: Fix off-on-delay-us for always-on/boot-on regulators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regulators marked with "regulator-always-on" or "regulator-boot-on" as well as an "off-on-delay-us", may run into cycling issues that are hard to detect. This is caused by the "last_off" state not being initialized in this case. Fix the "last_off" initialization by setting it to the current kernel time upon initialization, regardless of always_on/boot_on state. Signed-off-by: Christian Kohlschütter Link: https://lore.kernel.org/r/FAFD5B39-E9C4-47C7-ACF1-2A04CD59758D@kohlschutter.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 1e54a833f2cf..398c8d6afd47 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1565,6 +1565,9 @@ static int set_machine_constraints(struct regulator_dev *rdev) rdev->constraints->always_on = true; } + if (rdev->desc->off_on_delay) + rdev->last_off = ktime_get(); + /* If the constraints say the regulator should be on at this point * and we have control then make sure it is enabled. */ @@ -1592,8 +1595,6 @@ static int set_machine_constraints(struct regulator_dev *rdev) if (rdev->constraints->always_on) rdev->use_count++; - } else if (rdev->desc->off_on_delay) { - rdev->last_off = ktime_get(); } print_constraints(rdev); From 4c7da3741804fc5631d94f4df24382ce6e88f02c Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Tue, 19 Jul 2022 15:50:09 -0600 Subject: [PATCH 32/35] regulator: Add missing type for 'regulator-microvolt-offset' 'regulator-microvolt-offset' is missing a type definition. The type should be 'uint32'. Signed-off-by: Rob Herring Link: https://lore.kernel.org/r/20220719215010.1875363-1-robh@kernel.org Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/regulator.yaml b/Documentation/devicetree/bindings/regulator/regulator.yaml index a9b66ececccf..6e8aa9eed3aa 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/regulator.yaml @@ -23,6 +23,7 @@ properties: regulator-microvolt-offset: description: Offset applied to voltages to compensate for voltage drops + $ref: "/schemas/types.yaml#/definitions/uint32" regulator-min-microamp: description: smallest current consumers may set From 9cc0590ae351a354c51375a1ee22edc2e4931fd0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 27 Jul 2022 13:26:37 +0200 Subject: [PATCH 33/35] regulator: mt6380: Fix unused array warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the following configuration options: CONFIG_OF is not set CONFIG_REGULATOR_MT6380=y we get the following build warning: CC drivers/regulator/mt6380-regulator.o drivers/regulator/mt6380-regulator.c:322:34: warning: ‘mt6380_of_match’ defined but not used [-Wunused-const-variable=] Fix this by annotating that array with __maybe_unused, as done in various regulator drivers. Signed-off-by: Jean Delvare Reported-by: kernel test robot Link: https://lore.kernel.org/all/202207240252.ZY5hSCNB-lkp@intel.com/ Cc: Liam Girdwood Cc: Mark Brown Cc: Matthias Brugger Cc: Chenglin Xu Link: https://lore.kernel.org/r/20220727132637.76d6073f@endymion.delvare Signed-off-by: Mark Brown --- drivers/regulator/mt6380-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/mt6380-regulator.c b/drivers/regulator/mt6380-regulator.c index 2e6b61d3b0cf..43234296df36 100644 --- a/drivers/regulator/mt6380-regulator.c +++ b/drivers/regulator/mt6380-regulator.c @@ -319,7 +319,7 @@ static const struct platform_device_id mt6380_platform_ids[] = { }; MODULE_DEVICE_TABLE(platform, mt6380_platform_ids); -static const struct of_device_id mt6380_of_match[] = { +static const struct of_device_id __maybe_unused mt6380_of_match[] = { { .compatible = "mediatek,mt6380-regulator", }, { /* sentinel */ }, }; From 6eabfc018e8d1033e7fc1efce30a872e2dccb537 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 26 Jul 2022 10:38:21 -0700 Subject: [PATCH 34/35] regulator: core: Allow specifying an initial load w/ the bulk API There are a number of drivers that follow a pattern that looks like this: 1. Use the regulator bulk API to get a bunch of regulators. 2. Set the load on each of the regulators to use whenever the regulators are enabled. Let's make this easier by just allowing the drivers to pass the load in. As part of this change we need to move the error printing in regulator_bulk_get() around; let's switch to the new dev_err_probe() to simplify it. Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20220726103631.v2.4.Ie85f68215ada39f502a96dcb8a1f3ad977e3f68a@changeid Signed-off-by: Mark Brown --- drivers/regulator/core.c | 20 ++++++++++++-------- include/linux/regulator/consumer.h | 12 ++++++++---- 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 1e54a833f2cf..17c476fc8adb 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -4783,22 +4783,26 @@ int regulator_bulk_get(struct device *dev, int num_consumers, consumers[i].consumer = regulator_get(dev, consumers[i].supply); if (IS_ERR(consumers[i].consumer)) { - ret = PTR_ERR(consumers[i].consumer); consumers[i].consumer = NULL; + ret = dev_err_probe(dev, PTR_ERR(consumers[i].consumer), + "Failed to get supply '%s'", + consumers[i].supply); goto err; } + + if (consumers[i].init_load_uA > 0) { + ret = regulator_set_load(consumers[i].consumer, + consumers[i].init_load_uA); + if (ret) { + i++; + goto err; + } + } } return 0; err: - if (ret != -EPROBE_DEFER) - dev_err(dev, "Failed to get supply '%s': %pe\n", - consumers[i].supply, ERR_PTR(ret)); - else - dev_dbg(dev, "Failed to get supply '%s', deferring\n", - consumers[i].supply); - while (--i >= 0) regulator_put(consumers[i].consumer); diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index bbf6590a6dec..5779f4466e62 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -171,10 +171,13 @@ struct regulator; /** * struct regulator_bulk_data - Data used for bulk regulator operations. * - * @supply: The name of the supply. Initialised by the user before - * using the bulk regulator APIs. - * @consumer: The regulator consumer for the supply. This will be managed - * by the bulk API. + * @supply: The name of the supply. Initialised by the user before + * using the bulk regulator APIs. + * @init_load_uA: After getting the regulator, regulator_set_load() will be + * called with this load. Initialised by the user before + * using the bulk regulator APIs. + * @consumer: The regulator consumer for the supply. This will be managed + * by the bulk API. * * The regulator APIs provide a series of regulator_bulk_() API calls as * a convenience to consumers which require multiple supplies. This @@ -182,6 +185,7 @@ struct regulator; */ struct regulator_bulk_data { const char *supply; + int init_load_uA; struct regulator *consumer; /* private: Internal use */ From 1de452a0edda26f1483d1d934f692eab13ba669a Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 26 Jul 2022 10:38:23 -0700 Subject: [PATCH 35/35] regulator: core: Allow drivers to define their init data as const Drivers tend to want to define the names of their regulators somewhere in their source file as "static const". This means, inevitable, that every driver out there open codes something like this: static const char * const supply_names[] = { "vcc", "vccl", }; static int get_regulators(struct my_data *data) { int i; data->supplies = devm_kzalloc(...) if (!data->supplies) return -ENOMEM; for (i = 0; i < ARRAY_SIZE(supply_names); i++) data->supplies[i].supply = supply_names[i]; return devm_regulator_bulk_get(data->dev, ARRAY_SIZE(supply_names), data->supplies); } Let's make this more convenient by doing providing a helper that does the copy. I have chosen to have the "const" input structure here be the exact same structure as the normal one passed to devm_regulator_bulk_get(). This is slightly inefficent since the input data can't possibly have anything useful for "ret" or consumer and thus we waste 8 bytes per structure. This seems an OK tradeoff for not introducing an extra structure. Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20220726103631.v2.6.I38fc508a73135a5c1b873851f3553ff2a3a625f5@changeid Signed-off-by: Mark Brown --- drivers/regulator/devres.c | 28 ++++++++++++++++++++++++++++ include/linux/regulator/consumer.h | 4 ++++ 2 files changed, 32 insertions(+) diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c index 9113233f41cd..32823a87fd40 100644 --- a/drivers/regulator/devres.c +++ b/drivers/regulator/devres.c @@ -166,6 +166,34 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, } EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); +/** + * devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data + * + * @dev: device to supply + * @num_consumers: number of consumers to register + * @in_consumers: const configuration of consumers + * @out_consumers: in_consumers is copied here and this is passed to + * devm_regulator_bulk_get(). + * + * This is a convenience function to allow bulk regulator configuration + * to be stored "static const" in files. + * + * Return: 0 on success, an errno on failure. + */ +int devm_regulator_bulk_get_const(struct device *dev, int num_consumers, + const struct regulator_bulk_data *in_consumers, + struct regulator_bulk_data **out_consumers) +{ + *out_consumers = devm_kmemdup(dev, in_consumers, + num_consumers * sizeof(*in_consumers), + GFP_KERNEL); + if (*out_consumers == NULL) + return -ENOMEM; + + return devm_regulator_bulk_get(dev, num_consumers, *out_consumers); +} +EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_const); + static void devm_rdev_release(struct device *dev, void *res) { regulator_unregister(*(struct regulator_dev **)res); diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index 5779f4466e62..bc6cda706d1f 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -244,6 +244,10 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, struct regulator_bulk_data *consumers); +int __must_check devm_regulator_bulk_get_const( + struct device *dev, int num_consumers, + const struct regulator_bulk_data *in_consumers, + struct regulator_bulk_data **out_consumers); int __must_check regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers); int regulator_bulk_disable(int num_consumers,