From c266209eaef4fef863363557817f7d6a68314321 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 24 May 2025 18:10:38 +0200 Subject: [PATCH 01/40] regulator: tps6594-regulator: Constify struct tps6594_regulator_irq_type 'struct tps6594_regulator_irq_type' are not modified in this driver. Constifying this structure moves some data to a read-only section, so increases overall security. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 25645 14480 64 40189 9cfd drivers/regulator/tps6594-regulator.o After: ===== text data bss dec hex filename 27949 12176 64 40189 9cfd drivers/regulator/tps6594-regulator.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/1446fb1938f3f38115be3e53f5dda3c8bb0ba5a1.1748103005.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 60 +++++++++++++-------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index ac53792e3fed..0193efb5dffa 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -56,7 +56,7 @@ struct tps6594_regulator_irq_type { unsigned long event; }; -static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { { TPS6594_IRQ_NAME_VCCA_OV, "VCCA", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_VCCA_UV, "VCCA", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_VMON1_OV, "VMON1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, @@ -69,7 +69,7 @@ static struct tps6594_regulator_irq_type tps6594_ext_regulator_irq_types[] = { REGULATOR_EVENT_OVER_VOLTAGE_WARN }, }; -static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { { TPS65224_IRQ_NAME_VCCA_UVOV, "VCCA", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, { TPS65224_IRQ_NAME_VMON1_UVOV, "VMON1", "voltage out of range", @@ -80,13 +80,13 @@ static struct tps6594_regulator_irq_type tps65224_ext_regulator_irq_types[] = { struct tps6594_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; struct regulator_dev *rdev; }; struct tps6594_ext_regulator_irq_data { struct device *dev; - struct tps6594_regulator_irq_type *type; + const struct tps6594_regulator_irq_type *type; }; #define TPS6594_REGULATOR(_name, _of, _id, _type, _ops, _n, _vr, _vm, _er, \ @@ -262,7 +262,7 @@ static const struct regulator_desc tps65224_buck_regs[] = { 4, 0, 0, NULL, 0, 0), }; -static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { { TPS6594_IRQ_NAME_BUCK1_OV, "BUCK1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK1_UV, "BUCK1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK1_SC, "BUCK1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -270,7 +270,7 @@ static struct tps6594_regulator_irq_type tps6594_buck1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { { TPS6594_IRQ_NAME_BUCK2_OV, "BUCK2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK2_UV, "BUCK2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK2_SC, "BUCK2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -278,7 +278,7 @@ static struct tps6594_regulator_irq_type tps6594_buck2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { { TPS6594_IRQ_NAME_BUCK3_OV, "BUCK3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK3_UV, "BUCK3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK3_SC, "BUCK3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -286,7 +286,7 @@ static struct tps6594_regulator_irq_type tps6594_buck3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { { TPS6594_IRQ_NAME_BUCK4_OV, "BUCK4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK4_UV, "BUCK4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK4_SC, "BUCK4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -294,7 +294,7 @@ static struct tps6594_regulator_irq_type tps6594_buck4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { { TPS6594_IRQ_NAME_BUCK5_OV, "BUCK5", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_BUCK5_UV, "BUCK5", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_BUCK5_SC, "BUCK5", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -302,7 +302,7 @@ static struct tps6594_regulator_irq_type tps6594_buck5_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { { TPS6594_IRQ_NAME_LDO1_OV, "LDO1", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO1_UV, "LDO1", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO1_SC, "LDO1", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -310,7 +310,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo1_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { { TPS6594_IRQ_NAME_LDO2_OV, "LDO2", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO2_UV, "LDO2", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO2_SC, "LDO2", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -318,7 +318,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo2_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { { TPS6594_IRQ_NAME_LDO3_OV, "LDO3", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO3_UV, "LDO3", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO3_SC, "LDO3", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -326,7 +326,7 @@ static struct tps6594_regulator_irq_type tps6594_ldo3_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { { TPS6594_IRQ_NAME_LDO4_OV, "LDO4", "overvoltage", REGULATOR_EVENT_OVER_VOLTAGE_WARN }, { TPS6594_IRQ_NAME_LDO4_UV, "LDO4", "undervoltage", REGULATOR_EVENT_UNDER_VOLTAGE }, { TPS6594_IRQ_NAME_LDO4_SC, "LDO4", "short circuit", REGULATOR_EVENT_REGULATION_OUT }, @@ -334,42 +334,42 @@ static struct tps6594_regulator_irq_type tps6594_ldo4_irq_types[] = { REGULATOR_EVENT_OVER_CURRENT }, }; -static struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck1_irq_types[] = { { TPS65224_IRQ_NAME_BUCK1_UVOV, "BUCK1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck2_irq_types[] = { { TPS65224_IRQ_NAME_BUCK2_UVOV, "BUCK2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck3_irq_types[] = { { TPS65224_IRQ_NAME_BUCK3_UVOV, "BUCK3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_buck4_irq_types[] = { { TPS65224_IRQ_NAME_BUCK4_UVOV, "BUCK4", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo1_irq_types[] = { { TPS65224_IRQ_NAME_LDO1_UVOV, "LDO1", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo2_irq_types[] = { { TPS65224_IRQ_NAME_LDO2_UVOV, "LDO2", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { +static const struct tps6594_regulator_irq_type tps65224_ldo3_irq_types[] = { { TPS65224_IRQ_NAME_LDO3_UVOV, "LDO3", "voltage out of range", REGULATOR_EVENT_REGULATION_OUT }, }; -static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck1_irq_types, tps6594_buck2_irq_types, tps6594_buck3_irq_types, @@ -377,21 +377,21 @@ static struct tps6594_regulator_irq_type *tps6594_bucks_irq_types[] = { tps6594_buck5_irq_types, }; -static struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps6594_ldos_irq_types[] = { tps6594_ldo1_irq_types, tps6594_ldo2_irq_types, tps6594_ldo3_irq_types, tps6594_ldo4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_bucks_irq_types[] = { tps65224_buck1_irq_types, tps65224_buck2_irq_types, tps65224_buck3_irq_types, tps65224_buck4_irq_types, }; -static struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { +static const struct tps6594_regulator_irq_type *tps65224_ldos_irq_types[] = { tps65224_ldo1_irq_types, tps65224_ldo2_irq_types, tps65224_ldo3_irq_types, @@ -516,11 +516,11 @@ static irqreturn_t tps6594_regulator_irq_handler(int irq, void *data) static int tps6594_request_reg_irqs(struct platform_device *pdev, struct regulator_dev *rdev, struct tps6594_regulator_irq_data *irq_data, - struct tps6594_regulator_irq_type *regs_irq_types, + const struct tps6594_regulator_irq_type *regs_irq_types, size_t interrupt_cnt, int *irq_idx) { - struct tps6594_regulator_irq_type *irq_type; + const struct tps6594_regulator_irq_type *irq_type; struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); size_t j; int irq; @@ -558,8 +558,8 @@ static int tps6594_regulator_probe(struct platform_device *pdev) struct regulator_config config = {}; struct tps6594_regulator_irq_data *irq_data; struct tps6594_ext_regulator_irq_data *irq_ext_reg_data; - struct tps6594_regulator_irq_type *irq_type; - struct tps6594_regulator_irq_type *irq_types; + const struct tps6594_regulator_irq_type *irq_type; + const struct tps6594_regulator_irq_type *irq_types; bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; @@ -573,9 +573,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) unsigned int irq_count; unsigned int multi_phase_cnt; size_t reg_irq_nb; - struct tps6594_regulator_irq_type **bucks_irq_types; + const struct tps6594_regulator_irq_type **bucks_irq_types; const struct regulator_desc *multi_regs; - struct tps6594_regulator_irq_type **ldos_irq_types; + const struct tps6594_regulator_irq_type **ldos_irq_types; const struct regulator_desc *ldo_regs; size_t interrupt_count; From 9bb3c7df546aac38ea64c736a839ef2c75297631 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sat, 24 May 2025 18:10:39 +0200 Subject: [PATCH 02/40] regulator: tps6594-regulator: Remove a useless static qualifier There is no point in having 'npname' a static variable. So remove the static qualifier. This is cleaner and saves a few bytes. On a x86_64, with allmodconfig: Before: ====== text data bss dec hex filename 27949 12176 64 40189 9cfd drivers/regulator/tps6594-regulator.o After: ===== text data bss dec hex filename 27947 12112 0 40059 9c7b drivers/regulator/tps6594-regulator.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/ebc53d4049ec19796ef07e1bb734de19a2814727.1748103005.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 0193efb5dffa..51264c869aa0 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -563,7 +563,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; - static const char *npname; + const char *npname; int error, i, irq, multi; int irq_idx = 0; int buck_idx = 0; From d7181a2d43cffb19f1e5c19f6d2328f190c87d70 Mon Sep 17 00:00:00 2001 From: Martijn de Gouw Date: Sun, 25 May 2025 09:18:20 +0200 Subject: [PATCH 03/40] dt-bindings: regulator: add pca9450: Add regulator-allowed-modes Make the PWM mode on the buck controllers configurable from devicetree. Some boards require forced PWM mode to keep the supply ripple within acceptable limits under light load conditions. Signed-off-by: Martijn de Gouw Acked-by: Conor Dooley Link: https://patch.msgid.link/20250525071823.819342-1-martijn.de.gouw@prodrive-technologies.com Signed-off-by: Mark Brown --- .../regulator/nxp,pca9450-regulator.yaml | 14 ++++++++++++++ .../regulator/nxp,pca9450-regulator.h | 18 ++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 include/dt-bindings/regulator/nxp,pca9450-regulator.h diff --git a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml index 4ffe5c3faea0..a5486c36830f 100644 --- a/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/nxp,pca9450-regulator.yaml @@ -100,6 +100,15 @@ properties: PMIC default "STANDBY" state voltage in uV. Only Buck1~3 have such dvs(dynamic voltage scaling) property. + regulator-allowed-modes: + description: | + Buck regulator operating modes allowed. Valid values below. + Users should use the macros from dt-bindings/regulator/nxp,pca9450-regulator.h + 0 (PCA9450_BUCK_MODE_AUTO): Auto PFM/PWM mode + 1 (PCA9450_BUCK_MODE_FORCE_PWM): Forced PWM mode + items: + enum: [ 0, 1 ] + unevaluatedProperties: false additionalProperties: false @@ -143,6 +152,7 @@ allOf: examples: - | #include + #include i2c { #address-cells = <1>; @@ -179,6 +189,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-initial-mode = ; + regulator-allowed-modes = ; }; buck5: BUCK5 { regulator-name = "BUCK5"; @@ -186,6 +198,8 @@ examples: regulator-max-microvolt = <3400000>; regulator-boot-on; regulator-always-on; + regulator-allowed-modes = ; }; buck6: BUCK6 { regulator-name = "BUCK6"; diff --git a/include/dt-bindings/regulator/nxp,pca9450-regulator.h b/include/dt-bindings/regulator/nxp,pca9450-regulator.h new file mode 100644 index 000000000000..08434caef429 --- /dev/null +++ b/include/dt-bindings/regulator/nxp,pca9450-regulator.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Device Tree binding constants for the NXP PCA9450A/B/C PMIC regulators + */ + +#ifndef _DT_BINDINGS_REGULATORS_NXP_PCA9450_H +#define _DT_BINDINGS_REGULATORS_NXP_PCA9450_H + +/* + * Buck mode constants which may be used in devicetree properties (eg. + * regulator-initial-mode, regulator-allowed-modes). + * See the manufacturer's datasheet for more information on these modes. + */ + +#define PCA9450_BUCK_MODE_AUTO 0 +#define PCA9450_BUCK_MODE_FORCE_PWM 1 + +#endif From 548d770c330cd1027549947a6ea899c56b5bc4e4 Mon Sep 17 00:00:00 2001 From: Martijn de Gouw Date: Sun, 25 May 2025 09:18:21 +0200 Subject: [PATCH 04/40] regulator: pca9450: Add support for mode operations Make the PWM mode on the buck controllers configurable from devicetree. Some boards require forced PWM mode to keep the supply ripple within acceptable limits under light load conditions. Signed-off-by: Martijn de Gouw Link: https://patch.msgid.link/20250525071823.819342-2-martijn.de.gouw@prodrive-technologies.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 120 +++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 4 deletions(-) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index 14d19a6d6655..f6faf14a9c53 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -17,12 +17,18 @@ #include #include #include +#include + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev); +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode); struct pc9450_dvs_config { unsigned int run_reg; /* dvs0 */ unsigned int run_mask; unsigned int standby_reg; /* dvs1 */ unsigned int standby_mask; + unsigned int mode_reg; /* ctrl */ + unsigned int mode_mask; }; struct pca9450_regulator_desc { @@ -80,6 +86,8 @@ static const struct regulator_ops pca9450_dvs_buck_regulator_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, .set_ramp_delay = regulator_set_ramp_delay_regmap, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_buck_regulator_ops = { @@ -90,6 +98,8 @@ static const struct regulator_ops pca9450_buck_regulator_ops = { .set_voltage_sel = regulator_set_voltage_sel_regmap, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_mode = pca9450_buck_set_mode, + .get_mode = pca9450_buck_get_mode, }; static const struct regulator_ops pca9450_ldo_regulator_ops = { @@ -285,7 +295,64 @@ static int pca9450_set_dvs_levels(struct device_node *np, return ret; } -static const struct pca9450_regulator_desc pca9450a_regulators[] = { +static inline unsigned int pca9450_map_mode(unsigned int mode) +{ + switch (mode) { + case PCA9450_BUCK_MODE_AUTO: + return REGULATOR_MODE_NORMAL; + case PCA9450_BUCK_MODE_FORCE_PWM: + return REGULATOR_MODE_FAST; + default: + return REGULATOR_MODE_INVALID; + } +} + +static int pca9450_buck_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int val; + + switch (mode) { + case REGULATOR_MODE_FAST: + val = dvs->mode_mask; + break; + case REGULATOR_MODE_NORMAL: + val = 0; + break; + default: + return -EINVAL; + } + + dev_dbg(&rdev->dev, "pca9450 buck set_mode %#x, %#x, %#x\n", + dvs->mode_reg, dvs->mode_mask, val); + + return regmap_update_bits(rdev->regmap, dvs->mode_reg, + dvs->mode_mask, val); +} + +static unsigned int pca9450_buck_get_mode(struct regulator_dev *rdev) +{ + struct pca9450_regulator_desc *desc = container_of(rdev->desc, + struct pca9450_regulator_desc, desc); + const struct pc9450_dvs_config *dvs = &desc->dvs; + int ret = 0, regval; + + ret = regmap_read(rdev->regmap, dvs->mode_reg, ®val); + if (ret != 0) { + dev_err(&rdev->dev, + "Failed to get pca9450 buck mode: %d\n", ret); + return ret; + } + + if ((regval & dvs->mode_mask) == dvs->mode_mask) + return REGULATOR_MODE_FAST; + + return REGULATOR_MODE_NORMAL; +} + +static struct pca9450_regulator_desc pca9450a_regulators[] = { { .desc = { .name = "buck1", @@ -308,12 +375,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -338,12 +408,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -368,12 +441,15 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK3OUT_DVS0, .run_mask = BUCK3OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK3OUT_DVS1, .standby_mask = BUCK3OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK3CTRL, + .mode_mask = BUCK3_FPWM, }, }, { @@ -393,6 +469,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -412,6 +493,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -431,6 +517,11 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -529,7 +620,7 @@ static const struct pca9450_regulator_desc pca9450a_regulators[] = { * Buck3 removed on PCA9450B and connected with Buck1 internal for dual phase * on PCA9450C as no Buck3. */ -static const struct pca9450_regulator_desc pca9450bc_regulators[] = { +static struct pca9450_regulator_desc pca9450bc_regulators[] = { { .desc = { .name = "buck1", @@ -552,12 +643,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -582,12 +676,15 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -607,6 +704,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -626,6 +728,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -645,6 +752,11 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { @@ -739,7 +851,7 @@ static const struct pca9450_regulator_desc pca9450bc_regulators[] = { }, }; -static const struct pca9450_regulator_desc pca9451a_regulators[] = { +static struct pca9450_regulator_desc pca9451a_regulators[] = { { .desc = { .name = "buck1", @@ -990,7 +1102,7 @@ static int pca9450_i2c_probe(struct i2c_client *i2c) { enum pca9450_chip_type type = (unsigned int)(uintptr_t) of_device_get_match_data(&i2c->dev); - const struct pca9450_regulator_desc *regulator_desc; + const struct pca9450_regulator_desc *regulator_desc; struct regulator_config config = { }; struct regulator_dev *ldo5; struct pca9450 *pca9450; From 6d09c6e474bd27a86352deaf73d02c8c21eeec7c Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Jun 2025 02:06:41 +0200 Subject: [PATCH 05/40] regulator: dt-bindings: rpi-panel: Add regulator for 7" Raspberry Pi 720x1280 Document regulator compatible string for the 7" Raspberry Pi 720x1280 DSI panel based on ili9881. This is the Raspberry Pi DSI Panel V2. The newer Raspberry Pi 5" and 7" panels have a slightly different register map to the original one and offers PWM backlight control. Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609000748.1665219-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- .../raspberrypi,7inch-touchscreen-panel-regulator.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml index 41678400e63f..18944d39d08f 100644 --- a/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/raspberrypi,7inch-touchscreen-panel-regulator.yaml @@ -12,14 +12,17 @@ maintainers: description: | The RaspberryPi 7" display has an ATTINY88-based regulator/backlight controller on the PCB, which is used to turn the display unit on/off - and control the backlight. + and control the backlight. The V2 supports 5" and 7" panels and also + offers PWM backlight control. allOf: - $ref: regulator.yaml# properties: compatible: - const: raspberrypi,7inch-touchscreen-panel-regulator + enum: + - raspberrypi,7inch-touchscreen-panel-regulator + - raspberrypi,touchscreen-panel-regulator-v2 reg: maxItems: 1 From d49305862fdc4d9ff1b1093b4ed7d8e0cb9971b4 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 9 Jun 2025 02:06:42 +0200 Subject: [PATCH 06/40] regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280 Add regulator for the 7" Raspberry Pi 720x1280 DSI panel based on ili9881. This is the Raspberry Pi DSI Panel V2. The newer Raspberry Pi 5" and 7" panels have a slightly different register map to the original one. Add a new driver for this "regulator" chip, this time by exposing two GPIOs and one PWM controller, both of which can be consumed by panel driver and pwm-backlight respectively. Signed-off-by: Dave Stevenson Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609000748.1665219-2-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 1 + drivers/regulator/rpi-panel-v2-regulator.c | 114 +++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 drivers/regulator/rpi-panel-v2-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 6d8988387da4..21ad6d938e4d 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1153,6 +1153,16 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY touchscreen unit. The regulator is used to enable power to the TC358762, display and to control backlight. +config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 + tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" + depends on I2C + select GPIO_REGMAP + select REGMAP_I2C + help + This driver supports regulator on the V2 Raspberry Pi touchscreen + unit. The regulator is used to enable power to the display and to + control backlight PWM. + config REGULATOR_RC5T583 tristate "RICOH RC5T583 Power regulators" depends on MFD_RC5T583 diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index c0bc7a0f4e67..be98b29d6675 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -136,6 +136,7 @@ obj-$(CONFIG_REGULATOR_PBIAS) += pbias-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_RAA215300) += raa215300.o obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY) += rpi-panel-attiny-regulator.o +obj-$(CONFIG_REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2) += rpi-panel-v2-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o obj-$(CONFIG_REGULATOR_RK808) += rk808-regulator.o obj-$(CONFIG_REGULATOR_RN5T618) += rn5t618-regulator.o diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c new file mode 100644 index 000000000000..b77383584a3a --- /dev/null +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2022 Raspberry Pi Ltd. + * Copyright (C) 2025 Marek Vasut + */ + +#include +#include +#include +#include +#include +#include +#include + +/* I2C registers of the microcontroller. */ +#define REG_ID 0x01 +#define REG_POWERON 0x02 +#define REG_PWM 0x03 + +/* Bits for poweron register */ +#define LCD_RESET_BIT BIT(0) +#define CTP_RESET_BIT BIT(1) + +/* Bits for the PWM register */ +#define PWM_BL_ENABLE BIT(7) +#define PWM_BL_MASK GENMASK(4, 0) + +/* Treat LCD_RESET and CTP_RESET as GPIOs */ +#define NUM_GPIO 2 + +static const struct regmap_config rpi_panel_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = REG_PWM, + .can_sleep = true, +}; + +static int rpi_panel_v2_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct regmap *regmap = pwmchip_get_drvdata(chip); + unsigned int duty; + + if (state->polarity != PWM_POLARITY_NORMAL) + return -EINVAL; + + if (!state->enabled) + return regmap_write(regmap, REG_PWM, 0); + + duty = pwm_get_relative_duty_cycle(state, PWM_BL_MASK); + return regmap_write(regmap, REG_PWM, duty | PWM_BL_ENABLE); +} + +static const struct pwm_ops rpi_panel_v2_pwm_ops = { + .apply = rpi_panel_v2_pwm_apply, +}; + +/* + * I2C driver interface functions + */ +static int rpi_panel_v2_i2c_probe(struct i2c_client *i2c) +{ + struct gpio_regmap_config gconfig = { + .ngpio = NUM_GPIO, + .ngpio_per_reg = NUM_GPIO, + .parent = &i2c->dev, + .reg_set_base = REG_POWERON, + }; + struct regmap *regmap; + struct pwm_chip *pc; + int ret; + + pc = devm_pwmchip_alloc(&i2c->dev, 1, 0); + if (IS_ERR(pc)) + return PTR_ERR(pc); + + pc->ops = &rpi_panel_v2_pwm_ops; + + regmap = devm_regmap_init_i2c(i2c, &rpi_panel_regmap_config); + if (IS_ERR(regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(regmap), "Failed to allocate regmap\n"); + + pwmchip_set_drvdata(pc, regmap); + + regmap_write(regmap, REG_POWERON, 0); + + gconfig.regmap = regmap; + ret = PTR_ERR_OR_ZERO(devm_gpio_regmap_register(&i2c->dev, &gconfig)); + if (ret) + return dev_err_probe(&i2c->dev, ret, "Failed to create gpiochip\n"); + + return devm_pwmchip_add(&i2c->dev, pc); +} + +static const struct of_device_id rpi_panel_v2_dt_ids[] = { + { .compatible = "raspberrypi,touchscreen-panel-regulator-v2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, rpi_panel_v2_dt_ids); + +static struct i2c_driver rpi_panel_v2_regulator_driver = { + .driver = { + .name = "rpi_touchscreen_v2", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(rpi_panel_v2_dt_ids), + }, + .probe = rpi_panel_v2_i2c_probe, +}; + +module_i2c_driver(rpi_panel_v2_regulator_driver); + +MODULE_AUTHOR("Dave Stevenson "); +MODULE_DESCRIPTION("Regulator device driver for Raspberry Pi 7-inch V2 touchscreen"); +MODULE_LICENSE("GPL"); From d6fa0ca959db8efd4462d7beef4bdc5568640fd0 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Mon, 9 Jun 2025 22:02:06 +0200 Subject: [PATCH 07/40] regulator: rpi-panel-v2: Add missing GPIOLIB dependency Add missing GPIOLIB dependency reported by the LKP test robot. This fixes the following report: " kismet warnings: (new ones prefixed by >>) >> kismet: WARNING: unmet direct dependencies detected for GPIO_REGMAP when selected by REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 WARNING: unmet direct dependencies detected for GPIO_REGMAP Depends on [n]: GPIOLIB [=n] Selected by [y]: - REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 [=y] && REGULATOR [=y] && I2C [=y] " Fixes: d49305862fdc ("regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506092341.enbNKMOR-lkp@intel.com/ Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609200242.31271-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 21ad6d938e4d..9a3dc883ff40 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1155,6 +1155,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" + depends on GPIOLIB depends on I2C select GPIO_REGMAP select REGMAP_I2C From 55d9fd9819de09e70401b3b5262ff46d5de951b7 Mon Sep 17 00:00:00 2001 From: Matti Vaittinen Date: Tue, 10 Jun 2025 08:32:06 +0300 Subject: [PATCH 08/40] regulator: bd718x7: Clarify comment by moving it The BD718x7 needs to disable voltage monitoring for a duration of certain voltage changes. The comment explaining use of msleep(1) instead of a more accurate delay(), was placed to a function which disabled the protection. The actual sleeping is done in a different place of the code, after the voltage has been changed. Browsing through the comment and code after the years made me to scratch my head for a second. I may have figured why me and so many fellow developers are slowly getting bald. Clarify things a bit and move the comment about required delay directly above the sleep. Leave only a small comment explaining why the protection is disabled to the spot where the logic for disabling is. Signed-off-by: Matti Vaittinen Link: https://patch.msgid.link/a90cb77e66a253f4055bbb99672dc81c7457de66.1749533040.git.mazziesaccount@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/bd718x7-regulator.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/regulator/bd718x7-regulator.c b/drivers/regulator/bd718x7-regulator.c index 1bb048de3ecd..e803cc59d68a 100644 --- a/drivers/regulator/bd718x7-regulator.c +++ b/drivers/regulator/bd718x7-regulator.c @@ -134,9 +134,19 @@ static void voltage_change_done(struct regulator_dev *rdev, unsigned int sel, if (*mask) { /* - * Let's allow scheduling as we use I2C anyways. We just need to - * guarantee minimum of 1ms sleep - it shouldn't matter if we - * exceed it due to the scheduling. + * We had fault detection disabled for the duration of the + * voltage change. + * + * According to HW colleagues the maximum time it takes is + * 1000us. I assume that on systems with light load this + * might be less - and we could probably use DT to give + * system specific delay value if performance matters. + * + * Well, knowing we use I2C here and can add scheduling delays + * I don't think it is worth the hassle and I just add fixed + * 1ms sleep here (and allow scheduling). If this turns out to + * be a problem we can change it to delay and make the delay + * time configurable. */ msleep(1); @@ -173,16 +183,7 @@ static int voltage_change_prepare(struct regulator_dev *rdev, unsigned int sel, /* * If we increase LDO voltage when LDO is enabled we need to * disable the power-good detection until voltage has reached - * the new level. According to HW colleagues the maximum time - * it takes is 1000us. I assume that on systems with light load - * this might be less - and we could probably use DT to give - * system specific delay value if performance matters. - * - * Well, knowing we use I2C here and can add scheduling delays - * I don't think it is worth the hassle and I just add fixed - * 1ms sleep here (and allow scheduling). If this turns out to - * be a problem we can change it to delay and make the delay - * time configurable. + * the new level. */ if (new > now) { int tmp; From a4eb71ff98c4792f441f108910bd829da7a04092 Mon Sep 17 00:00:00 2001 From: Marek Vasut Date: Tue, 10 Jun 2025 00:30:00 +0200 Subject: [PATCH 09/40] regulator: rpi-panel-v2: Fix missing OF dependency Add missing OF dependency and drop of_match_ptr() use. This fixes the following LKP report: " >> drivers/regulator/rpi-panel-v2-regulator.c:95:34: warning: 'rpi_panel_v2_dt_ids' defined but not used [-Wunused-const-variable=] static const struct of_device_id rpi_panel_v2_dt_ids[] = { ^~~~~~~~~~~~~~~~~~~ " Fixes: d49305862fdc ("regulator: rpi-panel-v2: Add regulator for 7" Raspberry Pi 720x1280") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202506100440.fyTGO7CG-lkp@intel.com/ Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250609223012.87764-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- drivers/regulator/rpi-panel-v2-regulator.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 9a3dc883ff40..7423954153b0 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1156,7 +1156,7 @@ config REGULATOR_RASPBERRYPI_TOUCHSCREEN_ATTINY config REGULATOR_RASPBERRYPI_TOUCHSCREEN_V2 tristate "Raspberry Pi 7-inch touchscreen panel V2 regulator" depends on GPIOLIB - depends on I2C + depends on I2C && OF select GPIO_REGMAP select REGMAP_I2C help diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c index b77383584a3a..c506fd699d57 100644 --- a/drivers/regulator/rpi-panel-v2-regulator.c +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -102,7 +102,7 @@ static struct i2c_driver rpi_panel_v2_regulator_driver = { .driver = { .name = "rpi_touchscreen_v2", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = of_match_ptr(rpi_panel_v2_dt_ids), + .of_match_table = rpi_panel_v2_dt_ids, }, .probe = rpi_panel_v2_i2c_probe, }; From 3e1c01d06e1f52f78fe00ef26a9cf80dbb0a3115 Mon Sep 17 00:00:00 2001 From: Dave Stevenson Date: Mon, 16 Jun 2025 17:40:07 +0200 Subject: [PATCH 10/40] regulator: rpi-panel-v2: Add shutdown hook Add shutdown hook so that the panel gets powered off with the system. Signed-off-by: Dave Stevenson Signed-off-by: Marek Vasut Link: https://patch.msgid.link/20250616154018.430004-1-marek.vasut+renesas@mailbox.org Signed-off-by: Mark Brown --- drivers/regulator/rpi-panel-v2-regulator.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/regulator/rpi-panel-v2-regulator.c b/drivers/regulator/rpi-panel-v2-regulator.c index c506fd699d57..30b78aa75ee3 100644 --- a/drivers/regulator/rpi-panel-v2-regulator.c +++ b/drivers/regulator/rpi-panel-v2-regulator.c @@ -89,9 +89,19 @@ static int rpi_panel_v2_i2c_probe(struct i2c_client *i2c) if (ret) return dev_err_probe(&i2c->dev, ret, "Failed to create gpiochip\n"); + i2c_set_clientdata(i2c, regmap); + return devm_pwmchip_add(&i2c->dev, pc); } +static void rpi_panel_v2_i2c_shutdown(struct i2c_client *client) +{ + struct regmap *regmap = i2c_get_clientdata(client); + + regmap_write(regmap, REG_PWM, 0); + regmap_write(regmap, REG_POWERON, 0); +} + static const struct of_device_id rpi_panel_v2_dt_ids[] = { { .compatible = "raspberrypi,touchscreen-panel-regulator-v2" }, { }, @@ -105,6 +115,7 @@ static struct i2c_driver rpi_panel_v2_regulator_driver = { .of_match_table = rpi_panel_v2_dt_ids, }, .probe = rpi_panel_v2_i2c_probe, + .shutdown = rpi_panel_v2_i2c_shutdown, }; module_i2c_driver(rpi_panel_v2_regulator_driver); From 19cbc930c209d59a2c9828de4c7b767e9f14667e Mon Sep 17 00:00:00 2001 From: Primoz Fiser Date: Wed, 18 Jun 2025 08:33:39 +0200 Subject: [PATCH 11/40] regulator: pca9450: Support PWM mode also for pca9451a Previous commit 548d770c330c ("regulator: pca9450: Add support for mode operations") added support for setting forced PWM mode on the buck regulators for pca9450a and pca9450bc parts. However part pca9451a also supports this feature, thus add support for it. Fixes: 548d770c330c ("regulator: pca9450: Add support for mode operations") Signed-off-by: Primoz Fiser Link: https://patch.msgid.link/20250618063339.2508893-1-primoz.fiser@norik.com Signed-off-by: Mark Brown --- drivers/regulator/pca9450-regulator.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c index f6faf14a9c53..feadb21a8f30 100644 --- a/drivers/regulator/pca9450-regulator.c +++ b/drivers/regulator/pca9450-regulator.c @@ -873,12 +873,15 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK1OUT_DVS0, .run_mask = BUCK1OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK1OUT_DVS1, .standby_mask = BUCK1OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK1CTRL, + .mode_mask = BUCK1_FPWM, }, }, { @@ -902,12 +905,15 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .n_ramp_values = ARRAY_SIZE(pca9450_dvs_buck_ramp_table), .owner = THIS_MODULE, .of_parse_cb = pca9450_set_dvs_levels, + .of_map_mode = pca9450_map_mode, }, .dvs = { .run_reg = PCA9450_REG_BUCK2OUT_DVS0, .run_mask = BUCK2OUT_DVS0_MASK, .standby_reg = PCA9450_REG_BUCK2OUT_DVS1, .standby_mask = BUCK2OUT_DVS1_MASK, + .mode_reg = PCA9450_REG_BUCK2CTRL, + .mode_mask = BUCK2_FPWM, }, }, { @@ -927,6 +933,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK4_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK4CTRL, + .mode_mask = BUCK4_FPWM, }, }, { @@ -946,6 +957,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK5_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK5CTRL, + .mode_mask = BUCK5_FPWM, }, }, { @@ -965,6 +981,11 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = { .enable_mask = BUCK6_ENMODE_MASK, .enable_val = BUCK_ENMODE_ONREQ, .owner = THIS_MODULE, + .of_map_mode = pca9450_map_mode, + }, + .dvs = { + .mode_reg = PCA9450_REG_BUCK6CTRL, + .mode_mask = BUCK6_FPWM, }, }, { From a6c05c2e6871c94b349703c1ec8584763797fd8e Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:41 +0200 Subject: [PATCH 12/40] dt-bindings: regulator: mediatek-dvfsrc: Add MT6893 support Add a compatible for the MediaTek Dimensity 1200 (MT6893) SoC's regulators over DVFSRC. This SoC uses different values for the vsel, hence it is not compatible with the currently supported ones. Signed-off-by: AngeloGioacchino Del Regno Acked-by: "Rob Herring (Arm)" Link: https://patch.msgid.link/20250623120144.109359-2-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index 704828687970..acac5c869f2c 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -17,6 +17,7 @@ properties: compatible: enum: - mediatek,mt6873-dvfsrc-regulator + - mediatek,mt6893-dvfsrc-regulator - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator From 7aafbb463be85472dc6db194ab9df45fd497c998 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:42 +0200 Subject: [PATCH 13/40] regulator: mtk-dvfsrc: Add support for Dimensity 1200 MT6893 The MediaTek Dimensity 1200 (MT6893) features the same DVFSRC regulators as the other currently supported SoCs, but with a different select value: add an array describing the possible voltages for the VCORE and VSCP regulators, and assign it to a new compatible for this SoC. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-3-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/regulator/mtk-dvfsrc-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c index f5662c569464..5bc4d85517f0 100644 --- a/drivers/regulator/mtk-dvfsrc-regulator.c +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -117,6 +117,24 @@ static const struct dvfsrc_regulator_pdata mt6873_data = { .size = ARRAY_SIZE(mt6873_regulators), }; +static const unsigned int mt6893_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 750000, +}; + +static const struct regulator_desc mt6893_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt6893_voltages), + MTK_DVFSRC_VREG("dvfsrc-vscp", VSCP, mt6893_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt6893_data = { + .descs = mt6893_regulators, + .size = ARRAY_SIZE(mt6893_regulators), +}; + static const unsigned int mt8183_voltages[] = { 725000, 800000, @@ -173,6 +191,7 @@ static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) static const struct of_device_id mtk_dvfsrc_regulator_match[] = { { .compatible = "mediatek,mt6873-dvfsrc-regulator", .data = &mt6873_data }, + { .compatible = "mediatek,mt6893-dvfsrc-regulator", .data = &mt6893_data }, { .compatible = "mediatek,mt8183-dvfsrc-regulator", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-dvfsrc-regulator", .data = &mt6873_data }, { .compatible = "mediatek,mt8195-dvfsrc-regulator", .data = &mt8195_data }, From ae77b8e8b0326818bd170818b37d055aa57a1569 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:43 +0200 Subject: [PATCH 14/40] dt-bindings: regulator: mediatek-dvfsrc: Add MT8196 support Add a compatible for the MediaTek MT8196 Chromebook SoC's regulators over DVFSRC. This SoC has only one regulator "dvfsrc-vcore" and different values for vsel compared to the others. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-4-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- .../bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml index acac5c869f2c..685ccf9cf4d4 100644 --- a/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/mediatek,mt6873-dvfsrc-regulator.yaml @@ -21,6 +21,7 @@ properties: - mediatek,mt8183-dvfsrc-regulator - mediatek,mt8192-dvfsrc-regulator - mediatek,mt8195-dvfsrc-regulator + - mediatek,mt8196-dvfsrc-regulator dvfsrc-vcore: description: DVFSRC-controlled SoC Vcore regulator From 024f39fff6d222cedde361f7fe34d9ba4e6afb92 Mon Sep 17 00:00:00 2001 From: AngeloGioacchino Del Regno Date: Mon, 23 Jun 2025 14:01:44 +0200 Subject: [PATCH 15/40] regulator: mtk-dvfsrc: Add support for MediaTek MT8196 DVFSRC The MediaTek MT8196 Chromebook SoC features one DVFSRC regulator with 6 voltage steps. Signed-off-by: AngeloGioacchino Del Regno Link: https://patch.msgid.link/20250623120144.109359-5-angelogioacchino.delregno@collabora.com Signed-off-by: Mark Brown --- drivers/regulator/mtk-dvfsrc-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/mtk-dvfsrc-regulator.c b/drivers/regulator/mtk-dvfsrc-regulator.c index 5bc4d85517f0..c0c9a6751c26 100644 --- a/drivers/regulator/mtk-dvfsrc-regulator.c +++ b/drivers/regulator/mtk-dvfsrc-regulator.c @@ -166,6 +166,24 @@ static const struct dvfsrc_regulator_pdata mt8195_data = { .size = ARRAY_SIZE(mt8195_regulators), }; +static const unsigned int mt8196_voltages[] = { + 575000, + 600000, + 650000, + 725000, + 825000, + 875000, +}; + +static const struct regulator_desc mt8196_regulators[] = { + MTK_DVFSRC_VREG("dvfsrc-vcore", VCORE, mt8196_voltages), +}; + +static const struct dvfsrc_regulator_pdata mt8196_data = { + .descs = mt8196_regulators, + .size = ARRAY_SIZE(mt8196_regulators), +}; + static int dvfsrc_vcore_regulator_probe(struct platform_device *pdev) { struct regulator_config config = { .dev = &pdev->dev }; @@ -195,6 +213,7 @@ static const struct of_device_id mtk_dvfsrc_regulator_match[] = { { .compatible = "mediatek,mt8183-dvfsrc-regulator", .data = &mt8183_data }, { .compatible = "mediatek,mt8192-dvfsrc-regulator", .data = &mt6873_data }, { .compatible = "mediatek,mt8195-dvfsrc-regulator", .data = &mt8195_data }, + { .compatible = "mediatek,mt8196-dvfsrc-regulator", .data = &mt8196_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mtk_dvfsrc_regulator_match); From ce57bc9771411d6d27f2ca7b40396cbd7d684ba9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 26 Jun 2025 18:23:07 +0300 Subject: [PATCH 16/40] regulator: core: Don't use "proxy" headers Update header inclusions to follow IWYU (Include What You Use) principle. Note that kernel.h is discouraged to be included as it's written at the top of that file. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20250626152307.322627-1-andriy.shevchenko@linux.intel.com Signed-off-by: Mark Brown --- include/linux/regulator/coupler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/linux/regulator/coupler.h b/include/linux/regulator/coupler.h index 73291f280a23..5e314a4294fb 100644 --- a/include/linux/regulator/coupler.h +++ b/include/linux/regulator/coupler.h @@ -8,7 +8,8 @@ #ifndef __LINUX_REGULATOR_COUPLER_H_ #define __LINUX_REGULATOR_COUPLER_H_ -#include +#include +#include #include struct regulator_coupler; From bd7c7976f9716da4cc961ab8ff4e17811d522602 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:48:03 +0800 Subject: [PATCH 17/40] regulator: rt5739: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629094803.776-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/rt5739.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/rt5739.c b/drivers/regulator/rt5739.c index 91412c905ce6..5fcddd7c2da7 100644 --- a/drivers/regulator/rt5739.c +++ b/drivers/regulator/rt5739.c @@ -24,6 +24,8 @@ #define RT5739_REG_NSEL1 0x01 #define RT5739_REG_CNTL1 0x02 #define RT5739_REG_ID1 0x03 +#define RT5739_REG_ID2 0x04 +#define RT5739_REG_MON 0x05 #define RT5739_REG_CNTL2 0x06 #define RT5739_REG_CNTL4 0x08 @@ -236,11 +238,18 @@ static void rt5739_init_regulator_desc(struct regulator_desc *desc, } } +static bool rt5739_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == RT5739_REG_MON; +} + static const struct regmap_config rt5739_regmap_config = { .name = "rt5739", .reg_bits = 8, .val_bits = 8, .max_register = RT5739_REG_CNTL4, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = rt5739_volatile_reg, }; static int rt5739_probe(struct i2c_client *i2c) From b402dfe84057e376b39c8adadeb65ad51aaffeb4 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:58:22 +0800 Subject: [PATCH 18/40] regulator: tps6287x-regulator: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629095822.868-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6287x-regulator.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/regulator/tps6287x-regulator.c b/drivers/regulator/tps6287x-regulator.c index c0f5f0a186a3..7b7d3ae39206 100644 --- a/drivers/regulator/tps6287x-regulator.c +++ b/drivers/regulator/tps6287x-regulator.c @@ -27,10 +27,17 @@ #define TPS6287X_CTRL3 0x03 #define TPS6287X_STATUS 0x04 +static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6287X_STATUS; +} + static const struct regmap_config tps6287x_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = TPS6287X_STATUS, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6287x_volatile_reg, }; static const struct linear_range tps6287x_voltage_ranges[] = { From 427ceac823e58813b510e585011488f603f0d891 Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:51:07 +0800 Subject: [PATCH 19/40] regulator: tps6286x-regulator: Enable REGCACHE_MAPLE Enable regmap cache to reduce i2c transactions and corresponding interrupts if regulator is accessed frequently. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250629095107.804-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6286x-regulator.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c index 75f441f36de7..778f169b0acc 100644 --- a/drivers/regulator/tps6286x-regulator.c +++ b/drivers/regulator/tps6286x-regulator.c @@ -19,13 +19,22 @@ #define TPS6286X_CONTROL_FPWM BIT(4) #define TPS6286X_CONTROL_SWEN BIT(5) +#define TPS6286X_STATUS 0x05 + #define TPS6286X_MIN_MV 400 #define TPS6286X_MAX_MV 1675 #define TPS6286X_STEP_MV 5 +static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +{ + return reg == TPS6286X_STATUS; +} + static const struct regmap_config tps6286x_regmap_config = { .reg_bits = 8, .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tps6287x_volatile_reg, }; static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) From c61e94e5e4e6bc50064119e6a779564d1d2ac0e7 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 4 Jul 2025 10:54:44 +0300 Subject: [PATCH 20/40] regulator: stm32-vrefbuf: Remove redundant pm_runtime_mark_last_busy() calls pm_runtime_put_autosuspend(), pm_runtime_put_sync_autosuspend(), pm_runtime_autosuspend() and pm_request_autosuspend() now include a call to pm_runtime_mark_last_busy(). Remove the now-reduntant explicit call to pm_runtime_mark_last_busy(). Signed-off-by: Sakari Ailus Link: https://patch.msgid.link/20250704075444.3221445-1-sakari.ailus@linux.intel.com Signed-off-by: Mark Brown --- drivers/regulator/stm32-vrefbuf.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c index a85ea94f0673..9e391206f09d 100644 --- a/drivers/regulator/stm32-vrefbuf.c +++ b/drivers/regulator/stm32-vrefbuf.c @@ -67,7 +67,6 @@ static int stm32_vrefbuf_enable(struct regulator_dev *rdev) writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); } - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -87,7 +86,6 @@ static int stm32_vrefbuf_disable(struct regulator_dev *rdev) val &= ~STM32_ENVR; writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -104,7 +102,6 @@ static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev) ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR; - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -125,7 +122,6 @@ static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev, val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel); writel_relaxed(val, priv->base + STM32_VREFBUF_CSR); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return 0; @@ -144,7 +140,6 @@ static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev) val = readl_relaxed(priv->base + STM32_VREFBUF_CSR); ret = FIELD_GET(STM32_VRS, val); - pm_runtime_mark_last_busy(priv->dev); pm_runtime_put_autosuspend(priv->dev); return ret; @@ -218,7 +213,6 @@ static int stm32_vrefbuf_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, rdev); - pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); return 0; From 5054740e0092aac528c0589251f612b3b41c9e7b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Sun, 29 Jun 2025 17:57:16 +0800 Subject: [PATCH 21/40] regulator: sy8827n: make enable gpio NONEXCLUSIVE On some platforms, the sy8827n enable gpio may also be used for other purpose, so make it NONEXCLUSIVE to support this case. Signed-off-by: Jisheng Zhang Acked-by: Bartosz Golaszewski Link: https://patch.msgid.link/20250629095716.841-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/sy8827n.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/sy8827n.c b/drivers/regulator/sy8827n.c index f11ff38b36c9..0b811514782f 100644 --- a/drivers/regulator/sy8827n.c +++ b/drivers/regulator/sy8827n.c @@ -140,7 +140,8 @@ static int sy8827n_i2c_probe(struct i2c_client *client) return -EINVAL; } - di->en_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH); + di->en_gpio = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE); if (IS_ERR(di->en_gpio)) return PTR_ERR(di->en_gpio); From 626bb0a45584d544d84eab909795ccb355062bcc Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:12 +0200 Subject: [PATCH 22/40] mfd: tps6594: Add TI TPS652G1 support The TPS652G1 is a stripped down version of the TPS65224. From a software point of view, it lacks any voltage monitoring, the watchdog, the ESM and the ADC. Signed-off-by: Michael Walle Link: https://lore.kernel.org/r/20250613114518.1772109-2-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/mfd/tps6594-core.c | 88 ++++++++++++++++++++++++++++++++++--- drivers/mfd/tps6594-i2c.c | 10 ++++- drivers/mfd/tps6594-spi.c | 10 ++++- include/linux/mfd/tps6594.h | 1 + 4 files changed, 99 insertions(+), 10 deletions(-) diff --git a/drivers/mfd/tps6594-core.c b/drivers/mfd/tps6594-core.c index a7223e873cd1..c16c37e36617 100644 --- a/drivers/mfd/tps6594-core.c +++ b/drivers/mfd/tps6594-core.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Core functions for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * Core functions for following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -414,6 +419,61 @@ static const unsigned int tps65224_irq_reg[] = { TPS6594_REG_INT_FSM_ERR, }; +/* TPS652G1 Resources */ + +static const struct mfd_cell tps652g1_common_cells[] = { + MFD_CELL_RES("tps6594-pfsm", tps65224_pfsm_resources), + MFD_CELL_RES("tps6594-pinctrl", tps65224_pinctrl_resources), + MFD_CELL_NAME("tps6594-regulator"), +}; + +static const struct regmap_irq tps652g1_irqs[] = { + /* INT_GPIO register */ + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO1, 2, TPS65224_BIT_GPIO1_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO2, 2, TPS65224_BIT_GPIO2_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO3, 2, TPS65224_BIT_GPIO3_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO4, 2, TPS65224_BIT_GPIO4_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO5, 2, TPS65224_BIT_GPIO5_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_GPIO6, 2, TPS65224_BIT_GPIO6_INT), + + /* INT_STARTUP register */ + REGMAP_IRQ_REG(TPS65224_IRQ_VSENSE, 3, TPS65224_BIT_VSENSE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ENABLE, 3, TPS6594_BIT_ENABLE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_SHORT, 3, TPS65224_BIT_PB_SHORT_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_FSD, 3, TPS6594_BIT_FSD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOFT_REBOOT, 3, TPS6594_BIT_SOFT_REBOOT_INT), + + /* INT_MISC register */ + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_PASS, 4, TPS6594_BIT_BIST_PASS_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_EXT_CLK, 4, TPS6594_BIT_EXT_CLK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_UNLOCK, 4, TPS65224_BIT_REG_UNLOCK_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_TWARN, 4, TPS6594_BIT_TWARN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_LONG, 4, TPS65224_BIT_PB_LONG_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_FALL, 4, TPS65224_BIT_PB_FALL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PB_RISE, 4, TPS65224_BIT_PB_RISE_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ADC_CONV_READY, 4, TPS65224_BIT_ADC_CONV_READY_INT), + + /* INT_MODERATE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_ORD, 5, TPS6594_BIT_TSD_ORD_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BIST_FAIL, 5, TPS6594_BIT_BIST_FAIL_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_REG_CRC_ERR, 5, TPS6594_BIT_REG_CRC_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_RECOV_CNT, 5, TPS6594_BIT_RECOV_CNT_INT), + + /* INT_SEVERE_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_TSD_IMM, 6, TPS6594_BIT_TSD_IMM_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_VCCA_OVP, 6, TPS6594_BIT_VCCA_OVP_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_PFSM_ERR, 6, TPS6594_BIT_PFSM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_BG_XMON, 6, TPS65224_BIT_BG_XMON_INT), + + /* INT_FSM_ERR register */ + REGMAP_IRQ_REG(TPS65224_IRQ_IMM_SHUTDOWN, 7, TPS6594_BIT_IMM_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_ORD_SHUTDOWN, 7, TPS6594_BIT_ORD_SHUTDOWN_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_MCU_PWR_ERR, 7, TPS6594_BIT_MCU_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_SOC_PWR_ERR, 7, TPS6594_BIT_SOC_PWR_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_COMM_ERR, 7, TPS6594_BIT_COMM_ERR_INT), + REGMAP_IRQ_REG(TPS65224_IRQ_I2C2_ERR, 7, TPS65224_BIT_I2C2_ERR_INT), +}; + static inline unsigned int tps6594_get_irq_reg(struct regmap_irq_chip_data *data, unsigned int base, int index) { @@ -443,7 +503,7 @@ static int tps6594_handle_post_irq(void *irq_drv_data) * a new interrupt. */ if (tps->use_crc) { - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_INT_FSM_ERR; mask_val = TPS6594_BIT_COMM_ERR_INT; } else { @@ -481,6 +541,18 @@ static struct regmap_irq_chip tps65224_irq_chip = { .handle_post_irq = tps6594_handle_post_irq, }; +static struct regmap_irq_chip tps652g1_irq_chip = { + .ack_base = TPS6594_REG_INT_BUCK, + .ack_invert = 1, + .clear_ack = 1, + .init_ack_masked = 1, + .num_regs = ARRAY_SIZE(tps65224_irq_reg), + .irqs = tps652g1_irqs, + .num_irqs = ARRAY_SIZE(tps652g1_irqs), + .get_irq_reg = tps65224_get_irq_reg, + .handle_post_irq = tps6594_handle_post_irq, +}; + static const struct regmap_range tps6594_volatile_ranges[] = { regmap_reg_range(TPS6594_REG_INT_TOP, TPS6594_REG_STAT_READBACK_ERR), regmap_reg_range(TPS6594_REG_RTC_STATUS, TPS6594_REG_RTC_STATUS), @@ -507,7 +579,7 @@ static int tps6594_check_crc_mode(struct tps6594 *tps, bool primary_pmic) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -537,7 +609,7 @@ static int tps6594_set_crc_feature(struct tps6594 *tps) int ret; unsigned int regmap_reg, mask_val; - if (tps->chip_id == TPS65224) { + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) { regmap_reg = TPS6594_REG_CONFIG_2; mask_val = TPS65224_BIT_I2C1_SPI_CRC_EN; } else { @@ -628,6 +700,10 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) irq_chip = &tps65224_irq_chip; n_cells = ARRAY_SIZE(tps65224_common_cells); cells = tps65224_common_cells; + } else if (tps->chip_id == TPS652G1) { + irq_chip = &tps652g1_irq_chip; + n_cells = ARRAY_SIZE(tps652g1_common_cells); + cells = tps652g1_common_cells; } else { irq_chip = &tps6594_irq_chip; n_cells = ARRAY_SIZE(tps6594_common_cells); @@ -651,8 +727,8 @@ int tps6594_device_init(struct tps6594 *tps, bool enable_crc) if (ret) return dev_err_probe(dev, ret, "Failed to add common child devices\n"); - /* No RTC for LP8764 and TPS65224 */ - if (tps->chip_id != LP8764 && tps->chip_id != TPS65224) { + /* No RTC for LP8764, TPS65224 and TPS652G1 */ + if (tps->chip_id != LP8764 && tps->chip_id != TPS65224 && tps->chip_id != TPS652G1) { ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, tps6594_rtc_cells, ARRAY_SIZE(tps6594_rtc_cells), NULL, 0, regmap_irq_get_domain(tps->irq_data)); diff --git a/drivers/mfd/tps6594-i2c.c b/drivers/mfd/tps6594-i2c.c index 4ab91c34d9fb..7ff7516286fd 100644 --- a/drivers/mfd/tps6594-i2c.c +++ b/drivers/mfd/tps6594-i2c.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * I2C access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * I2C access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -197,6 +202,7 @@ static const struct of_device_id tps6594_i2c_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_i2c_of_match_table); @@ -222,7 +228,7 @@ static int tps6594_i2c_probe(struct i2c_client *client) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_i2c_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, client, &tps6594_i2c_regmap_config); diff --git a/drivers/mfd/tps6594-spi.c b/drivers/mfd/tps6594-spi.c index 6ebccb79f0cc..944b7313a1d9 100644 --- a/drivers/mfd/tps6594-spi.c +++ b/drivers/mfd/tps6594-spi.c @@ -1,6 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 /* - * SPI access driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * SPI access driver for the following TI PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6593 + * - TPS6594 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -82,6 +87,7 @@ static const struct of_device_id tps6594_spi_of_match_table[] = { { .compatible = "ti,tps6593-q1", .data = (void *)TPS6593, }, { .compatible = "ti,lp8764-q1", .data = (void *)LP8764, }, { .compatible = "ti,tps65224-q1", .data = (void *)TPS65224, }, + { .compatible = "ti,tps652g1", .data = (void *)TPS652G1, }, {} }; MODULE_DEVICE_TABLE(of, tps6594_spi_of_match_table); @@ -107,7 +113,7 @@ static int tps6594_spi_probe(struct spi_device *spi) return dev_err_probe(dev, -EINVAL, "Failed to find matching chip ID\n"); tps->chip_id = (unsigned long)match->data; - if (tps->chip_id == TPS65224) + if (tps->chip_id == TPS65224 || tps->chip_id == TPS652G1) tps6594_spi_regmap_config.volatile_table = &tps65224_volatile_table; tps->regmap = devm_regmap_init(dev, NULL, spi, &tps6594_spi_regmap_config); diff --git a/include/linux/mfd/tps6594.h b/include/linux/mfd/tps6594.h index 16543fd4d83e..021db8875963 100644 --- a/include/linux/mfd/tps6594.h +++ b/include/linux/mfd/tps6594.h @@ -19,6 +19,7 @@ enum pmic_id { TPS6593, LP8764, TPS65224, + TPS652G1, }; /* Macro to get page index from register address */ From 9cba6a7ebf65c603b80c0b3c7fa8c7c03f1b704c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:13 +0200 Subject: [PATCH 23/40] misc: tps6594-pfsm: Add TI TPS652G1 PMIC PFSM The TPS652G1 is a stripped down TPS65224, but the PFSM is the same. Thus, handle it the same way as the TPS65224 in the driver. Signed-off-by: Michael Walle Acked-by: Arnd Bergmann # drivers/misc/ Link: https://lore.kernel.org/r/20250613114518.1772109-3-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/misc/tps6594-pfsm.c | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/drivers/misc/tps6594-pfsm.c b/drivers/misc/tps6594-pfsm.c index 6db1c9d48f8f..44fa81d6cec2 100644 --- a/drivers/misc/tps6594-pfsm.c +++ b/drivers/misc/tps6594-pfsm.c @@ -1,6 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 /* - * PFSM (Pre-configurable Finite State Machine) driver for TI TPS65224/TPS6594/TPS6593/LP8764 PMICs + * PFSM (Pre-configurable Finite State Machine) driver for the following + * PMICs: + * - LP8764 + * - TPS65224 + * - TPS652G1 + * - TPS6594 + * - TPS6593 * * Copyright (C) 2023 BayLibre Incorporated - https://www.baylibre.com/ */ @@ -141,7 +147,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a switch (cmd) { case PMIC_GOTO_STANDBY: /* Disable LP mode on TPS6594 Family PMIC */ - if (pfsm->chip_id != TPS65224) { + if (pfsm->chip_id != TPS65224 && pfsm->chip_id != TPS652G1) { ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_RTC_CTRL_2, TPS6594_BIT_LP_STANDBY_SEL); @@ -154,8 +160,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_TRIGGER_I2C(0), TPS6594_BIT_TRIGGER_I2C(0)); break; case PMIC_GOTO_LP_STANDBY: - /* TPS65224 does not support LP STANDBY */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support LP STANDBY */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; /* Enable LP mode */ @@ -179,8 +185,8 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a TPS6594_BIT_NSLEEP1B | TPS6594_BIT_NSLEEP2B); break; case PMIC_SET_MCU_ONLY_STATE: - /* TPS65224 does not support MCU_ONLY_STATE */ - if (pfsm->chip_id == TPS65224) + /* TPS65224/TPS652G1 does not support MCU_ONLY_STATE */ + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) return ret; if (copy_from_user(&state_opt, argp, sizeof(state_opt))) @@ -206,7 +212,7 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return -EFAULT; /* Configure wake-up destination */ - if (pfsm->chip_id == TPS65224) { + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) { regmap_reg = TPS65224_REG_STARTUP_CTRL; mask = TPS65224_MASK_STARTUP_DEST; } else { @@ -230,9 +236,14 @@ static long tps6594_pfsm_ioctl(struct file *f, unsigned int cmd, unsigned long a return ret; /* Modify NSLEEP1-2 bits */ - ret = regmap_clear_bits(pfsm->regmap, TPS6594_REG_FSM_NSLEEP_TRIGGERS, - pfsm->chip_id == TPS65224 ? - TPS6594_BIT_NSLEEP1B : TPS6594_BIT_NSLEEP2B); + if (pfsm->chip_id == TPS65224 || pfsm->chip_id == TPS652G1) + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP1B); + else + ret = regmap_clear_bits(pfsm->regmap, + TPS6594_REG_FSM_NSLEEP_TRIGGERS, + TPS6594_BIT_NSLEEP2B); break; } From f6420de1c810e282c34de65c70e6cc6177c12394 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Fri, 13 Jun 2025 13:45:14 +0200 Subject: [PATCH 24/40] pinctrl: pinctrl-tps6594: Add TPS652G1 PMIC pinctrl and GPIO The TPS652G1 is a stripped down version of the TPS65224. Compared to the TPS65224 it lacks some pin mux functions, like the ADC, voltage monitoring and the second I2C bus. Signed-off-by: Michael Walle Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20250613114518.1772109-4-mwalle@kernel.org Signed-off-by: Lee Jones --- drivers/pinctrl/pinctrl-tps6594.c | 35 +++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/pinctrl/pinctrl-tps6594.c b/drivers/pinctrl/pinctrl-tps6594.c index 54cc810f79d6..6726853110d1 100644 --- a/drivers/pinctrl/pinctrl-tps6594.c +++ b/drivers/pinctrl/pinctrl-tps6594.c @@ -226,6 +226,10 @@ static const char *const tps65224_nerr_mcu_func_group_names[] = { "GPIO5", }; +static const char *const tps652g1_cs_spi_func_group_names[] = { + "GPIO1", +}; + struct tps6594_pinctrl_function { struct pinfunction pinfunction; u8 muxval; @@ -287,6 +291,18 @@ static const struct tps6594_pinctrl_function tps65224_pinctrl_functions[] = { FUNCTION(tps65224, nerr_mcu, TPS65224_PINCTRL_NERR_MCU_FUNCTION), }; +static const struct tps6594_pinctrl_function tps652g1_pinctrl_functions[] = { + FUNCTION(tps65224, gpio, TPS6594_PINCTRL_GPIO_FUNCTION), + FUNCTION(tps65224, sda_i2c2_sdo_spi, TPS65224_PINCTRL_SDA_I2C2_SDO_SPI_FUNCTION), + FUNCTION(tps65224, nsleep2, TPS65224_PINCTRL_NSLEEP2_FUNCTION), + FUNCTION(tps65224, nint, TPS65224_PINCTRL_NINT_FUNCTION), + FUNCTION(tps652g1, cs_spi, TPS65224_PINCTRL_SCL_I2C2_CS_SPI_FUNCTION), + FUNCTION(tps65224, nsleep1, TPS65224_PINCTRL_NSLEEP1_FUNCTION), + FUNCTION(tps65224, pb, TPS65224_PINCTRL_PB_FUNCTION), + FUNCTION(tps65224, wkup, TPS65224_PINCTRL_WKUP_FUNCTION), + FUNCTION(tps65224, syncclkin, TPS65224_PINCTRL_SYNCCLKIN_FUNCTION), +}; + struct tps6594_pinctrl { struct tps6594 *tps; struct gpio_regmap *gpio_regmap; @@ -300,6 +316,16 @@ struct tps6594_pinctrl { struct muxval_remap *remap; }; +static struct tps6594_pinctrl tps652g1_template_pinctrl = { + .funcs = tps652g1_pinctrl_functions, + .func_cnt = ARRAY_SIZE(tps652g1_pinctrl_functions), + .pins = tps65224_pins, + .num_pins = ARRAY_SIZE(tps65224_pins), + .mux_sel_mask = TPS65224_MASK_GPIO_SEL, + .remap = tps65224_muxval_remap, + .remap_cnt = ARRAY_SIZE(tps65224_muxval_remap), +}; + static struct tps6594_pinctrl tps65224_template_pinctrl = { .funcs = tps65224_pinctrl_functions, .func_cnt = ARRAY_SIZE(tps65224_pinctrl_functions), @@ -475,6 +501,15 @@ static int tps6594_pinctrl_probe(struct platform_device *pdev) return -ENOMEM; switch (tps->chip_id) { + case TPS652G1: + pctrl_desc->pins = tps65224_pins; + pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); + + *pinctrl = tps652g1_template_pinctrl; + + config.ngpio = ARRAY_SIZE(tps65224_gpio_func_group_names); + config.ngpio_per_reg = TPS65224_NGPIO_PER_REG; + break; case TPS65224: pctrl_desc->pins = tps65224_pins; pctrl_desc->npins = ARRAY_SIZE(tps65224_pins); From d90171bc2e5f69c038d1807e6f64fba3d1ad6bee Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:46 +0200 Subject: [PATCH 25/40] dt-bindings: mfd: ti,tps6594: Add TI TPS652G1 PMIC The TPS652G1 is a stripped down version of the TPS65224. From a software point of view, it lacks any voltage monitoring, the watchdog, the ESM and the ADC. Signed-off-by: Michael Walle Acked-by: Conor Dooley Link: https://lore.kernel.org/r/20250703113153.2447110-2-mwalle@kernel.org Signed-off-by: Lee Jones --- Documentation/devicetree/bindings/mfd/ti,tps6594.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml index 6341b6070366..a48cb00afe43 100644 --- a/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml +++ b/Documentation/devicetree/bindings/mfd/ti,tps6594.yaml @@ -22,6 +22,7 @@ properties: - ti,tps6593-q1 - ti,tps6594-q1 - ti,tps65224-q1 + - ti,tps652g1 reg: description: I2C slave address or SPI chip select number. From 16d1a9bf36ef649b1fdb866985b4b87584491fac Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:50 +0200 Subject: [PATCH 26/40] regulator: tps6594-regulator: remove interrupt_count In .probe() interrupt_count and nr_types is essentially the same. It contains the number of different interrupt per LDO or buck converter. Drop one. This is a preparation patch to further simplify the handling of different variants of this PMIC. This patch is only compile-time tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-6-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 51264c869aa0..26669f3f1033 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -577,18 +577,15 @@ static int tps6594_regulator_probe(struct platform_device *pdev) const struct regulator_desc *multi_regs; const struct tps6594_regulator_irq_type **ldos_irq_types; const struct regulator_desc *ldo_regs; - size_t interrupt_count; if (tps->chip_id == TPS65224) { bucks_irq_types = tps65224_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps65224_buck1_irq_types); multi_regs = tps65224_multi_regs; ldos_irq_types = tps65224_ldos_irq_types; ldo_regs = tps65224_ldo_regs; multi_phase_cnt = ARRAY_SIZE(tps65224_multi_regs); } else { bucks_irq_types = tps6594_bucks_irq_types; - interrupt_count = ARRAY_SIZE(tps6594_buck1_irq_types); multi_regs = tps6594_multi_regs; ldos_irq_types = tps6594_ldos_irq_types; ldo_regs = tps6594_ldo_regs; @@ -686,29 +683,27 @@ static int tps6594_regulator_probe(struct platform_device *pdev) error = tps6594_request_reg_irqs(pdev, rdev, irq_data, bucks_irq_types[buck_idx], - interrupt_count, &irq_idx); + nr_types, &irq_idx); if (error) return error; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, bucks_irq_types[buck_idx + 1], - interrupt_count, &irq_idx); + nr_types, &irq_idx); if (error) return error; if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, tps6594_bucks_irq_types[buck_idx + 2], - interrupt_count, - &irq_idx); + nr_types, &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, tps6594_bucks_irq_types[buck_idx + 3], - interrupt_count, - &irq_idx); + nr_types, &irq_idx); if (error) return error; } @@ -727,7 +722,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[i], interrupt_count, &irq_idx); + bucks_irq_types[i], nr_types, &irq_idx); if (error) return error; } @@ -742,7 +737,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) pdev->name); error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - ldos_irq_types[i], interrupt_count, + ldos_irq_types[i], nr_types, &irq_idx); if (error) return error; From 180a135eafa9e05657559bb04cc9eb6a86ca45f3 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:51 +0200 Subject: [PATCH 27/40] regulator: tps6594-regulator: remove hardcoded buck config Commit 00c826525fba ("regulator: tps6594-regulator: Add TI TPS65224 PMIC regulators") added support for the TPS65224 and made the description of the multi-phase buck converter variable depending on the variant of the PMIC. But this was just done for MUTLI_BUCK12 and MULTI_BUCK12_34 configs probably because this variant only supports a multi-phase configuration on buck 1 and 2. Remove the hardcoded value for the remaining two configs, too as future PMIC variants might also support these. This is a preparation patch to refactor the regulator description and is compile-time only tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-7-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 26669f3f1033..2c7c4df80695 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -695,14 +695,14 @@ static int tps6594_regulator_probe(struct platform_device *pdev) if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 2], + bucks_irq_types[buck_idx + 2], nr_types, &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - tps6594_bucks_irq_types[buck_idx + 3], + bucks_irq_types[buck_idx + 3], nr_types, &irq_idx); if (error) return error; From e64ee27abfe1e9baea14b31c0a6b6bf93ac8652c Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:52 +0200 Subject: [PATCH 28/40] regulator: tps6594-regulator: refactor variant descriptions Instead of using conditionals or tri state operators throughout the .probe() provide a description per variant. This will make it much easier to add new variants later. While at it, make the variable naming more consistent. This patch is only compile-time tested. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-8-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 223 ++++++++++++++------------ 1 file changed, 124 insertions(+), 99 deletions(-) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 2c7c4df80695..39adb2db6de8 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -21,10 +21,6 @@ #define BUCK_NB 5 #define LDO_NB 4 #define MULTI_PHASE_NB 4 -/* TPS6593 and LP8764 supports OV, UV, SC, ILIM */ -#define REGS_INT_NB 4 -/* TPS65224 supports OV or UV */ -#define TPS65224_REGS_INT_NB 1 enum tps6594_regulator_id { /* DCDC's */ @@ -192,7 +188,7 @@ static const struct regulator_ops tps6594_ldos_4_ops = { .map_voltage = regulator_map_voltage_linear_range, }; -static const struct regulator_desc buck_regs[] = { +static const struct regulator_desc tps6594_buck_regs[] = { TPS6594_REGULATOR("BUCK1", "buck1", TPS6594_BUCK_1, REGULATOR_VOLTAGE, tps6594_bucks_ops, TPS6594_MASK_BUCKS_VSET, TPS6594_REG_BUCKX_VOUT_1(0), @@ -549,6 +545,63 @@ static int tps6594_request_reg_irqs(struct platform_device *pdev, return 0; } +struct tps6594_regulator_desc { + const struct regulator_desc *multi_phase_regs; + unsigned int num_multi_phase_regs; + + const struct regulator_desc *buck_regs; + int num_buck_regs; + + const struct regulator_desc *ldo_regs; + int num_ldo_regs; + + const struct tps6594_regulator_irq_type **bucks_irq_types; + const struct tps6594_regulator_irq_type **ldos_irq_types; + int num_irq_types; + + const struct tps6594_regulator_irq_type *ext_irq_types; + int num_ext_irqs; +}; + +static const struct tps6594_regulator_desc tps65224_reg_desc = { + .multi_phase_regs = tps65224_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps65224_multi_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .bucks_irq_types = tps65224_bucks_irq_types, + .ldos_irq_types = tps65224_ldos_irq_types, + .num_irq_types = 1, /* OV or UV */ + .ext_irq_types = tps65224_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types), +}; + +static const struct tps6594_regulator_desc tps6594_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .ldo_regs = tps6594_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps6594_ldo_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .ldos_irq_types = tps6594_ldos_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = 2, /* only VCCA OV and UV */ +}; + +static const struct tps6594_regulator_desc lp8764_reg_desc = { + .multi_phase_regs = tps6594_multi_regs, + .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), + .buck_regs = tps6594_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps6594_buck_regs), + .bucks_irq_types = tps6594_bucks_irq_types, + .num_irq_types = 4, /* OV, UV, SC and ILIM */ + .ext_irq_types = tps6594_ext_regulator_irq_types, + .num_ext_irqs = ARRAY_SIZE(tps6594_ext_regulator_irq_types), +}; + static int tps6594_regulator_probe(struct platform_device *pdev) { struct tps6594 *tps = dev_get_drvdata(pdev->dev.parent); @@ -559,38 +612,32 @@ static int tps6594_regulator_probe(struct platform_device *pdev) struct tps6594_regulator_irq_data *irq_data; struct tps6594_ext_regulator_irq_data *irq_ext_reg_data; const struct tps6594_regulator_irq_type *irq_type; - const struct tps6594_regulator_irq_type *irq_types; bool buck_configured[BUCK_NB] = { false }; bool buck_multi[MULTI_PHASE_NB] = { false }; + const struct tps6594_regulator_desc *desc; + const struct regulator_desc *multi_regs; const char *npname; int error, i, irq, multi; int irq_idx = 0; int buck_idx = 0; - int nr_ldo; - int nr_buck; - int nr_types; - unsigned int irq_count; - unsigned int multi_phase_cnt; size_t reg_irq_nb; - const struct tps6594_regulator_irq_type **bucks_irq_types; - const struct regulator_desc *multi_regs; - const struct tps6594_regulator_irq_type **ldos_irq_types; - const struct regulator_desc *ldo_regs; - if (tps->chip_id == TPS65224) { - bucks_irq_types = tps65224_bucks_irq_types; - multi_regs = tps65224_multi_regs; - ldos_irq_types = tps65224_ldos_irq_types; - ldo_regs = tps65224_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps65224_multi_regs); - } else { - bucks_irq_types = tps6594_bucks_irq_types; - multi_regs = tps6594_multi_regs; - ldos_irq_types = tps6594_ldos_irq_types; - ldo_regs = tps6594_ldo_regs; - multi_phase_cnt = ARRAY_SIZE(tps6594_multi_regs); - } + switch (tps->chip_id) { + case TPS65224: + desc = &tps65224_reg_desc; + break; + case TPS6594: + case TPS6593: + desc = &tps6594_reg_desc; + break; + case LP8764: + desc = &lp8764_reg_desc; + break; + default: + dev_err(tps->dev, "unknown chip_id %lu\n", tps->chip_id); + return -EINVAL; + }; enum { MULTI_BUCK12, @@ -611,13 +658,14 @@ static int tps6594_regulator_probe(struct platform_device *pdev) * In case of Multiphase configuration, value should be defined for * buck_configured to avoid creating bucks for every buck in multiphase */ - for (multi = 0; multi < multi_phase_cnt; multi++) { - np = of_find_node_by_name(tps->dev->of_node, multi_regs[multi].supply_name); + for (multi = 0; multi < desc->num_multi_phase_regs; multi++) { + multi_regs = &desc->multi_phase_regs[multi]; + np = of_find_node_by_name(tps->dev->of_node, multi_regs->supply_name); npname = of_node_full_name(np); np_pmic_parent = of_get_parent(of_get_parent(np)); if (of_node_cmp(of_node_full_name(np_pmic_parent), tps->dev->of_node->full_name)) continue; - if (strcmp(npname, multi_regs[multi].supply_name) == 0) { + if (strcmp(npname, multi_regs->supply_name) == 0) { switch (multi) { case MULTI_BUCK12: buck_multi[0] = true; @@ -650,28 +698,19 @@ static int tps6594_regulator_probe(struct platform_device *pdev) } } - if (tps->chip_id == TPS65224) { - nr_buck = ARRAY_SIZE(tps65224_buck_regs); - nr_ldo = ARRAY_SIZE(tps65224_ldo_regs); - nr_types = TPS65224_REGS_INT_NB; - } else { - nr_buck = ARRAY_SIZE(buck_regs); - nr_ldo = (tps->chip_id == LP8764) ? 0 : ARRAY_SIZE(tps6594_ldo_regs); - nr_types = REGS_INT_NB; - } - - reg_irq_nb = nr_types * (nr_buck + nr_ldo); + reg_irq_nb = desc->num_irq_types * (desc->num_buck_regs + desc->num_ldo_regs); irq_data = devm_kmalloc_array(tps->dev, reg_irq_nb, sizeof(struct tps6594_regulator_irq_data), GFP_KERNEL); if (!irq_data) return -ENOMEM; - for (i = 0; i < multi_phase_cnt; i++) { + for (i = 0; i < desc->num_multi_phase_regs; i++) { if (!buck_multi[i]) continue; - rdev = devm_regulator_register(&pdev->dev, &multi_regs[i], &config); + rdev = devm_regulator_register(&pdev->dev, &desc->multi_phase_regs[i], + &config); if (IS_ERR(rdev)) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", @@ -682,89 +721,74 @@ static int tps6594_regulator_probe(struct platform_device *pdev) buck_idx = 2; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx], + desc->num_irq_types, &irq_idx); if (error) return error; error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 1], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx + 1], + desc->num_irq_types, &irq_idx); if (error) return error; if (i == MULTI_BUCK123 || i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 2], - nr_types, &irq_idx); + desc->bucks_irq_types[buck_idx + 2], + desc->num_irq_types, + &irq_idx); if (error) return error; } if (i == MULTI_BUCK1234) { error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[buck_idx + 3], - nr_types, &irq_idx); - if (error) - return error; - } - } - - for (i = 0; i < nr_buck; i++) { - if (buck_configured[i]) - continue; - - const struct regulator_desc *buck_cfg = (tps->chip_id == TPS65224) ? - tps65224_buck_regs : buck_regs; - - rdev = devm_regulator_register(&pdev->dev, &buck_cfg[i], &config); - if (IS_ERR(rdev)) - return dev_err_probe(tps->dev, PTR_ERR(rdev), - "failed to register %s regulator\n", pdev->name); - - error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - bucks_irq_types[i], nr_types, &irq_idx); - if (error) - return error; - } - - /* LP8764 doesn't have LDO */ - if (tps->chip_id != LP8764) { - for (i = 0; i < nr_ldo; i++) { - rdev = devm_regulator_register(&pdev->dev, &ldo_regs[i], &config); - if (IS_ERR(rdev)) - return dev_err_probe(tps->dev, PTR_ERR(rdev), - "failed to register %s regulator\n", - pdev->name); - - error = tps6594_request_reg_irqs(pdev, rdev, irq_data, - ldos_irq_types[i], nr_types, + desc->bucks_irq_types[buck_idx + 3], + desc->num_irq_types, &irq_idx); if (error) return error; } } - if (tps->chip_id == TPS65224) { - irq_types = tps65224_ext_regulator_irq_types; - irq_count = ARRAY_SIZE(tps65224_ext_regulator_irq_types); - } else { - irq_types = tps6594_ext_regulator_irq_types; - if (tps->chip_id == LP8764) - irq_count = ARRAY_SIZE(tps6594_ext_regulator_irq_types); - else - /* TPS6593 supports only VCCA OV and UV */ - irq_count = 2; + for (i = 0; i < desc->num_buck_regs; i++) { + if (buck_configured[i]) + continue; + + rdev = devm_regulator_register(&pdev->dev, &desc->buck_regs[i], &config); + if (IS_ERR(rdev)) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "failed to register %s regulator\n", pdev->name); + + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, + desc->bucks_irq_types[i], + desc->num_irq_types, &irq_idx); + if (error) + return error; + } + + for (i = 0; i < desc->num_ldo_regs; i++) { + rdev = devm_regulator_register(&pdev->dev, &desc->ldo_regs[i], &config); + if (IS_ERR(rdev)) + return dev_err_probe(tps->dev, PTR_ERR(rdev), + "failed to register %s regulator\n", + pdev->name); + + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, + desc->ldos_irq_types[i], + desc->num_irq_types, &irq_idx); + if (error) + return error; } irq_ext_reg_data = devm_kmalloc_array(tps->dev, - irq_count, + desc->num_ext_irqs, sizeof(struct tps6594_ext_regulator_irq_data), GFP_KERNEL); if (!irq_ext_reg_data) return -ENOMEM; - for (i = 0; i < irq_count; ++i) { - irq_type = &irq_types[i]; + for (i = 0; i < desc->num_ext_irqs; ++i) { + irq_type = &desc->ext_irq_types[i]; irq = platform_get_irq_byname(pdev, irq_type->irq_name); if (irq < 0) return -EINVAL; @@ -782,6 +806,7 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to request %s IRQ %d\n", irq_type->irq_name, irq); } + return 0; } From b30d390812c8559c5835f8ae5f490b38488fafc8 Mon Sep 17 00:00:00 2001 From: Michael Walle Date: Thu, 3 Jul 2025 13:31:53 +0200 Subject: [PATCH 29/40] regulator: tps6594-regulator: Add TI TPS652G1 PMIC regulators The TI TPS652G1 is a stripped down version of the TPS65224 PMIC. It doesn't feature the multiphase buck converter nor any voltage monitoring. Due to the latter there are no interrupts serviced. In case of the TPS652G1 any interrupt related setup is just skipped. Signed-off-by: Michael Walle Acked-by: Mark Brown Link: https://patch.msgid.link/20250703113153.2447110-9-mwalle@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6594-regulator.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/regulator/tps6594-regulator.c b/drivers/regulator/tps6594-regulator.c index 39adb2db6de8..ab882daec7c5 100644 --- a/drivers/regulator/tps6594-regulator.c +++ b/drivers/regulator/tps6594-regulator.c @@ -577,6 +577,13 @@ static const struct tps6594_regulator_desc tps65224_reg_desc = { .num_ext_irqs = ARRAY_SIZE(tps65224_ext_regulator_irq_types), }; +static const struct tps6594_regulator_desc tps652g1_reg_desc = { + .ldo_regs = tps65224_ldo_regs, + .num_ldo_regs = ARRAY_SIZE(tps65224_ldo_regs), + .buck_regs = tps65224_buck_regs, + .num_buck_regs = ARRAY_SIZE(tps65224_buck_regs), +}; + static const struct tps6594_regulator_desc tps6594_reg_desc = { .multi_phase_regs = tps6594_multi_regs, .num_multi_phase_regs = ARRAY_SIZE(tps6594_multi_regs), @@ -627,6 +634,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) case TPS65224: desc = &tps65224_reg_desc; break; + case TPS652G1: + desc = &tps652g1_reg_desc; + break; case TPS6594: case TPS6593: desc = &tps6594_reg_desc; @@ -716,6 +726,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + /* config multiphase buck12+buck34 */ if (i == MULTI_BUCK12_34) buck_idx = 2; @@ -759,6 +772,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) return dev_err_probe(tps->dev, PTR_ERR(rdev), "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, desc->bucks_irq_types[i], desc->num_irq_types, &irq_idx); @@ -773,6 +789,9 @@ static int tps6594_regulator_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); + if (!desc->num_irq_types) + continue; + error = tps6594_request_reg_irqs(pdev, rdev, irq_data, desc->ldos_irq_types[i], desc->num_irq_types, &irq_idx); From 729ff4a936c6f3faba78aaa8bc4291b6477c6576 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:39 +0200 Subject: [PATCH 30/40] regulator: dt-bindings: qcom,rpmh: Add PM7550 compatible Add the PM7550 compatible for the regulators in the PMIC found with the Milos SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-1-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- .../regulator/qcom,rpmh-regulator.yaml | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3a5a0a6cf5cc..3dd150e5dad8 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -40,6 +40,7 @@ description: | For PM660, smps1 - smps6, ldo1 - ldo3, ldo5 - ldo19 For PM660L, smps1 - smps3, smps5, ldo1 - ldo8, bob For PM7325, smps1 - smps8, ldo1 - ldo19 + For PM7550, smps1 - smps6, ldo1 - ldo23, bob For PM8005, smps1 - smps4 For PM8009, smps1 - smps2, ldo1 - ldo7 For PM8010, ldo1 - ldo7 @@ -66,6 +67,7 @@ properties: - qcom,pm660-rpmh-regulators - qcom,pm660l-rpmh-regulators - qcom,pm7325-rpmh-regulators + - qcom,pm7550-rpmh-regulators - qcom,pm8005-rpmh-regulators - qcom,pm8009-rpmh-regulators - qcom,pm8009-1-rpmh-regulators @@ -218,6 +220,25 @@ allOf: "^vdd-l[358]-supply$": true "^vdd-s[1-8]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pm7550-rpmh-regulators + then: + properties: + vdd-bob-supply: + description: BOB regulator parent supply phandle. + vdd-l2-l3-supply: true + vdd-l4-l5-supply: true + vdd-l9-l10-supply: true + vdd-l12-l14-supply: true + vdd-l13-l16-supply: true + vdd-l15-l17-l18-l19-l20-l21-l22-l23-supply: true + patternProperties: + "^vdd-l(1|[6-8]|11)-supply$": true + "^vdd-s[1-6]-supply$": true + - if: properties: compatible: From 20a01de0808364c26836cc8f47ed3b59a40a927d Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:40 +0200 Subject: [PATCH 31/40] regulator: dt-bindings: qcom,rpmh: Add PMR735B compatible Add the PMR735B compatible for the regulators in the PMIC found with the Milos SoC. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-2-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- .../bindings/regulator/qcom,rpmh-regulator.yaml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml index 3dd150e5dad8..4c5b0629aa3e 100644 --- a/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml +++ b/Documentation/devicetree/bindings/regulator/qcom,rpmh-regulator.yaml @@ -54,6 +54,7 @@ description: | For PMI8998, bob For PMC8380, smps1 - smps8, ldo1 - lodo3 For PMR735A, smps1 - smps3, ldo1 - ldo7 + For PMR735B, ldo1 - ldo12 For PMX55, smps1 - smps7, ldo1 - ldo16 For PMX65, smps1 - smps8, ldo1 - ldo21 For PMX75, smps1 - smps10, ldo1 - ldo21 @@ -89,6 +90,7 @@ properties: - qcom,pmm8155au-rpmh-regulators - qcom,pmm8654au-rpmh-regulators - qcom,pmr735a-rpmh-regulators + - qcom,pmr735b-rpmh-regulators - qcom,pmx55-rpmh-regulators - qcom,pmx65-rpmh-regulators - qcom,pmx75-rpmh-regulators @@ -445,6 +447,18 @@ allOf: patternProperties: "^vdd-s[1-3]-supply$": true + - if: + properties: + compatible: + enum: + - qcom,pmr735b-rpmh-regulators + then: + properties: + vdd-l1-l2-supply: true + vdd-l7-l8-supply: true + patternProperties: + "^vdd-l([3-6]|9|1[0-2])-supply$": true + - if: properties: compatible: From 28758434900ff4c4dce4e104fb5982ef3c0141ba Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:41 +0200 Subject: [PATCH 32/40] regulator: qcom-rpmh: add support for pmr735b regulators Add RPMH regulators exposed by Qualcomm Technologies, Inc. PMR735B PMIC. It has 12 LDOs with 2 different types, L4 & L10 are LDO512 LV PMOS and the rest are LDO512 NMOS. Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-3-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 7870722b6ee2..7b1743d51fd1 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1476,6 +1476,22 @@ static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pmr735b_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo_lv, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), + {} +}; + static const struct rpmh_vreg_init_data pm660_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), @@ -1667,6 +1683,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pmr735a-rpmh-regulators", .data = pmr735a_vreg_data, }, + { + .compatible = "qcom,pmr735b-rpmh-regulators", + .data = pmr735b_vreg_data, + }, { .compatible = "qcom,pm660-rpmh-regulators", .data = pm660_vreg_data, From 3aa47d2ec83316c24e1ed15a492b331802dc6a69 Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Fri, 11 Jul 2025 09:28:42 +0200 Subject: [PATCH 33/40] regulator: qcom-rpmh: add support for pm7550 regulators Add RPMH regulators exposed by Qualcomm Technologies, Inc. PM7550 PMIC. It has 6 FTS525 (FT-SMPS) and 23 LDOs with 3 different types. L1-L11 are LDO515 LV NMOS, L12-L13 are LDO515 MV PMOS, L14-L23 are LDO512 MV PMOS. Reviewed-by: Konrad Dybcio Signed-off-by: Luca Weiss Link: https://patch.msgid.link/20250711-pm7550-pmr735b-rpmh-regs-v2-4-bca8cc15c199@fairphone.com Signed-off-by: Mark Brown --- drivers/regulator/qcom-rpmh-regulator.c | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/regulator/qcom-rpmh-regulator.c b/drivers/regulator/qcom-rpmh-regulator.c index 7b1743d51fd1..109f0aae09b1 100644 --- a/drivers/regulator/qcom-rpmh-regulator.c +++ b/drivers/regulator/qcom-rpmh-regulator.c @@ -1462,6 +1462,40 @@ static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { {} }; +static const struct rpmh_vreg_init_data pm7550_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-l4-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo515_mv, "vdd-l12-l14"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo515_mv, "vdd-l13-l16"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l12-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo22", "ldo%s22", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("ldo23", "ldo%s23", &pmic5_pldo, "vdd-l15-l17-l18-l19-l20-l21-l22-l23"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), @@ -1679,6 +1713,10 @@ static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { .compatible = "qcom,pm7325-rpmh-regulators", .data = pm7325_vreg_data, }, + { + .compatible = "qcom,pm7550-rpmh-regulators", + .data = pm7550_vreg_data, + }, { .compatible = "qcom,pmr735a-rpmh-regulators", .data = pmr735a_vreg_data, From 43728a6434f9eca0385fd180d8452a5071678a5b Mon Sep 17 00:00:00 2001 From: Jisheng Zhang Date: Mon, 14 Jul 2025 09:04:56 +0800 Subject: [PATCH 34/40] regulator: tps6286x-regulator: Fix a copy & paste error The volatile_reg function is named as tps6287x_volatile_reg by mistake when enabing the REGCACHE_MAPLE support. Signed-off-by: Jisheng Zhang Link: https://patch.msgid.link/20250714010456.4906-1-jszhang@kernel.org Signed-off-by: Mark Brown --- drivers/regulator/tps6286x-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/tps6286x-regulator.c b/drivers/regulator/tps6286x-regulator.c index 778f169b0acc..e29aab06bf79 100644 --- a/drivers/regulator/tps6286x-regulator.c +++ b/drivers/regulator/tps6286x-regulator.c @@ -25,7 +25,7 @@ #define TPS6286X_MAX_MV 1675 #define TPS6286X_STEP_MV 5 -static bool tps6287x_volatile_reg(struct device *dev, unsigned int reg) +static bool tps6286x_volatile_reg(struct device *dev, unsigned int reg) { return reg == TPS6286X_STATUS; } @@ -34,7 +34,7 @@ static const struct regmap_config tps6286x_regmap_config = { .reg_bits = 8, .val_bits = 8, .cache_type = REGCACHE_MAPLE, - .volatile_reg = tps6287x_volatile_reg, + .volatile_reg = tps6286x_volatile_reg, }; static int tps6286x_set_mode(struct regulator_dev *rdev, unsigned int mode) From 9b614ceada7cb846de1a1c3bb0b29b0a2726ef45 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 14 Jul 2025 15:52:04 -0300 Subject: [PATCH 35/40] rust: regulator: add a bare minimum regulator abstraction Add a bare minimum regulator abstraction to be used by Rust drivers. This abstraction adds a small subset of the regulator API, which is thought to be sufficient for the drivers we have now. Regulators provide the power needed by many hardware blocks and thus are likely to be needed by a lot of drivers. It was tested on rk3588, where it was used to power up the "mali" regulator in order to power up the GPU. Reviewed-by: Alexandre Courbot Signed-off-by: Daniel Almeida Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20250714-topics-tyr-regulator2-v8-1-c7ab3955d524@collabora.com Signed-off-by: Mark Brown --- rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/regulator.c | 43 ++++ rust/kernel/lib.rs | 1 + rust/kernel/regulator.rs | 418 ++++++++++++++++++++++++++++++++ 5 files changed, 464 insertions(+) create mode 100644 rust/helpers/regulator.c create mode 100644 rust/kernel/regulator.rs diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index 8cbb660e2ec2..2d51f9d05609 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -65,6 +65,7 @@ #include #include #include +#include #include #include #include diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index b15b3cddad4e..f1f26ba91d97 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -33,6 +33,7 @@ #include "pci.c" #include "pid_namespace.c" #include "rbtree.c" +#include "regulator.c" #include "rcu.c" #include "refcount.c" #include "security.c" diff --git a/rust/helpers/regulator.c b/rust/helpers/regulator.c new file mode 100644 index 000000000000..cd8b7ba648ee --- /dev/null +++ b/rust/helpers/regulator.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +#ifndef CONFIG_REGULATOR + +void rust_helper_regulator_put(struct regulator *regulator) +{ + regulator_put(regulator); +} + +int rust_helper_regulator_set_voltage(struct regulator *regulator, int min_uV, + int max_uV) +{ + return regulator_set_voltage(regulator, min_uV, max_uV); +} + +int rust_helper_regulator_get_voltage(struct regulator *regulator) +{ + return regulator_get_voltage(regulator); +} + +struct regulator *rust_helper_regulator_get(struct device *dev, const char *id) +{ + return regulator_get(dev, id); +} + +int rust_helper_regulator_enable(struct regulator *regulator) +{ + return regulator_enable(regulator); +} + +int rust_helper_regulator_disable(struct regulator *regulator) +{ + return regulator_disable(regulator); +} + +int rust_helper_regulator_is_enabled(struct regulator *regulator) +{ + return regulator_is_enabled(regulator); +} + +#endif diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c3..5e4cd8c5e6ff 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -100,6 +100,7 @@ pub mod prelude; pub mod print; pub mod rbtree; +pub mod regulator; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs new file mode 100644 index 000000000000..65f3a125348f --- /dev/null +++ b/rust/kernel/regulator.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Regulator abstractions, providing a standard kernel interface to control +//! voltage and current regulators. +//! +//! The intention is to allow systems to dynamically control regulator power +//! output in order to save power and prolong battery life. This applies to both +//! voltage regulators (where voltage output is controllable) and current sinks +//! (where current limit is controllable). +//! +//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h) +//! +//! Regulators are modeled in Rust with a collection of states. Each state may +//! enforce a given invariant, and they may convert between each other where applicable. +//! +//! See [Voltage and current regulator API](https://docs.kernel.org/driver-api/regulator.html) +//! for more information. + +use crate::{ + bindings, + device::Device, + error::{from_err_ptr, to_result, Result}, + prelude::*, +}; + +use core::{marker::PhantomData, mem::ManuallyDrop, ptr::NonNull}; + +mod private { + pub trait Sealed {} + + impl Sealed for super::Enabled {} + impl Sealed for super::Disabled {} + impl Sealed for super::Dynamic {} +} + +/// A trait representing the different states a [`Regulator`] can be in. +pub trait RegulatorState: private::Sealed + 'static { + /// Whether the regulator should be disabled when dropped. + const DISABLE_ON_DROP: bool; +} + +/// A state where the [`Regulator`] is known to be enabled. +/// +/// The `enable` reference count held by this state is decremented when it is +/// dropped. +pub struct Enabled; + +/// A state where this [`Regulator`] handle has not specifically asked for the +/// underlying regulator to be enabled. This means that this reference does not +/// own an `enable` reference count, but the regulator may still be on. +pub struct Disabled; + +/// A state that models the C API. The [`Regulator`] can be either enabled or +/// disabled, and the user is in control of the reference count. This is also +/// the default state. +/// +/// Use [`Regulator::is_enabled`] to check the regulator's current state. +pub struct Dynamic; + +impl RegulatorState for Enabled { + const DISABLE_ON_DROP: bool = true; +} + +impl RegulatorState for Disabled { + const DISABLE_ON_DROP: bool = false; +} + +impl RegulatorState for Dynamic { + const DISABLE_ON_DROP: bool = false; +} + +/// A trait that abstracts the ability to check if a [`Regulator`] is enabled. +pub trait IsEnabled: RegulatorState {} +impl IsEnabled for Disabled {} +impl IsEnabled for Dynamic {} + +/// An error that can occur when trying to convert a [`Regulator`] between states. +pub struct Error { + /// The error that occurred. + pub error: kernel::error::Error, + + /// The regulator that caused the error, so that the operation may be retried. + pub regulator: Regulator, +} + +/// A `struct regulator` abstraction. +/// +/// # Examples +/// +/// ## Enabling a regulator +/// +/// This example uses [`Regulator`], which is suitable for drivers that +/// enable a regulator at probe time and leave them on until the device is +/// removed or otherwise shutdown. +/// +/// These users can store [`Regulator`] directly in their driver's +/// private data struct. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Disabled, Enabled}; +/// fn enable(dev: &Device, min_voltage: Voltage, max_voltage: Voltage) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // The voltage can be set before enabling the regulator if needed, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Enables the regulator, consuming the previous value. +/// // +/// // From now on, the regulator is known to be enabled because of the type +/// // `Enabled`. +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_enabled().map_err(|error| error.error)?; +/// +/// // The voltage can also be set after enabling the regulator, e.g.: +/// regulator.set_voltage(min_voltage, max_voltage)?; +/// +/// // The same applies for `get_voltage()`, i.e.: +/// let voltage: Voltage = regulator.get_voltage()?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// A more concise shortcut is available for enabling a regulator. This is +/// equivalent to `regulator_get_enable()`: +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Voltage, Regulator, Enabled}; +/// fn enable(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator and enable it. +/// let regulator: Regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// // Dropping an enabled regulator will disable it. The refcount will be +/// // decremented. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Disabling a regulator +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Enabled, Disabled}; +/// fn disable(dev: &Device, regulator: Regulator) -> Result { +/// // We can also disable an enabled regulator without reliquinshing our +/// // refcount: +/// // +/// // If this operation fails, the `Error` will contain the regulator +/// // reference, so that the operation may be retried. +/// let regulator: Regulator = +/// regulator.try_into_disabled().map_err(|error| error.error)?; +/// +/// // The refcount will be decremented when `regulator` is dropped. +/// drop(regulator); +/// +/// // ... +/// +/// Ok(()) +/// } +/// ``` +/// +/// ## Using [`Regulator`] +/// +/// This example mimics the behavior of the C API, where the user is in +/// control of the enabled reference count. This is useful for drivers that +/// might call enable and disable to manage the `enable` reference count at +/// runtime, perhaps as a result of `open()` and `close()` calls or whatever +/// other driver-specific or subsystem-specific hooks. +/// +/// ``` +/// # use kernel::prelude::*; +/// # use kernel::c_str; +/// # use kernel::device::Device; +/// # use kernel::regulator::{Regulator, Dynamic}; +/// struct PrivateData { +/// regulator: Regulator, +/// } +/// +/// // A fictictious probe function that obtains a regulator and sets it up. +/// fn probe(dev: &Device) -> Result { +/// // Obtain a reference to a (fictitious) regulator. +/// let mut regulator = Regulator::::get(dev, c_str!("vcc"))?; +/// +/// Ok(PrivateData { regulator }) +/// } +/// +/// // A fictictious function that indicates that the device is going to be used. +/// fn open(dev: &Device, data: &mut PrivateData) -> Result { +/// // Increase the `enabled` reference count. +/// data.regulator.enable()?; +/// +/// Ok(()) +/// } +/// +/// fn close(dev: &Device, data: &mut PrivateData) -> Result { +/// // Decrease the `enabled` reference count. +/// data.regulator.disable()?; +/// +/// Ok(()) +/// } +/// +/// fn remove(dev: &Device, data: PrivateData) -> Result { +/// // `PrivateData` is dropped here, which will drop the +/// // `Regulator` in turn. +/// // +/// // The reference that was obtained by `regulator_get()` will be +/// // released, but it is up to the user to make sure that the number of calls +/// // to `enable()` and `disabled()` are balanced before this point. +/// Ok(()) +/// } +/// ``` +/// +/// # Invariants +/// +/// - `inner` is a non-null wrapper over a pointer to a `struct +/// regulator` obtained from [`regulator_get()`]. +/// +/// [`regulator_get()`]: https://docs.kernel.org/driver-api/regulator.html#c.regulator_get +pub struct Regulator +where + State: RegulatorState, +{ + inner: NonNull, + _phantom: PhantomData, +} + +impl Regulator { + /// Sets the voltage for the regulator. + /// + /// This can be used to ensure that the device powers up cleanly. + pub fn set_voltage(&self, min_voltage: Voltage, max_voltage: Voltage) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { + bindings::regulator_set_voltage( + self.inner.as_ptr(), + min_voltage.as_microvolts(), + max_voltage.as_microvolts(), + ) + }) + } + + /// Gets the current voltage of the regulator. + pub fn get_voltage(&self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + let voltage = unsafe { bindings::regulator_get_voltage(self.inner.as_ptr()) }; + if voltage < 0 { + Err(kernel::error::Error::from_errno(voltage)) + } else { + Ok(Voltage::from_microvolts(voltage)) + } + } + + fn get_internal(dev: &Device, name: &CStr) -> Result> { + // SAFETY: It is safe to call `regulator_get()`, on a device pointer + // received from the C code. + let inner = from_err_ptr(unsafe { bindings::regulator_get(dev.as_raw(), name.as_ptr()) })?; + + // SAFETY: We can safely trust `inner` to be a pointer to a valid + // regulator if `ERR_PTR` was not returned. + let inner = unsafe { NonNull::new_unchecked(inner) }; + + Ok(Self { + inner, + _phantom: PhantomData, + }) + } + + fn enable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) }) + } + + fn disable_internal(&mut self) -> Result { + // SAFETY: Safe as per the type invariants of `Regulator`. + to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Attempts to convert the regulator to an enabled state. + pub fn try_into_enabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count to + // `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .enable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system and enables it. + /// + /// This is equivalent to calling `regulator_get_enable()` in the C API. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::::get_internal(dev, name)? + .try_into_enabled() + .map_err(|error| error.error) + } + + /// Attempts to convert the regulator to a disabled state. + pub fn try_into_disabled(self) -> Result, Error> { + // We will be transferring the ownership of our `regulator_get()` count + // to `Regulator`. + let mut regulator = ManuallyDrop::new(self); + + regulator + .disable_internal() + .map(|()| Regulator { + inner: regulator.inner, + _phantom: PhantomData, + }) + .map_err(|error| Error { + error, + regulator: ManuallyDrop::into_inner(regulator), + }) + } +} + +impl Regulator { + /// Obtains a [`Regulator`] instance from the system. The current state of + /// the regulator is unknown and it is up to the user to manage the enabled + /// reference count. + /// + /// This closely mimics the behavior of the C API and can be used to + /// dynamically manage the enabled reference count at runtime. + pub fn get(dev: &Device, name: &CStr) -> Result { + Regulator::get_internal(dev, name) + } + + /// Increases the `enabled` reference count. + pub fn enable(&mut self) -> Result { + self.enable_internal() + } + + /// Decreases the `enabled` reference count. + pub fn disable(&mut self) -> Result { + self.disable_internal() + } +} + +impl Regulator { + /// Checks if the regulator is enabled. + pub fn is_enabled(&self) -> bool { + // SAFETY: Safe as per the type invariants of `Regulator`. + unsafe { bindings::regulator_is_enabled(self.inner.as_ptr()) != 0 } + } +} + +impl Drop for Regulator { + fn drop(&mut self) { + if T::DISABLE_ON_DROP { + // SAFETY: By the type invariants, we know that `self` owns a + // reference on the enabled refcount, so it is safe to relinquish it + // now. + unsafe { bindings::regulator_disable(self.inner.as_ptr()) }; + } + // SAFETY: By the type invariants, we know that `self` owns a reference, + // so it is safe to relinquish it now. + unsafe { bindings::regulator_put(self.inner.as_ptr()) }; + } +} + +/// A voltage. +/// +/// This type represents a voltage value in microvolts. +#[repr(transparent)] +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct Voltage(i32); + +impl Voltage { + /// Creates a new `Voltage` from a value in microvolts. + pub fn from_microvolts(uv: i32) -> Self { + Self(uv) + } + + /// Returns the value of the voltage in microvolts as an [`i32`]. + pub fn as_microvolts(self) -> i32 { + self.0 + } +} From d9f334fca5448907cc47ba8553926f9ba148512f Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 14 Jul 2025 15:52:05 -0300 Subject: [PATCH 36/40] MAINTAINERS: add regulator.rs to the regulator API entry Add this file to the regulator API entry as requested by Mark Brown. Signed-off-by: Daniel Almeida Acked-by: Miguel Ojeda Link: https://patch.msgid.link/20250714-topics-tyr-regulator2-v8-2-c7ab3955d524@collabora.com Signed-off-by: Mark Brown --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 60bba48f5479..09ff9ae60e6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -26561,6 +26561,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git F: Documentation/devicetree/bindings/regulator/ F: Documentation/power/regulator/ F: drivers/regulator/ +F: rust/kernel/regulator.rs F: include/dt-bindings/regulator/ F: include/linux/regulator/ K: regulator_get_optional From 63be976da994260ea116c431a2e61485dbede1b0 Mon Sep 17 00:00:00 2001 From: Jeff Chang Date: Wed, 16 Jul 2025 10:08:30 +0800 Subject: [PATCH 37/40] regulator: rt6160: Add rt6166 vout min_uV setting for compatible 1. remove unintentional GPL change 2. using switch case for Device ID probe check. Signed-off-by: Jeff Chang Link: https://patch.msgid.link/20250716021230.2660564-1-jeff_chang@richtek.com Signed-off-by: Mark Brown --- drivers/regulator/rt6160-regulator.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/rt6160-regulator.c b/drivers/regulator/rt6160-regulator.c index e2a0eee95c61..548ffdf537d3 100644 --- a/drivers/regulator/rt6160-regulator.c +++ b/drivers/regulator/rt6160-regulator.c @@ -31,8 +31,11 @@ #define RT6160_PGSTAT_MASK BIT(0) #define RT6160_VENDOR_ID 0xA0 +#define RT6166_VENDOR_ID 0xB0 #define RT6160_VOUT_MINUV 2025000 #define RT6160_VOUT_MAXUV 5200000 +#define RT6166_VOUT_MINUV 1800000 +#define RT6166_VOUD_MAXUV 4950000 #define RT6160_VOUT_STPUV 25000 #define RT6160_N_VOUTS ((RT6160_VOUT_MAXUV - RT6160_VOUT_MINUV) / RT6160_VOUT_STPUV + 1) @@ -43,6 +46,7 @@ struct rt6160_priv { struct gpio_desc *enable_gpio; struct regmap *regmap; bool enable_state; + uint8_t devid; }; static const unsigned int rt6160_ramp_tables[] = { @@ -260,15 +264,26 @@ static int rt6160_probe(struct i2c_client *i2c) if (ret) return ret; - if ((devid & RT6160_VID_MASK) != RT6160_VENDOR_ID) { + devid = devid & RT6160_VID_MASK; + + switch (devid) { + case RT6166_VENDOR_ID: + case RT6160_VENDOR_ID: + break; + default: dev_err(&i2c->dev, "VID not correct [0x%02x]\n", devid); return -ENODEV; } + priv->devid = devid; + priv->desc.name = "rt6160-buckboost"; priv->desc.type = REGULATOR_VOLTAGE; priv->desc.owner = THIS_MODULE; - priv->desc.min_uV = RT6160_VOUT_MINUV; + if (priv->devid == RT6166_VENDOR_ID) + priv->desc.min_uV = RT6166_VOUT_MINUV; + else + priv->desc.min_uV = RT6160_VOUT_MINUV; priv->desc.uV_step = RT6160_VOUT_STPUV; if (vsel_active_low) priv->desc.vsel_reg = RT6160_REG_VSELL; From d511206dc7443120637efd9cfa3ab06a26da33dd Mon Sep 17 00:00:00 2001 From: Romain Gantois Date: Fri, 18 Jul 2025 16:11:36 +0200 Subject: [PATCH 38/40] regulator: core: repeat voltage setting request for stepped regulators The regulator_set_voltage() function may exhibit unexpected behavior if the target regulator has a maximum voltage step constraint. With such a constraint, the regulator core may clamp the requested voltage to a lesser value, to ensure that the voltage delta stays under the specified limit. This means that the resulting regulator voltage depends on the current voltage, as well as the requested range, which invalidates the assumption that a repeated request for a specific voltage range will amount to a noop. Considering the case of a regulator with a maximum voltage step constraint of 1V: initial voltage: 2.5V consumer requests 4V expected result: 3.5V resulting voltage: 3.5V consumer requests 4V again expected result: 4V actual result: 3.5V Correct this by repeating attempts to balance the regulator voltage until the result converges. Signed-off-by: Romain Gantois Link: https://patch.msgid.link/20250718-regulator-stepping-v2-1-e28c9ac5d54a@bootlin.com Signed-off-by: Mark Brown --- drivers/regulator/core.c | 43 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index cbd6d53ebfb5..8ed9b96518cf 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3797,6 +3797,16 @@ static int _regulator_do_set_suspend_voltage(struct regulator_dev *rdev, return 0; } +static int regulator_get_voltage_delta(struct regulator_dev *rdev, int uV) +{ + int current_uV = regulator_get_voltage_rdev(rdev); + + if (current_uV < 0) + return current_uV; + + return abs(current_uV - uV); +} + static int regulator_set_voltage_unlocked(struct regulator *regulator, int min_uV, int max_uV, suspend_state_t state) @@ -3804,8 +3814,8 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, struct regulator_dev *rdev = regulator->rdev; struct regulator_voltage *voltage = ®ulator->voltage[state]; int ret = 0; + int current_uV, delta, new_delta; int old_min_uV, old_max_uV; - int current_uV; /* If we're setting the same range as last time the change * should be a noop (some cpufreq implementations use the same @@ -3852,6 +3862,37 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator, voltage->max_uV = old_max_uV; } + if (rdev->constraints->max_uV_step > 0) { + /* For regulators with a maximum voltage step, reaching the desired + * voltage might take a few retries. + */ + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + delta = ret; + + while (delta > 0) { + ret = regulator_balance_voltage(rdev, state); + if (ret < 0) + goto out; + + ret = regulator_get_voltage_delta(rdev, min_uV); + if (ret < 0) + goto out; + + new_delta = ret; + + /* check that voltage is converging quickly enough */ + if (new_delta - delta > rdev->constraints->max_uV_step) { + ret = -EWOULDBLOCK; + goto out; + } + + delta = new_delta; + } + } + out: return ret; } From 71d141edbfa3e0a213c537e979790835550270d6 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Jul 2025 12:31:12 +0100 Subject: [PATCH 39/40] regulator: Kconfig: Fix spelling mistake "regualtor" -> "regulator" There is a spelling mistake in the REGULATOR_RT4803 config. Fix it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250724113113.143009-1-colin.i.king@gmail.com 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 7423954153b0..eaa6df1c9f80 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1203,7 +1203,7 @@ config REGULATOR_RT4801 The device supports two regulators (DSVP/DSVN). config REGULATOR_RT4803 - tristate "Richtek RT4803 boost regualtor" + tristate "Richtek RT4803 boost regulator" depends on I2C select REGMAP_I2C help From 0bd042ae771d61ef7ccd5882f7aeca59a25f71d9 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Thu, 24 Jul 2025 12:48:32 +0100 Subject: [PATCH 40/40] regulator: mt6370: Fix spelling mistake in mt6370_regualtor_register The function name mt6370_regualtor_register contains a spelling mistake, fix it. Signed-off-by: Colin Ian King Link: https://patch.msgid.link/20250724114832.146718-1-colin.i.king@gmail.com Signed-off-by: Mark Brown --- drivers/regulator/mt6370-regulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/regulator/mt6370-regulator.c b/drivers/regulator/mt6370-regulator.c index 27cb32b726e0..c2cea904b0ca 100644 --- a/drivers/regulator/mt6370-regulator.c +++ b/drivers/regulator/mt6370-regulator.c @@ -320,7 +320,7 @@ static int mt6370_regulator_irq_register(struct mt6370_priv *priv) return 0; } -static int mt6370_regualtor_register(struct mt6370_priv *priv) +static int mt6370_regulator_register(struct mt6370_priv *priv) { struct regulator_dev *rdev; struct regulator_config cfg = {}; @@ -363,7 +363,7 @@ static int mt6370_regulator_probe(struct platform_device *pdev) return -ENODEV; } - ret = mt6370_regualtor_register(priv); + ret = mt6370_regulator_register(priv); if (ret) return ret;