* [PATCH v2 1/2] regulator: pca9450: Use LDO5 low-control enable mask
@ 2026-08-18 1:40 Fabio Estevam
2026-08-18 1:40 ` [PATCH v2 2/2] regulator: pca9450: Support regulator-off-in-suspend Fabio Estevam
0 siblings, 1 reply; 2+ messages in thread
From: Fabio Estevam @ 2026-08-18 1:40 UTC (permalink / raw)
To: broonie; +Cc: linux-kernel, imx, Fabio Estevam
From: Fabio Estevam <festevam@nabladev.com>
LDO5 has voltage-selection fields in both LDO5CTRL_L and LDO5CTRL_H,
selected by the SD_VSEL pin. Its ENMODE field, however, is only in
LDO5CTRL_L.
The regulator descriptors correctly use LDO5CTRL_L as enable_reg, but
use the misleading LDO5H_EN_MASK name for enable_mask. Both mask
definitions have the same value, so this causes no functional problem.
Use LDO5L_EN_MASK in all LDO5 descriptors and remove the unused
LDO5H_EN_MASK definition.
Signed-off-by: Fabio Estevam <festevam@nabladev.com>
---
Changes since v1:
- New patch clarifying that the LDO5 ENMODE field is in LDO5CTRL_L.
drivers/regulator/pca9450-regulator.c | 6 +++---
include/linux/regulator/pca9450.h | 1 -
2 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c
index c41db70fa052..93d0af7f7eda 100644
--- a/drivers/regulator/pca9450-regulator.c
+++ b/drivers/regulator/pca9450-regulator.c
@@ -621,7 +621,7 @@ static struct pca9450_regulator_desc pca9450a_regulators[] = {
.vsel_reg = PCA9450_REG_LDO5CTRL_H,
.vsel_mask = LDO5HOUT_MASK,
.enable_reg = PCA9450_REG_LDO5CTRL_L,
- .enable_mask = LDO5H_EN_MASK,
+ .enable_mask = LDO5L_EN_MASK,
.owner = THIS_MODULE,
},
},
@@ -866,7 +866,7 @@ static struct pca9450_regulator_desc pca9450bc_regulators[] = {
.vsel_reg = PCA9450_REG_LDO5CTRL_H,
.vsel_mask = LDO5HOUT_MASK,
.enable_reg = PCA9450_REG_LDO5CTRL_L,
- .enable_mask = LDO5H_EN_MASK,
+ .enable_mask = LDO5L_EN_MASK,
.owner = THIS_MODULE,
},
},
@@ -1086,7 +1086,7 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = {
.vsel_reg = PCA9450_REG_LDO5CTRL_H,
.vsel_mask = LDO5HOUT_MASK,
.enable_reg = PCA9450_REG_LDO5CTRL_L,
- .enable_mask = LDO5H_EN_MASK,
+ .enable_mask = LDO5L_EN_MASK,
.owner = THIS_MODULE,
},
},
diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h
index 0df8b3c48082..2aa73624d0b8 100644
--- a/include/linux/regulator/pca9450.h
+++ b/include/linux/regulator/pca9450.h
@@ -210,7 +210,6 @@ enum {
#define LDO5L_EN_MASK 0xC0
#define LDO5LOUT_MASK 0x0F
-#define LDO5H_EN_MASK 0xC0
#define LDO5HOUT_MASK 0x0F
/* PCA9450_REG_IRQ bits */
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH v2 2/2] regulator: pca9450: Support regulator-off-in-suspend
2026-08-18 1:40 [PATCH v2 1/2] regulator: pca9450: Use LDO5 low-control enable mask Fabio Estevam
@ 2026-08-18 1:40 ` Fabio Estevam
0 siblings, 0 replies; 2+ messages in thread
From: Fabio Estevam @ 2026-08-18 1:40 UTC (permalink / raw)
To: broonie; +Cc: linux-kernel, imx, Fabio Estevam
From: Fabio Estevam <festevam@nabladev.com>
The PCA9450 uses each regulator's ENMODE field to control whether the
regulator remains enabled when the PMIC transitions from RUN to STANDBY
mode.
The driver does not currently implement set_suspend_disable(), so a
regulator configured with regulator-off-in-suspend remains enabled
during system suspend.
Implement set_suspend_disable() for the buck regulators and LDO3-LDO5.
The suspend and runtime controls share ENMODE, so first read the field
and leave it unchanged when it is 00b. This preserves the state of a
regulator that was already disabled at runtime. For an enabled regulator,
program 10b to keep it on in RUN and turn it off while PMIC_STBY_REQ is
asserted.
Most buck descriptors set enable_val to 01b, while BUCK2 uses 10b. When
enable_val is nonzero, regulator_is_enabled_regmap() checks for an exact
match. It would therefore report most bucks as disabled after their ENMODE
is changed from 01b to 10b, even though all valid nonzero ENMODE values
enable the regulator in RUN. Use a custom is_enabled() helper for the buck
operation tables that considers a nonzero ENMODE enabled. The LDO
descriptors leave enable_val at zero, for which the generic helper already
performs this nonzero check, so keep using it for the LDOs.
Runtime enable and disable operations remain unchanged: disable writes
00b and enable writes the regulator's default mode. The suspend callback
reapplies 10b on each suspend after any intervening runtime operation.
Keep LDO1 and LDO2 on regulator operations without
set_suspend_disable(), because these regulators supply the SNVS domain
and must remain enabled in STANDBY mode.
Measured on a custom i.MX8MP board, turning off NVCC_SD2 (LDO5) during
system suspend reduced power consumption by approximately 64 mW.
Signed-off-by: Fabio Estevam <festevam@nabladev.com>
---
Changes since v1:
- Preserve the state of regulators that are disabled at runtime.
- Add a custom is_enabled() callback for buck regulators so that valid
nonzero ENMODE values are reported as enabled.
drivers/regulator/pca9450-regulator.c | 75 ++++++++++++++++++++++++---
include/linux/regulator/pca9450.h | 3 ++
2 files changed, 70 insertions(+), 8 deletions(-)
diff --git a/drivers/regulator/pca9450-regulator.c b/drivers/regulator/pca9450-regulator.c
index 93d0af7f7eda..613bce411f31 100644
--- a/drivers/regulator/pca9450-regulator.c
+++ b/drivers/regulator/pca9450-regulator.c
@@ -77,10 +77,56 @@ static const unsigned int pca9450_dvs_buck_ramp_table[] = {
25000, 12500, 6250, 3125
};
+static int pca9450_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
+ if (ret)
+ return ret;
+
+ /* All valid nonzero ENMODE values enable the regulator in RUN mode. */
+ return !!(val & rdev->desc->enable_mask);
+}
+
+static int pca9450_set_suspend_disable(struct regulator_dev *rdev,
+ unsigned int suspend_val)
+{
+ int ret;
+
+ /* Do not enable a runtime-disabled regulator when returning to RUN. */
+ ret = pca9450_regulator_is_enabled(rdev);
+ if (ret < 0)
+ return ret;
+ if (!ret)
+ return 0;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask,
+ suspend_val);
+}
+
+/*
+ * ENMODE = 10b keeps the regulator enabled in RUN mode and disables it
+ * while PMIC_STBY_REQ is asserted.
+ */
+static int pca9450_buck_set_suspend_disable(struct regulator_dev *rdev)
+{
+ return pca9450_set_suspend_disable(rdev,
+ BUCK_ENMODE_ONREQ_STBYREQ);
+}
+
+static int pca9450_ldo_set_suspend_disable(struct regulator_dev *rdev)
+{
+ return pca9450_set_suspend_disable(rdev,
+ LDO_ENMODE_ONREQ_STBYREQ);
+}
+
static const struct regulator_ops pca9450_dvs_buck_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
- .is_enabled = regulator_is_enabled_regmap,
+ .is_enabled = pca9450_regulator_is_enabled,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
@@ -88,18 +134,20 @@ static const struct regulator_ops pca9450_dvs_buck_regulator_ops = {
.set_ramp_delay = regulator_set_ramp_delay_regmap,
.set_mode = pca9450_buck_set_mode,
.get_mode = pca9450_buck_get_mode,
+ .set_suspend_disable = pca9450_buck_set_suspend_disable,
};
static const struct regulator_ops pca9450_buck_regulator_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
- .is_enabled = regulator_is_enabled_regmap,
+ .is_enabled = pca9450_regulator_is_enabled,
.list_voltage = regulator_list_voltage_linear_range,
.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,
+ .set_suspend_disable = pca9450_buck_set_suspend_disable,
};
static const struct regulator_ops pca9450_ldo_regulator_ops = {
@@ -111,6 +159,16 @@ static const struct regulator_ops pca9450_ldo_regulator_ops = {
.get_voltage_sel = regulator_get_voltage_sel_regmap,
};
+static const struct regulator_ops pca9450_ldo_suspend_regulator_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_suspend_disable = pca9450_ldo_set_suspend_disable,
+};
+
static unsigned int pca9450_ldo5_get_reg_voltage_sel(struct regulator_dev *rdev)
{
struct pca9450 *pca9450 = rdev_get_drvdata(rdev);
@@ -164,6 +222,7 @@ static const struct regulator_ops pca9450_ldo5_regulator_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = pca9450_ldo5_set_voltage_sel_regmap,
.get_voltage_sel = pca9450_ldo5_get_voltage_sel_regmap,
+ .set_suspend_disable = pca9450_ldo_set_suspend_disable,
};
/*
@@ -575,7 +634,7 @@ static struct pca9450_regulator_desc pca9450a_regulators[] = {
.of_match = of_match_ptr("LDO3"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO3,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO3_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
@@ -594,7 +653,7 @@ static struct pca9450_regulator_desc pca9450a_regulators[] = {
.of_match = of_match_ptr("LDO4"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO4,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO4_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
@@ -820,7 +879,7 @@ static struct pca9450_regulator_desc pca9450bc_regulators[] = {
.of_match = of_match_ptr("LDO3"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO3,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO3_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
@@ -839,7 +898,7 @@ static struct pca9450_regulator_desc pca9450bc_regulators[] = {
.of_match = of_match_ptr("LDO4"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO4,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO4_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
@@ -1040,7 +1099,7 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = {
.of_match = of_match_ptr("LDO3"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO3,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO3_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
@@ -1059,7 +1118,7 @@ static struct pca9450_regulator_desc pca9451a_regulators[] = {
.of_match = of_match_ptr("LDO4"),
.regulators_node = of_match_ptr("regulators"),
.id = PCA9450_LDO4,
- .ops = &pca9450_ldo_regulator_ops,
+ .ops = &pca9450_ldo_suspend_regulator_ops,
.type = REGULATOR_VOLTAGE,
.n_voltages = PCA9450_LDO4_VOLTAGE_NUM,
.linear_ranges = pca9450_ldo34_volts,
diff --git a/include/linux/regulator/pca9450.h b/include/linux/regulator/pca9450.h
index 2aa73624d0b8..bf94df5fafe3 100644
--- a/include/linux/regulator/pca9450.h
+++ b/include/linux/regulator/pca9450.h
@@ -212,6 +212,9 @@ enum {
#define LDO5HOUT_MASK 0x0F
+/* LDO ENMODE value: ON in RUN, OFF while PMIC_STBY_REQ is asserted */
+#define LDO_ENMODE_ONREQ_STBYREQ 0x80
+
/* PCA9450_REG_IRQ bits */
#define IRQ_PWRON 0x80
#define IRQ_WDOGB 0x40
--
2.43.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-08-18 1:41 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18 1:40 [PATCH v2 1/2] regulator: pca9450: Use LDO5 low-control enable mask Fabio Estevam
2026-08-18 1:40 ` [PATCH v2 2/2] regulator: pca9450: Support regulator-off-in-suspend Fabio Estevam
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox