From: Michael Trimarchi <michael@amarulasolutions.com>
To: Peng Fan <peng.fan@nxp.com>, Jaehoon Chung <jh80.chung@samsung.com>
Cc: Tom Rini <trini@konsulko.com>,
Dario Binacchi <dario.binacchi@amarulasolutions.com>,
u-boot@lists.denx.de, linux-amarula@amarulasolutions.com,
Michael Trimarchi <michael@amarulasolutions.com>
Subject: [PATCH V2 2/2] power: regulator: pfuze100: Decouple hardware base voltage from DTS constraints
Date: Sun, 11 Jan 2026 09:23:47 +0100 [thread overview]
Message-ID: <20260111082347.1302003-3-michael@amarulasolutions.com> (raw)
In-Reply-To: <20260111082347.1302003-1-michael@amarulasolutions.com>
Currently, the driver uses the device tree property `regulator-min-microvolt`
(accessed via `uc_pdata->min_uV`) as the base voltage for calculating
voltage register values.
However, the device tree property defines the safety constraint (the
minimum voltage allowed for the specific board/consumer), not the physical
minimum voltage the regulator outputs when the selector is at 0.
Using the DTS constraint as the linear base leads to incorrect voltage
calculations if the constraint does not exactly match the hardware's
zero-index voltage. For example, if a regulator physically starts at
700mV but the DTS constrains it to 1000mV, the driver incorrectly calculates
the selector assuming 0 corresponds to 1000mV.
Fix this by:
1. Adding a `min_uV` field to the regulator descriptor to hold the
true hardware base voltage as defined in the datasheet.
2. Updating the regulator definitions with the correct base voltages
(aligned with Linux kernel driver definitions).
3. Using the descriptor's hardware minimum for all voltage-to-selector
calculations, instead of the DTS constraint.
4. Adding a validation check to ensure the DTS constraint is within the
hardware's possible range.
Signed-off-by: Michael Trimarchi <michael@amarulasolutions.com>
---
V1->V2: no changes
---
drivers/power/regulator/pfuze100.c | 94 ++++++++++++++++--------------
1 file changed, 51 insertions(+), 43 deletions(-)
diff --git a/drivers/power/regulator/pfuze100.c b/drivers/power/regulator/pfuze100.c
index 63d39c21bb8..107f036d33f 100644
--- a/drivers/power/regulator/pfuze100.c
+++ b/drivers/power/regulator/pfuze100.c
@@ -32,6 +32,7 @@ struct pfuze100_regulator_desc {
enum regulator_type type;
bool hi_bit;
unsigned int uV_step;
+ unsigned int min_uV;
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int stby_reg;
@@ -54,13 +55,15 @@ struct pfuze100_regulator_plat {
.name = #_name, \
.type = REGULATOR_TYPE_FIXED, \
.voltage = (vol), \
+ .min_uV = (vol), \
}
-#define PFUZE100_SW_REG(_name, base, step, hbit) \
+#define PFUZE100_SW_REG(_name, base, min, step, hbit) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
.hi_bit = (hbit), \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x3F, \
@@ -88,10 +91,11 @@ struct pfuze100_regulator_plat {
.volt_table = (voltages), \
}
-#define PFUZE100_VGEN_REG(_name, base, step) \
+#define PFUZE100_VGEN_REG(_name, base, min, step) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base), \
.vsel_mask = 0xF, \
@@ -99,10 +103,11 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x20, \
}
-#define PFUZE3000_VCC_REG(_name, base, step) \
+#define PFUZE3000_VCC_REG(_name, base, min, step) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base), \
.vsel_mask = 0x3, \
@@ -110,10 +115,11 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x20, \
}
-#define PFUZE3000_SW1_REG(_name, base, step) \
+#define PFUZE3000_SW1_REG(_name, base, min, step) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x1F, \
@@ -121,10 +127,11 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x1F, \
}
-#define PFUZE3000_SW2_REG(_name, base, step) \
+#define PFUZE3000_SW2_REG(_name, base, min, step) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x7, \
@@ -132,10 +139,11 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x7, \
}
-#define PFUZE3000_SW3_REG(_name, base, step) \
+#define PFUZE3000_SW3_REG(_name, base, min, step) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0xF, \
@@ -165,55 +173,55 @@ static unsigned int pfuze3000_sw2hi[] = {
/* PFUZE100 */
static struct pfuze100_regulator_desc pfuze100_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, false),
- PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000, false),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, true),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, true),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, true),
- PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000, true),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 300000, 25000, false),
+ PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 300000, 25000, false),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 400000, 25000, true),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 400000, 25000, true),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 400000, 25000, true),
+ PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 400000, 25000, true),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst, false),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000),
- PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000),
- PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000),
- PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000),
- PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 800000, 50000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 800000, 50000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 1800000, 100000),
};
/* PFUZE200 */
static struct pfuze100_regulator_desc pfuze200_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, false),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, true),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, true),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, true),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 300000, 25000, false),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 400000, 25000, true),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 400000, 25000, true),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 400000, 25000, true),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst, false),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze100_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 50000),
- PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000),
- PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000),
- PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000),
- PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vgen1, PFUZE100_VGEN1VOL, 800000, 50000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 800000, 50000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 1800000, 100000),
};
/* PFUZE3000 */
static struct pfuze100_regulator_desc pfuze3000_regulators[] = {
- PFUZE3000_SW1_REG(sw1a, PFUZE100_SW1ABVOL, 25000),
- PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 25000),
+ PFUZE3000_SW1_REG(sw1a, PFUZE100_SW1ABVOL, 700000, 25000),
+ PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 700000, 25000),
PFUZE100_SWB_REG(sw2, PFUZE100_SW2VOL, 0x7, 50000, pfuze3000_sw2lo, true),
- PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000),
+ PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 900000, 50000),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst, false),
PFUZE100_SNVS_REG(vsnvs, PFUZE100_VSNVSVOL, 0x7, pfuze3000_vsnvs),
PFUZE100_FIXED_REG(vrefddr, PFUZE100_VREFDDRCON, 750000),
- PFUZE100_VGEN_REG(vldo1, PFUZE100_VGEN1VOL, 100000),
- PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 50000),
- PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 150000),
- PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 150000),
- PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 100000),
- PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 100000),
+ PFUZE100_VGEN_REG(vldo1, PFUZE100_VGEN1VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 800000, 50000),
+ PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 2850000, 150000),
+ PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 2850000, 150000),
+ PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 1800000, 100000),
+ PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 1800000, 100000),
};
#define MODE(_id, _val, _name) { \
@@ -483,15 +491,15 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
val &= desc->vsel_mask;
*uV = desc->volt_table[val];
} else {
- if (uc_pdata->min_uV < 0) {
- debug("Need to provide min_uV in dts.\n");
+ if (uc_pdata->min_uV < desc->min_uV) {
+ debug("min_uV in dts can not be below regulator min_uV.\n");
return -EINVAL;
}
val = pmic_reg_read(dev->parent, desc->vsel_reg);
if (val < 0)
return val;
val &= desc->vsel_mask;
- *uV = uc_pdata->min_uV + (int)val * desc->uV_step;
+ *uV = desc->min_uV + (int)val * desc->uV_step;
}
return 0;
@@ -513,13 +521,13 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
return pmic_clrsetbits(dev->parent, desc->vsel_reg,
desc->vsel_mask, i);
} else {
- if (uc_pdata->min_uV < 0) {
- debug("Need to provide min_uV in dts.\n");
+ if (uc_pdata->min_uV < desc->min_uV) {
+ debug("min_uV in dts can not be below regulator min_uV.\n");
return -EINVAL;
}
return pmic_clrsetbits(dev->parent, desc->vsel_reg,
desc->vsel_mask,
- (*uV - uc_pdata->min_uV) / desc->uV_step);
+ (*uV - desc->min_uV) / desc->uV_step);
}
return 0;
--
2.51.0
next prev parent reply other threads:[~2026-01-11 8:24 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-11 8:23 [PATCH V2 0/2] power: regulator: pfuze100: Fix voltage calculation and support high-range Michael Trimarchi
2026-01-11 8:23 ` [PATCH V2 1/2] power: regulator: pfuze100: support high voltage range bit Michael Trimarchi
2026-01-11 8:23 ` Michael Trimarchi [this message]
2026-01-13 8:58 ` [PATCH V2 0/2] power: regulator: pfuze100: Fix voltage calculation and support high-range Peng Fan
2026-01-13 10:46 ` Michael Nazzareno Trimarchi
2026-01-13 12:16 ` Peng Fan
2026-01-13 18:16 ` Michael Nazzareno Trimarchi
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260111082347.1302003-3-michael@amarulasolutions.com \
--to=michael@amarulasolutions.com \
--cc=dario.binacchi@amarulasolutions.com \
--cc=jh80.chung@samsung.com \
--cc=linux-amarula@amarulasolutions.com \
--cc=peng.fan@nxp.com \
--cc=trini@konsulko.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox