mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support
@ 2026-09-14  7:45 Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 01/10] regulator: ab8500: Fix AB8505 VANA voltage selectors Linus Walleij
                   ` (9 more replies)
  0 siblings, 10 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:45 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

While working on the Ux500 power domains it became apparent that the
device trees were using the DB8500 power-domain regulator for supplies
which actually come from buck converters in the AB8500 PMIC.

First: fix a bunch of bugs. All of these patches have Fixes: tags.
I do not consider any of them urgent or regressions, they can just
be queued in front of the new functionality.

Move some regulators over to using linear ranges before adding new
stuff since linear ranges are nice.

Add regulator driver support for the six SMPS1, SMPS2, SMPS3, ARM,
APE and MOD buck converters on AB8500. On AB8505, the corresponding
rails are named VSMPSA, VSMPSB, VSAFE, VARM, VSMPSC and VSMPSM.

The driver follows the active hardware selector, including the
additional AB8505 selector banks, and uses the variant-specific VARM
voltage encoding. It provides enable and low-power mode control for the
three peripheral bucks while leaving the SoC-controlled rails
voltage-only.

Since peripheral buck enable state is programmed by OTP and can power
discrete components outside the device tree, preserve any rail which
the OTP leaves enabled when regulator constraints are completed.

To: Stephan Gerhold <stephan@gerhold.net>
To: Liam Girdwood <lgirdwood@gmail.com>
To: Mark Brown <broonie@kernel.org>
To: Axel Lin <axel.lin@ingics.com>
Cc: linux-kernel@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>

Changes in v3:
- Dropped patches applied to the MFD and Ux500 SoC tree.
- Resend just the regulator patches to make things easier for the
  regulator maintainer.
- Link to v2: https://lore.kernel.org/r/20260901-ux500-dts-snowball-regulator-v2-0-fe88b01829bf@kernel.org

Changes in v2:
- Pick up Conor's ACK.
- Fix some terminology and clarify the DT bindings a bit.
- Link to v1: https://lore.kernel.org/r/20260831-ux500-dts-snowball-regulator-v1-0-fe279fff5e18@kernel.org

---
Linus Walleij (10):
      regulator: ab8500: Fix AB8505 VANA voltage selectors
      regulator: ab8500: Add AB8505 VAUX3 3.05 V setting
      regulator: ab8500: Handle AB8505 VINTCORE selector 7
      regulator: ab8500: Treat cut 1.0 VAUX3 as fixed
      regulator: ab8500: Test dedicated enable bits only
      regulator: ab8500: Propagate mode enable read errors
      regulator: ab8500: Use linear ranges for LDO voltages
      regulator: ab8500: Add buck converter support
      regulator: ab8500: Preserve OTP-enabled buck regulators
      regulator: ab8500: Use scoped guard for shared mode mutex

 drivers/regulator/ab8500.c | 748 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 687 insertions(+), 61 deletions(-)
---
base-commit: cee9395acd8043be0644b25c34bfa86623f2b935
change-id: 20260820-ux500-dts-snowball-regulator-b35d2252bd57

Best regards,
-- 
Linus Walleij <linusw@kernel.org>


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 01/10] regulator: ab8500: Fix AB8505 VANA voltage selectors
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 02/10] regulator: ab8500: Add AB8505 VAUX3 3.05 V setting Linus Walleij
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

The AB8505 VANA voltage table assumes selector 0 represents 1.05 V
and all eight selectors increase linearly. Selector 0 actually
represents 1.2 V. Selectors 1 through 6 cover 1.05 V through
1.175 V, and selector 7 represents 1.225 V.

Correct the table so each selector reports and programs the documented
voltage.

Reference: AB8505 User Manual, DM00046744 Rev 3, p. 257

Fixes: 8a3b1b8703fe ("regulator: ab8500: Add voltage selection for AUDIO and ANA on AB8505")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 3705c98b0713..ca59406d42a8 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -264,13 +264,13 @@ static const unsigned int fixed_2050000_voltage[] = {
 };
 
 static const unsigned int ldo_vana_voltages[] = {
+	1200000,
 	1050000,
 	1075000,
 	1100000,
 	1125000,
 	1150000,
 	1175000,
-	1200000,
 	1225000,
 };
 

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 02/10] regulator: ab8500: Add AB8505 VAUX3 3.05 V setting
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 01/10] regulator: ab8500: Fix AB8505 VANA voltage selectors Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 03/10] regulator: ab8500: Handle AB8505 VINTCORE selector 7 Linus Walleij
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

AB8505 has an additional VAUX3 voltage setting which is not encoded
in the normal three-bit selector. ArmRegu2.Vaux3Sel3 overrides that
selector and selects 3.05 V.

Add the missing voltage and use the override bit as an extended
selector. Program the ordinary selector before clearing the override
so VAUX3 does not briefly switch to a stale voltage.

Reference: AB8505 User Manual, DM00046744 Rev 3, pp. 229 and 254

Fixes: 547f384f33db ("regulator: ab8500: add support for ab8505")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 101 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 98 insertions(+), 3 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index ca59406d42a8..07d96f8ff944 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -173,6 +173,7 @@ struct ab8500_shared_mode {
  * @voltage_bank: bank to control regulator voltage
  * @voltage_reg: register to control regulator voltage
  * @voltage_mask: mask to control regulator voltage
+ * @expand_register: additional register used to select an extra voltage
  */
 struct ab8500_regulator_info {
 	struct device		*dev;
@@ -193,6 +194,12 @@ struct ab8500_regulator_info {
 	u8 voltage_bank;
 	u8 voltage_reg;
 	u8 voltage_mask;
+	struct {
+		u8 voltage_limit;
+		u8 voltage_bank;
+		u8 voltage_reg;
+		u8 voltage_mask;
+	} expand_register;
 };
 
 /* voltage tables for the vauxn/vintcore supplies */
@@ -226,6 +233,18 @@ static const unsigned int ldo_vaux3_voltages[] = {
 	2910000,
 };
 
+static const unsigned int ldo_vaux3_ab8505_voltages[] = {
+	1200000,
+	1500000,
+	1800000,
+	2100000,
+	2500000,
+	2750000,
+	2790000,
+	2910000,
+	3050000,
+};
+
 static const unsigned int ldo_vaux56_voltages[] = {
 	1800000,
 	1050000,
@@ -593,6 +612,64 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
 	return ret;
 }
 
+static int ab8500_regulator_get_voltage_sel_expand(struct regulator_dev *rdev)
+{
+	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+	u8 regval;
+	int ret;
+
+	if (!info)
+		return -EINVAL;
+
+	ret = abx500_get_register_interruptible(info->dev,
+			info->expand_register.voltage_bank,
+			info->expand_register.voltage_reg, &regval);
+	if (ret < 0) {
+		dev_err(rdev_get_dev(rdev),
+			"couldn't read voltage expand reg for regulator\n");
+		return ret;
+	}
+
+	if (regval & info->expand_register.voltage_mask)
+		return info->expand_register.voltage_limit;
+
+	return ab8500_regulator_get_voltage_sel(rdev);
+}
+
+static int ab8500_regulator_set_voltage_sel_expand(struct regulator_dev *rdev,
+						   unsigned int selector)
+{
+	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+	u8 regval;
+	int ret;
+
+	if (!info)
+		return -EINVAL;
+
+	if (selector > info->expand_register.voltage_limit)
+		return -EINVAL;
+
+	if (selector < info->expand_register.voltage_limit) {
+		ret = ab8500_regulator_set_voltage_sel(rdev, selector);
+		if (ret < 0)
+			return ret;
+
+		regval = 0;
+	} else {
+		regval = info->expand_register.voltage_mask;
+	}
+
+	ret = abx500_mask_and_set_register_interruptible(info->dev,
+			info->expand_register.voltage_bank,
+			info->expand_register.voltage_reg,
+			info->expand_register.voltage_mask, regval);
+	if (ret < 0)
+		dev_err(rdev_get_dev(rdev),
+			"couldn't set voltage expand reg for regulator\n");
+
+	return ret;
+}
+
 static const struct regulator_ops ab8500_regulator_volt_mode_ops = {
 	.enable			= ab8500_regulator_enable,
 	.disable		= ab8500_regulator_disable,
@@ -605,6 +682,18 @@ static const struct regulator_ops ab8500_regulator_volt_mode_ops = {
 	.list_voltage		= regulator_list_voltage_table,
 };
 
+static const struct regulator_ops ab8500_regulator_volt_mode_expand_ops = {
+	.enable			= ab8500_regulator_enable,
+	.disable		= ab8500_regulator_disable,
+	.is_enabled		= ab8500_regulator_is_enabled,
+	.get_optimum_mode	= ab8500_regulator_get_optimum_mode,
+	.set_mode		= ab8500_regulator_set_mode,
+	.get_mode		= ab8500_regulator_get_mode,
+	.get_voltage_sel	= ab8500_regulator_get_voltage_sel_expand,
+	.set_voltage_sel	= ab8500_regulator_set_voltage_sel_expand,
+	.list_voltage		= regulator_list_voltage_table,
+};
+
 static const struct regulator_ops ab8500_regulator_volt_ops = {
 	.enable		= ab8500_regulator_enable,
 	.disable	= ab8500_regulator_disable,
@@ -908,12 +997,12 @@ static struct ab8500_regulator_info
 	[AB8505_LDO_AUX3] = {
 		.desc = {
 			.name		= "LDO-AUX3",
-			.ops		= &ab8500_regulator_volt_mode_ops,
+			.ops		= &ab8500_regulator_volt_mode_expand_ops,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_LDO_AUX3,
 			.owner		= THIS_MODULE,
-			.n_voltages	= ARRAY_SIZE(ldo_vaux3_voltages),
-			.volt_table	= ldo_vaux3_voltages,
+			.n_voltages	= ARRAY_SIZE(ldo_vaux3_ab8505_voltages),
+			.volt_table	= ldo_vaux3_ab8505_voltages,
 		},
 		.load_lp_uA		= 5000,
 		.update_bank		= 0x04,
@@ -925,6 +1014,12 @@ static struct ab8500_regulator_info
 		.voltage_bank		= 0x04,
 		.voltage_reg		= 0x21,
 		.voltage_mask		= 0x07,
+		.expand_register = {
+			.voltage_limit	= 8,
+			.voltage_bank	= 0x04,
+			.voltage_reg	= 0x01,
+			.voltage_mask	= 0x10,
+		},
 	},
 	[AB8505_LDO_AUX4] = {
 		.desc = {

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 03/10] regulator: ab8500: Handle AB8505 VINTCORE selector 7
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 01/10] regulator: ab8500: Fix AB8505 VANA voltage selectors Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 02/10] regulator: ab8500: Add AB8505 VAUX3 3.05 V setting Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 04/10] regulator: ab8500: Treat cut 1.0 VAUX3 as fixed Linus Walleij
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

The AB8505 VINTCORE table exposes only selectors 0 through 6. The
hardware also accepts selector 7 and maps it to 1.35 V, just like
selector 6. Omitting it can make an OTP-programmed selector 7 appear
invalid to the regulator core.

Give AB8505 its own eight-entry selector table while leaving the
AB8500 table unchanged.

Reference: AB8505 User Manual, DM00046744 Rev 3, p. 223

Fixes: 547f384f33db ("regulator: ab8500: add support for ab8505")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 07d96f8ff944..5e24e6cdc773 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -266,6 +266,17 @@ static const unsigned int ldo_vintcore_voltages[] = {
 	1350000,
 };
 
+static const unsigned int ldo_vintcore_ab8505_voltages[] = {
+	1200000,
+	1225000,
+	1250000,
+	1275000,
+	1300000,
+	1325000,
+	1350000,
+	1350000,
+};
+
 static const unsigned int fixed_1200000_voltage[] = {
 	1200000,
 };
@@ -1095,8 +1106,8 @@ static struct ab8500_regulator_info
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_LDO_INTCORE,
 			.owner		= THIS_MODULE,
-			.n_voltages	= ARRAY_SIZE(ldo_vintcore_voltages),
-			.volt_table	= ldo_vintcore_voltages,
+			.n_voltages	= ARRAY_SIZE(ldo_vintcore_ab8505_voltages),
+			.volt_table	= ldo_vintcore_ab8505_voltages,
 		},
 		.load_lp_uA		= 5000,
 		.update_bank		= 0x03,

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 04/10] regulator: ab8500: Treat cut 1.0 VAUX3 as fixed
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (2 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 03/10] regulator: ab8500: Handle AB8505 VINTCORE selector 7 Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 05/10] regulator: ab8500: Test dedicated enable bits only Linus Walleij
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

The early-cut workaround currently gives AB8500 cut 1.0 the 16
programmable VAUX3 settings introduced with cut 1.1. On cut 1.0 the
selector is not programmable and VAUX3 is fixed at 1.2 V.

Register VAUX3 as a fixed-voltage regulator on cut 1.0 and retain the
16-value workaround only for cut 1.1.

Reference: AB8500 User Manual, UM0836 Rev 3, p. 240

Fixes: 2b75151a1041 ("regulators: Added ab8500 v2 support")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 5e24e6cdc773..42cb8dfdc7c9 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -1783,9 +1783,14 @@ static int ab8500_regulator_register(struct platform_device *pdev,
 	config.driver_data = info;
 	config.of_node = np;
 
-	/* fix for hardware before ab8500v2.0 */
-	if (is_ab8500_1p1_or_earlier(ab8500)) {
-		if (info->desc.id == AB8500_LDO_AUX3) {
+	/* Handle the different VAUX3 implementations in early AB8500 cuts. */
+	if (info->desc.id == AB8500_LDO_AUX3) {
+		if (is_ab8500_1p0_or_earlier(ab8500)) {
+			info->desc.ops = &ab8500_regulator_mode_ops;
+			info->desc.n_voltages = 1;
+			info->desc.volt_table = fixed_1200000_voltage;
+			info->voltage_mask = 0;
+		} else if (is_ab8500_1p1_or_earlier(ab8500)) {
 			info->desc.n_voltages =
 				ARRAY_SIZE(ldo_vauxn_voltages);
 			info->desc.volt_table = ldo_vauxn_voltages;

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 05/10] regulator: ab8500: Test dedicated enable bits only
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (3 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 04/10] regulator: ab8500: Treat cut 1.0 VAUX3 as fixed Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 06/10] regulator: ab8500: Propagate mode enable read errors Linus Walleij
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

Some regulator control registers have independent enable and low-power
bits. is_enabled() currently tests their combined update mask, so an
off regulator with its low-power bit set is incorrectly reported as
enabled.

Add an optional enable mask and use it for VINTCORE, AB8500 TVOUT,
AB8505 ADC, and AB8505 VAUX5/6. Regulators whose two-bit field encodes
the complete operating state continue to test the full update mask.

References: AB8500 User Manual, UM0836 Rev 3, p. 214;
AB8505 User Manual, DM00046744 Rev 3, pp. 171-172 and 223

Fixes: 65e03ed2d0cd ("regulators: Fixed errors in ab8500 register mapping")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 42cb8dfdc7c9..05c6b9c7af0c 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -162,6 +162,7 @@ struct ab8500_shared_mode {
  * @update_bank: bank to control on/off
  * @update_reg: register to control on/off
  * @update_mask: mask to enable/disable and set mode of regulator
+ * @enable_mask: optional mask for an enable bit separate from the mode bit
  * @update_val: bits holding the regulator current mode
  * @update_val_idle: bits to enable the regulator in idle (low power) mode
  * @update_val_normal: bits to enable the regulator in normal (high power) mode
@@ -183,6 +184,7 @@ struct ab8500_regulator_info {
 	u8 update_bank;
 	u8 update_reg;
 	u8 update_mask;
+	u8 enable_mask;
 	u8 update_val;
 	u8 update_val_idle;
 	u8 update_val_normal;
@@ -377,6 +379,7 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
 {
 	int ret;
 	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+	u8 enable_mask;
 	u8 regval;
 
 	if (info == NULL) {
@@ -392,13 +395,15 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
 		return ret;
 	}
 
+	enable_mask = info->enable_mask ? info->enable_mask : info->update_mask;
+
 	dev_vdbg(rdev_get_dev(rdev),
 		"%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
 		" 0x%x\n",
 		info->desc.name, info->update_bank, info->update_reg,
-		info->update_mask, regval);
+		enable_mask, regval);
 
-	if (regval & info->update_mask)
+	if (regval & enable_mask)
 		return 1;
 	else
 		return 0;
@@ -833,6 +838,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x03,
 		.update_reg		= 0x80,
 		.update_mask		= 0x44,
+		.enable_mask		= 0x04,
 		.update_val		= 0x44,
 		.update_val_idle	= 0x44,
 		.update_val_normal	= 0x04,
@@ -861,6 +867,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x03,
 		.update_reg		= 0x80,
 		.update_mask		= 0x82,
+		.enable_mask		= 0x02,
 		.update_val		= 0x02,
 		.update_val_idle	= 0x82,
 		.update_val_normal	= 0x02,
@@ -1070,6 +1077,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x01,
 		.update_reg		= 0x55,
 		.update_mask		= 0x18,
+		.enable_mask		= 0x10,
 		.update_val		= 0x10,
 		.update_val_idle	= 0x18,
 		.update_val_normal	= 0x10,
@@ -1092,6 +1100,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x01,
 		.update_reg		= 0x56,
 		.update_mask		= 0x18,
+		.enable_mask		= 0x10,
 		.update_val		= 0x10,
 		.update_val_idle	= 0x18,
 		.update_val_normal	= 0x10,
@@ -1113,6 +1122,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x03,
 		.update_reg		= 0x80,
 		.update_mask		= 0x44,
+		.enable_mask		= 0x04,
 		.update_val		= 0x04,
 		.update_val_idle	= 0x44,
 		.update_val_normal	= 0x04,
@@ -1141,6 +1151,7 @@ static struct ab8500_regulator_info
 		.update_bank		= 0x03,
 		.update_reg		= 0x80,
 		.update_mask		= 0x82,
+		.enable_mask		= 0x02,
 		.update_val		= 0x02,
 		.update_val_idle	= 0x82,
 		.update_val_normal	= 0x02,

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 06/10] regulator: ab8500: Propagate mode enable read errors
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (4 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 05/10] regulator: ab8500: Test dedicated enable bits only Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 07/10] regulator: ab8500: Use linear ranges for LDO voltages Linus Walleij
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

For regulators whose enable and mode share a state field, set_mode()
first reads that field so changing the requested mode does not enable a
disabled rail. A register read error is currently treated as true and
the driver proceeds to write the new mode, potentially enabling a rail
whose state is unknown.

Return the read error without changing the register or cached mode.

References: AB8500 User Manual, UM0836 Rev 3, p. 227;
AB8505 User Manual, DM00046744 Rev 3, p. 237

Fixes: 438e695b87e0 ("regulator: ab8500: Get rid of is_enabled from struct ab8500_regulator_info")
Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 05c6b9c7af0c..e4c3502c8dbc 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -433,7 +433,7 @@ static unsigned int ab8500_regulator_get_optimum_mode(
 static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 				     unsigned int mode)
 {
-	int ret = 0;
+	int enabled, ret = 0;
 	u8 bank, reg, mask, val;
 	bool lp_mode_req = false;
 	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
@@ -490,7 +490,17 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 		goto out_unlock;
 	}
 
-	if (info->mode_mask || ab8500_regulator_is_enabled(rdev)) {
+	if (info->mode_mask) {
+		enabled = 1;
+	} else {
+		enabled = ab8500_regulator_is_enabled(rdev);
+		if (enabled < 0) {
+			ret = enabled;
+			goto out_unlock;
+		}
+	}
+
+	if (enabled) {
 		ret = abx500_mask_and_set_register_interruptible(info->dev,
 			bank, reg, mask, val);
 		if (ret < 0) {

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 07/10] regulator: ab8500: Use linear ranges for LDO voltages
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (5 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 06/10] regulator: ab8500: Propagate mode enable read errors Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 08/10] regulator: ab8500: Add buck converter support Linus Walleij
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

VINTCORE uses consecutive selectors with uniform 25 mV steps, with
AB8505 duplicating the highest voltage at selector 7. AB8505 VAUDIO
likewise has uniform 100 mV steps followed by a duplicate selector for
its highest voltage.

Describe these selector encodings with linear ranges and the matching
regulator helpers instead of enumerated voltage tables. Keep tables for
the irregular and non-monotonic VAUX and VANA selectors.

Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 74 +++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 37 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index e4c3502c8dbc..d1540099104c 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -258,25 +258,13 @@ static const unsigned int ldo_vaux56_voltages[] = {
 	2790000,
 };
 
-static const unsigned int ldo_vintcore_voltages[] = {
-	1200000,
-	1225000,
-	1250000,
-	1275000,
-	1300000,
-	1325000,
-	1350000,
+static const struct linear_range ldo_vintcore_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1200000, 0, 6, 25000),
 };
 
-static const unsigned int ldo_vintcore_ab8505_voltages[] = {
-	1200000,
-	1225000,
-	1250000,
-	1275000,
-	1300000,
-	1325000,
-	1350000,
-	1350000,
+static const struct linear_range ldo_vintcore_ab8505_ranges[] = {
+	REGULATOR_LINEAR_RANGE(1200000, 0, 6, 25000),
+	REGULATOR_LINEAR_RANGE(1350000, 7, 7, 0),
 };
 
 static const unsigned int fixed_1200000_voltage[] = {
@@ -306,15 +294,10 @@ static const unsigned int ldo_vana_voltages[] = {
 	1225000,
 };
 
-static const unsigned int ldo_vaudio_voltages[] = {
-	2000000,
-	2100000,
-	2200000,
-	2300000,
-	2400000,
-	2500000,
-	2600000,
-	2600000,	/* Duplicated in Vaudio and IsoUicc Control register. */
+static const struct linear_range ldo_vaudio_ranges[] = {
+	REGULATOR_LINEAR_RANGE(2000000, 0, 6, 100000),
+	/* Duplicated in Vaudio and IsoUicc Control register. */
+	REGULATOR_LINEAR_RANGE(2600000, 7, 7, 0),
 };
 
 static DEFINE_MUTEX(shared_mode_mutex);
@@ -720,13 +703,27 @@ static const struct regulator_ops ab8500_regulator_volt_mode_expand_ops = {
 	.list_voltage		= regulator_list_voltage_table,
 };
 
-static const struct regulator_ops ab8500_regulator_volt_ops = {
+static const struct regulator_ops ab8500_regulator_linear_range_volt_mode_ops = {
+	.enable			= ab8500_regulator_enable,
+	.disable		= ab8500_regulator_disable,
+	.is_enabled		= ab8500_regulator_is_enabled,
+	.get_optimum_mode	= ab8500_regulator_get_optimum_mode,
+	.set_mode		= ab8500_regulator_set_mode,
+	.get_mode		= ab8500_regulator_get_mode,
+	.get_voltage_sel	= ab8500_regulator_get_voltage_sel,
+	.set_voltage_sel	= ab8500_regulator_set_voltage_sel,
+	.list_voltage		= regulator_list_voltage_linear_range,
+	.map_voltage		= regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_ops ab8500_regulator_linear_range_volt_ops = {
 	.enable		= ab8500_regulator_enable,
 	.disable	= ab8500_regulator_disable,
 	.is_enabled	= ab8500_regulator_is_enabled,
 	.get_voltage_sel = ab8500_regulator_get_voltage_sel,
 	.set_voltage_sel = ab8500_regulator_set_voltage_sel,
-	.list_voltage	= regulator_list_voltage_table,
+	.list_voltage	= regulator_list_voltage_linear_range,
+	.map_voltage	= regulator_map_voltage_linear_range,
 };
 
 static const struct regulator_ops ab8500_regulator_mode_ops = {
@@ -836,12 +833,13 @@ static struct ab8500_regulator_info
 	[AB8500_LDO_INTCORE] = {
 		.desc = {
 			.name		= "LDO-INTCORE",
-			.ops		= &ab8500_regulator_volt_mode_ops,
+			.ops		= &ab8500_regulator_linear_range_volt_mode_ops,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8500_LDO_INTCORE,
 			.owner		= THIS_MODULE,
-			.n_voltages	= ARRAY_SIZE(ldo_vintcore_voltages),
-			.volt_table	= ldo_vintcore_voltages,
+			.n_voltages	= 7,
+			.linear_ranges	= ldo_vintcore_ranges,
+			.n_linear_ranges = ARRAY_SIZE(ldo_vintcore_ranges),
 			.enable_time	= 750,
 		},
 		.load_lp_uA		= 5000,
@@ -1121,12 +1119,13 @@ static struct ab8500_regulator_info
 	[AB8505_LDO_INTCORE] = {
 		.desc = {
 			.name		= "LDO-INTCORE",
-			.ops		= &ab8500_regulator_volt_mode_ops,
+			.ops		= &ab8500_regulator_linear_range_volt_mode_ops,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_LDO_INTCORE,
 			.owner		= THIS_MODULE,
-			.n_voltages	= ARRAY_SIZE(ldo_vintcore_ab8505_voltages),
-			.volt_table	= ldo_vintcore_ab8505_voltages,
+			.n_voltages	= 8,
+			.linear_ranges	= ldo_vintcore_ab8505_ranges,
+			.n_linear_ranges = ARRAY_SIZE(ldo_vintcore_ab8505_ranges),
 		},
 		.load_lp_uA		= 5000,
 		.update_bank		= 0x03,
@@ -1169,12 +1168,13 @@ static struct ab8500_regulator_info
 	[AB8505_LDO_AUDIO] = {
 		.desc = {
 			.name		= "LDO-AUDIO",
-			.ops		= &ab8500_regulator_volt_ops,
+			.ops		= &ab8500_regulator_linear_range_volt_ops,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_LDO_AUDIO,
 			.owner		= THIS_MODULE,
-			.n_voltages	= ARRAY_SIZE(ldo_vaudio_voltages),
-			.volt_table	= ldo_vaudio_voltages,
+			.n_voltages	= 8,
+			.linear_ranges	= ldo_vaudio_ranges,
+			.n_linear_ranges = ARRAY_SIZE(ldo_vaudio_ranges),
 		},
 		.update_bank		= 0x03,
 		.update_reg		= 0x83,

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 08/10] regulator: ab8500: Add buck converter support
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (6 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 07/10] regulator: ab8500: Use linear ranges for LDO voltages Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators Linus Walleij
  2026-09-14  7:46 ` [PATCH v3 10/10] regulator: ab8500: Use scoped guard for shared mode mutex Linus Walleij
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

Register the SMPS1, SMPS2, SMPS3, ARM, APE and MOD buck converters on
AB8500 and the VSMPSA, VSMPSB, VSAFE, VARM, VSMPSC and VSMPSM buck
converters on AB8505 so that the new device tree nodes can supply
consumers. Match each variant through its own device tree node names.

AB8500 SMPS3 supplies Vsafe and AB8505 VSAFE occupies the corresponding
control and selector registers at 0x0405 and 0x041b through 0x041d.
AB8500 VAPE and AB8505 VSMPSC instead use 0x0402 and the 0x040e through
0x0410 selector registers. Keep separate AB8505 regulator descriptors
and identifiers so these variant-specific rails are not conflated.

Describe the hardware selector ranges and follow the selector-control
registers when reading or changing voltage. This accounts for AB8505
using Sel2 after reset, its additional selector registers and its
separate 7-bit VARM range. Use the AB8500-compatible and low-range OTP
profiles found on the supported platforms for the other rails.

SMPS1 through SMPS3 and VSMPSA, VSMPSB and VSAFE also expose enable and
low-power mode control. Keep the ARM, APE, MOD, VARM, VSMPSC and VSMPSM
rails voltage-only since their on/off state is managed with the SoC.

Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 461 ++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 452 insertions(+), 9 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index d1540099104c..130febf2680a 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -9,10 +9,12 @@
  * AB8500 peripheral regulators
  *
  * AB8500 supports the following regulators:
- *   VAUX1/2/3, VINTCORE, VTVOUT, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
+ *   VSMPS1/2/3, VARM, VAPE, VMOD, VAUX1/2/3, VINTCORE, VTVOUT,
+ *   VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
  *
  * AB8505 supports the following regulators:
- *   VAUX1/2/3/4/5/6, VINTCORE, VADC, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
+ *   VSMPSA/B/C/M, VSAFE, VARM, VAUX1/2/3/4/5/6, VINTCORE,
+ *   VADC, VUSB, VAUDIO, VAMIC1/2, VDMIC, VANA
  */
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -39,6 +41,12 @@ enum ab8500_regulator_id {
 	AB8500_LDO_ANAMIC2,
 	AB8500_LDO_DMIC,
 	AB8500_LDO_ANA,
+	AB8500_BUCK_SMPS1,
+	AB8500_BUCK_SMPS2,
+	AB8500_BUCK_SMPS3,
+	AB8500_BUCK_ARM,
+	AB8500_BUCK_APE,
+	AB8500_BUCK_MOD,
 	AB8500_NUM_REGULATORS,
 };
 
@@ -57,6 +65,12 @@ enum ab8505_regulator_id {
 	AB8505_LDO_ANAMIC2,
 	AB8505_LDO_AUX8,
 	AB8505_LDO_ANA,
+	AB8505_BUCK_SMPSA,
+	AB8505_BUCK_SMPSB,
+	AB8505_BUCK_SAFE,
+	AB8505_BUCK_ARM,
+	AB8505_BUCK_SMPSC,
+	AB8505_BUCK_SMPSM,
 	AB8505_NUM_REGULATORS,
 };
 
@@ -172,9 +186,17 @@ struct ab8500_shared_mode {
  * @mode_val_idle: mode setting for low power
  * @mode_val_normal: mode setting for normal power
  * @voltage_bank: bank to control regulator voltage
- * @voltage_reg: register to control regulator voltage
+ * @voltage_reg: first register containing a selectable regulator voltage
  * @voltage_mask: mask to control regulator voltage
  * @expand_register: additional register used to select an extra voltage
+ * @voltage_ctrl_bank: bank containing the voltage selector control
+ * @voltage_ctrl_reg: register containing the voltage selector control
+ * @voltage_ctrl_mask: mask selecting one of the first voltage registers
+ * @voltage_ext_ctrl_bank: bank containing the extended selector control
+ * @voltage_ext_ctrl_reg: register containing the extended selector control
+ * @voltage_ext_ctrl_mask: mask selecting one of the extended voltage registers
+ * @voltage_ext_reg: first extended voltage register
+ * @voltage_ext_regs: number of extended voltage registers
  */
 struct ab8500_regulator_info {
 	struct device		*dev;
@@ -202,6 +224,14 @@ struct ab8500_regulator_info {
 		u8 voltage_reg;
 		u8 voltage_mask;
 	} expand_register;
+	u8 voltage_ctrl_bank;
+	u8 voltage_ctrl_reg;
+	u8 voltage_ctrl_mask;
+	u8 voltage_ext_ctrl_bank;
+	u8 voltage_ext_ctrl_reg;
+	u8 voltage_ext_ctrl_mask;
+	u8 voltage_ext_reg;
+	u8 voltage_ext_regs;
 };
 
 /* voltage tables for the vauxn/vintcore supplies */
@@ -300,6 +330,40 @@ static const struct linear_range ldo_vaudio_ranges[] = {
 	REGULATOR_LINEAR_RANGE(2600000, 7, 7, 0),
 };
 
+/*
+ * AB8505 buck ranges except VARM are selected by OTP.  The supported
+ * platforms use the AB8500-compatible profiles for VSMPSA/B and the low
+ * profiles for VSAFE, VSMPSC and VSMPSM.
+ */
+static const struct linear_range buck_low_voltages[] = {
+	REGULATOR_LINEAR_RANGE(700000, 0, 53, 12500),
+	REGULATOR_LINEAR_RANGE(1362500, 54, 63, 0),
+};
+
+/* VSMPS3 and VSAFE have a 7-bit selector, but the same low range. */
+static const struct linear_range buck_low_7bit_voltages[] = {
+	REGULATOR_LINEAR_RANGE(700000, 0, 53, 12500),
+	REGULATOR_LINEAR_RANGE(1362500, 54, 127, 0),
+};
+
+/* AB8505 VARM uses a separate 0.6 V to 1.39375 V selector range. */
+static const struct linear_range ab8505_buck_arm_voltages[] = {
+	REGULATOR_LINEAR_RANGE(600000, 0, 127, 6250),
+};
+
+/* VSMPS1 and the VSMPSA AB8500-compatible profile clamp to this range. */
+static const struct linear_range buck_smps1_voltages[] = {
+	REGULATOR_LINEAR_RANGE(1100000, 0, 32, 0),
+	REGULATOR_LINEAR_RANGE(1112500, 33, 48, 12500),
+	REGULATOR_LINEAR_RANGE(1300000, 49, 63, 0),
+};
+
+/* VSMPS2 and the VSMPSB AB8500-compatible profile clamp to this range. */
+static const struct linear_range buck_smps2_voltages[] = {
+	REGULATOR_LINEAR_RANGE(1800000, 0, 57, 0),
+	REGULATOR_LINEAR_RANGE(1812500, 58, 63, 12500),
+};
+
 static DEFINE_MUTEX(shared_mode_mutex);
 static struct ab8500_shared_mode ldo_anamic1_shared;
 static struct ab8500_shared_mode ldo_anamic2_shared;
@@ -558,11 +622,67 @@ static unsigned int ab8500_regulator_get_mode(struct regulator_dev *rdev)
 	return ret;
 }
 
+static int ab8500_regulator_get_voltage_reg(struct regulator_dev *rdev,
+					    u8 *voltage_reg)
+{
+	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+	u8 regval;
+	unsigned int selector;
+	int ret;
+
+	if (info->voltage_ext_ctrl_mask) {
+		ret = abx500_get_register_interruptible(info->dev,
+							info->voltage_ext_ctrl_bank,
+							info->voltage_ext_ctrl_reg, &regval);
+		if (ret < 0)
+			return ret;
+
+		selector = (regval & info->voltage_ext_ctrl_mask) >>
+			(ffs(info->voltage_ext_ctrl_mask) - 1);
+		if (selector) {
+			selector = min_t(unsigned int, selector,
+					 info->voltage_ext_regs);
+			*voltage_reg = info->voltage_ext_reg + selector - 1;
+			return 0;
+		}
+	}
+
+	if (!info->voltage_ctrl_mask) {
+		*voltage_reg = info->voltage_reg;
+		return 0;
+	}
+
+	ret = abx500_get_register_interruptible(info->dev,
+						info->voltage_ctrl_bank,
+						info->voltage_ctrl_reg, &regval);
+	if (ret < 0)
+		return ret;
+
+	/* The three hardware selector layouts all use consecutive registers. */
+	switch (info->voltage_ctrl_mask) {
+	case 0x0c:
+		selector = min((unsigned int)((regval & 0x0c) >> 2), 2U);
+		break;
+	case 0x24:
+		selector = regval & BIT(5) ? 2 : !!(regval & BIT(2));
+		break;
+	case 0x04:
+		selector = !!(regval & BIT(2));
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	*voltage_reg = info->voltage_reg + selector;
+
+	return 0;
+}
+
 static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
 {
 	int ret, voltage_shift;
 	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-	u8 regval;
+	u8 regval, voltage_reg;
 
 	if (info == NULL) {
 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
@@ -571,8 +691,15 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
 
 	voltage_shift = ffs(info->voltage_mask) - 1;
 
+	ret = ab8500_regulator_get_voltage_reg(rdev, &voltage_reg);
+	if (ret < 0) {
+		dev_err(rdev_get_dev(rdev),
+			"couldn't read voltage selector control\n");
+		return ret;
+	}
+
 	ret = abx500_get_register_interruptible(info->dev,
-			info->voltage_bank, info->voltage_reg, &regval);
+			info->voltage_bank, voltage_reg, &regval);
 	if (ret < 0) {
 		dev_err(rdev_get_dev(rdev),
 			"couldn't read voltage reg for regulator\n");
@@ -583,7 +710,7 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev)
 		"%s-get_voltage (bank, reg, mask, shift, value): "
 		"0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
 		info->desc.name, info->voltage_bank,
-		info->voltage_reg, info->voltage_mask,
+		voltage_reg, info->voltage_mask,
 		voltage_shift, regval);
 
 	return (regval & info->voltage_mask) >> voltage_shift;
@@ -594,7 +721,7 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
 {
 	int ret, voltage_shift;
 	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-	u8 regval;
+	u8 regval, voltage_reg;
 
 	if (info == NULL) {
 		dev_err(rdev_get_dev(rdev), "regulator info null pointer\n");
@@ -603,10 +730,17 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
 
 	voltage_shift = ffs(info->voltage_mask) - 1;
 
+	ret = ab8500_regulator_get_voltage_reg(rdev, &voltage_reg);
+	if (ret < 0) {
+		dev_err(rdev_get_dev(rdev),
+			"couldn't read voltage selector control\n");
+		return ret;
+	}
+
 	/* set the registers for the request */
 	regval = (u8)selector << voltage_shift;
 	ret = abx500_mask_and_set_register_interruptible(info->dev,
-			info->voltage_bank, info->voltage_reg,
+			info->voltage_bank, voltage_reg,
 			info->voltage_mask, regval);
 	if (ret < 0)
 		dev_err(rdev_get_dev(rdev),
@@ -615,7 +749,7 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev,
 	dev_vdbg(rdev_get_dev(rdev),
 		"%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
 		" 0x%x\n",
-		info->desc.name, info->voltage_bank, info->voltage_reg,
+		info->desc.name, info->voltage_bank, voltage_reg,
 		info->voltage_mask, regval);
 
 	return ret;
@@ -726,6 +860,26 @@ static const struct regulator_ops ab8500_regulator_linear_range_volt_ops = {
 	.map_voltage	= regulator_map_voltage_linear_range,
 };
 
+static const struct regulator_ops ab8500_buck_ops = {
+	.enable			= ab8500_regulator_enable,
+	.disable		= ab8500_regulator_disable,
+	.is_enabled		= ab8500_regulator_is_enabled,
+	.get_optimum_mode	= ab8500_regulator_get_optimum_mode,
+	.set_mode		= ab8500_regulator_set_mode,
+	.get_mode		= ab8500_regulator_get_mode,
+	.get_voltage_sel	= ab8500_regulator_get_voltage_sel,
+	.set_voltage_sel	= ab8500_regulator_set_voltage_sel,
+	.list_voltage		= regulator_list_voltage_linear_range,
+	.map_voltage		= regulator_map_voltage_linear_range,
+};
+
+static const struct regulator_ops ab8500_buck_voltage_ops = {
+	.get_voltage_sel	= ab8500_regulator_get_voltage_sel,
+	.set_voltage_sel	= ab8500_regulator_set_voltage_sel,
+	.list_voltage		= regulator_list_voltage_linear_range,
+	.map_voltage		= regulator_map_voltage_linear_range,
+};
+
 static const struct regulator_ops ab8500_regulator_mode_ops = {
 	.enable			= ab8500_regulator_enable,
 	.disable		= ab8500_regulator_disable,
@@ -967,6 +1121,137 @@ static struct ab8500_regulator_info
 		.update_val_idle	= 0x0c,
 		.update_val_normal	= 0x04,
 	},
+
+	/* Buck converters */
+	[AB8500_BUCK_SMPS1] = {
+		.desc = {
+			.name		= "BUCK-SMPS1",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_SMPS1,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_smps1_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_smps1_voltages),
+		},
+		.load_lp_uA		= 20000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x03,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x13,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x03,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8500_BUCK_SMPS2] = {
+		.desc = {
+			.name		= "BUCK-SMPS2",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_SMPS2,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_smps2_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_smps2_voltages),
+		},
+		.load_lp_uA		= 20000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x04,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x17,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x04,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8500_BUCK_SMPS3] = {
+		.desc = {
+			.name		= "BUCK-SMPS3",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_SMPS3,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 128,
+			.linear_ranges	= buck_low_7bit_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_7bit_voltages),
+		},
+		.load_lp_uA		= 50000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x05,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x1b,
+		.voltage_mask		= 0x7f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x05,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8500_BUCK_ARM] = {
+		.desc = {
+			.name		= "BUCK-ARM",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_ARM,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_low_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x0b,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x00,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8500_BUCK_APE] = {
+		.desc = {
+			.name		= "BUCK-APE",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_APE,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_low_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x0e,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x02,
+		.voltage_ctrl_mask	= 0x24,
+	},
+	[AB8500_BUCK_MOD] = {
+		.desc = {
+			.name		= "BUCK-MOD",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8500_BUCK_MOD,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_low_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x41,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x40,
+		.voltage_ctrl_mask	= 0x04,
+	},
 };
 
 /* AB8505 regulator information */
@@ -1265,6 +1550,152 @@ static struct ab8500_regulator_info
 		.voltage_reg		= 0x29,
 		.voltage_mask		= 0x7,
 	},
+
+	/* Buck converters */
+	[AB8505_BUCK_SMPSA] = {
+		.desc = {
+			.name		= "BUCK-SMPSA",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_SMPSA,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_smps1_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_smps1_voltages),
+		},
+		.load_lp_uA		= 20000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x03,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x13,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x03,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8505_BUCK_SMPSB] = {
+		.desc = {
+			.name		= "BUCK-SMPSB",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_SMPSB,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_smps2_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_smps2_voltages),
+		},
+		.load_lp_uA		= 20000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x04,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x17,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x04,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8505_BUCK_SAFE] = {
+		.desc = {
+			.name		= "BUCK-SAFE",
+			.ops		= &ab8500_buck_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_SAFE,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 128,
+			.linear_ranges	= buck_low_7bit_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_7bit_voltages),
+		},
+		.load_lp_uA		= 50000,
+		.update_bank		= 0x04,
+		.update_reg		= 0x05,
+		.update_mask		= 0x03,
+		.update_val		= 0x01,
+		.update_val_idle	= 0x03,
+		.update_val_normal	= 0x01,
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x1b,
+		.voltage_mask		= 0x7f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x05,
+		.voltage_ctrl_mask	= 0x0c,
+	},
+	[AB8505_BUCK_ARM] = {
+		.desc = {
+			.name		= "BUCK-ARM",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_ARM,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 128,
+			.linear_ranges	= ab8505_buck_arm_voltages,
+			.n_linear_ranges = ARRAY_SIZE(ab8505_buck_arm_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x0b,
+		.voltage_mask		= 0x7f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x00,
+		.voltage_ctrl_mask	= 0x0c,
+		.voltage_ext_ctrl_bank = 0x04,
+		.voltage_ext_ctrl_reg	= 0x28,
+		.voltage_ext_ctrl_mask = 0x07,
+		.voltage_ext_reg	= 0x24,
+		.voltage_ext_regs	= 4,
+	},
+	[AB8505_BUCK_SMPSC] = {
+		.desc = {
+			.name		= "BUCK-SMPSC",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_SMPSC,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_low_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x0e,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x02,
+		.voltage_ctrl_mask	= 0x24,
+		.voltage_ext_ctrl_bank = 0x04,
+		.voltage_ext_ctrl_reg	= 0x2a,
+		.voltage_ext_ctrl_mask = 0x03,
+		.voltage_ext_reg	= 0x2b,
+		.voltage_ext_regs	= 2,
+	},
+	[AB8505_BUCK_SMPSM] = {
+		.desc = {
+			.name		= "BUCK-SMPSM",
+			.ops		= &ab8500_buck_voltage_ops,
+			.type		= REGULATOR_VOLTAGE,
+			.id		= AB8505_BUCK_SMPSM,
+			.owner		= THIS_MODULE,
+			.n_voltages	= 64,
+			.linear_ranges	= buck_low_voltages,
+			.n_linear_ranges = ARRAY_SIZE(buck_low_voltages),
+		},
+		.voltage_bank		= 0x04,
+		.voltage_reg		= 0x41,
+		.voltage_mask		= 0x3f,
+		.voltage_ctrl_bank	= 0x04,
+		.voltage_ctrl_reg	= 0x40,
+		.voltage_ctrl_mask	= 0x04,
+		.voltage_ext_ctrl_bank = 0x04,
+		.voltage_ext_ctrl_reg	= 0x47,
+		.voltage_ext_ctrl_mask = 0x03,
+		.voltage_ext_reg	= 0x45,
+		.voltage_ext_regs	= 2,
+	},
 };
 
 static struct ab8500_shared_mode ldo_anamic1_shared = {
@@ -1740,6 +2171,12 @@ static struct of_regulator_match ab8500_regulator_match[] = {
 	{ .name	= "ab8500_ldo_anamic2", .driver_data = (void *) AB8500_LDO_ANAMIC2, },
 	{ .name	= "ab8500_ldo_dmic",    .driver_data = (void *) AB8500_LDO_DMIC, },
 	{ .name	= "ab8500_ldo_ana",     .driver_data = (void *) AB8500_LDO_ANA, },
+	{ .name = "ab8500_buck_smps1",  .driver_data = (void *)AB8500_BUCK_SMPS1, },
+	{ .name = "ab8500_buck_smps2",  .driver_data = (void *)AB8500_BUCK_SMPS2, },
+	{ .name = "ab8500_buck_smps3",  .driver_data = (void *)AB8500_BUCK_SMPS3, },
+	{ .name = "ab8500_buck_arm",    .driver_data = (void *)AB8500_BUCK_ARM, },
+	{ .name = "ab8500_buck_ape",    .driver_data = (void *)AB8500_BUCK_APE, },
+	{ .name = "ab8500_buck_mod",    .driver_data = (void *)AB8500_BUCK_MOD, },
 };
 
 static struct of_regulator_match ab8505_regulator_match[] = {
@@ -1756,6 +2193,12 @@ static struct of_regulator_match ab8505_regulator_match[] = {
 	{ .name	= "ab8500_ldo_anamic2", .driver_data = (void *) AB8505_LDO_ANAMIC2, },
 	{ .name	= "ab8500_ldo_aux8",    .driver_data = (void *) AB8505_LDO_AUX8, },
 	{ .name	= "ab8500_ldo_ana",     .driver_data = (void *) AB8505_LDO_ANA, },
+	{ .name = "ab8505_buck_smpsa",  .driver_data = (void *)AB8505_BUCK_SMPSA, },
+	{ .name = "ab8505_buck_smpsb",  .driver_data = (void *)AB8505_BUCK_SMPSB, },
+	{ .name = "ab8505_buck_safe",   .driver_data = (void *)AB8505_BUCK_SAFE, },
+	{ .name = "ab8505_buck_arm",    .driver_data = (void *)AB8505_BUCK_ARM, },
+	{ .name = "ab8505_buck_smpsc",  .driver_data = (void *)AB8505_BUCK_SMPSC, },
+	{ .name = "ab8505_buck_smpsm",  .driver_data = (void *)AB8505_BUCK_SMPSM, },
 };
 
 static struct {

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (7 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 08/10] regulator: ab8500: Add buck converter support Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  2026-09-14 13:14   ` Mark Brown
  2026-09-14  7:46 ` [PATCH v3 10/10] regulator: ab8500: Use scoped guard for shared mode mutex Linus Walleij
  9 siblings, 1 reply; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

The SMPS enable fields are initialized from OTP and may leave a rail
enabled for discrete consumers which cannot be described in the device
tree. Such a rail currently looks unused to the regulator core and is
disabled when constraints are completed.

Read the enable field while registering each switchable buck
regulator. If it is nonzero, mark the regulator boot-on and always-on
dynamically so the unused-regulator sweep leaves it alone. Keep the
enable operation idempotent so applying the always-on constraint
preserves an OTP-selected hardware-control or low-power mode instead of
forcing high-power mode. Synchronize the cached mode with the preserved
field so an OTP-selected low-power state is also reported correctly.

Regulators which are disabled by OTP retain normal switchable
behavior.

Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 76 ++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 67 insertions(+), 9 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 130febf2680a..57ef3d4610e6 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -422,11 +422,10 @@ static int ab8500_regulator_disable(struct regulator_dev *rdev)
 	return ret;
 }
 
-static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
+static int ab8500_regulator_get_enable_value(struct regulator_dev *rdev)
 {
 	int ret;
 	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
-	u8 enable_mask;
 	u8 regval;
 
 	if (info == NULL) {
@@ -442,18 +441,71 @@ static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
 		return ret;
 	}
 
-	enable_mask = info->enable_mask ? info->enable_mask : info->update_mask;
-
 	dev_vdbg(rdev_get_dev(rdev),
 		"%s-is_enabled (bank, reg, mask, value): 0x%x, 0x%x, 0x%x,"
 		" 0x%x\n",
 		info->desc.name, info->update_bank, info->update_reg,
-		enable_mask, regval);
+		info->update_mask, regval);
+
+	return regval & info->update_mask;
+}
+
+static int ab8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+	struct ab8500_regulator_info *info = rdev_get_drvdata(rdev);
+	u8 enable_mask;
+	int ret;
+
+	ret = ab8500_regulator_get_enable_value(rdev);
+	if (ret < 0)
+		return ret;
+
+	enable_mask = info->enable_mask ? info->enable_mask : info->update_mask;
+
+	return !!(ret & enable_mask);
+}
 
-	if (regval & enable_mask)
-		return 1;
+static int ab8500_buck_enable(struct regulator_dev *rdev)
+{
+	int ret;
+
+	/* Keep an OTP-selected hardware or low-power mode intact. */
+	ret = ab8500_regulator_is_enabled(rdev);
+	if (ret)
+		return ret < 0 ? ret : 0;
+
+	return ab8500_regulator_enable(rdev);
+}
+
+static int ab8500_buck_init(struct regulator_dev *rdev,
+			    struct regulator_config *config)
+{
+	struct ab8500_regulator_info *info = config->driver_data;
+	int ret;
+
+	ret = ab8500_regulator_get_enable_value(rdev);
+	if (ret <= 0)
+		return ret;
+
+	/* Report forced LP accurately; HP and hardware control are normal mode. */
+	if (ret == info->update_val_idle)
+		info->update_val = info->update_val_idle;
 	else
-		return 0;
+		info->update_val = info->update_val_normal;
+
+	/*
+	 * The SMPS enable state is selected by OTP.  An enabled rail may
+	 * supply discrete board components which are not represented as
+	 * regulator consumers, so keep it out of the unused-regulator sweep.
+	 */
+	rdev->constraints->boot_on = true;
+	rdev->constraints->always_on = true;
+	rdev->constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS;
+
+	dev_dbg(config->dev, "%s: preserving OTP-enabled state\n",
+		info->desc.name);
+
+	return 0;
 }
 
 static unsigned int ab8500_regulator_get_optimum_mode(
@@ -861,7 +913,7 @@ static const struct regulator_ops ab8500_regulator_linear_range_volt_ops = {
 };
 
 static const struct regulator_ops ab8500_buck_ops = {
-	.enable			= ab8500_regulator_enable,
+	.enable			= ab8500_buck_enable,
 	.disable		= ab8500_regulator_disable,
 	.is_enabled		= ab8500_regulator_is_enabled,
 	.get_optimum_mode	= ab8500_regulator_get_optimum_mode,
@@ -1127,6 +1179,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SMPS1",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8500_BUCK_SMPS1,
 			.owner		= THIS_MODULE,
@@ -1152,6 +1205,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SMPS2",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8500_BUCK_SMPS2,
 			.owner		= THIS_MODULE,
@@ -1177,6 +1231,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SMPS3",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8500_BUCK_SMPS3,
 			.owner		= THIS_MODULE,
@@ -1556,6 +1611,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SMPSA",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_BUCK_SMPSA,
 			.owner		= THIS_MODULE,
@@ -1581,6 +1637,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SMPSB",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_BUCK_SMPSB,
 			.owner		= THIS_MODULE,
@@ -1606,6 +1663,7 @@ static struct ab8500_regulator_info
 		.desc = {
 			.name		= "BUCK-SAFE",
 			.ops		= &ab8500_buck_ops,
+			.init_cb	= ab8500_buck_init,
 			.type		= REGULATOR_VOLTAGE,
 			.id		= AB8505_BUCK_SAFE,
 			.owner		= THIS_MODULE,

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH v3 10/10] regulator: ab8500: Use scoped guard for shared mode mutex
  2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
                   ` (8 preceding siblings ...)
  2026-09-14  7:46 ` [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators Linus Walleij
@ 2026-09-14  7:46 ` Linus Walleij
  9 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14  7:46 UTC (permalink / raw)
  To: Stephan Gerhold, Liam Girdwood, Mark Brown, Axel Lin
  Cc: linux-kernel, Linus Walleij

Use a scoped mutex guard in ab8500_regulator_set_mode().

Assisted-by: LLM
Signed-off-by: Linus Walleij <linusw@kernel.org>
---
 drivers/regulator/ab8500.c | 23 ++++++++---------------
 1 file changed, 8 insertions(+), 15 deletions(-)

diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c
index 57ef3d4610e6..3c1962ad85d4 100644
--- a/drivers/regulator/ab8500.c
+++ b/drivers/regulator/ab8500.c
@@ -20,6 +20,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/abx500.h>
 #include <linux/mfd/abx500/ab8500.h>
@@ -542,6 +543,8 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 		return -EINVAL;
 	}
 
+	guard(mutex)(&shared_mode_mutex);
+
 	if (info->mode_mask) {
 		bank = info->mode_bank;
 		reg = info->mode_reg;
@@ -552,9 +555,6 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 		mask = info->update_mask;
 	}
 
-	if (info->shared_mode)
-		mutex_lock(&shared_mode_mutex);
-
 	switch (mode) {
 	case REGULATOR_MODE_NORMAL:
 		if (info->shared_mode)
@@ -573,7 +573,7 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 			if (!shared_regulator->shared_mode->lp_mode_req) {
 				/* Other regulator prevent LP mode */
 				info->shared_mode->lp_mode_req = true;
-				goto out_unlock;
+				return 0;
 			}
 
 			lp_mode_req = true;
@@ -585,18 +585,15 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 			val = info->update_val_idle;
 		break;
 	default:
-		ret = -EINVAL;
-		goto out_unlock;
+		return -EINVAL;
 	}
 
 	if (info->mode_mask) {
 		enabled = 1;
 	} else {
 		enabled = ab8500_regulator_is_enabled(rdev);
-		if (enabled < 0) {
-			ret = enabled;
-			goto out_unlock;
-		}
+		if (enabled < 0)
+			return enabled;
 	}
 
 	if (enabled) {
@@ -605,7 +602,7 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 		if (ret < 0) {
 			dev_err(rdev_get_dev(rdev),
 				"couldn't set regulator mode\n");
-			goto out_unlock;
+			return ret;
 		}
 
 		dev_vdbg(rdev_get_dev(rdev),
@@ -621,10 +618,6 @@ static int ab8500_regulator_set_mode(struct regulator_dev *rdev,
 	if (info->shared_mode)
 		info->shared_mode->lp_mode_req = lp_mode_req;
 
-out_unlock:
-	if (info->shared_mode)
-		mutex_unlock(&shared_mode_mutex);
-
 	return ret;
 }
 

-- 
2.55.0


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators
  2026-09-14  7:46 ` [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators Linus Walleij
@ 2026-09-14 13:14   ` Mark Brown
  2026-09-14 20:50     ` Linus Walleij
  0 siblings, 1 reply; 13+ messages in thread
From: Mark Brown @ 2026-09-14 13:14 UTC (permalink / raw)
  To: Linus Walleij; +Cc: Stephan Gerhold, Liam Girdwood, Axel Lin, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1120 bytes --]

On Mon, Sep 14, 2026 at 09:46:08AM +0200, Linus Walleij wrote:
> The SMPS enable fields are initialized from OTP and may leave a rail
> enabled for discrete consumers which cannot be described in the device
> tree. Such a rail currently looks unused to the regulator core and is
> disabled when constraints are completed.

> +static int ab8500_buck_init(struct regulator_dev *rdev,
> +			    struct regulator_config *config)
> +{

> +	/*
> +	 * The SMPS enable state is selected by OTP.  An enabled rail may
> +	 * supply discrete board components which are not represented as
> +	 * regulator consumers, so keep it out of the unused-regulator sweep.
> +	 */
> +	rdev->constraints->boot_on = true;
> +	rdev->constraints->always_on = true;
> +	rdev->constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS;

This unconditionally overrides machine constraints, the general idea is
that we should always trust machine constraints.  For this particular
driver it probably doesn't matter so much but it's a bad example and we
really should update the DT too, and if we're going to enforce in kernel
then warn about doing so.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators
  2026-09-14 13:14   ` Mark Brown
@ 2026-09-14 20:50     ` Linus Walleij
  0 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-09-14 20:50 UTC (permalink / raw)
  To: Mark Brown; +Cc: Stephan Gerhold, Liam Girdwood, Axel Lin, linux-kernel

On Mon, Sep 14, 2026 at 3:15 PM Mark Brown <broonie@kernel.org> wrote:

> > +     /*
> > +      * The SMPS enable state is selected by OTP.  An enabled rail may
> > +      * supply discrete board components which are not represented as
> > +      * regulator consumers, so keep it out of the unused-regulator sweep.
> > +      */
> > +     rdev->constraints->boot_on = true;
> > +     rdev->constraints->always_on = true;
> > +     rdev->constraints->valid_ops_mask &= ~REGULATOR_CHANGE_STATUS;
>
> This unconditionally overrides machine constraints, the general idea is
> that we should always trust machine constraints.  For this particular
> driver it probably doesn't matter so much but it's a bad example and we
> really should update the DT too, and if we're going to enforce in kernel
> then warn about doing so.

I was actually a bit back and forth about that in my head.

My thinking was along the lines that DT is hardware description
but the OTP settings is also a kind of hardware description
so which one should win.

I'll probably just put the expected state into the DT and
add some warning print if this differs from the OTP,
should be sufficient, there may be cases where people
want to force things off in the DT for example.

Yours,
Linus Walleij

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-09-14 20:51 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-14  7:45 [PATCH v3 00/10] Add AB8500 and AB8505 buck regulator support Linus Walleij
2026-09-14  7:46 ` [PATCH v3 01/10] regulator: ab8500: Fix AB8505 VANA voltage selectors Linus Walleij
2026-09-14  7:46 ` [PATCH v3 02/10] regulator: ab8500: Add AB8505 VAUX3 3.05 V setting Linus Walleij
2026-09-14  7:46 ` [PATCH v3 03/10] regulator: ab8500: Handle AB8505 VINTCORE selector 7 Linus Walleij
2026-09-14  7:46 ` [PATCH v3 04/10] regulator: ab8500: Treat cut 1.0 VAUX3 as fixed Linus Walleij
2026-09-14  7:46 ` [PATCH v3 05/10] regulator: ab8500: Test dedicated enable bits only Linus Walleij
2026-09-14  7:46 ` [PATCH v3 06/10] regulator: ab8500: Propagate mode enable read errors Linus Walleij
2026-09-14  7:46 ` [PATCH v3 07/10] regulator: ab8500: Use linear ranges for LDO voltages Linus Walleij
2026-09-14  7:46 ` [PATCH v3 08/10] regulator: ab8500: Add buck converter support Linus Walleij
2026-09-14  7:46 ` [PATCH v3 09/10] regulator: ab8500: Preserve OTP-enabled buck regulators Linus Walleij
2026-09-14 13:14   ` Mark Brown
2026-09-14 20:50     ` Linus Walleij
2026-09-14  7:46 ` [PATCH v3 10/10] regulator: ab8500: Use scoped guard for shared mode mutex Linus Walleij

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®