summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/Kconfig364
-rw-r--r--drivers/power/domain/Kconfig24
-rw-r--r--drivers/power/domain/Makefile2
-rw-r--r--drivers/power/domain/apple-pmgr.c5
-rw-r--r--drivers/power/domain/bcm6328-power-domain.c2
-rw-r--r--drivers/power/domain/imx8-power-domain-legacy.c2
-rw-r--r--drivers/power/domain/imx8-power-domain.c2
-rw-r--r--drivers/power/domain/imx8m-power-domain.c6
-rw-r--r--drivers/power/domain/imx8mp-mediamix.c2
-rw-r--r--drivers/power/domain/meson-ee-pwrc.c15
-rw-r--r--drivers/power/domain/meson-gx-pwrc-vpu.c15
-rw-r--r--drivers/power/domain/meson-secure-pwrc.c2
-rw-r--r--drivers/power/domain/mtk-power-domain.c2
-rw-r--r--drivers/power/domain/power-domain-uclass.c7
-rw-r--r--drivers/power/domain/qcom-rpmhpd.c278
-rw-r--r--drivers/power/domain/renesas-r8a78000-power-domain.c425
-rw-r--r--drivers/power/domain/sandbox-power-domain.c2
-rw-r--r--drivers/power/domain/scmi-power-domain.c10
-rw-r--r--drivers/power/domain/tegra186-power-domain.c2
-rw-r--r--drivers/power/domain/zynqmp-power-domain.c2
-rw-r--r--drivers/power/pmic/Kconfig384
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/i2c_pmic_emul.c2
-rw-r--r--drivers/power/pmic/mtk-pwrap.c118
-rw-r--r--drivers/power/pmic/pca9450.c2
-rw-r--r--drivers/power/pmic/pmic_qcom.c2
-rw-r--r--drivers/power/pmic/pmic_spacemit_p1.c94
-rw-r--r--drivers/power/regulator/Kconfig484
-rw-r--r--drivers/power/regulator/Makefile7
-rw-r--r--drivers/power/regulator/anatop_regulator.c3
-rw-r--r--drivers/power/regulator/mpq8785.c494
-rw-r--r--drivers/power/regulator/pfuze100.c166
-rw-r--r--drivers/power/regulator/pmbus_generic.c90
-rw-r--r--drivers/power/regulator/pmbus_helper.c315
-rw-r--r--drivers/power/regulator/pmbus_helper.h90
-rw-r--r--drivers/power/regulator/qcom-rpmh-regulator.c146
-rw-r--r--drivers/power/regulator/regulator-uclass.c27
-rw-r--r--drivers/power/regulator/regulator_common.c6
-rw-r--r--drivers/power/regulator/sandbox.c11
-rw-r--r--drivers/power/regulator/sandbox_pmbus.c171
-rw-r--r--drivers/power/regulator/scmi_regulator.c2
-rw-r--r--drivers/power/regulator/spacemit_p1_regulator.c464
-rw-r--r--drivers/power/regulator/tps6287x_regulator.c2
-rw-r--r--drivers/power/regulator/tps65941_regulator.c15
44 files changed, 3466 insertions, 799 deletions
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 1b06d8a66c7..66c389d073b 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -1,7 +1,7 @@
menuconfig POWER
- bool "Power"
- default y
- help
+ bool "Power"
+ default y
+ help
Enable support for power control in U-Boot. This includes support
for PMICs (Power-management Integrated Circuits) and some of the
features provided by PMICs. In particular, voltage regulators can
@@ -63,98 +63,98 @@ choice
config SUNXI_NO_PMIC
bool "board without a pmic"
- ---help---
- Select this for boards which do not use a PMIC.
+ help
+ Select this for boards which do not use a PMIC.
config AXP152_POWER
bool "axp152 pmic support"
depends on MACH_SUN5I
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the axp152 pmic found on most
- A10s boards.
+ help
+ Select this to enable support for the axp152 pmic found on most
+ A10s boards.
config AXP209_POWER
bool "axp209 pmic support"
depends on MACH_SUN4I || MACH_SUN5I || MACH_SUN7I || MACH_SUN8I_V3S
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the axp209 pmic found on most
- A10, A13 and A20 boards.
+ help
+ Select this to enable support for the axp209 pmic found on most
+ A10, A13 and A20 boards.
config AXP221_POWER
bool "axp221 / axp223 pmic support"
depends on MACH_SUN6I || MACH_SUN8I_A23 || MACH_SUN8I_A33 || MACH_SUN8I_R40
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the axp221/axp223 pmic found on most
- A23 and A31 boards.
+ help
+ Select this to enable support for the axp221/axp223 pmic found on most
+ A23 and A31 boards.
config AXP305_POWER
bool "axp305 pmic support"
depends on MACH_SUN50I_H616
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the axp305 pmic found on most
- H616 boards.
+ help
+ Select this to enable support for the axp305 pmic found on most
+ H616 boards.
config AXP313_POWER
bool "axp313 pmic support"
depends on MACH_SUN50I_H616
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the AXP313 PMIC found on some
- H616 boards.
+ help
+ Select this to enable support for the AXP313 PMIC found on some
+ H616 boards.
config AXP717_POWER
bool "axp717 pmic support"
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the AXP717 PMIC found on some boards.
+ help
+ Select this to enable support for the AXP717 PMIC found on some boards.
config AXP803_POWER
bool "AXP803 PMIC support"
select AXP_PMIC_BUS
- ---help---
- Select this to enable support for the AXP803 PMIC found on some boards.
+ help
+ Select this to enable support for the AXP803 PMIC found on some boards.
config AXP809_POWER
bool "axp809 pmic support"
depends on MACH_SUN9I
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Say y here to enable support for the axp809 pmic found on A80 boards.
+ help
+ Say y here to enable support for the axp809 pmic found on A80 boards.
config AXP818_POWER
bool "axp818 pmic support"
depends on MACH_SUN8I_A83T
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Say y here to enable support for the axp818 pmic found on
- A83T dev board.
+ help
+ Say y here to enable support for the axp818 pmic found on
+ A83T dev board.
config AXP318W_POWER
bool "axp318w pmic support"
select AXP_PMIC_BUS
select CMD_POWEROFF
- ---help---
- Select this to enable support for the AXP318W PMIC found on some
- A733 boards.
+ help
+ Select this to enable support for the AXP318W PMIC found on some
+ A733 boards.
config SY8106A_POWER
bool "SY8106A pmic support"
depends on MACH_SUNXI_H3_H5
- ---help---
- Select this to enable support for the SY8106A pmic found on some
- H3 boards.
+ help
+ Select this to enable support for the SY8106A pmic found on some
+ H3 boards.
endchoice
@@ -166,22 +166,22 @@ config AXP_I2C_ADDRESS
default 0x36 if AXP318W_POWER
default 0x30 if AXP152_POWER
default 0x34
- ---help---
- I2C address of the AXP PMIC, used for the SPL only.
+ help
+ I2C address of the AXP PMIC, used for the SPL only.
config AXP_DCDC1_VOLT
int "axp pmic dcdc1 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER
default 3300 if AXP818_POWER || MACH_SUN8I_R40 || AXP803_POWER
default 3000 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I
- ---help---
- Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to
- disable dcdc1. On A23 / A31 / A33 (axp221) boards dcdc1 is used for
- generic 3.3V IO voltage for external devices like the lcd-panal and
- sdcard interfaces, etc. On most boards dcdc1 is undervolted to 3.0V to
- save battery. On A31 devices dcdc1 is also used for VCC-IO. On A83T
- dcdc1 is used for VCC-IO, nand, usb0, sd , etc. On A80 dcdc1 normally
- powers some of the pingroups, NAND/eMMC, SD/MMC, and USB OTG.
+ help
+ Set the voltage (mV) to program the axp pmic dcdc1 at, set to 0 to
+ disable dcdc1. On A23 / A31 / A33 (axp221) boards dcdc1 is used for
+ generic 3.3V IO voltage for external devices like the lcd-panal and
+ sdcard interfaces, etc. On most boards dcdc1 is undervolted to 3.0V to
+ save battery. On A31 devices dcdc1 is also used for VCC-IO. On A83T
+ dcdc1 is used for VCC-IO, nand, usb0, sd , etc. On A80 dcdc1 normally
+ powers some of the pingroups, NAND/eMMC, SD/MMC, and USB OTG.
config AXP_DCDC2_VOLT
int "axp pmic dcdc2 voltage"
@@ -194,16 +194,16 @@ config AXP_DCDC2_VOLT
default 1200 if MACH_SUN6I
default 1100 if MACH_SUN8I
default 0 if MACH_SUN9I
- ---help---
- Set the voltage (mV) to program the axp pmic dcdc2 at, set to 0 to
- disable dcdc2.
- On A10(s) / A13 / A20 boards dcdc2 is VDD-CPU and should be 1.4V.
- On A31 boards dcdc2 is used for VDD-GPU and should be 1.2V.
- On A23/A33 boards dcdc2 is used for VDD-SYS and should be 1.1V.
- On A80 boards dcdc2 powers the GPU and can be left off.
- On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V.
- On R40 boards dcdc2 is VDD-CPU and should be 1.1V
- On boards using the AXP313 or AXP717 it's often VDD-CPU.
+ help
+ Set the voltage (mV) to program the axp pmic dcdc2 at, set to 0 to
+ disable dcdc2.
+ On A10(s) / A13 / A20 boards dcdc2 is VDD-CPU and should be 1.4V.
+ On A31 boards dcdc2 is used for VDD-GPU and should be 1.2V.
+ On A23/A33 boards dcdc2 is used for VDD-SYS and should be 1.1V.
+ On A80 boards dcdc2 powers the GPU and can be left off.
+ On A83T boards dcdc2 is used for VDD-CPUA(cluster 0) and should be 0.9V.
+ On R40 boards dcdc2 is VDD-CPU and should be 1.1V
+ On boards using the AXP313 or AXP717 it's often VDD-CPU.
config AXP_DCDC3_VOLT
int "axp pmic dcdc3 voltage"
@@ -214,18 +214,18 @@ config AXP_DCDC3_VOLT
default 1100 if AXP313_POWER
default 1100 if MACH_SUN8I_R40
default 1200 if MACH_SUN6I || MACH_SUN8I
- ---help---
- Set the voltage (mV) to program the axp pmic dcdc3 at, set to 0 to
- disable dcdc3.
- On A10(s) / A13 / A20 boards with an axp209 dcdc3 is VDD-INT-DLL and
- should be 1.25V.
- On A10s boards with an axp152 dcdc3 is VCC-DRAM and should be 1.5V.
- On A23 / A31 / A33 boards dcdc3 is VDD-CPU and should be 1.2V.
- On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V.
- On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V.
- On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V.
- On boards using the AXP313 or AXP717 it's often VDD-DRAM and should
- be 1.1V for LPDDR4.
+ help
+ Set the voltage (mV) to program the axp pmic dcdc3 at, set to 0 to
+ disable dcdc3.
+ On A10(s) / A13 / A20 boards with an axp209 dcdc3 is VDD-INT-DLL and
+ should be 1.25V.
+ On A10s boards with an axp152 dcdc3 is VCC-DRAM and should be 1.5V.
+ On A23 / A31 / A33 boards dcdc3 is VDD-CPU and should be 1.2V.
+ On A80 boards dcdc3 is used for VDD-CPUA(cluster 0) and should be 0.9V.
+ On A83T boards dcdc3 is used for VDD-CPUB(cluster 1) and should be 0.9V.
+ On R40 boards dcdc3 is VDD-SYS and VDD-GPU and should be 1.1V.
+ On boards using the AXP313 or AXP717 it's often VDD-DRAM and should
+ be 1.1V for LPDDR4.
config AXP_DCDC4_VOLT
int "axp pmic dcdc4 voltage"
@@ -235,25 +235,25 @@ config AXP_DCDC4_VOLT
default 0 if MACH_SUN8I
default 900 if MACH_SUN9I
default 1500 if AXP305_POWER
- ---help---
- Set the voltage (mV) to program the axp pmic dcdc4 at, set to 0 to
- disable dcdc4.
- On A10s boards with an axp152 dcdc4 is VDD-INT-DLL and should be 1.25V.
- On A31 boards dcdc4 is used for VDD-SYS and should be 1.2V.
- On A23 / A33 boards dcdc4 is unused and should be disabled.
- On A80 boards dcdc4 powers VDD-SYS, HDMI, USB OTG and should be 0.9V.
- On A83T boards dcdc4 is used for VDD-GPU.
- On H616 boards dcdcd is used for VCC-DRAM.
+ help
+ Set the voltage (mV) to program the axp pmic dcdc4 at, set to 0 to
+ disable dcdc4.
+ On A10s boards with an axp152 dcdc4 is VDD-INT-DLL and should be 1.25V.
+ On A31 boards dcdc4 is used for VDD-SYS and should be 1.2V.
+ On A23 / A33 boards dcdc4 is unused and should be disabled.
+ On A80 boards dcdc4 powers VDD-SYS, HDMI, USB OTG and should be 0.9V.
+ On A83T boards dcdc4 is used for VDD-GPU.
+ On H616 boards dcdcd is used for VCC-DRAM.
config AXP_DCDC5_VOLT
int "axp pmic dcdc5 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER || AXP803_POWER
default 1500 if MACH_SUN6I || MACH_SUN8I || MACH_SUN9I
- ---help---
- Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to
- disable dcdc5.
- On A23 / A31 / A33 / A80 / A83T / R40 boards dcdc5 is VCC-DRAM and
- should be 1.5V, 1.35V if DDR3L is used.
+ help
+ Set the voltage (mV) to program the axp pmic dcdc5 at, set to 0 to
+ disable dcdc5.
+ On A23 / A31 / A33 / A80 / A83T / R40 boards dcdc5 is VCC-DRAM and
+ should be 1.5V, 1.35V if DDR3L is used.
config AXP_ALDO1_VOLT
int "axp pmic (a)ldo1 voltage"
@@ -261,14 +261,14 @@ config AXP_ALDO1_VOLT
default 0 if MACH_SUN6I || MACH_SUN8I_R40
default 1800 if MACH_SUN8I_A83T
default 3000 if MACH_SUN8I || MACH_SUN9I
- ---help---
- Set the voltage (mV) to program the axp pmic aldo1 at, set to 0 to
- disable aldo1.
- On A31 boards aldo1 is often used to power the wifi module.
- On A23 / A33 boards aldo1 is used for VCC-IO and should be 3.0V.
- On A80 boards aldo1 powers the USB hosts and should be 3.0V.
- On A83T / H8 boards aldo1 is used for MIPI CSI, DSI, HDMI, EFUSE, and
- should be 1.8V.
+ help
+ Set the voltage (mV) to program the axp pmic aldo1 at, set to 0 to
+ disable aldo1.
+ On A31 boards aldo1 is often used to power the wifi module.
+ On A23 / A33 boards aldo1 is used for VCC-IO and should be 3.0V.
+ On A80 boards aldo1 powers the USB hosts and should be 3.0V.
+ On A83T / H8 boards aldo1 is used for MIPI CSI, DSI, HDMI, EFUSE, and
+ should be 1.8V.
config AXP_ALDO2_VOLT
int "axp pmic (a)ldo2 voltage"
@@ -277,188 +277,188 @@ config AXP_ALDO2_VOLT
default 0 if MACH_SUN6I || MACH_SUN9I
default 1800 if MACH_SUN8I_A83T
default 2500 if MACH_SUN8I
- ---help---
- Set the voltage (mV) to program the axp pmic aldo2 at, set to 0 to
- disable aldo2.
- On A10(s) / A13 / A20 boards aldo2 is AVCC and should be 3.0V.
- On A31 boards aldo2 is typically unused and should be disabled.
- On A31 boards aldo2 may be used for LPDDR2 then it should be 1.8V.
- On A23 / A33 boards aldo2 is used for VDD-DLL and should be 2.5V.
- On A80 boards aldo2 powers PB pingroup and camera IO and can be left off.
- On A83T / H8 boards aldo2 powers VDD-DLL, VCC18-PLL, CPVDD, VDD18-ADC,
- LPDDR2, and the codec. It should be 1.8V.
+ help
+ Set the voltage (mV) to program the axp pmic aldo2 at, set to 0 to
+ disable aldo2.
+ On A10(s) / A13 / A20 boards aldo2 is AVCC and should be 3.0V.
+ On A31 boards aldo2 is typically unused and should be disabled.
+ On A31 boards aldo2 may be used for LPDDR2 then it should be 1.8V.
+ On A23 / A33 boards aldo2 is used for VDD-DLL and should be 2.5V.
+ On A80 boards aldo2 powers PB pingroup and camera IO and can be left off.
+ On A83T / H8 boards aldo2 powers VDD-DLL, VCC18-PLL, CPVDD, VDD18-ADC,
+ LPDDR2, and the codec. It should be 1.8V.
config AXP_ALDO3_VOLT
int "axp pmic (a)ldo3 voltage"
depends on AXP209_POWER || AXP221_POWER || AXP809_POWER || AXP818_POWER
default 0 if AXP209_POWER || MACH_SUN9I
default 3000 if MACH_SUN6I || MACH_SUN8I
- ---help---
- Set the voltage (mV) to program the axp pmic aldo3 at, set to 0 to
- disable aldo3.
- On A10(s) / A13 / A20 boards aldo3 should be 2.8V.
- On A23 / A31 / A33 / R40 boards aldo3 is VCC-PLL and AVCC and should
- be 3.0V.
- On A80 boards aldo3 is normally not used.
- On A83T / H8 boards aldo3 is AVCC, VCC-PL, and VCC-LED, and should be
- 3.0V.
+ help
+ Set the voltage (mV) to program the axp pmic aldo3 at, set to 0 to
+ disable aldo3.
+ On A10(s) / A13 / A20 boards aldo3 should be 2.8V.
+ On A23 / A31 / A33 / R40 boards aldo3 is VCC-PLL and AVCC and should
+ be 3.0V.
+ On A80 boards aldo3 is normally not used.
+ On A83T / H8 boards aldo3 is AVCC, VCC-PL, and VCC-LED, and should be
+ 3.0V.
choice
prompt "axp pmic (a)ldo3 voltage rate control"
depends on AXP209_POWER
default AXP_ALDO3_VOLT_SLOPE_NONE
- ---help---
- The AXP can slowly ramp up voltage to reduce the inrush current when
- changing voltages.
- Note, this does not apply when enabling/disabling LDO3. See
- "axp pmic (a)ldo3 inrush quirk" below to enable a slew rate to limit
- inrush current on broken board designs.
+ help
+ The AXP can slowly ramp up voltage to reduce the inrush current when
+ changing voltages.
+ Note, this does not apply when enabling/disabling LDO3. See
+ "axp pmic (a)ldo3 inrush quirk" below to enable a slew rate to limit
+ inrush current on broken board designs.
config AXP_ALDO3_VOLT_SLOPE_NONE
bool "No voltage slope"
- ---help---
- Tries to reach the next voltage setting near instantaneously. Measurements
- indicate that this is about 0.0167 V/uS.
+ help
+ Tries to reach the next voltage setting near instantaneously. Measurements
+ indicate that this is about 0.0167 V/uS.
config AXP_ALDO3_VOLT_SLOPE_16
bool "1.6 mV per uS"
- ---help---
- Increases the voltage by 1.6 mV per uS until the final voltage has
- been reached. Note that the scaling is in 25 mV steps and thus
- the slew rate in reality is about 25 mV/31.250 uS.
+ help
+ Increases the voltage by 1.6 mV per uS until the final voltage has
+ been reached. Note that the scaling is in 25 mV steps and thus
+ the slew rate in reality is about 25 mV/31.250 uS.
config AXP_ALDO3_VOLT_SLOPE_08
bool "0.8 mV per uS"
- ---help---
- Increases the voltage by 0.8 mV per uS until the final voltage has
- been reached. Note that the scaling is in 25 mV steps however and thus
- the slew rate in reality is about 25 mV/15.625 uS.
- This is the slowest supported rate.
+ help
+ Increases the voltage by 0.8 mV per uS until the final voltage has
+ been reached. Note that the scaling is in 25 mV steps however and thus
+ the slew rate in reality is about 25 mV/15.625 uS.
+ This is the slowest supported rate.
endchoice
config AXP_ALDO3_INRUSH_QUIRK
bool "axp pmic (a)ldo3 inrush quirk"
depends on AXP209_POWER
- ---help---
- The reference design denotes a value of 4.7 uF for the output capacitor
- of LDO3. Some boards have too high capacitance causing an inrush current
- and resulting an AXP209 shutdown.
+ help
+ The reference design denotes a value of 4.7 uF for the output capacitor
+ of LDO3. Some boards have too high capacitance causing an inrush current
+ and resulting an AXP209 shutdown.
config AXP_ALDO4_VOLT
int "axp pmic (a)ldo4 voltage"
depends on AXP209_POWER
default 0 if AXP209_POWER
- ---help---
- Set the voltage (mV) to program the axp pmic aldo4 at, set to 0 to
- disable aldo4.
- On A10(s) / A13 / A20 boards aldo4 should be 2.8V.
+ help
+ Set the voltage (mV) to program the axp pmic aldo4 at, set to 0 to
+ disable aldo4.
+ On A10(s) / A13 / A20 boards aldo4 should be 2.8V.
config AXP_DLDO1_VOLT
int "axp pmic dldo1 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic dldo1 at, set to 0 to
- disable dldo1. On sun6i (A31) boards with ethernet dldo1 is often used
- to power the ethernet phy. On A23, A33 and A80 boards this is often
- used to power the wifi.
+ help
+ Set the voltage (mV) to program the axp pmic dldo1 at, set to 0 to
+ disable dldo1. On sun6i (A31) boards with ethernet dldo1 is often used
+ to power the ethernet phy. On A23, A33 and A80 boards this is often
+ used to power the wifi.
config AXP_DLDO2_VOLT
int "axp pmic dldo2 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
default 3000 if MACH_SUN9I
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic dldo2 at, set to 0 to
- disable dldo2.
- On A80 boards dldo2 normally powers the PL pins and should be 3.0V.
+ help
+ Set the voltage (mV) to program the axp pmic dldo2 at, set to 0 to
+ disable dldo2.
+ On A80 boards dldo2 normally powers the PL pins and should be 3.0V.
config AXP_DLDO3_VOLT
int "axp pmic dldo3 voltage"
depends on AXP221_POWER || AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic dldo3 at, set to 0 to
- disable dldo3.
+ help
+ Set the voltage (mV) to program the axp pmic dldo3 at, set to 0 to
+ disable dldo3.
config AXP_DLDO4_VOLT
int "axp pmic dldo4 voltage"
depends on AXP221_POWER || AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic dldo4 at, set to 0 to
- disable dldo4.
+ help
+ Set the voltage (mV) to program the axp pmic dldo4 at, set to 0 to
+ disable dldo4.
config AXP_ELDO1_VOLT
int "axp pmic eldo1 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic eldo1 at, set to 0 to
- disable eldo1.
+ help
+ Set the voltage (mV) to program the axp pmic eldo1 at, set to 0 to
+ disable eldo1.
config AXP_ELDO2_VOLT
int "axp pmic eldo2 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic eldo2 at, set to 0 to
- disable eldo2.
+ help
+ Set the voltage (mV) to program the axp pmic eldo2 at, set to 0 to
+ disable eldo2.
config AXP_ELDO3_VOLT
int "axp pmic eldo3 voltage"
depends on AXP221_POWER || AXP809_POWER || AXP818_POWER
default 3000 if MACH_SUN9I
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic eldo3 at, set to 0 to
- disable eldo3. On some A31(s) tablets it might be used to supply
- 1.2V for the SSD2828 chip (converter of parallel LCD interface
- into MIPI DSI).
- On A80 boards it powers the PM pingroup and should be 3.0V.
+ help
+ Set the voltage (mV) to program the axp pmic eldo3 at, set to 0 to
+ disable eldo3. On some A31(s) tablets it might be used to supply
+ 1.2V for the SSD2828 chip (converter of parallel LCD interface
+ into MIPI DSI).
+ On A80 boards it powers the PM pingroup and should be 3.0V.
config AXP_FLDO1_VOLT
int "axp pmic fldo1 voltage"
depends on AXP818_POWER
default 0 if MACH_SUN8I_A83T
- ---help---
- Set the voltage (mV) to program the axp pmic fldo1 at, set to 0 to
- disable fldo1.
- On A83T / H8 boards fldo1 is VCC-HSIC and should be 1.2V if HSIC is
- used.
+ help
+ Set the voltage (mV) to program the axp pmic fldo1 at, set to 0 to
+ disable fldo1.
+ On A83T / H8 boards fldo1 is VCC-HSIC and should be 1.2V if HSIC is
+ used.
config AXP_FLDO2_VOLT
int "axp pmic fldo2 voltage"
depends on AXP818_POWER
default 900 if MACH_SUN8I_A83T
- ---help---
- Set the voltage (mV) to program the axp pmic fldo2 at, set to 0 to
- disable fldo2.
- On A83T / H8 boards fldo2 is VCC-CPUS and should be 0.9V.
+ help
+ Set the voltage (mV) to program the axp pmic fldo2 at, set to 0 to
+ disable fldo2.
+ On A83T / H8 boards fldo2 is VCC-CPUS and should be 0.9V.
config AXP_FLDO3_VOLT
int "axp pmic fldo3 voltage"
depends on AXP818_POWER
default 0
- ---help---
- Set the voltage (mV) to program the axp pmic fldo3 at, set to 0 to
- disable fldo3.
+ help
+ Set the voltage (mV) to program the axp pmic fldo3 at, set to 0 to
+ disable fldo3.
config AXP_SW_ON
bool "axp pmic sw on"
depends on AXP809_POWER || AXP818_POWER
- ---help---
- Enable to turn on axp pmic sw.
+ help
+ Enable to turn on axp pmic sw.
config SY8106A_VOUT1_VOLT
int "SY8106A pmic VOUT1 voltage"
depends on SY8106A_POWER
default 1200
- ---help---
- Set the voltage (mV) to program the SY8106A pmic VOUT1. This
- is typically used to power the VDD-CPU and should be 1200mV.
- Values can range from 680mV till 1950mV.
+ help
+ Set the voltage (mV) to program the SY8106A pmic VOUT1. This
+ is typically used to power the VDD-CPU and should be 1200mV.
+ Values can range from 680mV till 1950mV.
config TPS6586X_POWER
bool "Enable legacy driver for TI TPS6586x power management chip"
@@ -467,9 +467,9 @@ config TWL4030_POWER
depends on OMAP34XX
bool "Enable driver for TI TWL4030 power management chip"
imply CMD_POWEROFF
- ---help---
- The TWL4030 in a combination audio CODEC/power management with
- GPIO and it is commonly used with the OMAP3 family of processors
+ help
+ The TWL4030 in a combination audio CODEC/power management with
+ GPIO and it is commonly used with the OMAP3 family of processors
config POWER_MT6323
bool "Poweroff driver for mediatek mt6323"
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 2f63a8e54e5..bb9c52155d2 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -35,10 +35,10 @@ config BCM6328_POWER_DOMAIN
config IMX8_POWER_DOMAIN
bool "Enable i.MX8 power domain driver"
- depends on ARCH_IMX8
- help
- Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC
- requests to the SCU.
+ depends on ARCH_IMX8
+ help
+ Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC
+ requests to the SCU.
config IMX8M_POWER_DOMAIN
bool "Enable i.MX8M power domain driver"
@@ -90,6 +90,22 @@ config MESON_SECURE_POWER_DOMAIN
Enable support for manipulating Amlogic Meson Secure power domains.
Support for Amlogic A1 series.
+config QCOM_RPMH_POWER_DOMAIN
+ bool "Enable the QCOM RPMH Power domain driver"
+ depends on POWER_DOMAIN && ARCH_SNAPDRAGON
+ help
+ Generic RPMH power domain implementation for QCOM devices.
+ The RPMH power domain driver is responsible for managing power
+ domains on Qualcomm SoCs.
+
+config RENESAS_R8A78000_POWER_DOMAIN
+ bool "Enable the Renesas R-Car MDLC Power domain and reset driver"
+ depends on POWER_DOMAIN && ARCH_RENESAS
+ help
+ Enable support for Renesas R-Car R8A78000 X5H MDLC Power domain
+ and reset driver. The MDLC is responsible for managing both
+ power domains and resets on R-Car R8A78000 X5H SoC.
+
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index b2c0bd8a61a..110153d5cf8 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
obj-$(CONFIG_MESON_SECURE_POWER_DOMAIN) += meson-secure-pwrc.o
+obj-$(CONFIG_RENESAS_R8A78000_POWER_DOMAIN) += renesas-r8a78000-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_SCMI_POWER_DOMAIN) += scmi-power-domain.o
@@ -23,3 +24,4 @@ obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
obj-$(CONFIG_TI_OMAP_PRM_POWER_DOMAIN) += ti-omap-prm.o
obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
+obj-$(CONFIG_QCOM_RPMH_POWER_DOMAIN) += qcom-rpmhpd.o
diff --git a/drivers/power/domain/apple-pmgr.c b/drivers/power/domain/apple-pmgr.c
index bf9940621ee..9873d3cd8db 100644
--- a/drivers/power/domain/apple-pmgr.c
+++ b/drivers/power/domain/apple-pmgr.c
@@ -67,7 +67,7 @@ static int apple_reset_deassert(struct reset_ctl *reset_ctl)
return 0;
}
-struct reset_ops apple_reset_ops = {
+static const struct reset_ops apple_reset_ops = {
.of_xlate = apple_reset_of_xlate,
.rst_assert = apple_reset_assert,
.rst_deassert = apple_reset_deassert,
@@ -110,6 +110,7 @@ static int apple_pmgr_of_xlate(struct power_domain *power_domain,
}
static const struct udevice_id apple_pmgr_ids[] = {
+ { .compatible = "apple,t8103-pmgr-pwrstate" },
{ .compatible = "apple,pmgr-pwrstate" },
{ /* sentinel */ }
};
@@ -138,7 +139,7 @@ static int apple_pmgr_probe(struct udevice *dev)
return 0;
}
-struct power_domain_ops apple_pmgr_ops = {
+static const struct power_domain_ops apple_pmgr_ops = {
.on = apple_pmgr_on,
.of_xlate = apple_pmgr_of_xlate,
};
diff --git a/drivers/power/domain/bcm6328-power-domain.c b/drivers/power/domain/bcm6328-power-domain.c
index 36b5a933748..5b449f4c29d 100644
--- a/drivers/power/domain/bcm6328-power-domain.c
+++ b/drivers/power/domain/bcm6328-power-domain.c
@@ -57,7 +57,7 @@ static const struct udevice_id bcm6328_power_domain_ids[] = {
{ /* sentinel */ }
};
-struct power_domain_ops bcm6328_power_domain_ops = {
+static const struct power_domain_ops bcm6328_power_domain_ops = {
.off = bcm6328_power_domain_off,
.on = bcm6328_power_domain_on,
.request = bcm6328_power_domain_request,
diff --git a/drivers/power/domain/imx8-power-domain-legacy.c b/drivers/power/domain/imx8-power-domain-legacy.c
index 713a51d7807..a646f667039 100644
--- a/drivers/power/domain/imx8-power-domain-legacy.c
+++ b/drivers/power/domain/imx8-power-domain-legacy.c
@@ -347,7 +347,7 @@ static const struct udevice_id imx8_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8_power_domain_ops = {
+static const struct power_domain_ops imx8_power_domain_ops = {
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
.of_xlate = imx8_power_domain_of_xlate,
diff --git a/drivers/power/domain/imx8-power-domain.c b/drivers/power/domain/imx8-power-domain.c
index e8dcc057fee..5740cd686db 100644
--- a/drivers/power/domain/imx8-power-domain.c
+++ b/drivers/power/domain/imx8-power-domain.c
@@ -51,7 +51,7 @@ static const struct udevice_id imx8_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8_power_domain_ops_v2 = {
+static const struct power_domain_ops imx8_power_domain_ops_v2 = {
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
};
diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c
index 1c731b897cc..a3ef49aa67c 100644
--- a/drivers/power/domain/imx8m-power-domain.c
+++ b/drivers/power/domain/imx8m-power-domain.c
@@ -476,7 +476,7 @@ static int imx8m_power_domain_bind(struct udevice *dev)
const char *name;
int ret = 0;
- ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
+ dev_for_each_subnode(subnode, dev) {
/* Bind the subnode to this driver */
name = ofnode_get_name(subnode);
@@ -531,7 +531,7 @@ static int imx8m_power_domain_of_to_plat(struct udevice *dev)
struct imx_pgc_domain_data *domain_data =
(struct imx_pgc_domain_data *)dev_get_driver_data(dev);
- pdata->resource_id = ofnode_read_u32_default(dev_ofnode(dev), "reg", -1);
+ pdata->resource_id = dev_read_u32_default(dev, "reg", -1);
pdata->domain = &domain_data->domains[pdata->resource_id];
pdata->regs = domain_data->pgc_regs;
pdata->base = dev_read_addr_ptr(dev->parent);
@@ -558,7 +558,7 @@ static const struct udevice_id imx8m_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8m_power_domain_ops = {
+static const struct power_domain_ops imx8m_power_domain_ops = {
.on = imx8m_power_domain_on,
.off = imx8m_power_domain_off,
.of_xlate = imx8m_power_domain_of_xlate,
diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c
index 504c22f7d36..66ea5d8e60c 100644
--- a/drivers/power/domain/imx8mp-mediamix.c
+++ b/drivers/power/domain/imx8mp-mediamix.c
@@ -194,7 +194,7 @@ static const struct udevice_id imx8mp_mediamix_ids[] = {
{ }
};
-struct power_domain_ops imx8mp_mediamix_ops = {
+static const struct power_domain_ops imx8mp_mediamix_ops = {
.on = imx8mp_mediamix_on,
.off = imx8mp_mediamix_off,
.of_xlate = imx8mp_mediamix_of_xlate,
diff --git a/drivers/power/domain/meson-ee-pwrc.c b/drivers/power/domain/meson-ee-pwrc.c
index 4d9f3bba644..882238f2937 100644
--- a/drivers/power/domain/meson-ee-pwrc.c
+++ b/drivers/power/domain/meson-ee-pwrc.c
@@ -386,7 +386,7 @@ static int meson_ee_pwrc_of_xlate(struct power_domain *power_domain,
return 0;
}
-struct power_domain_ops meson_ee_pwrc_ops = {
+static const struct power_domain_ops meson_ee_pwrc_ops = {
.off = meson_ee_pwrc_off,
.on = meson_ee_pwrc_on,
.of_xlate = meson_ee_pwrc_of_xlate,
@@ -435,8 +435,7 @@ static const struct udevice_id meson_ee_pwrc_ids[] = {
static int meson_ee_pwrc_probe(struct udevice *dev)
{
struct meson_ee_pwrc_priv *priv = dev_get_priv(dev);
- u32 ao_phandle;
- ofnode ao_node;
+ struct ofnode_phandle_args args;
int ret;
priv->data = (void *)dev_get_driver_data(dev);
@@ -447,16 +446,12 @@ static int meson_ee_pwrc_probe(struct udevice *dev)
if (IS_ERR(priv->regmap_hhi))
return PTR_ERR(priv->regmap_hhi);
- ret = ofnode_read_u32(dev_ofnode(dev), "amlogic,ao-sysctrl",
- &ao_phandle);
+ ret = dev_read_phandle_with_args(dev, "amlogic,ao-sysctrl", NULL, 0, 0,
+ &args);
if (ret)
return ret;
- ao_node = ofnode_get_by_phandle(ao_phandle);
- if (!ofnode_valid(ao_node))
- return -EINVAL;
-
- priv->regmap_ao = syscon_node_to_regmap(ao_node);
+ priv->regmap_ao = syscon_node_to_regmap(args.node);
if (IS_ERR(priv->regmap_ao))
return PTR_ERR(priv->regmap_ao);
diff --git a/drivers/power/domain/meson-gx-pwrc-vpu.c b/drivers/power/domain/meson-gx-pwrc-vpu.c
index 1c56e8508c3..e08c0fac49a 100644
--- a/drivers/power/domain/meson-gx-pwrc-vpu.c
+++ b/drivers/power/domain/meson-gx-pwrc-vpu.c
@@ -262,7 +262,7 @@ static int meson_pwrc_vpu_of_xlate(struct power_domain *power_domain,
return 0;
}
-struct power_domain_ops meson_gx_pwrc_vpu_ops = {
+static const struct power_domain_ops meson_gx_pwrc_vpu_ops = {
.off = meson_pwrc_vpu_off,
.on = meson_pwrc_vpu_on,
.of_xlate = meson_pwrc_vpu_of_xlate,
@@ -283,24 +283,19 @@ static const struct udevice_id meson_gx_pwrc_vpu_ids[] = {
static int meson_gx_pwrc_vpu_probe(struct udevice *dev)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev);
- u32 hhi_phandle;
- ofnode hhi_node;
+ struct ofnode_phandle_args args;
int ret;
priv->regmap_ao = syscon_node_to_regmap(dev_ofnode(dev_get_parent(dev)));
if (IS_ERR(priv->regmap_ao))
return PTR_ERR(priv->regmap_ao);
- ret = ofnode_read_u32(dev_ofnode(dev), "amlogic,hhi-sysctrl",
- &hhi_phandle);
+ ret = dev_read_phandle_with_args(dev, "amlogic,hhi-sysctrl", NULL, 0, 0,
+ &args);
if (ret)
return ret;
- hhi_node = ofnode_get_by_phandle(hhi_phandle);
- if (!ofnode_valid(hhi_node))
- return -EINVAL;
-
- priv->regmap_hhi = syscon_node_to_regmap(hhi_node);
+ priv->regmap_hhi = syscon_node_to_regmap(args.node);
if (IS_ERR(priv->regmap_hhi))
return PTR_ERR(priv->regmap_hhi);
diff --git a/drivers/power/domain/meson-secure-pwrc.c b/drivers/power/domain/meson-secure-pwrc.c
index f70f8e02423..1b82b58f3e5 100644
--- a/drivers/power/domain/meson-secure-pwrc.c
+++ b/drivers/power/domain/meson-secure-pwrc.c
@@ -120,7 +120,7 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
SEC_PD(RSA),
};
-struct power_domain_ops meson_secure_pwrc_ops = {
+static const struct power_domain_ops meson_secure_pwrc_ops = {
.on = meson_secure_pwrc_on,
.off = meson_secure_pwrc_off,
.of_xlate = meson_secure_pwrc_of_xlate,
diff --git a/drivers/power/domain/mtk-power-domain.c b/drivers/power/domain/mtk-power-domain.c
index 2d1ba1855a5..24dd540897d 100644
--- a/drivers/power/domain/mtk-power-domain.c
+++ b/drivers/power/domain/mtk-power-domain.c
@@ -392,7 +392,7 @@ static const struct udevice_id mtk_power_domain_ids[] = {
{ /* sentinel */ }
};
-struct power_domain_ops mtk_power_domain_ops = {
+static const struct power_domain_ops mtk_power_domain_ops = {
.off = scpsys_power_off,
.on = scpsys_power_on,
.request = scpsys_power_request,
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index cea68945cbd..b4cda5f6c16 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -10,6 +10,7 @@
#include <malloc.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
+#include <dm/device_compat.h>
#include <dm/device-internal.h>
struct power_domain_priv {
@@ -187,6 +188,12 @@ static int dev_power_domain_ctrl(struct udevice *dev, bool on)
"#power-domain-cells", 0);
for (i = 0; i < count; i++) {
ret = power_domain_get_by_index(dev, &pd, i);
+
+ if (ret == -ENODEV) {
+ dev_warn(dev, "power-domain driver not found\n");
+ return 0;
+ }
+
if (ret)
return ret;
if (on)
diff --git a/drivers/power/domain/qcom-rpmhpd.c b/drivers/power/domain/qcom-rpmhpd.c
new file mode 100644
index 00000000000..f51bc9a4bbb
--- /dev/null
+++ b/drivers/power/domain/qcom-rpmhpd.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+// Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <power-domain.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+
+#include <power-domain-uclass.h>
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <dt-bindings/power/qcom-rpmpd.h>
+#include <dm/device_compat.h>
+
+#define RPMH_ARC_MAX_LEVELS 16
+
+/**
+ * struct rpmhpd - top level RPMh power domain resource data structure
+ * @dev: rpmh power domain controller device
+ * @pd: generic_pm_domain corresponding to the power domain
+ * @parent: generic_pm_domain corresponding to the parent's power domain
+ * @enable_corner: lowest non-zero corner
+ * @level: An array of level (vlvl) to corner (hlvl) mappings
+ * derived from cmd-db
+ * @level_count: Number of levels supported by the power domain. max
+ * being 16 (0 - 15)
+ * @enabled: true if the power domain is enabled
+ * @res_name: Resource name used for cmd-db lookup
+ * @addr: Resource address as looped up using resource name from
+ * @skip_retention_level: Indicate that retention level should not be used for the power domain
+ */
+struct rpmhpd {
+ struct udevice *dev;
+ struct power_domain pd;
+ struct power_domain *parent;
+ unsigned int enable_corner;
+ u32 level[RPMH_ARC_MAX_LEVELS];
+ size_t level_count;
+ bool enabled;
+ const char *res_name;
+ u32 addr;
+ bool skip_retention_level;
+};
+
+struct rpmhpd_desc {
+ struct rpmhpd **rpmhpds;
+ size_t num_pds;
+};
+
+/* RPMH powerdomains */
+static struct rpmhpd mmcx_ao;
+static struct rpmhpd mmcx = {
+ .res_name = "mmcx.lvl",
+};
+
+static struct rpmhpd mmcx_ao = {
+ .res_name = "mmcx.lvl",
+};
+
+/* SA8775P RPMH power domains */
+static struct rpmhpd *sa8775p_rpmhpds[] = {
+ [SA8775P_MMCX] = &mmcx,
+ [SA8775P_MMCX_AO] = &mmcx_ao,
+};
+
+static const struct rpmhpd_desc sa8775p_desc = {
+ .rpmhpds = sa8775p_rpmhpds,
+ .num_pds = ARRAY_SIZE(sa8775p_rpmhpds),
+};
+
+/* stub RPMH power domains mapped for unsupported platforms */
+static struct rpmhpd *stub_rpmhpds[] = {};
+
+static const struct rpmhpd_desc stub_desc = {
+ .rpmhpds = stub_rpmhpds,
+ .num_pds = ARRAY_SIZE(stub_rpmhpds),
+};
+
+static const struct udevice_id rpmhpd_match_table[] = {
+ { .compatible = "qcom,sa8775p-rpmhpd", .data = (ulong)&sa8775p_desc },
+ { .compatible = "qcom,qcs615-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qcs8300-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qdu1000-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8155p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8540p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sar2130p-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sc7180-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc7280-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8180x-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8280xp-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm670-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm845-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdx55-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx65-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx75-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm4450-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm6350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm7150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8250-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8450-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8550-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8650-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8750-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,x1e80100-rpmhpd", .data = (ulong)&stub_desc },
+ { }
+};
+
+static int rpmhpd_send_corner(struct rpmhpd *pd, int state,
+ unsigned int corner, bool sync)
+{
+ struct tcs_cmd cmd = {
+ .addr = pd->addr,
+ .data = corner,
+ };
+
+ return rpmh_write(pd->dev, state, &cmd, 1);
+}
+
+static int rpmhpd_power_on(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = curr_rpmhpd->enable_corner;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = true;
+
+ return ret;
+}
+
+static int rpmhpd_power_off(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = 0;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = false;
+
+ return ret;
+}
+
+static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
+{
+ int i;
+ const u16 *buf;
+
+ buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* 2 bytes used for each command DB aux data entry */
+ rpmhpd->level_count >>= 1;
+
+ if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS)
+ return -EINVAL;
+
+ for (i = 0; i < rpmhpd->level_count; i++) {
+ if (rpmhpd->skip_retention_level && buf[i] == RPMH_REGULATOR_LEVEL_RETENTION)
+ continue;
+
+ rpmhpd->level[i] = buf[i];
+
+ /* Remember the first corner with non-zero level */
+ if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i])
+ rpmhpd->enable_corner = i;
+
+ /*
+ * The AUX data may be zero padded. These 0 valued entries at
+ * the end of the map must be ignored.
+ */
+ if (i > 0 && rpmhpd->level[i] == 0) {
+ rpmhpd->level_count = i;
+ break;
+ }
+ debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i,
+ rpmhpd->level[i]);
+ }
+
+ return 0;
+}
+
+static int rpmhpd_probe(struct udevice *dev)
+{
+ int i, ret = 0;
+ struct rpmhpd **rpmhpds;
+ struct rpmhpd *priv;
+ const struct rpmhpd_desc *desc;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+
+ for (i = 0; i < desc->num_pds; i++) {
+ if (!rpmhpds[i])
+ continue;
+
+ priv = rpmhpds[i];
+ priv->dev = dev;
+ priv->addr = cmd_db_read_addr(priv->res_name);
+ if (!priv->addr) {
+ dev_err(dev, "Could not find RPMh address for resource %s\n",
+ priv->res_name);
+ return -ENODEV;
+ }
+
+ ret = cmd_db_read_slave_id(priv->res_name);
+ if (ret != CMD_DB_HW_ARC) {
+ dev_err(dev, "RPMh slave ID mismatch\n");
+ return -EINVAL;
+ }
+
+ ret = rpmhpd_update_level_mapping(priv);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct power_domain_ops qcom_rpmhpd_power_ops = {
+ .on = rpmhpd_power_on,
+ .off = rpmhpd_power_off,
+};
+
+U_BOOT_DRIVER(qcom_rpmhpd_drv) = {
+ .name = "qcom_rpmhpd_drv",
+ .id = UCLASS_POWER_DOMAIN,
+ .of_match = rpmhpd_match_table,
+ .probe = rpmhpd_probe,
+ .ops = &qcom_rpmhpd_power_ops,
+};
diff --git a/drivers/power/domain/renesas-r8a78000-power-domain.c b/drivers/power/domain/renesas-r8a78000-power-domain.c
new file mode 100644
index 00000000000..57b3b56b2d9
--- /dev/null
+++ b/drivers/power/domain/renesas-r8a78000-power-domain.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car Gen5 MDLC driver
+ *
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include <power-domain-uclass.h>
+#include <reset-uclass.h>
+
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dt-bindings/power/r8a78000-power-scmi.h>
+#include <dt-bindings/reset/r8a78000-reset-scmi.h>
+
+#define PKC_PROT_LOCK 0xa5a5a500
+#define PKC_PROT_UNLOCK 0xa5a5a501
+
+#define MDLC_MSRESS_STANDBY 0
+#define MDLC_MSRESS_RESET 1
+#define MDLC_MSRESS_STOP 2
+#define MDLC_MSRESS_RUN 3
+
+#define MDLC_MSRES00 0x900
+#define MDLC_MSRESS00 0x960
+#define MDLC_PKCPROT1 0xcf4
+
+struct gen5_mdlc_priv {
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+ struct udevice *pd;
+#endif
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+ struct udevice *rst;
+#endif
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) || IS_ENABLED(CONFIG_RESET_SCMI)
+ u32 basever;
+#endif
+#if !IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) && !IS_ENABLED(CONFIG_RESET_SCMI)
+ void __iomem *base;
+#endif
+};
+
+static int gen5_pd_of_xlate(struct power_domain *power_domain,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ power_domain->id = args->args[0];
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+static int gen5_pd_on(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->on(&scmi);
+}
+
+static int gen5_pd_off(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->off(&scmi);
+}
+
+static const struct power_domain_ops pd_gen5_ops = {
+ .on = gen5_pd_on,
+ .off = gen5_pd_off,
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+static int gen5_pd_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_POWER_DOMAIN,
+ DM_DRIVER_GET(scmi_power_domain),
+ &priv->pd);
+}
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .probe = gen5_pd_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#else
+static const struct power_domain_ops pd_gen5_ops = {
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#endif
+
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_assert(&scmi);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_deassert(&scmi);
+}
+
+struct rst_map_in {
+ u16 dt_id; /* DT binding clock ID */
+ u16 fw_id; /* SCMI firmware clock ID */
+};
+
+#define GEN5_SCMI_SDK_4_28 0x010a0000
+#define GEN5_SCMI_SDK_4_29 0x010b0000
+#define GEN5_SCMI_SDK_4_30 0x010c0000
+#define GEN5_SCMI_SDK_4_31 0x010d0000
+#define GEN5_SCMI_SDK_4_32 0x010e0000
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_28[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 202 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 203 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 319 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 320 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 321 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 322 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 323 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 344 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 345 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 346 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 347 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 348 },
+};
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_31[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 198 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 199 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 312 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 313 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 314 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 315 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 319 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 340 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 341 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 342 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 343 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 344 },
+};
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const struct rst_map_in *map;
+ unsigned int map_size;
+ int i;
+
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (priv->basever == GEN5_SCMI_SDK_4_28) {
+ map = gen5_rst_map_dt_sdk_4_28;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_28);
+ } else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
+ priv->basever == GEN5_SCMI_SDK_4_32) {
+ map = gen5_rst_map_dt_sdk_4_31;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_31);
+ } else {
+ printf("Unsupported SCMI base protocol version %x\n", priv->basever);
+ return -EINVAL;
+ }
+
+ reset_ctl->id = -1;
+ for (i = 0; i < map_size; i++) {
+ if (map[i].dt_id != args->args[0])
+ continue;
+ reset_ctl->id = map[i].fw_id;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret = 0;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_RESET,
+ DM_DRIVER_GET(scmi_reset_domain),
+ &priv->rst);
+}
+#else
+static int mdlc_wait_for_reset(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 val;
+ int ret;
+
+ /* Wait 100ms for reset controller to synchronize. */
+ ret = readl_poll_timeout(res, val, val == readl(stat), 100000);
+ if (ret < 0)
+ dev_err(reset_ctl->dev, "Reset controller out of sync!\n");
+
+ return ret;
+}
+
+static void mdlc_rmw_msres(struct reset_ctl *reset_ctl, const int val)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *prot = priv->base + MDLC_PKCPROT1;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ u32 reg;
+
+ reg = readl(res);
+ reg &= ~mask;
+ reg |= field_prep(mask, val);
+
+ writel(PKC_PROT_UNLOCK, prot);
+ writel(reg, res);
+ writel(PKC_PROT_LOCK, prot);
+}
+
+static int gen5_reset_toggle(struct reset_ctl *reset_ctl, const u8 step1,
+ const u8 step2, const u8 step3, const u8 step4)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 status;
+ int ret;
+
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+
+ status = field_get(mask, readl(stat));
+ if (status == step1) {
+ mdlc_rmw_msres(reset_ctl, step2);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ status = field_get(mask, readl(stat));
+ }
+
+ if (status == step2 || status == step3) {
+ mdlc_rmw_msres(reset_ctl, step4);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_STANDBY,
+ MDLC_MSRESS_RUN, MDLC_MSRESS_RESET);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STANDBY, MDLC_MSRESS_RESET,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_RUN);
+}
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ reset_ctl->id = args->args[0];
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ return 0;
+}
+#endif
+
+U_BOOT_DRIVER(rst_gen5) = {
+ .name = "rst_gen5",
+ .id = UCLASS_RESET,
+ .ops = &rst_gen5_ops,
+ .probe = gen5_rst_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+
+int gen5_mdlc_bind(struct udevice *parent)
+{
+ struct udevice *pdev, *rdev;
+ struct driver *pdrv, *rdrv;
+ int ret;
+
+ pdrv = lists_driver_lookup_name("pd_gen5");
+ if (!pdrv)
+ return -ENOENT;
+
+ rdrv = lists_driver_lookup_name("rst_gen5");
+ if (!rdrv)
+ return -ENOENT;
+
+ ret = device_bind_with_driver_data(parent, pdrv, "pd_gen5", 0,
+ dev_ofnode(parent), &pdev);
+ if (ret)
+ return ret;
+
+ ret = device_bind_with_driver_data(parent, rdrv, "rst_gen5", (ulong)pdev,
+ dev_ofnode(parent), &rdev);
+ if (ret)
+ device_unbind(pdev);
+
+ return ret;
+}
+
+static const struct udevice_id r8a78000_mdlc_ids[] = {
+ { .compatible = "renesas,r8a78000-mdlc", },
+ { }
+};
+
+U_BOOT_DRIVER(mdlc_gen5) = {
+ .name = "mdlc_gen5",
+ .id = UCLASS_NOP,
+ .of_match = r8a78000_mdlc_ids,
+ .bind = gen5_mdlc_bind,
+ .priv_auto = sizeof(struct gen5_mdlc_priv),
+};
diff --git a/drivers/power/domain/sandbox-power-domain.c b/drivers/power/domain/sandbox-power-domain.c
index a8031657638..a30826a3b4d 100644
--- a/drivers/power/domain/sandbox-power-domain.c
+++ b/drivers/power/domain/sandbox-power-domain.c
@@ -78,7 +78,7 @@ static const struct udevice_id sandbox_power_domain_ids[] = {
{ }
};
-struct power_domain_ops sandbox_power_domain_ops = {
+static const struct power_domain_ops sandbox_power_domain_ops = {
.request = sandbox_power_domain_request,
.rfree = sandbox_power_domain_free,
.on = sandbox_power_domain_on,
diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c
index e8c0ba8878e..a369fe52f2f 100644
--- a/drivers/power/domain/scmi-power-domain.c
+++ b/drivers/power/domain/scmi-power-domain.c
@@ -165,21 +165,15 @@ static int scmi_power_domain_probe(struct udevice *dev)
for (i = 0; i < priv->num_pwdoms; i++) {
ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes,
&priv->prop[i].name);
- if (ret) {
+ if (ret)
dev_err(dev, "failed to get attributes pwd:%d (%d)\n",
i, ret);
- for (i--; i >= 0; i--)
- free(priv->prop[i].name);
- free(priv->prop);
-
- return ret;
- }
}
return 0;
}
-struct power_domain_ops scmi_power_domain_ops = {
+static const struct power_domain_ops scmi_power_domain_ops = {
.on = scmi_power_domain_on,
.off = scmi_power_domain_off,
};
diff --git a/drivers/power/domain/tegra186-power-domain.c b/drivers/power/domain/tegra186-power-domain.c
index 334c460c805..3865cd4cf47 100644
--- a/drivers/power/domain/tegra186-power-domain.c
+++ b/drivers/power/domain/tegra186-power-domain.c
@@ -55,7 +55,7 @@ static int tegra186_power_domain_off(struct power_domain *power_domain)
return tegra186_power_domain_common(power_domain, false);
}
-struct power_domain_ops tegra186_power_domain_ops = {
+static const struct power_domain_ops tegra186_power_domain_ops = {
.on = tegra186_power_domain_on,
.off = tegra186_power_domain_off,
};
diff --git a/drivers/power/domain/zynqmp-power-domain.c b/drivers/power/domain/zynqmp-power-domain.c
index a54de5c1439..0acfc54e787 100644
--- a/drivers/power/domain/zynqmp-power-domain.c
+++ b/drivers/power/domain/zynqmp-power-domain.c
@@ -57,7 +57,7 @@ static int zynqmp_power_domain_off(struct power_domain *power_domain)
return 0;
}
-struct power_domain_ops zynqmp_power_domain_ops = {
+static const struct power_domain_ops zynqmp_power_domain_ops = {
.request = zynqmp_power_domain_request,
.rfree = zynqmp_power_domain_free,
.on = zynqmp_power_domain_on,
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index 5bc14842e66..d45869cb30f 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -1,14 +1,14 @@
config DM_PMIC
bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC)"
depends on DM
- ---help---
- This config enables the driver-model PMIC support.
- UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
- For the multi-function PMIC devices, this can be used as parent I/O
- device for each IC's interface. Then, each children uses its parent
- for read/write. For detailed description, please refer to the files:
- - 'drivers/power/pmic/pmic-uclass.c'
- - 'include/power/pmic.h'
+ help
+ This config enables the driver-model PMIC support.
+ UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
+ For the multi-function PMIC devices, this can be used as parent I/O
+ device for each IC's interface. Then, each children uses its parent
+ for read/write. For detailed description, please refer to the files:
+ - 'drivers/power/pmic/pmic-uclass.c'
+ - 'include/power/pmic.h'
if DM_PMIC
@@ -16,34 +16,34 @@ config SPL_DM_PMIC
bool "Enable Driver Model for PMIC drivers (UCLASS_PMIC) in SPL"
depends on SPL_DM
default y
- ---help---
- This config enables the driver-model PMIC support in SPL.
- UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
- For the multi-function PMIC devices, this can be used as parent I/O
- device for each IC's interface. Then, each children uses its parent
- for read/write. For detailed description, please refer to the files:
- - 'drivers/power/pmic/pmic-uclass.c'
- - 'include/power/pmic.h'
+ help
+ This config enables the driver-model PMIC support in SPL.
+ UCLASS_PMIC - designed to provide an I/O interface for PMIC devices.
+ For the multi-function PMIC devices, this can be used as parent I/O
+ device for each IC's interface. Then, each children uses its parent
+ for read/write. For detailed description, please refer to the files:
+ - 'drivers/power/pmic/pmic-uclass.c'
+ - 'include/power/pmic.h'
config PMIC_CHILDREN
bool "Allow child devices for PMICs"
default y
- ---help---
- This allows PMICs to support child devices (such as regulators) in
- SPL. This adds quite a bit of code so if you are not using this
- feature you can turn it off. Most likely you should turn it on for
- U-Boot proper.
+ help
+ This allows PMICs to support child devices (such as regulators) in
+ SPL. This adds quite a bit of code so if you are not using this
+ feature you can turn it off. Most likely you should turn it on for
+ U-Boot proper.
config SPL_PMIC_CHILDREN
bool "Allow child devices for PMICs in SPL"
depends on SPL_DM_PMIC
default y
- ---help---
- This allows PMICs to support child devices (such as regulators) in
- SPL. This adds quite a bit of code so if you are not using this
- feature you can turn it off. In this case you may need a 'back door'
- to call your regulator code (e.g. see rk8xx.c for direct functions
- for use in SPL).
+ help
+ This allows PMICs to support child devices (such as regulators) in
+ SPL. This adds quite a bit of code so if you are not using this
+ feature you can turn it off. In this case you may need a 'back door'
+ to call your regulator code (e.g. see rk8xx.c for direct functions
+ for use in SPL).
config PMIC_AB8500
bool "Enable driver for ST-Ericsson AB8500 PMIC via PRCMU"
@@ -57,11 +57,11 @@ config PMIC_AB8500
config PMIC_ACT8846
bool "Enable support for the active-semi 8846 PMIC"
depends on DM_I2C
- ---help---
- This PMIC includes 4 DC/DC step-down buck regulators and 8 low-dropout
- regulators (LDOs). It also provides some GPIO, reset and battery
- functions. It uses an I2C interface and is designed for use with
- tablets and smartphones.
+ help
+ This PMIC includes 4 DC/DC step-down buck regulators and 8 low-dropout
+ regulators (LDOs). It also provides some GPIO, reset and battery
+ functions. It uses an I2C interface and is designed for use with
+ tablets and smartphones.
config PMIC_AXP
bool "Enable Driver Model for X-Powers AXP PMICs"
@@ -101,8 +101,8 @@ config PMIC_AS3722
required for a tablets or laptop.
config DM_PMIC_BD71837
- bool "Enable Driver Model for PMIC BD71837"
- help
+ bool "Enable Driver Model for PMIC BD71837"
+ help
This config enables implementation of driver-model pmic uclass features
for PMIC BD71837. The driver implements read/write operations.
@@ -173,257 +173,257 @@ config SPL_DM_PMIC_PCA9450
config DM_PMIC_PFUZE100
bool "Enable Driver Model for PMIC PFUZE100"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC PFUZE100. The driver implements read/write operations.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC PFUZE100. The driver implements read/write operations.
config SPL_DM_PMIC_PFUZE100
bool "Enable Driver Model for PMIC PFUZE100 in SPL"
depends on SPL_DM_PMIC
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC PFUZE100 in SPL. The driver implements read/write operations.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC PFUZE100 in SPL. The driver implements read/write operations.
config DM_PMIC_MAX8907
bool "Enable Driver Model for PMIC MAX8907"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC MAX8907. The driver implements read/write operations.
- This is a Power Management IC with a decent set of peripherals from which
- 3 DC-to-DC Step-Down (SD) Regulators, 20 Low-Dropout Linear (LDO) Regulators,
- Real-Time Clock (RTC) and more with I2C Compatible Interface.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX8907. The driver implements read/write operations.
+ This is a Power Management IC with a decent set of peripherals from which
+ 3 DC-to-DC Step-Down (SD) Regulators, 20 Low-Dropout Linear (LDO) Regulators,
+ Real-Time Clock (RTC) and more with I2C Compatible Interface.
config DM_PMIC_MAX77663
bool "Enable Driver Model for PMIC MAX77663"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC MAX77663. The driver implements read/write operations.
- This is a Power Management IC with a decent set of peripherals from which
- 4 DC-to-DC Step-Down (SD) Regulators, 9 Low-Dropout Linear (LDO) Regulators,
- 8 GPIOs, Real-Time Clock (RTC) and more with I2C Compatible Interface.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX77663. The driver implements read/write operations.
+ This is a Power Management IC with a decent set of peripherals from which
+ 4 DC-to-DC Step-Down (SD) Regulators, 9 Low-Dropout Linear (LDO) Regulators,
+ 8 GPIOs, Real-Time Clock (RTC) and more with I2C Compatible Interface.
config DM_PMIC_MAX77686
bool "Enable Driver Model for PMIC MAX77686"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC MAX77686. The driver implements read/write operations.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX77686. The driver implements read/write operations.
config DM_PMIC_MAX8998
bool "Enable Driver Model for PMIC MAX8998"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC MAX8998. The driver implements read/write operations.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX8998. The driver implements read/write operations.
config DM_PMIC_MC34708
bool "Enable Driver Model for PMIC MC34708"
help
- This config enables implementation of driver-model pmic uclass features
- for PMIC MC34708. The driver implements read/write operations.
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MC34708. The driver implements read/write operations.
config PMIC_MAX8997
bool "Enable Driver Model for PMIC MAX8997"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for PMIC MAX8997. The driver implements read/write operations.
- This is a Power Management IC with RTC, Fuel Gauge, MUIC control on Chip.
- - 21x LDOs
- - 12x GPIOs
- - Haptic motor driver
- - RTC with two alarms
- - Fuel Gauge and one backup battery charger
- - MUIC
- - Others
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for PMIC MAX8997. The driver implements read/write operations.
+ This is a Power Management IC with RTC, Fuel Gauge, MUIC control on Chip.
+ - 21x LDOs
+ - 12x GPIOs
+ - Haptic motor driver
+ - RTC with two alarms
+ - Fuel Gauge and one backup battery charger
+ - MUIC
+ - Others
config PMIC_QCOM
bool "Enable Driver Model for Qualcomm generic PMIC"
- ---help---
- The Qcom PMIC is connected to one (or several) processors
- with SPMI bus. It has 2 slaves with several peripherals:
- - 18x LDO
- - 4x GPIO
- - Power and Reset buttons
- - Watchdog
- - RTC
- - Vibrator drivers
- - Others
+ help
+ The Qcom PMIC is connected to one (or several) processors
+ with SPMI bus. It has 2 slaves with several peripherals:
+ - 18x LDO
+ - 4x GPIO
+ - Power and Reset buttons
+ - Watchdog
+ - RTC
+ - Vibrator drivers
+ - Others
- Driver binding info: doc/device-tree-bindings/pmic/qcom,spmi-pmic.txt
+ Driver binding info: doc/device-tree-bindings/pmic/qcom,spmi-pmic.txt
config PMIC_RK8XX
bool "Enable support for Rockchip PMIC RK8XX"
select SYSRESET_CMD_POWEROFF if SYSRESET && CMD_POWEROFF
- ---help---
- The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs,
- an RTC and two low Rds (resistance (drain to source)) switches. It is
- accessed via an I2C interface. The device is used with Rockchip SoCs.
- This driver implements register read/write operations.
+ help
+ The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs,
+ an RTC and two low Rds (resistance (drain to source)) switches. It is
+ accessed via an I2C interface. The device is used with Rockchip SoCs.
+ This driver implements register read/write operations.
config SPL_PMIC_RK8XX
bool "Enable support for Rockchip PMIC RK8XX in SPL"
depends on SPL_DM_PMIC
- ---help---
- The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs,
- an RTC and two low Rds (resistance (drain to source)) switches. It is
- accessed via an I2C interface. The device is used with Rockchip SoCs.
- This driver implements register read/write operations.
+ help
+ The Rockchip RK808 PMIC provides four buck DC-DC convertors, 8 LDOs,
+ an RTC and two low Rds (resistance (drain to source)) switches. It is
+ accessed via an I2C interface. The device is used with Rockchip SoCs.
+ This driver implements register read/write operations.
config PMIC_S2MPS11
bool "Enable Driver Model for PMIC Samsung S2MPS11"
- ---help---
- The Samsung S2MPS11 PMIC provides:
- - 38 adjustable LDO regulators
- - 9 High-Efficiency Buck Converters
- - 1 BuckBoost Converter
- - RTC with two alarms
- - Backup battery charger
- - I2C Configuration Interface
- This driver provides access to I/O interface only.
- Binding info: doc/device-tree-bindings/pmic/s2mps11.txt
+ help
+ The Samsung S2MPS11 PMIC provides:
+ - 38 adjustable LDO regulators
+ - 9 High-Efficiency Buck Converters
+ - 1 BuckBoost Converter
+ - RTC with two alarms
+ - Backup battery charger
+ - I2C Configuration Interface
+ This driver provides access to I/O interface only.
+ Binding info: doc/device-tree-bindings/pmic/s2mps11.txt
config DM_PMIC_SANDBOX
bool "Enable Driver Model for emulated Sandbox PMIC"
- ---help---
- Enable the driver for Sandbox PMIC emulation. The emulated PMIC device
- depends on two drivers:
- - sandbox PMIC I/O driver - implements dm pmic operations
- - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
+ help
+ Enable the driver for Sandbox PMIC emulation. The emulated PMIC device
+ depends on two drivers:
+ - sandbox PMIC I/O driver - implements dm pmic operations
+ - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
- A detailed information can be found in header: '<power/sandbox_pmic.h>'
+ A detailed information can be found in header: '<power/sandbox_pmic.h>'
- The Sandbox PMIC info:
- * I/O interface:
- - I2C chip address: 0x40
- - first register address: 0x0
- - register count: 0x10
- * Adjustable outputs:
- - 2x LDO
- - 2x BUCK
- - Each, with a different operating conditions (header).
- * Reset values:
- - set by i2c emul driver's probe() (defaults in header)
+ The Sandbox PMIC info:
+ * I/O interface:
+ - I2C chip address: 0x40
+ - first register address: 0x0
+ - register count: 0x10
+ * Adjustable outputs:
+ - 2x LDO
+ - 2x BUCK
+ - Each, with a different operating conditions (header).
+ * Reset values:
+ - set by i2c emul driver's probe() (defaults in header)
- Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt
+ Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt
config DM_PMIC_CPCAP
bool "Enable Driver Model for Motorola CPCAP"
help
- The CPCAP is a Motorola/ST-Ericsson creation, a multifunctional IC
- whose main purpose is power control. It was used in a wide variety of
- Motorola products, both Tegra and OMAP based. The most notable devices
- using this PMIC are the Motorola Droid 4, Atrix 4G, and Droid X2.
- Unlike most PMICs, this one is not I2C based; it uses the SPI bus. The
- core driver provides both read and write access to the device registers.
+ The CPCAP is a Motorola/ST-Ericsson creation, a multifunctional IC
+ whose main purpose is power control. It was used in a wide variety of
+ Motorola products, both Tegra and OMAP based. The most notable devices
+ using this PMIC are the Motorola Droid 4, Atrix 4G, and Droid X2.
+ Unlike most PMICs, this one is not I2C based; it uses the SPI bus. The
+ core driver provides both read and write access to the device registers.
config PMIC_S5M8767
bool "Enable Driver Model for the Samsung S5M8767 PMIC"
- ---help---
- The S5M8767 PMIC provides a large array of LDOs and BUCKs for use
- as a SoC power controller. It also provides 32KHz clock outputs. This
- driver provides basic register access and sets up the attached
- regulators if regulator support is enabled.
+ help
+ The S5M8767 PMIC provides a large array of LDOs and BUCKs for use
+ as a SoC power controller. It also provides 32KHz clock outputs. This
+ driver provides basic register access and sets up the attached
+ regulators if regulator support is enabled.
config PMIC_RN5T567
bool "Enable driver for Ricoh RN5T567 PMIC"
- ---help---
- The RN5T567 is a PMIC with 4 step-down DC/DC converters, 5 LDO
- regulators Real-Time Clock and 4 GPIOs. This driver provides
- register access only.
+ help
+ The RN5T567 is a PMIC with 4 step-down DC/DC converters, 5 LDO
+ regulators Real-Time Clock and 4 GPIOs. This driver provides
+ register access only.
config SPL_PMIC_RN5T567
bool "Enable driver for Ricoh RN5T567 PMIC in SPL"
depends on SPL_DM_PMIC
- ---help---
- The RN5T567 is a PMIC with 4 step-down DC/DC converters, 5 LDO
- regulators Real-Time Clock and 4 GPIOs. This driver provides
- register access only.
+ help
+ The RN5T567 is a PMIC with 4 step-down DC/DC converters, 5 LDO
+ regulators Real-Time Clock and 4 GPIOs. This driver provides
+ register access only.
config PMIC_TPS65090
bool "Enable driver for Texas Instruments TPS65090 PMIC"
- ---help---
- The TPS65090 is a PMIC containing several LDOs, DC to DC convertors,
- FETs and a battery charger. This driver provides register access
- only, and you can enable the regulator/charger drivers separately if
- required.
+ help
+ The TPS65090 is a PMIC containing several LDOs, DC to DC convertors,
+ FETs and a battery charger. This driver provides register access
+ only, and you can enable the regulator/charger drivers separately if
+ required.
config PMIC_PALMAS
bool "Enable driver for Texas Instruments PALMAS PMIC"
- ---help---
- The PALMAS is a PMIC containing several LDOs, SMPS.
- This driver binds the pmic children.
+ help
+ The PALMAS is a PMIC containing several LDOs, SMPS.
+ This driver binds the pmic children.
config PMIC_LP873X
bool "Enable driver for Texas Instruments LP873X PMIC"
- ---help---
- The LP873X is a PMIC containing couple of LDOs and couple of SMPS.
- This driver binds the pmic children.
+ help
+ The LP873X is a PMIC containing couple of LDOs and couple of SMPS.
+ This driver binds the pmic children.
config PMIC_LP87565
bool "Enable driver for Texas Instruments LP87565 PMIC"
- ---help---
- The LP87565 is a PMIC containing a bunch of SMPS.
- This driver binds the pmic children.
+ help
+ The LP87565 is a PMIC containing a bunch of SMPS.
+ This driver binds the pmic children.
config DM_PMIC_TPS65910
bool "Enable driver for Texas Instruments TPS65910 PMIC"
- ---help---
- The TPS65910 is a PMIC containing 3 buck DC-DC converters, one boost
- DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO
- pmic children.
+ help
+ The TPS65910 is a PMIC containing 3 buck DC-DC converters, one boost
+ DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO
+ pmic children.
config DM_PMIC_TPS80031
bool "Enable driver for Texas Instruments TPS80031/TPS80032 PMIC"
- ---help---
- This config enables implementation of driver-model pmic uclass features
- for TPS80031/TPS80032 PMICs. The driver implements read/write operations.
- This is a Power Management IC with a decent set of peripherals from which
- 5 Buck Converters refered as Switched-mode power supply (SMPS), 11 General-
- Purpose Low-Dropout Voltage Regulators (LDO), USB OTG Module, Real-Time
- Clock (RTC) with Timer and Alarm Wake-Up, Two Digital PWM Outputs and more
- with I2C Compatible Interface. PMIC occupies 4 I2C addresses.
+ help
+ This config enables implementation of driver-model pmic uclass features
+ for TPS80031/TPS80032 PMICs. The driver implements read/write operations.
+ This is a Power Management IC with a decent set of peripherals from which
+ 5 Buck Converters referred as Switched-mode power supply (SMPS), 11 General-
+ Purpose Low-Dropout Voltage Regulators (LDO), USB OTG Module, Real-Time
+ Clock (RTC) with Timer and Alarm Wake-Up, Two Digital PWM Outputs and more
+ with I2C Compatible Interface. PMIC occupies 4 I2C addresses.
config PMIC_STPMIC1
bool "Enable support for STMicroelectronics STPMIC1 PMIC"
depends on DM_I2C
select SYSRESET_CMD_POWEROFF if SYSRESET && CMD_POWEROFF && !ARM_PSCI_FW
- ---help---
- The STPMIC1 PMIC provides 4 BUCKs, 6 LDOs, 1 VREF and 2 power switches.
- It is accessed via an I2C interface. The device is used with STM32MP1
- SoCs. This driver implements register read/write operations.
+ help
+ The STPMIC1 PMIC provides 4 BUCKs, 6 LDOs, 1 VREF and 2 power switches.
+ It is accessed via an I2C interface. The device is used with STM32MP1
+ SoCs. This driver implements register read/write operations.
config SPL_PMIC_PALMAS
bool "Enable driver for Texas Instruments PALMAS PMIC"
depends on SPL_DM_PMIC
help
- The PALMAS is a PMIC containing several LDOs, SMPS.
- This driver binds the pmic children in SPL.
+ The PALMAS is a PMIC containing several LDOs, SMPS.
+ This driver binds the pmic children in SPL.
config SPL_PMIC_LP873X
bool "Enable driver for Texas Instruments LP873X PMIC"
depends on SPL_DM_PMIC
help
- The LP873X is a PMIC containing couple of LDOs and couple of SMPS.
- This driver binds the pmic children in SPL.
+ The LP873X is a PMIC containing couple of LDOs and couple of SMPS.
+ This driver binds the pmic children in SPL.
config SPL_PMIC_LP87565
bool "Enable driver for Texas Instruments LP87565 PMIC"
depends on SPL_DM_PMIC
help
- The LP87565 is a PMIC containing a bunch of SMPS.
- This driver binds the pmic children in SPL.
+ The LP87565 is a PMIC containing a bunch of SMPS.
+ This driver binds the pmic children in SPL.
config PMIC_TPS65941
bool "Enable driver for Texas Instruments TPS65941 PMIC"
depends on DM_PMIC
help
- The TPS65941 is a PMIC containing a bunch of SMPS & LDOs.
- This driver binds the pmic children.
+ The TPS65941 is a PMIC containing a bunch of SMPS & LDOs.
+ This driver binds the pmic children.
config PMIC_TPS65219
bool "Enable driver for Texas Instruments TPS65219 PMIC"
depends on DM_PMIC
help
- The TPS65219 is a PMIC containing a bunch of SMPS & LDOs.
- This driver binds the pmic children.
+ The TPS65219 is a PMIC containing a bunch of SMPS & LDOs.
+ This driver binds the pmic children.
config PMIC_RAA215300
bool "Renesas RAA215300 PMIC driver"
@@ -434,6 +434,14 @@ config PMIC_RAA215300
allows register access and will bind the sysreset driver
(CONFIG_SYSRESET_RAA215300) if it is enabled.
+config PMIC_SPACEMIT_P1
+ bool "Enable driver for Spacemit P1 power management chip"
+ depends on DM_PMIC
+ help
+ The P1 PMIC integrates multiple functions including
+ voltage regulators, a watchdog timer, GPIO interfaces, and a
+ real-time clock.
+
config DM_PMIC_MTK_PWRAP
bool "Enable driver for MediaTek PMIC Wrapper Support"
help
@@ -445,11 +453,11 @@ endif
config PMIC_TPS65217
bool "Enable driver for Texas Instruments TPS65217 PMIC"
- ---help---
- The TPS65217 is a PMIC containing several LDOs, DC to DC convertors,
- FETs and a battery charger. This driver provides register access
- only, and you can enable the regulator/charger drivers separately if
- required.
+ help
+ The TPS65217 is a PMIC containing several LDOs, DC to DC convertors,
+ FETs and a battery charger. This driver provides register access
+ only, and you can enable the regulator/charger drivers separately if
+ required.
config POWER_TPS65218
bool "Enable legacy driver for TPS65218 PMIC"
@@ -485,9 +493,9 @@ config POWER_PFUZE3000
config POWER_MC34VR500
bool "Enable driver for Freescale MC34VR500 PMIC"
- ---help---
- The MC34VR500 is used in conjunction with the FSL T1 and LS1 series
- SoC. It provides 4 buck DC-DC convertors and 5 LDOs, and it is accessed
- via an I2C interface.
+ help
+ The MC34VR500 is used in conjunction with the FSL T1 and LS1 series
+ SoC. It provides 4 buck DC-DC convertors and 5 LDOs, and it is accessed
+ via an I2C interface.
endif
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index 2cda5a892fd..0ac92084221 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o
obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o
obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o
obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o
+obj-$(CONFIG_PMIC_SPACEMIT_P1) += pmic_spacemit_p1.o
obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o
obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o
obj-$(CONFIG_PMIC_TPS65219) += tps65219.o
diff --git a/drivers/power/pmic/i2c_pmic_emul.c b/drivers/power/pmic/i2c_pmic_emul.c
index 6e81b9c3427..b5c8efd427a 100644
--- a/drivers/power/pmic/i2c_pmic_emul.c
+++ b/drivers/power/pmic/i2c_pmic_emul.c
@@ -146,7 +146,7 @@ static int sandbox_i2c_pmic_probe(struct udevice *emul)
return 0;
}
-struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = {
+static const struct dm_i2c_ops sandbox_i2c_pmic_emul_ops = {
.xfer = sandbox_i2c_pmic_xfer,
};
diff --git a/drivers/power/pmic/mtk-pwrap.c b/drivers/power/pmic/mtk-pwrap.c
index 3e3a691d9e8..47347785519 100644
--- a/drivers/power/pmic/mtk-pwrap.c
+++ b/drivers/power/pmic/mtk-pwrap.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * MT6357 regulator driver
+ * MediaTek PMIC Wrapper driver
*
* Copyright (c) 2026 BayLibre, SAS.
* Author: Julien Masson <[email protected]>
@@ -59,9 +59,11 @@ static const struct pmic_child_info mt6359_pmic_children_info[] = {
#define HAS_CAP(_c, _x_val) (((_c) & (_x_val)) == (_x_val))
/* Group of bits used for shown pwrap capability */
-#define PWRAP_CAP_INT1_EN BIT(3)
-#define PWRAP_CAP_WDT_SRC1 BIT(4)
-#define PWRAP_CAP_ARB BIT(5)
+#define PWRAP_CAP_WDT_SRC BIT(4)
+#define PWRAP_CAP_WDT_SRC1 BIT(5)
+#define PWRAP_CAP_ARB BIT(6)
+/* To implement this capability, the registers used in pwrap_init() must be defined. */
+#define PWRAP_CAP_INIT BIT(7)
/* defines for slave device wrapper registers */
enum dew_regs {
@@ -249,20 +251,20 @@ enum pwrap_regs {
};
static int mt8188_regs[] = {
- [PWRAP_INIT_DONE2] = 0x0,
- [PWRAP_STAUPD_CTRL] = 0x4C,
- [PWRAP_TIMER_EN] = 0x3E4,
- [PWRAP_INT_EN] = 0x420,
- [PWRAP_INT_FLG] = 0x428,
- [PWRAP_INT_CLR] = 0x42C,
- [PWRAP_INT1_EN] = 0x450,
- [PWRAP_INT1_FLG] = 0x458,
- [PWRAP_INT1_CLR] = 0x45C,
- [PWRAP_WACS2_CMD] = 0x880,
- [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
- [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
- [PWRAP_WACS2_VLDCLR] = 0x8A4,
- [PWRAP_WACS2_RDATA] = 0x8A8,
+ [PWRAP_INIT_DONE2] = 0x0,
+ [PWRAP_STAUPD_CTRL] = 0x4C,
+ [PWRAP_TIMER_EN] = 0x3E4,
+ [PWRAP_INT_EN] = 0x420,
+ [PWRAP_INT_FLG] = 0x428,
+ [PWRAP_INT_CLR] = 0x42C,
+ [PWRAP_INT1_EN] = 0x450,
+ [PWRAP_INT1_FLG] = 0x458,
+ [PWRAP_INT1_CLR] = 0x45C,
+ [PWRAP_WACS2_CMD] = 0x880,
+ [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
+ [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
+ [PWRAP_WACS2_VLDCLR] = 0x8A4,
+ [PWRAP_WACS2_RDATA] = 0x8A8,
};
static int mt8189_regs[] = {
@@ -276,6 +278,23 @@ static int mt8189_regs[] = {
[PWRAP_WACS2_RDATA] = 0x8A8,
};
+static int mt8195_regs[] = {
+ [PWRAP_INIT_DONE2] = 0x0,
+ [PWRAP_STAUPD_CTRL] = 0x4C,
+ [PWRAP_TIMER_EN] = 0x3E4,
+ [PWRAP_INT_EN] = 0x420,
+ [PWRAP_INT_FLG] = 0x428,
+ [PWRAP_INT_CLR] = 0x42C,
+ [PWRAP_INT1_EN] = 0x450,
+ [PWRAP_INT1_FLG] = 0x458,
+ [PWRAP_INT1_CLR] = 0x45C,
+ [PWRAP_WACS2_CMD] = 0x880,
+ [PWRAP_SWINF_2_WDATA_31_0] = 0x884,
+ [PWRAP_SWINF_2_RDATA_31_0] = 0x894,
+ [PWRAP_WACS2_VLDCLR] = 0x8A4,
+ [PWRAP_WACS2_RDATA] = 0x8A8,
+};
+
static int mt8365_regs[] = {
[PWRAP_MUX_SEL] = 0x0,
[PWRAP_WRAP_EN] = 0x4,
@@ -338,12 +357,6 @@ static int mt8365_regs[] = {
[PWRAP_WDT_SRC_EN_1] = 0xf8,
};
-enum pwrap_type {
- PWRAP_MT8188,
- PWRAP_MT8189,
- PWRAP_MT8365,
-};
-
struct pwrap_slv_type {
const u32 *dew_regs;
u32 caps;
@@ -362,10 +375,7 @@ struct pmic_wrapper {
struct pmic_wrapper_type {
int *regs;
- enum pwrap_type type;
u32 arb_en_all;
- u32 int_en_all;
- u32 int1_en_all;
u32 spi_w;
u32 wdt_src;
/* Flags indicating the capability for the target pwrap */
@@ -644,7 +654,7 @@ static const struct pwrap_slv_type pmic_mt6357 = {
static const struct pwrap_slv_type pmic_mt6359 = {
.dew_regs = mt6359_regs,
- .caps = PWRAP_SLV_CAP_DUALIO,
+ .caps = 0,
};
static const struct udevice_id mtk_pmic_ids[] = {
@@ -734,6 +744,11 @@ static int mtk_pwrap_probe(struct udevice *dev)
* Skip initialization here in this case.
*/
if (!pwrap_readl(wrp, PWRAP_INIT_DONE2)) {
+ if (!HAS_CAP(wrp->master->caps, PWRAP_CAP_INIT)) {
+ dev_err(dev, "initialization is required but not supported\n");
+ return -EOPNOTSUPP;
+ }
+
ret = pwrap_init(wrp);
if (ret) {
dev_err(dev, "init failed with %d\n", ret);
@@ -755,7 +770,8 @@ static int mtk_pwrap_probe(struct udevice *dev)
* Since STAUPD was not used on mt8173 platform,
* so STAUPD of WDT_SRC which should be turned off
*/
- pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN);
+ if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC))
+ pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN);
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_WDT_SRC1))
pwrap_writel(wrp, wrp->master->wdt_src, PWRAP_WDT_SRC_EN_1);
@@ -765,15 +781,6 @@ static int mtk_pwrap_probe(struct udevice *dev)
else
pwrap_writel(wrp, 0x1, PWRAP_TIMER_EN);
- pwrap_writel(wrp, wrp->master->int_en_all, PWRAP_INT_EN);
-
- /*
- * We add INT1 interrupt to handle starvation and request exception
- * If we support it, we should enable it here.
- */
- if (HAS_CAP(wrp->master->caps, PWRAP_CAP_INT1_EN))
- pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN);
-
return 0;
}
@@ -782,7 +789,6 @@ static int mtk_pwrap_bind(struct udevice *dev)
ofnode pmic_node, regulators_node;
int children;
const struct pmic_child_info *pmic_children_info;
- struct pmic_wrapper_type *pw_type = (void *)dev_get_driver_data(dev);
pmic_node = dev_read_first_subnode(dev);
if (!ofnode_valid(pmic_node)) {
@@ -790,16 +796,13 @@ static int mtk_pwrap_bind(struct udevice *dev)
return -ENXIO;
}
- switch (pw_type->type) {
- case PWRAP_MT8365:
+ if (ofnode_device_is_compatible(pmic_node, "mediatek,mt6357")) {
pmic_children_info = mt6357_pmic_children_info;
- break;
- case PWRAP_MT8188:
- case PWRAP_MT8189:
+ } else if (ofnode_device_is_compatible(pmic_node, "mediatek,mt6359")) {
pmic_children_info = mt6359_pmic_children_info;
- break;
- default:
- dev_err(dev, "pwrap type %d not supported\n", pw_type->type);
+ } else {
+ dev_err(dev, "pmic type %s not supported\n",
+ ofnode_read_string(pmic_node, "compatible"));
return -ENXIO;
}
@@ -848,20 +851,23 @@ static struct dm_pmic_ops mtk_pwrap_ops = {
static struct pmic_wrapper_type pwrap_mt8188 = {
.regs = mt8188_regs,
- .type = PWRAP_MT8188,
.arb_en_all = 0x777f,
- .int_en_all = 0x180000,
- .int1_en_all = 0x0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
- .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_ARB,
+ .caps = PWRAP_CAP_ARB,
};
static struct pmic_wrapper_type pwrap_mt8189 = {
.regs = mt8189_regs,
- .type = PWRAP_MT8189,
.arb_en_all = 0x777f,
- .int_en_all = 0x180000,
+ .spi_w = PWRAP_MAN_CMD_SPI_WRITE,
+ .wdt_src = PWRAP_WDT_SRC_MASK_ALL,
+ .caps = PWRAP_CAP_ARB,
+};
+
+static const struct pmic_wrapper_type pwrap_mt8195 = {
+ .regs = mt8195_regs,
+ .arb_en_all = 0x777f, /* NEED CONFIRM */
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
.caps = PWRAP_CAP_ARB,
@@ -869,18 +875,16 @@ static struct pmic_wrapper_type pwrap_mt8189 = {
static const struct pmic_wrapper_type pwrap_mt8365 = {
.regs = mt8365_regs,
- .type = PWRAP_MT8365,
.arb_en_all = 0x3ffff,
- .int_en_all = 0x7f1fffff,
- .int1_en_all = 0x0,
.spi_w = PWRAP_MAN_CMD_SPI_WRITE,
.wdt_src = PWRAP_WDT_SRC_MASK_ALL,
- .caps = PWRAP_CAP_INT1_EN | PWRAP_CAP_WDT_SRC1,
+ .caps = PWRAP_CAP_WDT_SRC1 | PWRAP_CAP_INIT,
};
static const struct udevice_id mtk_pwrap_ids[] = {
{ .compatible = "mediatek,mt8188-pwrap", .data = (ulong)&pwrap_mt8188 },
{ .compatible = "mediatek,mt8189-pwrap", .data = (ulong)&pwrap_mt8189 },
+ { .compatible = "mediatek,mt8195-pwrap", .data = (ulong)&pwrap_mt8195 },
{ .compatible = "mediatek,mt8365-pwrap", .data = (ulong)&pwrap_mt8365 },
{ }
};
diff --git a/drivers/power/pmic/pca9450.c b/drivers/power/pmic/pca9450.c
index c95e6357ee8..cbe8cd05be7 100644
--- a/drivers/power/pmic/pca9450.c
+++ b/drivers/power/pmic/pca9450.c
@@ -90,7 +90,7 @@ static int pca9450_probe(struct udevice *dev)
return ret;
}
- if (ofnode_read_bool(dev_ofnode(dev), "nxp,wdog_b-warm-reset"))
+ if (dev_read_bool(dev, "nxp,wdog_b-warm-reset"))
reset_ctrl = PCA9450_PMIC_RESET_WDOG_B_CFG_WARM;
else
reset_ctrl = PCA9450_PMIC_RESET_WDOG_B_CFG_COLD_LDO12;
diff --git a/drivers/power/pmic/pmic_qcom.c b/drivers/power/pmic/pmic_qcom.c
index 92d0a95859b..4b8dcc6104c 100644
--- a/drivers/power/pmic/pmic_qcom.c
+++ b/drivers/power/pmic/pmic_qcom.c
@@ -72,7 +72,7 @@ static int pmic_qcom_probe(struct udevice *dev)
* contains two discrete values, not a single 64-bit address.
* The address is the first value.
*/
- ret = ofnode_read_u32_index(dev_ofnode(dev), "reg", 0, &priv->usid);
+ ret = dev_read_u32_index(dev, "reg", 0, &priv->usid);
if (ret < 0)
return -EINVAL;
diff --git a/drivers/power/pmic/pmic_spacemit_p1.c b/drivers/power/pmic/pmic_spacemit_p1.c
new file mode 100644
index 00000000000..46c5926874c
--- /dev/null
+++ b/drivers/power/pmic/pmic_spacemit_p1.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/spacemit_p1.h>
+
+static int pmic_p1_reg_count(struct udevice *dev)
+{
+ return P1_MAX_REGS;
+}
+
+static int pmic_p1_write(struct udevice *dev, uint reg, const u8 *buffer,
+ int len)
+{
+ int ret;
+
+ ret = dm_i2c_write(dev, reg, buffer, len);
+ if (ret)
+ pr_err("%s write error on register %02x\n", dev->name, reg);
+
+ return ret;
+}
+
+static int pmic_p1_read(struct udevice *dev, uint reg, u8 *buffer,
+ int len)
+{
+ int ret;
+
+ ret = dm_i2c_read(dev, reg, buffer, len);
+ if (ret)
+ pr_err("%s read error on register %02x\n", dev->name, reg);
+
+ return ret;
+}
+
+static const struct pmic_child_info p1_children_info[] = {
+ { .prefix = "buck", .driver = P1_BUCK_DRIVER },
+ { .prefix = "aldo", .driver = P1_ALDO_DRIVER },
+ { .prefix = "dldo", .driver = P1_DLDO_DRIVER },
+ { },
+};
+
+static int pmic_p1_bind(struct udevice *dev)
+{
+ const struct pmic_child_info *p1_children_info =
+ (struct pmic_child_info *)dev_get_driver_data(dev);
+ ofnode regulators_node;
+ int children;
+
+ regulators_node = dev_read_subnode(dev, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ debug("%s regulators subnode not found\n", dev->name);
+ return -EINVAL;
+ }
+
+ children = pmic_bind_children(dev, regulators_node,
+ p1_children_info);
+ if (!children)
+ debug("%s has no children (regulators)\n", dev->name);
+
+ return 0;
+}
+
+static int pmic_p1_probe(struct udevice *dev)
+{
+ return 0;
+}
+
+static struct dm_pmic_ops pmic_p1_ops = {
+ .reg_count = pmic_p1_reg_count,
+ .read = pmic_p1_read,
+ .write = pmic_p1_write,
+};
+
+static const struct udevice_id pmic_p1_match[] = {
+ {
+ .compatible = "spacemit,p1",
+ .data = (ulong)&p1_children_info,
+ }, {
+ /* sentinel */
+ }
+};
+
+U_BOOT_DRIVER(pmic_p1) = {
+ .name = "pmic_p1",
+ .id = UCLASS_PMIC,
+ .of_match = pmic_p1_match,
+ .bind = pmic_p1_bind,
+ .probe = pmic_p1_probe,
+ .ops = &pmic_p1_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index a4ee5f1335a..c3dc5d6f97b 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -1,38 +1,38 @@
config DM_REGULATOR
bool "Enable Driver Model for REGULATOR drivers (UCLASS_REGULATOR)"
depends on DM
- ---help---
- This config enables the driver model regulator support.
- UCLASS_REGULATOR - designed to provide a common API for basic regulator's
- functions, like get/set Voltage or Current value, enable state, etc...
- Note:
- When enabling this, please read the description, found in the files:
- - 'include/power/pmic.h'
- - 'include/power/regulator.h'
- - 'drivers/power/pmic/pmic-uclass.c'
- - 'drivers/power/pmic/regulator-uclass.c'
- It's important to call the device_bind() with the proper node offset,
- when binding the regulator devices. The pmic_bind_childs() can be used
- for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_dev()
- otherwise. Detailed information can be found in the header file.
+ help
+ This config enables the driver model regulator support.
+ UCLASS_REGULATOR - designed to provide a common API for basic regulator's
+ functions, like get/set Voltage or Current value, enable state, etc...
+ Note:
+ When enabling this, please read the description, found in the files:
+ - 'include/power/pmic.h'
+ - 'include/power/regulator.h'
+ - 'drivers/power/pmic/pmic-uclass.c'
+ - 'drivers/power/pmic/regulator-uclass.c'
+ It's important to call the device_bind() with the proper node offset,
+ when binding the regulator devices. The pmic_bind_childs() can be used
+ for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_dev()
+ otherwise. Detailed information can be found in the header file.
config SPL_DM_REGULATOR
bool "Enable regulators for SPL"
depends on DM_REGULATOR && SPL_POWER
- ---help---
- Regulators are seldom needed in SPL. Even if they are accessed, some
- code space can be saved by accessing the PMIC registers directly.
- Enable this option if you need regulators in SPL and can cope with
- the extra code size.
+ help
+ Regulators are seldom needed in SPL. Even if they are accessed, some
+ code space can be saved by accessing the PMIC registers directly.
+ Enable this option if you need regulators in SPL and can cope with
+ the extra code size.
config REGULATOR_ACT8846
bool "Enable driver for ACT8846 regulator"
depends on DM_REGULATOR && PMIC_ACT8846
- ---help---
- Enable support for the regulator functions of the ACT8846 PMIC. The
- driver implements get/set api for the various BUCKS and LDOS supported
- by the PMIC device. This driver is controlled by a device tree node
- which includes voltage limits.
+ help
+ Enable support for the regulator functions of the ACT8846 PMIC. The
+ driver implements get/set api for the various BUCKS and LDOS supported
+ by the PMIC device. This driver is controlled by a device tree node
+ which includes voltage limits.
config REGULATOR_AS3722
bool "Enable driver for AS7322 regulator"
@@ -75,33 +75,33 @@ config DM_REGULATOR_BD71837
bool "Enable Driver Model for ROHM BD71837/BD71847 regulators"
depends on DM_REGULATOR && DM_PMIC_BD71837
help
- This config enables implementation of driver-model regulator uclass
- features for regulators on ROHM BD71837 and BD71847 PMICs.
- BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version
- containing 6 bucks and 6 LDOs. The driver implements get/set api for
- value and enable.
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM BD71837 and BD71847 PMICs.
+ BD71837 contains 8 bucks and 7 LDOS. BD71847 is reduced version
+ containing 6 bucks and 6 LDOs. The driver implements get/set api for
+ value and enable.
config SPL_DM_REGULATOR_BD71837
bool "Enable Driver Model for ROHM BD71837/BD71847 regulators in SPL"
depends on DM_REGULATOR_BD71837 && SPL
help
- This config enables implementation of driver-model regulator uclass
- features for regulators on ROHM BD71837 and BD71847 in SPL.
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM BD71837 and BD71847 in SPL.
config DM_REGULATOR_PCA9450
bool "Enable Driver Model for NXP PCA9450 regulators"
depends on DM_REGULATOR && DM_PMIC_PCA9450
help
- This config enables implementation of driver-model regulator uclass
- features for regulators on NXP PCA9450 PMICs. PCA9450 contains 6 bucks
- and 5 LDOS. The driver implements get/set api for value and enable.
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on NXP PCA9450 PMICs. PCA9450 contains 6 bucks
+ and 5 LDOS. The driver implements get/set api for value and enable.
config SPL_DM_REGULATOR_PCA9450
bool "Enable Driver Model for NXP PCA9450 regulators in SPL"
depends on DM_REGULATOR_PCA9450 && SPL
help
- This config enables implementation of driver-model regulator uclass
- features for regulators on ROHM PCA9450 in SPL.
+ This config enables implementation of driver-model regulator uclass
+ features for regulators on ROHM PCA9450 in SPL.
config DM_REGULATOR_DA9063
bool "Enable Driver Model for REGULATOR DA9063"
@@ -126,46 +126,56 @@ config SPL_DM_REGULATOR_DA9063
config DM_REGULATOR_PFUZE100
bool "Enable Driver Model for REGULATOR PFUZE100"
depends on DM_REGULATOR && DM_PMIC_PFUZE100
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for REGULATOR PFUZE100. The driver implements get/set api for:
- value, enable and mode.
+ default DM_PMIC_PFUZE100
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR PFUZE100. The driver implements get/set api for:
+ value, enable and mode.
+
+config SPL_DM_REGULATOR_PFUZE100
+ bool "Enable Driver Model for REGULATOR PFUZE100 in SPL"
+ depends on SPL_DM_REGULATOR && SPL_DM_PMIC_PFUZE100
+ default SPL_DM_PMIC_PFUZE100
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR PFUZE100. The driver implements get/set api for:
+ value, enable and mode.
config REGULATOR_PWM
bool "Enable driver for PWM regulators"
depends on DM_REGULATOR && DM_PWM
- ---help---
- Enable support for the PWM regulator functions which voltage are
- controlled by PWM duty ratio. Some of Rockchip board using this kind
- of regulator. The driver implements get/set api for the various BUCKS.
- This driver is controlled by a device tree node
- which includes voltage limits.
+ help
+ Enable support for the PWM regulator functions which voltage are
+ controlled by PWM duty ratio. Some of Rockchip board using this kind
+ of regulator. The driver implements get/set api for the various BUCKS.
+ This driver is controlled by a device tree node
+ which includes voltage limits.
config DM_REGULATOR_MAX8907
bool "Enable Driver Model for REGULATOR MAX8907"
depends on DM_REGULATOR && DM_PMIC_MAX8907
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for REGULATOR MAX8907. The driver supports both DC-to-DC
- Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators
- found in MAX8907 PMIC and implements get/set api for value and enable.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR MAX8907. The driver supports both DC-to-DC
+ Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators
+ found in MAX8907 PMIC and implements get/set api for value and enable.
config DM_REGULATOR_MAX77663
bool "Enable Driver Model for REGULATOR MAX77663"
depends on DM_REGULATOR && DM_PMIC_MAX77663
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for REGULATOR MAX77663. The driver supports both DC-to-DC
- Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators
- found in MAX77663 PMIC and implements get/set api for value and enable.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR MAX77663. The driver supports both DC-to-DC
+ Step-Down (SD) Regulators and Low-Dropout Linear (LDO) Regulators
+ found in MAX77663 PMIC and implements get/set api for value and enable.
config DM_REGULATOR_MAX77686
bool "Enable Driver Model for REGULATOR MAX77686"
depends on DM_REGULATOR && DM_PMIC_MAX77686
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for REGULATOR MAX77686. The driver implements get/set api for:
- value, enable and mode.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for REGULATOR MAX77686. The driver implements get/set api for:
+ value, enable and mode.
config DM_REGULATOR_NPCM8XX
bool "Enable Driver Model for NPCM8xx voltage supply"
@@ -211,33 +221,33 @@ config DM_REGULATOR_FIXED
bool "Enable Driver Model for REGULATOR Fixed value"
depends on DM_REGULATOR
select DM_REGULATOR_COMMON
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for fixed value regulators. The driver implements get/set api
- for enable and get only for voltage value.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for fixed value regulators. The driver implements get/set api
+ for enable and get only for voltage value.
config SPL_DM_REGULATOR_FIXED
bool "Enable Driver Model for REGULATOR Fixed value in SPL"
depends on DM_REGULATOR_FIXED && SPL
select SPL_DM_REGULATOR_COMMON
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for fixed value regulators in SPL.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for fixed value regulators in SPL.
config DM_REGULATOR_GPIO
bool "Enable Driver Model for GPIO REGULATOR"
depends on DM_REGULATOR && DM_GPIO
select DM_REGULATOR_COMMON
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for gpio regulators. The driver implements get/set for
- voltage value.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for gpio regulators. The driver implements get/set for
+ voltage value.
config DM_REGULATOR_QCOM_RPMH
bool "Enable driver model for Qualcomm RPMh regulator"
depends on DM_REGULATOR && QCOM_RPMH
select DEVRES
- ---help---
+ help
Enable support for the Qualcomm RPMh regulator. The driver
implements get/set api for a limited set of regulators used
by u-boot.
@@ -245,7 +255,7 @@ config DM_REGULATOR_QCOM_RPMH
config DM_REGULATOR_QCOM_USB_VBUS
bool "Enable driver model for Qualcomm USB vbus regulator"
depends on DM_REGULATOR && DM_PMIC
- ---help---
+ help
Enable support for the Qualcomm USB Vbus regulator. The driver
implements get/set api for the regulator to be used by u-boot.
@@ -253,18 +263,18 @@ config SPL_DM_REGULATOR_GPIO
bool "Enable Driver Model for GPIO REGULATOR in SPL"
depends on DM_REGULATOR_GPIO && SPL_DM_GPIO
select SPL_DM_REGULATOR_COMMON
- ---help---
- This config enables implementation of driver-model regulator uclass
- features for gpio regulators in SPL.
+ help
+ This config enables implementation of driver-model regulator uclass
+ features for gpio regulators in SPL.
config REGULATOR_RK8XX
bool "Enable driver for RK8XX regulators"
depends on DM_REGULATOR && PMIC_RK8XX
- ---help---
- Enable support for the regulator functions of the RK8XX PMIC. The
- driver implements get/set api for the various BUCKS and LDOs supported
- by the PMIC device. This driver is controlled by a device tree node
- which includes voltage limits.
+ help
+ Enable support for the regulator functions of the RK8XX PMIC. The
+ driver implements get/set api for the various BUCKS and LDOs supported
+ by the PMIC device. This driver is controlled by a device tree node
+ which includes voltage limits.
config SPL_REGULATOR_RK8XX
bool "Enable driver for RK8XX regulators in SPL"
@@ -278,162 +288,162 @@ config SPL_REGULATOR_RK8XX
config DM_REGULATOR_S2MPS11
bool "Enable driver for S2MPS11 regulator"
depends on DM_REGULATOR && PMIC_S2MPS11
- ---help---
- This enables implementation of driver-model regulator uclass
- features for REGULATOR S2MPS11.
- The driver implements get/set api for: value and enable.
+ help
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR S2MPS11.
+ The driver implements get/set api for: value and enable.
config REGULATOR_S5M8767
bool "Enable support for S5M8767 regulator"
depends on DM_REGULATOR && PMIC_S5M8767
- ---help---
- This enables the regulator features of the S5M8767, allowing voltages
- to be set, etc. The driver is not fully complete but supports most
- common requirements, including all LDOs and BUCKs. This allows many
- supplies to be set automatically using the device tree values.
+ help
+ This enables the regulator features of the S5M8767, allowing voltages
+ to be set, etc. The driver is not fully complete but supports most
+ common requirements, including all LDOs and BUCKs. This allows many
+ supplies to be set automatically using the device tree values.
config DM_REGULATOR_SANDBOX
bool "Enable Driver Model for Sandbox PMIC regulator"
depends on DM_REGULATOR && DM_PMIC_SANDBOX
- ---help---
- Enable the regulator driver for emulated Sandbox PMIC.
- The emulated PMIC device depends on two drivers:
- - sandbox PMIC I/O driver - implements dm pmic operations
- - sandbox PMIC regulator driver - implements dm regulator operations
- - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
+ help
+ Enable the regulator driver for emulated Sandbox PMIC.
+ The emulated PMIC device depends on two drivers:
+ - sandbox PMIC I/O driver - implements dm pmic operations
+ - sandbox PMIC regulator driver - implements dm regulator operations
+ - sandbox PMIC i2c emul driver - emulates the PMIC's I2C transmission
- The regulator driver provides uclass operations for sandbox PMIC's
- regulators. The driver implements get/set api for: voltage, current,
- operation mode and enable state.
- The driver supports LDO and BUCK regulators.
+ The regulator driver provides uclass operations for sandbox PMIC's
+ regulators. The driver implements get/set api for: voltage, current,
+ operation mode and enable state.
+ The driver supports LDO and BUCK regulators.
- The Sandbox PMIC info:
- * I/O interface:
- - I2C chip address: 0x40
- - first register address: 0x0
- - register count: 0x10
- * Adjustable outputs:
- - 2x LDO
- - 2x BUCK
- - Each, with a different operating conditions (header).
- * Reset values:
- - set by i2c emul driver's probe() (defaults in header)
+ The Sandbox PMIC info:
+ * I/O interface:
+ - I2C chip address: 0x40
+ - first register address: 0x0
+ - register count: 0x10
+ * Adjustable outputs:
+ - 2x LDO
+ - 2x BUCK
+ - Each, with a different operating conditions (header).
+ * Reset values:
+ - set by i2c emul driver's probe() (defaults in header)
- A detailed information can be found in header: '<power/sandbox_pmic.h>'
- Binding info: 'doc/device-tree-bindings/pmic/max77686.txt'
+ A detailed information can be found in header: '<power/sandbox_pmic.h>'
+ Binding info: 'doc/device-tree-bindings/pmic/max77686.txt'
config REGULATOR_TPS65090
bool "Enable driver for TPS65090 PMIC regulators"
depends on PMIC_TPS65090
- ---help---
- The TPS65090 provides several FETs (Field-effect Transistors,
- effectively switches) which are supported by this driver as
- regulators, one for each FET. The standard regulator interface is
- supported, but it is only possible to turn the regulators on or off.
- There is no voltage/current control.
+ help
+ The TPS65090 provides several FETs (Field-effect Transistors,
+ effectively switches) which are supported by this driver as
+ regulators, one for each FET. The standard regulator interface is
+ supported, but it is only possible to turn the regulators on or off.
+ There is no voltage/current control.
config DM_REGULATOR_PALMAS
bool "Enable driver for PALMAS PMIC regulators"
- depends on PMIC_PALMAS
- ---help---
- This enables implementation of driver-model regulator uclass
- features for REGULATOR PALMAS and the family of PALMAS PMICs.
- The driver implements get/set api for: value and enable.
+ depends on PMIC_PALMAS
+ help
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR PALMAS and the family of PALMAS PMICs.
+ The driver implements get/set api for: value and enable.
config DM_REGULATOR_PBIAS
bool "Enable driver for PBIAS regulator"
depends on DM_REGULATOR
select REGMAP
select SYSCON
- ---help---
- This enables implementation of driver-model regulator uclass
- features for pseudo-regulator PBIAS found in the OMAP SOCs.
- This pseudo-regulator is used to provide a BIAS voltage to MMC1
- signal pads and must be configured properly during a voltage switch.
- Voltage switching is required by some operating modes of SDcards and
- eMMC.
+ help
+ This enables implementation of driver-model regulator uclass
+ features for pseudo-regulator PBIAS found in the OMAP SOCs.
+ This pseudo-regulator is used to provide a BIAS voltage to MMC1
+ signal pads and must be configured properly during a voltage switch.
+ Voltage switching is required by some operating modes of SDcards and
+ eMMC.
config DM_REGULATOR_LP873X
bool "Enable driver for LP873X PMIC regulators"
- depends on PMIC_LP873X
- ---help---
- This enables implementation of driver-model regulator uclass
- features for REGULATOR LP873X and the family of LP873X PMICs.
- The driver implements get/set api for: value and enable.
+ depends on PMIC_LP873X
+ help
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR LP873X and the family of LP873X PMICs.
+ The driver implements get/set api for: value and enable.
config DM_REGULATOR_LP87565
bool "Enable driver for LP87565 PMIC regulators"
- depends on PMIC_LP87565
- ---help---
- This enables implementation of driver-model regulator uclass
- features for REGULATOR LP87565 and the family of LP87565 PMICs.
- LP87565 series of PMICs have 4 single phase BUCKs that can also
- be configured in multi phase modes. The driver implements
- get/set api for value and enable.
+ depends on PMIC_LP87565
+ help
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR LP87565 and the family of LP87565 PMICs.
+ LP87565 series of PMICs have 4 single phase BUCKs that can also
+ be configured in multi phase modes. The driver implements
+ get/set api for value and enable.
config DM_REGULATOR_STM32_VREFBUF
bool "Enable driver for STMicroelectronics STM32 VREFBUF"
depends on DM_REGULATOR && (STM32H7 || ARCH_STM32MP)
help
- This driver supports STMicroelectronics STM32 VREFBUF (voltage
- reference buffer) which can be used as voltage reference for
- internal ADCs, DACs and also for external components through
- dedicated Vref+ pin.
+ This driver supports STMicroelectronics STM32 VREFBUF (voltage
+ reference buffer) which can be used as voltage reference for
+ internal ADCs, DACs and also for external components through
+ dedicated Vref+ pin.
config DM_REGULATOR_TPS65910
bool "Enable driver for TPS65910 PMIC regulators"
depends on DM_PMIC_TPS65910
- ---help---
- The TPS65910 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all
- regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements
- the get/set api for value and enable.
+ help
+ The TPS65910 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all
+ regulator types of the TPS65910 (BUCK, BOOST and LDO). It implements
+ the get/set api for value and enable.
config DM_REGULATOR_TPS65911
bool "Enable driver for TPS65911 PMIC regulators"
depends on DM_PMIC_TPS65910
- ---help---
- This config enables implementation of driver-model regulator
- uclass features for the TPS65911 PMIC. The driver supports Step-Down
- DC-DC Converters for Processor Cores (VDD1 and VDD2), Step-Down DC-DC
- Converter for I/O Power (VIO), Controller for External FETs (VDDCtrl)
- and LDO Voltage Regulators found in TPS65911 PMIC and implements
- get/set api for value and enable.
+ help
+ This config enables implementation of driver-model regulator
+ uclass features for the TPS65911 PMIC. The driver supports Step-Down
+ DC-DC Converters for Processor Cores (VDD1 and VDD2), Step-Down DC-DC
+ Converter for I/O Power (VIO), Controller for External FETs (VDDCtrl)
+ and LDO Voltage Regulators found in TPS65911 PMIC and implements
+ get/set api for value and enable.
config DM_REGULATOR_TPS62360
bool "Enable driver for TPS6236x Power Regulator"
depends on DM_REGULATOR
help
- The TPS6236X DC/DC step down converter provides a single output
- power line peaking at 3A current. This driver supports all four
- variants of the chip (TPS62360, TPS62361, TPS62362, TPS62363). It
- implements the get/set api for value only, as the power line is
- always on.
+ The TPS6236X DC/DC step down converter provides a single output
+ power line peaking at 3A current. This driver supports all four
+ variants of the chip (TPS62360, TPS62361, TPS62362, TPS62363). It
+ implements the get/set api for value only, as the power line is
+ always on.
config DM_REGULATOR_TPS80031
bool "Enable driver for TPS80031/TPS80032 PMIC regulators"
depends on DM_PMIC_TPS80031
- ---help---
- This enables implementation of driver-model regulator uclass
- features for TPS80031/TPS80032 PMICs. The driver implements
- get/set api for: value and enable.
+ help
+ This enables implementation of driver-model regulator uclass
+ features for TPS80031/TPS80032 PMICs. The driver implements
+ get/set api for: value and enable.
config DM_REGULATOR_TPS6287X
bool "Enable driver for TPS6287x Power Regulator"
depends on DM_REGULATOR
help
- The TPS6287X is a step down converter with a fast transient
- response. This driver supports all four variants of the chip
- (TPS62870, TPS62871, TPS62872, TPS62873). It implements the
- get/set api for value only, as the power line is always on.
+ The TPS6287X is a step down converter with a fast transient
+ response. This driver supports all four variants of the chip
+ (TPS62870, TPS62871, TPS62872, TPS62873). It implements the
+ get/set api for value only, as the power line is always on.
config DM_REGULATOR_STPMIC1
bool "Enable driver for STPMIC1 regulators"
depends on DM_REGULATOR && PMIC_STPMIC1
- ---help---
- Enable support for the regulator functions of the STPMIC1 PMIC. The
- driver implements get/set api for the various BUCKS and LDOs supported
- by the PMIC device. This driver is controlled by a device tree node
- which includes voltage limits.
+ help
+ Enable support for the regulator functions of the STPMIC1 PMIC. The
+ driver implements get/set api for the various BUCKS and LDOs supported
+ by the PMIC device. This driver is controlled by a device tree node
+ which includes voltage limits.
config DM_REGULATOR_ANATOP
bool "Enable driver for ANATOP regulators"
@@ -441,18 +451,18 @@ config DM_REGULATOR_ANATOP
select REGMAP
select SYSCON
help
- Enable support for the Freescale i.MX on-chip ANATOP LDO
- regulators. It is recommended that this option be enabled on
- i.MX6 platform.
+ Enable support for the Freescale i.MX on-chip ANATOP LDO
+ regulators. It is recommended that this option be enabled on
+ i.MX6 platform.
config SPL_DM_REGULATOR_TPS6287X
bool "Enable driver for TPS6287x Power Regulator"
depends on SPL_DM_REGULATOR
help
- The TPS6287X is a step down converter with a fast transient
- response. This driver supports all four variants of the chip
- (TPS62870, TPS62871, TPS62872, TPS62873). It implements the
- get/set api for value only, as the power line is always on.
+ The TPS6287X is a step down converter with a fast transient
+ response. This driver supports all four variants of the chip
+ (TPS62870, TPS62871, TPS62872, TPS62873). It implements the
+ get/set api for value only, as the power line is always on.
config SPL_DM_REGULATOR_STPMIC1
bool "Enable driver for STPMIC1 regulators in SPL"
@@ -464,54 +474,54 @@ config SPL_DM_REGULATOR_PALMAS
bool "Enable driver for PALMAS PMIC regulators"
depends on SPL_PMIC_PALMAS
help
- This enables implementation of driver-model regulator uclass
- features for REGULATOR PALMAS and the family of PALMAS PMICs.
- The driver implements get/set api for: value and enable in SPL.
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR PALMAS and the family of PALMAS PMICs.
+ The driver implements get/set api for: value and enable in SPL.
config SPL_DM_REGULATOR_LP87565
bool "Enable driver for LP87565 PMIC regulators"
depends on SPL_PMIC_LP87565
help
- This enables implementation of driver-model regulator uclass
- features for REGULATOR LP87565 and the family of LP87565 PMICs.
- LP87565 series of PMICs have 4 single phase BUCKs that can also
- be configured in multi phase modes. The driver implements
- get/set api for value and enable in SPL.
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR LP87565 and the family of LP87565 PMICs.
+ LP87565 series of PMICs have 4 single phase BUCKs that can also
+ be configured in multi phase modes. The driver implements
+ get/set api for value and enable in SPL.
config SPL_DM_REGULATOR_LP873X
bool "Enable driver for LP873X PMIC regulators"
depends on SPL_PMIC_LP873X
help
- This enables implementation of driver-model regulator uclass
- features for REGULATOR LP873X and the family of LP873X PMICs.
- The driver implements get/set api for: value and enable in SPL.
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR LP873X and the family of LP873X PMICs.
+ The driver implements get/set api for: value and enable in SPL.
config DM_REGULATOR_TPS65941
bool "Enable driver for TPS65941 PMIC regulators"
- depends on PMIC_TPS65941
+ depends on PMIC_TPS65941
help
- This enables implementation of driver-model regulator uclass
- features for REGULATOR TPS65941 and the family of TPS65941 PMICs.
- TPS65941 series of PMICs have 5 single phase BUCKs that can also
- be configured in multi phase modes & 4 LDOs. The driver implements
- get/set api for value and enable.
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR TPS65941 and the family of TPS65941 PMICs.
+ TPS65941 series of PMICs have 5 single phase BUCKs that can also
+ be configured in multi phase modes & 4 LDOs. The driver implements
+ get/set api for value and enable.
config DM_REGULATOR_SCMI
bool "Enable driver for SCMI voltage domain regulators"
depends on DM_REGULATOR
select SCMI_AGENT
- help
- Enable this option if you want to support regulators exposed through
+ help
+ Enable this option if you want to support regulators exposed through
the SCMI voltage domain protocol by a SCMI server.
config DM_REGULATOR_TPS65219
bool "Enable driver for TPS65219 PMIC regulators"
- depends on PMIC_TPS65219
+ depends on PMIC_TPS65219
help
- This enables implementation of driver-model regulator uclass
- features for REGULATOR TPS65219 and the family of TPS65219 PMICs.
- TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs.
- The driver implements get/set api for value and enable.
+ This enables implementation of driver-model regulator uclass
+ features for REGULATOR TPS65219 and the family of TPS65219 PMICs.
+ TPS65219 series of PMICs have 3 single phase BUCKs & 4 LDOs.
+ The driver implements get/set api for value and enable.
config REGULATOR_RZG2L_USBPHY
bool "Enable driver for RZ/G2L USB PHY VBUS supply"
@@ -524,11 +534,11 @@ config REGULATOR_RZG2L_USBPHY
config DM_REGULATOR_CPCAP
bool "Enable driver for CPCAP PMIC regulators"
depends on DM_REGULATOR && DM_PMIC_CPCAP
- ---help---
- Enable implementation of driver-model regulator uclass features for
- REGULATOR CPCAP. The driver supports both DC-to-DC Step-Down Switching
- (SW) Regulators and Low-Dropout Linear (LDO) Regulators found in CPCAP
- PMIC and implements get/set api for voltage and state.
+ help
+ Enable implementation of driver-model regulator uclass features for
+ REGULATOR CPCAP. The driver supports both DC-to-DC Step-Down Switching
+ (SW) Regulators and Low-Dropout Linear (LDO) Regulators found in CPCAP
+ PMIC and implements get/set api for voltage and state.
config DM_REGULATOR_MT6357
bool "Enable driver for MediaTek MT6357 PMIC regulators"
@@ -547,3 +557,53 @@ config DM_REGULATOR_MT6359
MediaTek MT6359 PMIC.
This driver supports the control of different power rails of device
through regulator interface.
+
+config DM_REGULATOR_PMBUS_HELPER
+ bool "Shared regulator helpers for PMBus chip drivers"
+ depends on DM_REGULATOR && PMBUS && DM_I2C
+ help
+ Provide shared get_value / set_value / get_enable / set_enable
+ operations for UCLASS_REGULATOR drivers that bind PMBus 1.x
+ compliant voltage regulators. Per chip drivers
+ (mps,mpq8785, lltc,ltc3882, ...) consume this helper to avoid
+ duplicating the LINEAR16 / DIRECT decoder dispatch and the
+ VOUT_MODE / VOUT_COMMAND / OPERATION transport sequences.
+
+config DM_REGULATOR_PMBUS_GENERIC
+ bool "Generic PMBus 1.x regulator driver (compatible=\"pmbus\")"
+ depends on DM_REGULATOR_PMBUS_HELPER
+ help
+ Catch all UCLASS_REGULATOR driver bound to compatible = "pmbus".
+ Auto detects the VOUT numeric format from the chip's VOUT_MODE
+ register and exposes telemetry plus voltage set / get against
+ the standard PMBus 1.x command codes. Use this for PMBus
+ compliant chips that have no per chip driver yet; promote to a
+ per chip driver only when chip specific quirks (vendor
+ registers, VID coercion, ADDR pin auto promotion, non standard
+ m / b / R coefficients) need handling.
+
+config DM_REGULATOR_MPQ8785
+ bool "MPS MPQ8785 / MPM3695 / MPM82504 PMBus voltage regulator"
+ depends on DM_REGULATOR_PMBUS_HELPER
+ help
+ Driver for the Monolithic Power Systems MPQ8785 family of
+ digital multiphase voltage regulators with PMBus. Supports
+ MPM3695, MPM3695-25, MPM82504, and MPQ8785. Adapted from the
+ Linux drivers/hwmon/pmbus/mpq8785.c reference.
+
+config SANDBOX_PMBUS
+ bool "Sandbox PMBus 1.x chip emulator"
+ depends on SANDBOX && PMBUS && DM_I2C
+ help
+ Emulate a PMBus 1.x compliant chip behind a sandbox I2C bus so
+ the PMBus framework (lib/pmbus.c), the generic regulator
+ (DM_REGULATOR_PMBUS_GENERIC) and the pmbus CLI command can be
+ exercised by the dm unit tests with no real hardware. Only
+ useful for testing; say N on real boards.
+
+config DM_REGULATOR_SPACEMIT_P1
+ bool "Enable driver for Spacemit P1 PMIC regulators"
+ depends on DM_REGULATOR && PMIC_SPACEMIT_P1
+ help
+ Enable implementation of driver-model regulator uclass features
+ for regulator P1. The driver supports BUCKs, LDOs and SWITCHes.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 9e303d4f7f8..3a148545387 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -15,7 +15,7 @@ obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX8907) += max8907_regulator.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o
-obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PFUZE100) += pfuze100.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o
obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o
@@ -29,6 +29,7 @@ obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o
obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
+obj-$(CONFIG_DM_REGULATOR_SPACEMIT_P1) += spacemit_p1_regulator.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o
@@ -49,3 +50,7 @@ obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o
obj-$(CONFIG_$(PHASE_)DM_REGULATOR_CPCAP) += cpcap_regulator.o
obj-$(CONFIG_DM_REGULATOR_MT6357) += mt6357_regulator.o
obj-$(CONFIG_DM_REGULATOR_MT6359) += mt6359_regulator.o
+obj-$(CONFIG_DM_REGULATOR_PMBUS_HELPER) += pmbus_helper.o
+obj-$(CONFIG_DM_REGULATOR_PMBUS_GENERIC) += pmbus_generic.o
+obj-$(CONFIG_DM_REGULATOR_MPQ8785) += mpq8785.o
+obj-$(CONFIG_SANDBOX_PMBUS) += sandbox_pmbus.o
diff --git a/drivers/power/regulator/anatop_regulator.c b/drivers/power/regulator/anatop_regulator.c
index 824a753db16..88570a1f624 100644
--- a/drivers/power/regulator/anatop_regulator.c
+++ b/drivers/power/regulator/anatop_regulator.c
@@ -170,8 +170,7 @@ static int anatop_regulator_probe(struct udevice *dev)
anatop_reg = dev_get_plat(dev);
uc_pdata = dev_get_uclass_plat(dev);
- anatop_reg->name = ofnode_read_string(dev_ofnode(dev),
- "regulator-name");
+ anatop_reg->name = dev_read_string(dev, "regulator-name");
if (!anatop_reg->name)
return log_msg_ret("regulator-name", -EINVAL);
diff --git a/drivers/power/regulator/mpq8785.c b/drivers/power/regulator/mpq8785.c
new file mode 100644
index 00000000000..dc321f0e410
--- /dev/null
+++ b/drivers/power/regulator/mpq8785.c
@@ -0,0 +1,494 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * MPS MPQ8785 / MPM3695 / MPM3695-25 / MPM82504 PMBus voltage
+ * regulator driver. Adapted from
+ * linux/drivers/hwmon/pmbus/mpq8785.c
+ * (Charles Hsu, GPL-2.0-or-later) with the kernel hwmon publication
+ * and caching layers stripped.
+ *
+ * Hooks the shared pmbus_helper UCLASS_REGULATOR ops + adds the MPS
+ * specific identify (VOUT_MODE switch between LINEAR16 and DIRECT
+ * m=64 R=1 for the chip's "VID" mode), the MPS vendor extension
+ * (pmbus mps last|clear last|clear force), and ADDR_VBOOT auto
+ * promotion when the DT declared address fails the MFR_ID probe.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <vsprintf.h>
+#include <linux/bitops.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+/* Chip family identifiers (driver_data). */
+enum mpq_chip_id {
+ MPQ_MPM3695 = 0,
+ MPQ_MPM3695_25 = 1,
+ MPQ_MPM82504 = 2,
+ MPQ_MPQ8785 = 3,
+};
+
+/*
+ * MPS vendor extended command codes (NOT in PMBus 1.3 Part II).
+ *
+ * CLEAR_LAST_FAULT (08h) clears the NVM backed PROTECTION_LAST
+ * register. Gated by MFR_CFG_EXT (F5h)
+ * bit[6] = 1; chip silently no ops if the
+ * gate is cleared.
+ * PROTECTION_LAST (FBh) single event, NVM backed log of the last
+ * protection event. Survives the chip's own
+ * power cycle. The boot time post mortem the
+ * SoC has no other way to obtain.
+ *
+ * NEVER issue CLEAR_LAST_FAULT (08h) implicitly; it would erase the
+ * post mortem trail. Only the explicit pmbus mps clear last and
+ * pmbus mps clear force subcommands write it.
+ */
+#define PMBUS_CLEAR_LAST_FAULT 0x08
+#define MPS_PROTECTION_LAST 0xfb
+
+/*
+ * MFR_CFG_EXT (F5h) is an MPS extended config WORD (16 bits, not a
+ * byte). Bit[6] (MFR_CLR_FAULT_CFG) gates CLEAR_LAST_FAULT (08h)
+ * clearing PROTECTION_LAST. Several other bits are fixed and MUST be
+ * preserved on writeback; always read modify write the full 16 bits,
+ * only flip bit[6], restore on the way out.
+ */
+#define MPS_MFR_CFG_EXT 0xf5
+#define MPS_MFR_CFG_EXT_CLR_LAST_EN BIT(6)
+
+/*
+ * MPQ8785 driver_info (transcribed from Linux's
+ * drivers/hwmon/pmbus/mpq8785.c::mpq8785_info). DIRECT format with
+ * chip specific m / b / R coefficients on VIN, IOUT, TEMPERATURE.
+ * VOUT format is selected at probe time from VOUT_MODE: bits[7:5] == 0
+ * selects LINEAR16, bits[7:5] == 1 or 2 selects DIRECT m=64 R=1.
+ */
+
+/*
+ * MPS-extended STATUS_* bit names. The MPQ8785 family reuses several
+ * bit positions documented as RESERVED / UNKNOWN / NONE_ABOVE /
+ * MFR_SPECIFIC by PMBus 1.3 for chip specific signals. The override
+ * table below substitutes the chip name for the standard one when
+ * the bit is set, leaving every other PMBus 1.3 standard bit
+ * (VOUT_OV, IOUT_OC, TEMP, CML, ...) unchanged.
+ *
+ * STATUS_WORD bit[12] spec MFR_SPECIFIC chip NVM_SUMMARY (NVM
+ * backed PROTECTION_LAST
+ * register is non zero)
+ * STATUS_WORD bit[8] spec UNKNOWN chip WATCH_DOG (internal
+ * calculation FSM watchdog
+ * overflow)
+ * STATUS_WORD bit[0] spec NONE_ABOVE chip DRMOS_FAULT (DrMOS
+ * stage fault)
+ * STATUS_CML bit[4] spec MEMORY chip MTP_CRC_FAULT (NVM
+ * CRC mismatch on restore)
+ * STATUS_CML bit[0] spec OTHER_MEM_LOGIC chip MTP_FAULT (NVM
+ * signature fault)
+ * STATUS_TEMPERATURE bit[0] (PMBus leaves bits[3:0] reserved on
+ * this register) chip
+ * OT_SELF (controller die
+ * OT condition)
+ */
+static const struct pmbus_status_override mpq8785_status_overrides[] = {
+ { PMBUS_STATUS_WORD, BIT(12), "NVM_SUMMARY" },
+ { PMBUS_STATUS_WORD, BIT(8), "WATCH_DOG" },
+ { PMBUS_STATUS_WORD, BIT(0), "DRMOS_FAULT" },
+ { PMBUS_STATUS_CML, BIT(4), "MTP_CRC_FAULT" },
+ { PMBUS_STATUS_CML, BIT(0), "MTP_FAULT" },
+ { PMBUS_STATUS_TEMPERATURE, BIT(0), "OT_SELF" },
+ { /* sentinel */ }
+};
+
+static struct pmbus_driver_info mpq8785_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_IN] = pmbus_fmt_direct,
+ .format[PSC_VOLTAGE_OUT] = pmbus_fmt_linear, /* refined per VOUT_MODE */
+ .format[PSC_CURRENT_OUT] = pmbus_fmt_direct,
+ .format[PSC_TEMPERATURE] = pmbus_fmt_direct,
+ .m[PSC_VOLTAGE_IN] = 4, .b[PSC_VOLTAGE_IN] = 0, .R[PSC_VOLTAGE_IN] = 1,
+ .m[PSC_CURRENT_OUT] = 16, .b[PSC_CURRENT_OUT] = 0, .R[PSC_CURRENT_OUT] = 0,
+ .m[PSC_TEMPERATURE] = 1, .b[PSC_TEMPERATURE] = 0, .R[PSC_TEMPERATURE] = 0,
+ /*
+ * Sensor set this family actually implements with calibrated
+ * coefficients: VIN, VOUT, IOUT, TEMP. READ_IIN / READ_POUT are
+ * ACKed by the silicon but uncalibrated here (the kernel's mpq8646
+ * / mpq8785 drivers expose neither), so declaring the set keeps
+ * pmbus_print_telemetry from printing a bogus POUT / IIN -- matching
+ * the kernel's per-chip sensor list.
+ */
+ .classes_present = BIT(PSC_VOLTAGE_IN) | BIT(PSC_VOLTAGE_OUT) |
+ BIT(PSC_CURRENT_OUT) | BIT(PSC_TEMPERATURE),
+ .status_overrides = mpq8785_status_overrides,
+};
+
+/*
+ * MPM3695 / MPM3695-25 / MPM82504 driver_info: VOUT in DIRECT format
+ * with chip family default m=8 R=2. Other sensor classes default to
+ * LINEAR (the chip family does not document non standard formats for
+ * VIN / IOUT / TEMPERATURE; the helper falls back to LINEAR11 when
+ * the active info is non NULL but format[c] is linear).
+ */
+static struct pmbus_driver_info mpm82504_info = {
+ .pages = 1,
+ .format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct,
+ .m[PSC_VOLTAGE_OUT] = 8, .b[PSC_VOLTAGE_OUT] = 0, .R[PSC_VOLTAGE_OUT] = 2,
+ .format[PSC_VOLTAGE_IN] = pmbus_fmt_linear,
+ .format[PSC_CURRENT_OUT] = pmbus_fmt_linear,
+ .format[PSC_TEMPERATURE] = pmbus_fmt_linear,
+};
+
+/*
+ * Chip match for the framework's pmbus dev <bus>:<addr> raw I2C
+ * path. Used when the operator selects the chip directly by address
+ * instead of by regulator-name; the framework probes MFR_ID, sees
+ * "MPS" (after the byte reverse helper), and caches mpq8785_info.
+ */
+static const struct pmbus_chip_match mpq8785_match = {
+ .mfr_id = "MPS",
+ .mfr_id_reverse = true,
+ .vendor = "mps",
+ .info = &mpq8785_info,
+};
+
+static const struct pmbus_bit mpq_protection_last_bits[] = {
+ { 1u << 15, "INIT_FAULT" },
+ { 1u << 14, "NVM_CRC_ERROR" },
+ { 1u << 13, "NVM_FAULT" },
+ { 1u << 12, "OC_PHASE_FAULT" },
+ { 1u << 11, "OTP_SELF_FAULT" },
+ { 1u << 9, "SWITCH_PRD_FAULT" },
+ { 1u << 8, "VIN_OV_FAULT" },
+ { 1u << 7, "VOUT_OV_FAULT" },
+ { 1u << 6, "VOUT_UV_FAULT" },
+ { 1u << 5, "OC_TOT_FAULT" },
+ { 1u << 4, "VIN_UVLO_FAULT" },
+ { 1u << 3, "DRMOS_OTP" },
+ { /* sentinel */ }
+};
+
+static int mps_require_active(struct udevice **chip)
+{
+ const struct pmbus_active_dev *act = pmbus_active();
+
+ if (!act) {
+ printf("pmbus mps: no active device. Use 'pmbus dev <bus>:<addr>' first.\n");
+ return CMD_RET_FAILURE;
+ }
+ if (strcmp(act->vendor, "mps") != 0) {
+ printf("pmbus mps: active device is not from vendor 'mps' (got '%s')\n",
+ act->vendor[0] ? act->vendor : "(generic)");
+ return CMD_RET_FAILURE;
+ }
+ if (pmbus_active_get_i2c(chip)) {
+ printf("pmbus mps: cannot reach i2c%d:0x%02x\n",
+ act->bus_seq, act->addr);
+ return CMD_RET_FAILURE;
+ }
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_last(struct udevice *chip)
+{
+ u16 prot_last = 0;
+
+ if (pmbus_read_word(chip, MPS_PROTECTION_LAST, &prot_last)) {
+ printf("pmbus mps: PROTECTION_LAST (FBh) read failed\n");
+ return CMD_RET_FAILURE;
+ }
+ printf("PROTECTION_LAST (FBh) = 0x%04x [", prot_last);
+ pmbus_print_bits(prot_last, mpq_protection_last_bits);
+ printf("] (NVM, survives MPQ power cycle)\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_clear_last(struct udevice *chip)
+{
+ int ret;
+
+ printf("pmbus mps: WARNING, erasing NVM PROTECTION_LAST (FBh) post mortem\n");
+ ret = dm_i2c_write(chip, PMBUS_CLEAR_LAST_FAULT, NULL, 0);
+ if (ret) {
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) write failed (%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) issued; gated by MFR_CFG_EXT bit[6]\n");
+ printf(" (chip silently no ops if F5h bit[6] = 0; verify by re reading FBh)\n");
+ return CMD_RET_SUCCESS;
+}
+
+static int mps_do_clear_force(struct udevice *chip)
+{
+ u8 wp_orig = 0;
+ u16 cfg_orig = 0, cfg_unlocked;
+ int ret, last_rc = 0, rc;
+
+ printf("pmbus mps: FORCE; temporarily lowering WRITE_PROTECT and MFR_CFG_EXT.CLEAR_LAST_EN\n");
+ printf("pmbus mps: WARNING, erasing NVM PROTECTION_LAST (FBh) post mortem\n");
+
+ ret = pmbus_read_byte(chip, PMBUS_WRITE_PROTECT, &wp_orig);
+ if (ret) {
+ printf("pmbus mps: WRITE_PROTECT (10h) read failed (%d), aborting force\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ ret = pmbus_read_word(chip, MPS_MFR_CFG_EXT, &cfg_orig);
+ if (ret) {
+ printf("pmbus mps: MFR_CFG_EXT (F5h) read failed (%d), aborting force\n", ret);
+ return CMD_RET_FAILURE;
+ }
+ printf("pmbus mps: saved WRITE_PROTECT=0x%02x MFR_CFG_EXT=0x%04x\n",
+ wp_orig, cfg_orig);
+
+ if (wp_orig != 0) {
+ u8 wp_open = 0x00;
+
+ ret = dm_i2c_write(chip, PMBUS_WRITE_PROTECT, &wp_open, 1);
+ if (ret) {
+ printf("pmbus mps: WRITE_PROTECT clear failed (%d), chip refuses unlock\n",
+ ret);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ cfg_unlocked = cfg_orig | MPS_MFR_CFG_EXT_CLR_LAST_EN;
+ ret = pmbus_write_word(chip, MPS_MFR_CFG_EXT, cfg_unlocked);
+ if (ret) {
+ printf("pmbus mps: MFR_CFG_EXT <- 0x%04x failed (%d)\n",
+ cfg_unlocked, ret);
+ goto restore_wp;
+ }
+
+ last_rc = dm_i2c_write(chip, PMBUS_CLEAR_LAST_FAULT, NULL, 0);
+ if (last_rc)
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) write failed (%d) even with gate open\n",
+ last_rc);
+ else
+ printf("pmbus mps: CLEAR_LAST_FAULT (08h) issued with MFR_CFG_EXT bit[6]=1, PROTECTION_LAST should now read 0x0000\n");
+
+ rc = pmbus_write_word(chip, MPS_MFR_CFG_EXT, cfg_orig);
+ if (rc)
+ printf("pmbus mps: MFR_CFG_EXT restore failed (%d), gate may stay open until POR\n",
+ rc);
+
+restore_wp:
+ if (wp_orig != 0) {
+ rc = dm_i2c_write(chip, PMBUS_WRITE_PROTECT, &wp_orig, 1);
+ if (rc)
+ printf("pmbus mps: WRITE_PROTECT restore failed (%d), chip stays unlocked until POR\n",
+ rc);
+ }
+
+ return (ret || last_rc) ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
+
+static int mps_vendor_handler(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct udevice *chip;
+ int rc;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ rc = mps_require_active(&chip);
+ if (rc)
+ return rc;
+
+ if (!strcmp(argv[1], "last") && argc == 2)
+ return mps_do_last(chip);
+ if (!strcmp(argv[1], "clear") && argc >= 3) {
+ if (!strcmp(argv[2], "last"))
+ return mps_do_clear_last(chip);
+ if (!strcmp(argv[2], "force"))
+ return mps_do_clear_force(chip);
+ }
+ return CMD_RET_USAGE;
+}
+
+static const struct pmbus_vendor_op mps_vendor_op = {
+ .vendor = "mps",
+ .handler = mps_vendor_handler,
+ .help = "pmbus mps last : read MPS PROTECTION_LAST (FBh)\n"
+ "pmbus mps clear last : issue MPS CLEAR_LAST_FAULT (08h) (DESTRUCTIVE)\n"
+ "pmbus mps clear force : force clear via MFR_CFG_EXT bit[6] (DESTRUCTIVE)\n",
+};
+
+static void mpq8785_identify_vout(struct udevice *i2c_dev)
+{
+ enum pmbus_data_format fmt;
+
+ /*
+ * Let the shared helper read VOUT_MODE and pick the base format
+ * (the single source of truth for the bit layout). The MPS quirk:
+ * this family encodes VOUT in DIRECT with m=64 R=1 whenever
+ * VOUT_MODE reports VID *or* DIRECT -- override the helper's
+ * generic DIRECT m=1 / VID-unwired result in those two modes.
+ * LINEAR and IEEE754 keep the helper's selection unchanged.
+ */
+ fmt = pmbus_regulator_identify_vout(i2c_dev, &mpq8785_info);
+ if (fmt == pmbus_fmt_vid || fmt == pmbus_fmt_direct) {
+ mpq8785_info.format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct;
+ mpq8785_info.m[PSC_VOLTAGE_OUT] = 64;
+ mpq8785_info.b[PSC_VOLTAGE_OUT] = 0;
+ mpq8785_info.R[PSC_VOLTAGE_OUT] = 1;
+ }
+}
+
+/*
+ * The MPQ8785 datasheet revision letter changes which window the
+ * analog ADDR_VBOOT level resolves to. Boards have been observed at
+ * 0x10 (later die rev) versus the 0x20 the original driver assumed.
+ * If the DT declared address fails the MFR_ID probe at probe time,
+ * walk the three documented windows looking for an MPS responder.
+ *
+ * Each window covers 16 consecutive 7 bit I2C addresses; the low
+ * nibble selects the chip's MFR_ADDR_PMBUS slot within the window.
+ */
+#define MPS_ADDR_VBOOT_WINDOW_SIZE 16
+static const u8 mps_addr_window_starts[] = { 0x10, 0x20, 0x60 };
+
+static int mpq8785_probe_addr(struct udevice *bus, u8 addr,
+ struct udevice **chip_out)
+{
+ char id[PMBUS_MFR_STRING_MAX] = "";
+ struct udevice *chip;
+ int ret;
+
+ ret = i2c_get_chip(bus, addr, 1, &chip);
+ if (ret)
+ return ret;
+ ret = pmbus_read_string(chip, PMBUS_MFR_ID, id, sizeof(id), true);
+ if (ret < 0)
+ return ret;
+ if (strncmp(id, "MPS", 3) != 0)
+ return -ENODEV;
+ *chip_out = chip;
+ return 0;
+}
+
+static int mpq8785_scan_windows(struct udevice *bus, u8 *found_addr,
+ struct udevice **chip_out)
+{
+ unsigned int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(mps_addr_window_starts); i++) {
+ for (j = 0; j < MPS_ADDR_VBOOT_WINDOW_SIZE; j++) {
+ u8 addr = mps_addr_window_starts[i] + j;
+
+ if (mpq8785_probe_addr(bus, addr, chip_out) == 0) {
+ *found_addr = addr;
+ return 0;
+ }
+ }
+ }
+ return -ENODEV;
+}
+
+static struct pmbus_driver_info *mpq8785_pick_info(enum mpq_chip_id chip_id)
+{
+ switch (chip_id) {
+ case MPQ_MPM3695:
+ case MPQ_MPM3695_25:
+ case MPQ_MPM82504:
+ return &mpm82504_info;
+ case MPQ_MPQ8785:
+ default:
+ return &mpq8785_info;
+ }
+}
+
+static int mpq8785_probe(struct udevice *dev)
+{
+ enum mpq_chip_id chip_id = (enum mpq_chip_id)dev_get_driver_data(dev);
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ struct pmbus_driver_info *info = mpq8785_pick_info(chip_id);
+ static bool match_registered;
+ static bool vendor_registered;
+ u32 fb_div;
+ int ret;
+
+ ret = pmbus_regulator_probe_common(dev, info, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Verify the chip answers MFR_ID="MPS" at the DT declared
+ * address. If it doesn't, walk the documented ADDR_VBOOT windows
+ * looking for it (a die rev address shift). On a hit, replace
+ * priv->i2c_dev with the discovered chip handle and continue.
+ */
+ {
+ char id[PMBUS_MFR_STRING_MAX] = "";
+
+ ret = pmbus_read_string(priv->i2c_dev, PMBUS_MFR_ID, id,
+ sizeof(id), true);
+ if (ret < 0 || strncmp(id, "MPS", 3) != 0) {
+ struct udevice *bus = dev_get_parent(dev);
+ struct udevice *promoted;
+ u8 found = 0;
+
+ if (mpq8785_scan_windows(bus, &found, &promoted) == 0) {
+ printf("MPQ8785: DT addr 0x%02x silent, auto promoted to 0x%02x\n",
+ (unsigned int)dev_read_addr(dev), found);
+ priv->i2c_dev = promoted;
+ } else {
+ printf("MPQ8785: no MPS responder found in 0x10..0x1f / 0x20..0x2f / 0x60..0x6f\n");
+ return -ENODEV;
+ }
+ }
+ }
+
+ /* MPQ8785 specific: refine VOUT format from VOUT_MODE. */
+ if (chip_id == MPQ_MPQ8785)
+ mpq8785_identify_vout(priv->i2c_dev);
+
+ /* Apply mps,vout-fb-divider-ratio-permille if present in DT. */
+ fb_div = dev_read_u32_default(dev, "mps,vout-fb-divider-ratio-permille", 0);
+ if (fb_div) {
+ ret = pmbus_regulator_apply_voltage_scale(dev, fb_div);
+ if (ret) {
+ printf("MPQ8785: VOUT_SCALE_LOOP write failed (%d)\n", ret);
+ return ret;
+ }
+ }
+
+ /*
+ * Register the chip match and the MPS vendor handler exactly
+ * once across all bound MPS regulators (a board could legally
+ * carry several). Both registries are global and idempotent
+ * matches return -ENOSPC, so the static guards keep things
+ * tidy.
+ */
+ if (!match_registered) {
+ if (pmbus_register_chip(&mpq8785_match) == 0)
+ match_registered = true;
+ }
+ if (!vendor_registered) {
+ if (pmbus_register_vendor_handler(&mps_vendor_op) == 0)
+ vendor_registered = true;
+ }
+ return 0;
+}
+
+static const struct udevice_id mpq8785_ids[] = {
+ { .compatible = "mps,mpm3695", .data = MPQ_MPM3695 },
+ { .compatible = "mps,mpm3695-25", .data = MPQ_MPM3695_25 },
+ { .compatible = "mps,mpm82504", .data = MPQ_MPM82504 },
+ { .compatible = "mps,mpq8785", .data = MPQ_MPQ8785 },
+ { }
+};
+
+U_BOOT_DRIVER(mpq8785_regulator) = {
+ .name = "mpq8785_regulator",
+ .id = UCLASS_REGULATOR,
+ .of_match = mpq8785_ids,
+ .probe = mpq8785_probe,
+ .ops = &pmbus_regulator_ops,
+ .priv_auto = sizeof(struct pmbus_regulator_priv),
+};
diff --git a/drivers/power/regulator/pfuze100.c b/drivers/power/regulator/pfuze100.c
index f864b1d8834..57af16cfbb9 100644
--- a/drivers/power/regulator/pfuze100.c
+++ b/drivers/power/regulator/pfuze100.c
@@ -13,29 +13,43 @@
#include <power/regulator.h>
#include <power/pfuze100_pmic.h>
+struct pfuze100_high_volt_desc {
+ unsigned int uV_step;
+ unsigned int min_uV;
+};
+
/**
* struct pfuze100_regulator_desc - regulator descriptor
*
* @name: Identify name for the regulator.
* @type: Indicates the regulator type.
* @uV_step: Voltage increase for each selector.
+ * @min_uV: Indicates the minimal voltage supported.
* @vsel_reg: Register for adjust regulator voltage for normal.
* @vsel_mask: Mask bit for setting regulator voltage for normal.
* @stby_reg: Register for adjust regulator voltage for standby.
* @stby_mask: Mask bit for setting regulator voltage for standby.
* @volt_table: Voltage mapping table (if table based mapping).
* @voltage: Current voltage for REGULATOR_TYPE_FIXED type regulator.
+ * @high_volt_mask: Low or High Output voltage mode mask
+ * @high_volt_desc: High Output voltage description
+ * @b: Some regulators could work together to provide one output when working
+ * in single phase or dual phase mode.
*/
struct pfuze100_regulator_desc {
char *name;
enum regulator_type type;
unsigned int uV_step;
+ unsigned int min_uV;
unsigned int vsel_reg;
unsigned int vsel_mask;
unsigned int stby_reg;
unsigned int stby_mask;
unsigned int *volt_table;
unsigned int voltage;
+ unsigned int high_volt_mask;
+ struct pfuze100_high_volt_desc *high_volt_desc;
+ struct pfuze100_regulator_desc *b;
};
/**
@@ -54,15 +68,33 @@ struct pfuze100_regulator_plat {
.voltage = (vol), \
}
-#define PFUZE100_SW_REG(_name, base, step) \
+#define PFUZE100_SW_REG(_name, base, step, min, desc, mask) \
+ { \
+ .name = #_name, \
+ .type = REGULATOR_TYPE_BUCK, \
+ .uV_step = (step), \
+ .min_uV = (min), \
+ .vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
+ .vsel_mask = 0x3F, \
+ .stby_reg = (base) + PFUZE100_STBY_OFFSET, \
+ .stby_mask = 0x3F, \
+ .high_volt_desc = (desc), \
+ .high_volt_mask = (mask), \
+ }
+
+#define PFUZE100_SWAB_REG(_name, base, step, min, volt_desc, mask, desc) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x3F, \
.stby_reg = (base) + PFUZE100_STBY_OFFSET, \
.stby_mask = 0x3F, \
+ .high_volt_desc = (volt_desc), \
+ .high_volt_mask = (mask), \
+ .b = (desc), \
}
#define PFUZE100_SWB_REG(_name, base, mask, step, voltages) \
@@ -84,32 +116,35 @@ struct pfuze100_regulator_plat {
.volt_table = (voltages), \
}
-#define PFUZE100_VGEN_REG(_name, base, step) \
+#define PFUZE100_VGEN_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base), \
.vsel_mask = 0xF, \
.stby_reg = (base), \
.stby_mask = 0x20, \
}
-#define PFUZE3000_VCC_REG(_name, base, step) \
+#define PFUZE3000_VCC_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_LDO, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base), \
.vsel_mask = 0x3, \
.stby_reg = (base), \
.stby_mask = 0x20, \
}
-#define PFUZE3000_SW1_REG(_name, base, step) \
+#define PFUZE3000_SW1_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
+ .min_uV = (min), \
.uV_step = (step), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0x1F, \
@@ -128,11 +163,12 @@ struct pfuze100_regulator_plat {
.stby_mask = 0x7, \
}
-#define PFUZE3000_SW3_REG(_name, base, step) \
+#define PFUZE3000_SW3_REG(_name, base, step, min) \
{ \
.name = #_name, \
.type = REGULATOR_TYPE_BUCK, \
.uV_step = (step), \
+ .min_uV = (min), \
.vsel_reg = (base) + PFUZE100_VOL_OFFSET, \
.vsel_mask = 0xF, \
.stby_reg = (base) + PFUZE100_STBY_OFFSET, \
@@ -155,57 +191,66 @@ static unsigned int pfuze3000_sw2lo[] = {
1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000
};
+static struct pfuze100_high_volt_desc high_desc = {
+ .min_uV = 800000,
+ .uV_step = 50000,
+};
+
/* PFUZE100 */
static struct pfuze100_regulator_desc pfuze100_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000),
- PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000),
- PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw1c, PFUZE100_SW1CVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SWAB_REG(sw3ab, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6),
+ &pfuze100_regulators[5]),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw4, PFUZE100_SW4VOL, 25000, 400000, &high_desc, BIT(6)),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
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, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000, 1800000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000, 1800000),
};
/* PFUZE200 */
static struct pfuze100_regulator_desc pfuze200_regulators[] = {
- PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000),
- PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000),
- PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000),
- PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000),
+ PFUZE100_SW_REG(sw1ab, PFUZE100_SW1ABVOL, 25000, 300000, NULL, 0),
+ PFUZE100_SW_REG(sw2, PFUZE100_SW2VOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SW_REG(sw3a, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6)),
+ PFUZE100_SWAB_REG(sw3ab, PFUZE100_SW3AVOL, 25000, 400000, &high_desc, BIT(6),
+ &pfuze200_regulators[4]),
+ PFUZE100_SW_REG(sw3b, PFUZE100_SW3BVOL, 25000, 400000, &high_desc, BIT(6)),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
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, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen2, PFUZE100_VGEN2VOL, 50000, 800000),
+ PFUZE100_VGEN_REG(vgen3, PFUZE100_VGEN3VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen4, PFUZE100_VGEN4VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen5, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vgen6, PFUZE100_VGEN6VOL, 100000, 1800000),
};
/* 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, 25000, 700000),
+ PFUZE3000_SW1_REG(sw1b, PFUZE100_SW1CVOL, 25000, 700000),
PFUZE100_SWB_REG(sw2, PFUZE100_SW2VOL, 0x7, 50000, pfuze3000_sw2lo),
- PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000),
+ PFUZE3000_SW3_REG(sw3, PFUZE100_SW3AVOL, 50000, 900000),
PFUZE100_SWB_REG(swbst, PFUZE100_SWBSTCON1, 0x3, 50000, pfuze100_swbst),
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, 100000, 1800000),
+ PFUZE100_VGEN_REG(vldo2, PFUZE100_VGEN2VOL, 50000, 800000),
+ PFUZE3000_VCC_REG(vccsd, PFUZE100_VGEN3VOL, 150000, 2850000),
+ PFUZE3000_VCC_REG(v33, PFUZE100_VGEN4VOL, 150000, 2850000),
+ PFUZE100_VGEN_REG(vldo3, PFUZE100_VGEN5VOL, 100000, 1800000),
+ PFUZE100_VGEN_REG(vldo4, PFUZE100_VGEN6VOL, 100000, 1800000),
};
#define MODE(_id, _val, _name) { \
@@ -357,6 +402,16 @@ static int pfuze100_regulator_mode(struct udevice *dev, int op, int *opmode)
desc->vsel_reg + PFUZE100_MODE_OFFSET,
SW_MODE_MASK,
*opmode << SW_MODE_SHIFT);
+ if (val)
+ return val;
+
+ if (desc->b) {
+ desc = desc->b;
+ val = pmic_clrsetbits(dev->parent,
+ desc->vsel_reg + PFUZE100_MODE_OFFSET,
+ SW_MODE_MASK,
+ *opmode << SW_MODE_SHIFT);
+ }
} else if (desc->type == REGULATOR_TYPE_LDO) {
val = pmic_clrsetbits(dev->parent, desc->vsel_reg,
@@ -435,8 +490,8 @@ static int pfuze100_regulator_enable(struct udevice *dev, int op, bool *enable)
static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
{
- int i;
- int val;
+ int i, ret;
+ int val, min_uV, uV_step;
struct pfuze100_regulator_plat *plat = dev_get_plat(dev);
struct pfuze100_regulator_desc *desc = plat->desc;
struct dm_regulator_uclass_plat *uc_pdata =
@@ -460,8 +515,15 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
val = pmic_reg_read(dev->parent, desc->vsel_reg);
if (val < 0)
return val;
+ if (desc->high_volt_mask && (val & desc->high_volt_mask)) {
+ min_uV = desc->high_volt_desc->min_uV;
+ uV_step = desc->high_volt_desc->uV_step;
+ } else {
+ min_uV = desc->min_uV ?: uc_pdata->min_uV;
+ uV_step = desc->uV_step;
+ }
val &= desc->vsel_mask;
- *uV = uc_pdata->min_uV + (int)val * desc->uV_step;
+ *uV = min_uV + (int)val * uV_step;
}
return 0;
@@ -487,9 +549,29 @@ static int pfuze100_regulator_val(struct udevice *dev, int op, int *uV)
debug("Need to provide min_uV in dts.\n");
return -EINVAL;
}
- return pmic_clrsetbits(dev->parent, desc->vsel_reg,
- desc->vsel_mask,
- (*uV - uc_pdata->min_uV) / desc->uV_step);
+ val = pmic_reg_read(dev->parent, desc->vsel_reg);
+ if (val < 0)
+ return val;
+ if (desc->high_volt_mask && (val & desc->high_volt_mask)) {
+ min_uV = desc->high_volt_desc->min_uV;
+ uV_step = desc->high_volt_desc->uV_step;
+ } else {
+ min_uV = desc->min_uV ?: uc_pdata->min_uV;
+ uV_step = desc->uV_step;
+ }
+
+ ret = pmic_clrsetbits(dev->parent, desc->vsel_reg,
+ desc->vsel_mask, (*uV - min_uV) / uV_step);
+ if (ret)
+ return ret;
+
+ if (desc->b) {
+ desc = desc->b;
+ ret = pmic_clrsetbits(dev->parent, desc->vsel_reg,
+ desc->vsel_mask, (*uV - min_uV) / uV_step);
+ if (ret)
+ return ret;
+ }
}
return 0;
diff --git a/drivers/power/regulator/pmbus_generic.c b/drivers/power/regulator/pmbus_generic.c
new file mode 100644
index 00000000000..6ba50ce08f8
--- /dev/null
+++ b/drivers/power/regulator/pmbus_generic.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Generic PMBus 1.x compatible voltage regulator driver.
+ *
+ * Catch all driver bound to compatible = "pmbus" for chips that have
+ * no per chip driver under drivers/power/regulator/. The probe path
+ * detects the VOUT numeric format from VOUT_MODE bits[7:5]:
+ *
+ * - 0 LINEAR16 with the exponent supplied via VOUT_MODE bits[4:0]
+ * - 1 VID; mapped to pmbus_fmt_vid (decoder returns 0 today; per
+ * chip driver still required to plug a VID table)
+ * - 2 DIRECT; default coefficients m=1, b=0, R=0 (per chip
+ * coefficients arrive via PMBUS_QUERY / PMBUS_COEFFICIENTS,
+ * not yet consumed by U-Boot; values may need a per chip
+ * driver if telemetry numbers are wrong)
+ * - 3 IEEE754; mapped to pmbus_fmt_ieee754 (decoder returns 0
+ * today; per chip driver required)
+ *
+ * Other sensor classes (VIN, IIN, IOUT, TEMPERATURE) default to
+ * LINEAR which is the spec baseline for compliant chips. If an
+ * operator sees wrong telemetry numbers on this driver, the answer
+ * is to write a per chip driver with the correct format[] / m / b / R.
+ *
+ * Adapted in spirit from linux/drivers/hwmon/pmbus/pmbus.c (the
+ * kernel's generic probe driver). The U-Boot version drops the
+ * page count auto detection (most generic compliant parts are
+ * single rail; multi rail chips are quirky enough to need a per
+ * chip driver) and the kernel hwmon publication layers.
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+struct pmbus_generic_priv {
+ struct pmbus_regulator_priv base; /* must be first */
+ struct pmbus_driver_info info;
+};
+
+static int pmbus_generic_probe(struct udevice *dev)
+{
+ struct pmbus_generic_priv *gpriv = dev_get_priv(dev);
+ struct pmbus_driver_info *info = &gpriv->info;
+ enum pmbus_sensor_classes c;
+ int ret;
+
+ info->pages = 1;
+ for (c = 0; c < PSC_NUM_CLASSES; c++) {
+ info->format[c] = pmbus_fmt_linear;
+ info->m[c] = 0;
+ info->b[c] = 0;
+ info->R[c] = 0;
+ }
+
+ ret = pmbus_regulator_probe_common(dev, info, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Avoid reading non supported pages to avoid device's sticky
+ * status.
+ */
+ info->pages = dev_read_u32_default(dev, "pmbus,num-pages", 1);
+ if (info->pages < 1)
+ info->pages = 1;
+
+ pmbus_regulator_identify_vout(gpriv->base.i2c_dev, info);
+
+ return 0;
+}
+
+static const struct udevice_id pmbus_generic_ids[] = {
+ { .compatible = "pmbus" },
+ { }
+};
+
+U_BOOT_DRIVER(pmbus_generic_regulator) = {
+ .name = "pmbus_generic_regulator",
+ .id = UCLASS_REGULATOR,
+ .of_match = pmbus_generic_ids,
+ .probe = pmbus_generic_probe,
+ .ops = &pmbus_regulator_ops,
+ .priv_auto = sizeof(struct pmbus_generic_priv),
+};
diff --git a/drivers/power/regulator/pmbus_helper.c b/drivers/power/regulator/pmbus_helper.c
new file mode 100644
index 00000000000..4763470442d
--- /dev/null
+++ b/drivers/power/regulator/pmbus_helper.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Shared UCLASS_REGULATOR operations over the PMBus 1.x framework.
+ * See pmbus_helper.h for the API surface and doc/develop/pmbus.rst
+ * for the porting guide.
+ *
+ * No code in this file may reference a specific chip family or
+ * board. Chip specific quirks (vendor registers, VID coercion,
+ * ADDR pin auto promotion, byte reversed MFR strings, etc.) belong
+ * in the per chip driver under drivers/power/regulator/<chip>.c.
+ */
+
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <i2c.h>
+#include <log.h>
+#include <pmbus.h>
+#include <vsprintf.h>
+#include <linux/types.h>
+#include <power/regulator.h>
+
+#include "pmbus_helper.h"
+
+static int pmbus_regulator_select_page(struct pmbus_regulator_priv *priv)
+{
+ u8 p;
+
+ if (priv->page <= 0)
+ return 0;
+ p = (u8)priv->page;
+ return dm_i2c_write(priv->i2c_dev, PMBUS_PAGE, &p, 1);
+}
+
+static int pmbus_regulator_get_value(struct udevice *dev)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 vout_mode = 0;
+ u16 raw = 0;
+ s64 uv;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ pmbus_read_byte(priv->i2c_dev, PMBUS_VOUT_MODE, &vout_mode);
+ if (pmbus_read_word(priv->i2c_dev, PMBUS_READ_VOUT, &raw))
+ return -EIO;
+
+ if (priv->info)
+ uv = pmbus_reg2data(priv->info, PSC_VOLTAGE_OUT, raw, vout_mode);
+ else
+ uv = pmbus_reg2data_linear16(raw, vout_mode);
+
+ if (uv > INT_MAX)
+ uv = INT_MAX;
+ if (uv < INT_MIN)
+ uv = INT_MIN;
+ return (int)uv;
+}
+
+static int pmbus_regulator_set_value(struct udevice *dev, int uV)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 vout_mode = 0;
+ u8 buf[2];
+ u16 raw;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_VOUT_MODE, &vout_mode))
+ return -EIO;
+
+ /*
+ * Dispatch on the chip's VOUT_MODE selector. LINEAR16 and DIRECT
+ * are wired today; VID and IEEE754 return -ENOSYS until their
+ * encoders land. For DIRECT, the m / b / R triple comes from the
+ * chip's pmbus_driver_info[PSC_VOLTAGE_OUT]; if the per chip
+ * driver did not populate them, the encoder cannot run.
+ */
+ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) {
+ case PB_VOUT_MODE_LINEAR:
+ raw = pmbus_data2reg_linear16((s64)uV, vout_mode);
+ break;
+ case PB_VOUT_MODE_DIRECT:
+ if (!priv->info ||
+ priv->info->format[PSC_VOLTAGE_OUT] != pmbus_fmt_direct)
+ return -ENODATA;
+ raw = pmbus_data2reg_direct((s64)uV,
+ priv->info->m[PSC_VOLTAGE_OUT],
+ priv->info->b[PSC_VOLTAGE_OUT],
+ priv->info->R[PSC_VOLTAGE_OUT]);
+ break;
+ default:
+ return -ENOSYS;
+ }
+
+ buf[0] = (u8)(raw & 0xff);
+ buf[1] = (u8)((raw >> 8) & 0xff);
+ return dm_i2c_write(priv->i2c_dev, PMBUS_VOUT_COMMAND, buf, 2);
+}
+
+static int pmbus_regulator_get_enable(struct udevice *dev)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 op = 0;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_OPERATION, &op))
+ return -EIO;
+ return (op & PB_OPERATION_ON) ? 1 : 0;
+}
+
+static int pmbus_regulator_set_enable(struct udevice *dev, bool enable)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 op = 0;
+ int ret;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_byte(priv->i2c_dev, PMBUS_OPERATION, &op))
+ return -EIO;
+
+ if (enable)
+ op |= PB_OPERATION_ON;
+ else
+ op &= (u8)~PB_OPERATION_ON;
+ return dm_i2c_write(priv->i2c_dev, PMBUS_OPERATION, &op, 1);
+}
+
+const struct dm_regulator_ops pmbus_regulator_ops = {
+ .get_value = pmbus_regulator_get_value,
+ .set_value = pmbus_regulator_set_value,
+ .get_enable = pmbus_regulator_get_enable,
+ .set_enable = pmbus_regulator_set_enable,
+};
+
+int pmbus_regulator_read_temp(struct udevice *reg_dev, int *temp_mc)
+{
+ struct pmbus_regulator_priv *priv;
+ u16 raw = 0;
+ s64 udeg;
+ int ret;
+
+ if (!reg_dev || !temp_mc)
+ return -EINVAL;
+ priv = dev_get_priv(reg_dev);
+ if (!priv || !priv->i2c_dev)
+ return -ENODEV;
+
+ ret = pmbus_regulator_select_page(priv);
+ if (ret)
+ return ret;
+ if (pmbus_read_word(priv->i2c_dev, PMBUS_READ_TEMPERATURE_1, &raw))
+ return -EIO;
+
+ /*
+ * vout_mode is meaningless for the temperature class. With a
+ * chip info record the dispatcher honours its per-class format
+ * (DIRECT m/b/R for MPS, LINEAR11 for spec-compliant parts);
+ * without one, fall back to the PMBus 1.x standard LINEAR11.
+ */
+ if (priv->info)
+ udeg = pmbus_reg2data(priv->info, PSC_TEMPERATURE, raw, 0);
+ else
+ udeg = pmbus_reg2data_linear11(raw);
+
+ *temp_mc = (int)(udeg / 1000);
+ return 0;
+}
+
+enum pmbus_data_format
+pmbus_regulator_identify_vout(struct udevice *i2c_dev,
+ struct pmbus_driver_info *info)
+{
+ u8 vout_mode = 0;
+
+ if (pmbus_read_byte(i2c_dev, PMBUS_VOUT_MODE, &vout_mode))
+ return info->format[PSC_VOLTAGE_OUT];
+
+ switch (vout_mode & PB_VOUT_MODE_MODE_MASK) {
+ case PB_VOUT_MODE_LINEAR:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_linear;
+ break;
+ case PB_VOUT_MODE_VID:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_vid;
+ break;
+ case PB_VOUT_MODE_DIRECT:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_direct;
+ info->m[PSC_VOLTAGE_OUT] = 1;
+ info->b[PSC_VOLTAGE_OUT] = 0;
+ info->R[PSC_VOLTAGE_OUT] = 0;
+ break;
+ case PB_VOUT_MODE_IEEE754:
+ info->format[PSC_VOLTAGE_OUT] = pmbus_fmt_ieee754;
+ break;
+ default:
+ break;
+ }
+ return info->format[PSC_VOLTAGE_OUT];
+}
+
+const struct pmbus_driver_info *pmbus_regulator_info_by_addr(int bus_seq,
+ u8 addr)
+{
+ struct uclass *uc;
+ struct udevice *r;
+
+ if (uclass_get(UCLASS_REGULATOR, &uc))
+ return NULL;
+
+ uclass_foreach_dev(r, uc) {
+ struct udevice *parent = dev_get_parent(r);
+ struct pmbus_regulator_priv *priv;
+ int ra;
+
+ if (!parent || device_get_uclass_id(parent) != UCLASS_I2C)
+ continue;
+ if (dev_seq(parent) != bus_seq)
+ continue;
+ ra = dev_read_addr(r);
+ if (ra < 0 || (u8)ra != addr)
+ continue;
+
+ /*
+ * Address matches. Only chips driven through this helper
+ * carry a pmbus_regulator_priv at the head of their priv;
+ * identify them by their shared ops vector so we never
+ * misread a foreign regulator's private layout.
+ */
+ if (!r->driver || r->driver->ops != &pmbus_regulator_ops)
+ return NULL;
+ if (device_probe(r))
+ return NULL;
+ priv = dev_get_priv(r);
+ return priv ? priv->info : NULL;
+ }
+ return NULL;
+}
+
+/*
+ * Spawn the generic UCLASS_THERMAL companion (drivers/thermal/
+ * pmbus_thermal.c) as a child of this regulator so READ_TEMPERATURE_1
+ * is reachable through the standard `temperature list` / `temperature
+ * get` interface. Named "<regulator-name>-temp" so several PMBus rails
+ * on one board produce distinct, descriptive device names. Failure is
+ * non-fatal: the chip still works as a UCLASS_REGULATOR.
+ */
+static void pmbus_regulator_bind_thermal(struct udevice *dev)
+{
+ struct udevice *therm;
+ const char *rname;
+ char name[48];
+
+ if (!IS_ENABLED(CONFIG_PMBUS_THERMAL))
+ return;
+ if (device_bind_driver(dev, "pmbus_thermal", "pmbus-temp", &therm))
+ return;
+ rname = dev_read_string(dev, "regulator-name");
+ snprintf(name, sizeof(name), "%s-temp", rname ? rname : dev->name);
+ device_set_name(therm, name);
+}
+
+int pmbus_regulator_probe_common(struct udevice *dev,
+ const struct pmbus_driver_info *info,
+ int page)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ int chip_addr;
+ int ret;
+
+ chip_addr = dev_read_addr(dev);
+ if (chip_addr < 0)
+ return -EINVAL;
+
+ ret = i2c_get_chip(dev_get_parent(dev), (u32)chip_addr, 1, &priv->i2c_dev);
+ if (ret)
+ return ret;
+
+ priv->info = info;
+ priv->page = page;
+
+ if (page > 0) {
+ u8 p = (u8)page;
+
+ ret = dm_i2c_write(priv->i2c_dev, PMBUS_PAGE, &p, 1);
+ if (ret)
+ return ret;
+ }
+
+ pmbus_regulator_bind_thermal(dev);
+ return 0;
+}
+
+int pmbus_regulator_apply_voltage_scale(struct udevice *dev,
+ u32 fb_divider_permille)
+{
+ struct pmbus_regulator_priv *priv = dev_get_priv(dev);
+ u8 buf[2];
+
+ if (fb_divider_permille == 0)
+ return 0;
+ buf[0] = (u8)(fb_divider_permille & 0xff);
+ buf[1] = (u8)((fb_divider_permille >> 8) & 0xff);
+ return dm_i2c_write(priv->i2c_dev, PMBUS_VOUT_SCALE_LOOP, buf, 2);
+}
diff --git a/drivers/power/regulator/pmbus_helper.h b/drivers/power/regulator/pmbus_helper.h
new file mode 100644
index 00000000000..82f263cd513
--- /dev/null
+++ b/drivers/power/regulator/pmbus_helper.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2026 Free Mobile, Vincent Jardin
+ *
+ * Shared UCLASS_REGULATOR ops for PMBus 1.x voltage regulator chips.
+ *
+ * Per chip drivers under drivers/power/regulator/<chip>.c bind a
+ * vendor,chip compatible from DT and call pmbus_regulator_probe_common()
+ * in their .probe. They install pmbus_regulator_ops as the .ops vector;
+ * the helper handles VOUT_MODE / READ_VOUT / VOUT_COMMAND / OPERATION
+ * via the tree level <pmbus.h> framework.
+ *
+ * Per chip drivers retain control of identify hooks (VOUT_MODE based
+ * format selection), chip specific quirks (vendor registers, ADDR pin
+ * auto promotion), and DT property handling (e.g. MPS
+ * mps,vout-fb-divider-ratio-permille).
+ */
+
+#ifndef _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_
+#define _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_
+
+#include <linux/types.h>
+#include <pmbus.h>
+
+struct udevice;
+struct dm_regulator_ops;
+
+/*
+ * Per chip private state. The first field of every per chip driver's
+ * priv_auto must be (or contain at offset 0) a struct
+ * pmbus_regulator_priv so the shared ops vector can recover it via
+ * dev_get_priv(dev).
+ *
+ * i2c_dev chip handle obtained from dev->parent at probe time
+ * (the parent must be a UCLASS_I2C bus).
+ * page PMBUS_PAGE selector for multi rail chips. Single rail
+ * chips set page = 0; the helper writes PMBUS_PAGE only
+ * when page > 0 to avoid wasted bus traffic on single
+ * rail parts.
+ * info pointer to the chip's pmbus_driver_info; consumed by
+ * pmbus_reg2data() / pmbus_data2reg_linear16() to pick
+ * the right format[] / m / b / R coefficients.
+ */
+struct pmbus_regulator_priv {
+ struct udevice *i2c_dev;
+ int page;
+ const struct pmbus_driver_info *info;
+};
+
+extern const struct dm_regulator_ops pmbus_regulator_ops;
+
+/*
+ * Per chip probe glue. Reads `reg` from DT, gets the I2C chip handle
+ * from dev->parent, populates priv->i2c_dev / page / info, and writes
+ * PMBUS_PAGE if page > 0. Per chip drivers call this in their .probe
+ * before any chip specific identification.
+ */
+int pmbus_regulator_probe_common(struct udevice *dev,
+ const struct pmbus_driver_info *info,
+ int page);
+
+/*
+ * Optional helper for per chip drivers that honour an external
+ * feedback divider DT property (e.g. MPS mps,vout-fb-divider-ratio-
+ * permille). Writes the supplied ratio to PMBUS_VOUT_SCALE_LOOP at
+ * probe time. fb_divider_permille == 0 leaves the chip default.
+ */
+int pmbus_regulator_apply_voltage_scale(struct udevice *dev,
+ u32 fb_divider_permille);
+
+/*
+ * Read PMBUS_VOUT_MODE and set info->format[PSC_VOLTAGE_OUT] from its
+ * mode selector bits[7:5] per PMBus 1.3 Part II sec 8.3:
+ * LINEAR -> pmbus_fmt_linear
+ * VID -> pmbus_fmt_vid
+ * DIRECT -> pmbus_fmt_direct (default coefficients m=1, b=0, R=0)
+ * IEEE754 -> pmbus_fmt_ieee754
+ *
+ * The single place that knows the VOUT_MODE bit layout; both the
+ * generic regulator and per chip drivers call it so they never
+ * re-implement the switch. Returns the selected format so a chip
+ * driver can post-adjust a quirk (e.g. MPS encodes VOUT in DIRECT
+ * with m=64 R=1 even when VOUT_MODE reports VID). On a VOUT_MODE read
+ * failure the format is left unchanged and the prior value is returned.
+ */
+enum pmbus_data_format
+pmbus_regulator_identify_vout(struct udevice *i2c_dev,
+ struct pmbus_driver_info *info);
+
+#endif /* _DRIVERS_POWER_REGULATOR_PMBUS_HELPER_H_ */
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c
index 3f0f1845469..c458f227bed 100644
--- a/drivers/power/regulator/qcom-rpmh-regulator.c
+++ b/drivers/power/regulator/qcom-rpmh-regulator.c
@@ -5,6 +5,7 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/err.h>
+#include <dm.h>
#include <dm/device_compat.h>
#include <dm/device.h>
#include <dm/devres.h>
@@ -295,6 +296,56 @@ static int rpmh_regulator_vrm_get_value(struct udevice *rdev)
return vreg->uv;
}
+static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
+ unsigned int mode, bool bypassed)
+{
+ struct tcs_cmd cmd = {
+ .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
+ };
+ struct dm_regulator_mode *pmic_mode;
+ int i;
+
+ if (mode > REGULATOR_MODE_HPM)
+ return -EINVAL;
+
+ for (i = 0; i < vreg->hw_data->n_modes; i++) {
+ pmic_mode = &vreg->hw_data->pmic_mode_map[i];
+ if (pmic_mode->id == mode)
+ break;
+ }
+ if (pmic_mode->id != mode) {
+ printf("Invalid mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ if (bypassed)
+ // XXX: should have a version check for PMIC4 but we don't have any yet
+ // and we don't use bypass mode
+ cmd.data = PMIC5_BOB_MODE_PASS;
+ else
+ cmd.data = pmic_mode->register_value;
+
+ return rpmh_regulator_send_request(vreg, &cmd, true);
+}
+
+static int rpmh_regulator_vrm_set_mode(struct udevice *rdev,
+ int mode)
+{
+ struct rpmh_vreg *vreg = dev_get_priv(rdev);
+ int ret;
+
+ debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode);
+
+ if (mode == vreg->mode)
+ return 0;
+
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
+ if (!ret)
+ vreg->mode = mode;
+
+ return ret;
+}
+
static int rpmh_regulator_is_enabled(struct udevice *rdev)
{
struct rpmh_vreg *vreg = dev_get_priv(rdev);
@@ -331,6 +382,12 @@ static int rpmh_regulator_set_enable_state(struct udevice *rdev,
debug("%s: set_enable %d (current %d)\n", rdev->name, enable,
vreg->enabled);
+ if (vreg->mode != -EINVAL) {
+ ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, vreg->bypassed);
+ if (ret < 0)
+ return ret;
+ }
+
if (vreg->enabled == -EINVAL &&
vreg->uv != -ENOTRECOVERABLE) {
ret = _rpmh_regulator_vrm_set_value(rdev,
@@ -346,56 +403,6 @@ static int rpmh_regulator_set_enable_state(struct udevice *rdev,
return ret;
}
-static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg,
- unsigned int mode, bool bypassed)
-{
- struct tcs_cmd cmd = {
- .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE,
- };
- struct dm_regulator_mode *pmic_mode;
- int i;
-
- if (mode > REGULATOR_MODE_HPM)
- return -EINVAL;
-
- for (i = 0; i < vreg->hw_data->n_modes; i++) {
- pmic_mode = &vreg->hw_data->pmic_mode_map[i];
- if (pmic_mode->id == mode)
- break;
- }
- if (pmic_mode->id != mode) {
- printf("Invalid mode %d\n", mode);
- return -EINVAL;
- }
-
- if (bypassed)
- // XXX: should have a version check for PMIC4 but we don't have any yet
- // and we don't use bypass mode
- cmd.data = PMIC5_BOB_MODE_PASS;
- else
- cmd.data = pmic_mode->register_value;
-
- return rpmh_regulator_send_request(vreg, &cmd, true);
-}
-
-static int rpmh_regulator_vrm_set_mode(struct udevice *rdev,
- int mode)
-{
- struct rpmh_vreg *vreg = dev_get_priv(rdev);
- int ret;
-
- debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode);
-
- if (mode == vreg->mode)
- return 0;
-
- ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed);
- if (!ret)
- vreg->mode = mode;
-
- return ret;
-}
-
static int rpmh_regulator_vrm_get_pmic_mode(struct rpmh_vreg *vreg, int *pmic_mode)
{
struct tcs_cmd cmd = {
@@ -640,6 +647,35 @@ static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = {
{}
};
+static const struct rpmh_vreg_init_data pm7550_vreg_data[] = {
+ /* smps1 - smps6 are not added to u-boot yet */
+ 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 pm8150_vreg_data[] = {
RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"),
RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"),
@@ -853,7 +889,7 @@ static int rpmh_regulator_probe(struct udevice *dev)
priv->hw_data = init_data->hw_data;
priv->enabled = -EINVAL;
priv->uv = -ENOTRECOVERABLE;
- if (ofnode_read_u32(dev_ofnode(dev), "regulator-initial-mode", &priv->mode))
+ if (dev_read_u32(dev, "regulator-initial-mode", &priv->mode))
priv->mode = -EINVAL;
plat_data->mode = priv->hw_data->pmic_mode_map;
@@ -904,7 +940,7 @@ static int rpmh_regulators_bind(struct udevice *dev)
return -ENODEV;
}
- pmic_id = ofnode_read_string(dev_ofnode(dev), "qcom,pmic-id");
+ pmic_id = dev_read_string(dev, "qcom,pmic-id");
if (!pmic_id) {
dev_err(dev, "No PMIC ID\n");
return -ENODEV;
@@ -912,7 +948,7 @@ static int rpmh_regulators_bind(struct udevice *dev)
drv = lists_driver_lookup_name("rpmh_regulator_drm");
- ofnode_for_each_subnode(node, dev_ofnode(dev)) {
+ dev_for_each_subnode(node, dev) {
data = vreg_get_init_data(init_data, node);
if (!data)
continue;
@@ -947,6 +983,10 @@ static const struct udevice_id rpmh_regulator_ids[] = {
.data = (ulong)pm7325_vreg_data,
},
{
+ .compatible = "qcom,pm7550-rpmh-regulators",
+ .data = (ulong)pm7550_vreg_data,
+ },
+ {
.compatible = "qcom,pm8150-rpmh-regulators",
.data = (ulong)pm8150_vreg_data,
},
diff --git a/drivers/power/regulator/regulator-uclass.c b/drivers/power/regulator/regulator-uclass.c
index 1c7f75a9338..0f5ba51ad6c 100644
--- a/drivers/power/regulator/regulator-uclass.c
+++ b/drivers/power/regulator/regulator-uclass.c
@@ -111,6 +111,33 @@ int regulator_get_suspend_value(struct udevice *dev)
return ops->get_suspend_value(dev);
}
+int regulator_set_value_clamp(struct udevice *dev,
+ int min_uV, int target_uV, int max_uV)
+{
+ const struct dm_regulator_ops *ops = dev_get_driver_ops(dev);
+ struct dm_regulator_uclass_plat *uc_pdata;
+ int uV;
+
+ if (!ops || !ops->set_value)
+ return -ENOSYS;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ if (uc_pdata->min_uV != -ENODATA && max_uV < uc_pdata->min_uV)
+ return -EINVAL;
+ if (uc_pdata->max_uV != -ENODATA && min_uV > uc_pdata->max_uV)
+ return -EINVAL;
+ if (min_uV > max_uV)
+ return -EINVAL;
+
+ if (uc_pdata->min_uV != -ENODATA)
+ min_uV = max(min_uV, uc_pdata->min_uV);
+ if (uc_pdata->max_uV != -ENODATA)
+ max_uV = min(max_uV, uc_pdata->max_uV);
+ uV = clamp(target_uV, min_uV, max_uV);
+
+ return regulator_set_value(dev, uV);
+}
+
/*
* To be called with at most caution as there is no check
* before setting the actual voltage value.
diff --git a/drivers/power/regulator/regulator_common.c b/drivers/power/regulator/regulator_common.c
index 85af8d599ad..c0387eff4fc 100644
--- a/drivers/power/regulator/regulator_common.c
+++ b/drivers/power/regulator/regulator_common.c
@@ -87,6 +87,9 @@ int regulator_common_set_enable(const struct udevice *dev,
}
}
+ if (enable && plat->off_on_delay_us)
+ udelay(plat->off_on_delay_us);
+
ret = dm_gpio_set_value(&plat->gpio, enable);
if (ret) {
pr_err("Can't set regulator : %s gpio to: %d\n", dev->name,
@@ -97,9 +100,6 @@ int regulator_common_set_enable(const struct udevice *dev,
if (enable && plat->startup_delay_us)
udelay(plat->startup_delay_us);
- if (!enable && plat->off_on_delay_us)
- udelay(plat->off_on_delay_us);
-
if (enable)
plat->enable_count++;
else
diff --git a/drivers/power/regulator/sandbox.c b/drivers/power/regulator/sandbox.c
index 80a68f5a30d..813ee301e73 100644
--- a/drivers/power/regulator/sandbox.c
+++ b/drivers/power/regulator/sandbox.c
@@ -56,10 +56,11 @@ static struct dm_regulator_mode sandbox_buck_modes[] = {
MODE(BUCK_OM_PWM, OM2REG(BUCK_OM_PWM), "PWM"),
};
-/* LDO: 1,2 - voltage range */
+/* LDO: 1,2,3 - voltage range */
static struct output_range ldo_voltage_range[] = {
RANGE(OUT_LDO1_UV_MIN, OUT_LDO1_UV_MAX, OUT_LDO1_UV_STEP),
RANGE(OUT_LDO2_UV_MIN, OUT_LDO2_UV_MAX, OUT_LDO2_UV_STEP),
+ RANGE(OUT_LDO3_UV_MIN, OUT_LDO3_UV_MAX, OUT_LDO3_UV_STEP),
};
/* LDO: 1 - current range */
@@ -288,8 +289,8 @@ static int ldo_set_voltage(struct udevice *dev, int uV)
static int ldo_get_current(struct udevice *dev)
{
- /* LDO2 - unsupported */
- if (dev->driver_data == 2)
+ /* LDO: 2,3 - unsupported */
+ if (dev->driver_data >= 2)
return -ENOSYS;
return out_get_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
@@ -298,8 +299,8 @@ static int ldo_get_current(struct udevice *dev)
static int ldo_set_current(struct udevice *dev, int uA)
{
- /* LDO2 - unsupported */
- if (dev->driver_data == 2)
+ /* LDO: 2,3 - unsupported */
+ if (dev->driver_data >= 2)
return -ENOSYS;
return out_set_value(dev, SANDBOX_LDO_COUNT, OUT_REG_UA,
diff --git a/drivers/power/regulator/sandbox_pmbus.c b/drivers/power/regulator/sandbox_pmbus.c
new file mode 100644
index 00000000000..7b52d2ca293
--- /dev/null
+++ b/drivers/power/regulator/sandbox_pmbus.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Free Mobile - Vincent Jardin
+ *
+ * Sandbox PMBus 1.x chip stub (UCLASS_I2C_EMUL).
+ *
+ * Stub a DT i2c node so the PMBus framework, the generic pmbus
+ * regulator and the pmbus CLI command can be tested.
+ * The model is a flat per-command 16 bit
+ * register file combined with some fixed identification strings.
+ */
+
+#include <dm.h>
+#include <i2c.h>
+#include <pmbus.h>
+#include <linux/ctype.h>
+
+#define PMBUS_EMUL_NREG 256
+
+struct sandbox_pmbus_priv {
+ u16 reg[PMBUS_EMUL_NREG];
+ bool supported[PMBUS_EMUL_NREG];
+};
+
+/* Identification strings reported in the natural (forward) byte order. */
+static const char *pmbus_emul_string(u8 cmd)
+{
+ switch (cmd) {
+ case PMBUS_MFR_ID:
+ return "SANDBOX";
+ case PMBUS_MFR_MODEL:
+ return "PMBUS-EMUL";
+ case PMBUS_MFR_REVISION:
+ return "1.0";
+ default:
+ return NULL;
+ }
+}
+
+static int sandbox_pmbus_read(struct sandbox_pmbus_priv *priv, u8 cmd,
+ u8 *buf, int len)
+{
+ const char *str = pmbus_emul_string(cmd);
+ int i;
+
+ if (len < 1)
+ return -EINVAL;
+
+ if (str) {
+ int slen = strlen(str);
+
+ /*
+ * Block payload: [length][bytes...]. A one-byte read is
+ * the SMBus length probe and reports the true length (the
+ * master just NAKs early). Any longer read must have room
+ * for length + payload, otherwise the length byte would
+ * lie about the data actually returned.
+ */
+ if (len > 1 && len < slen + 1)
+ return -EREMOTEIO;
+ buf[0] = (u8)slen;
+ for (i = 1; i < len; i++)
+ buf[i] = (i - 1 < slen) ? (u8)str[i - 1] : 0;
+ return 0;
+ }
+
+ if (!priv->supported[cmd])
+ return -EREMOTEIO; /* chip NAKs an unimplemented command */
+
+ for (i = 0; i < len; i++)
+ buf[i] = (u8)(priv->reg[cmd] >> (8 * i));
+ return 0;
+}
+
+static int sandbox_pmbus_write(struct sandbox_pmbus_priv *priv, u8 cmd,
+ const u8 *buf, int len)
+{
+ if (len == 0)
+ return 0; /* send-byte (eg CLEAR_FAULTS): just ACK */
+ if (!priv->supported[cmd])
+ return -EREMOTEIO;
+ if (len == 1)
+ priv->reg[cmd] = buf[0];
+ else
+ priv->reg[cmd] = (u16)buf[0] | ((u16)buf[1] << 8);
+ return 0;
+}
+
+static int sandbox_pmbus_xfer(struct udevice *emul, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
+ u8 cmd;
+
+ if (nmsgs == 0)
+ return 0;
+ /* A PMBus transaction always opens with the command-code write. */
+ if (msg[0].flags & I2C_M_RD)
+ return -EIO;
+ if (msg[0].len == 0)
+ return 0; /* address-only probe */
+ cmd = msg[0].buf[0];
+
+ if (nmsgs >= 2 && (msg[1].flags & I2C_M_RD))
+ return sandbox_pmbus_read(priv, cmd, msg[1].buf, msg[1].len);
+
+ return sandbox_pmbus_write(priv, cmd, msg[0].buf + 1, msg[0].len - 1);
+}
+
+static void sandbox_pmbus_support(struct sandbox_pmbus_priv *priv, u8 cmd,
+ u16 val)
+{
+ priv->supported[cmd] = true;
+ priv->reg[cmd] = val;
+}
+
+static int sandbox_pmbus_probe(struct udevice *emul)
+{
+ struct sandbox_pmbus_priv *priv = dev_get_priv(emul);
+
+ /* Configuration / identification. */
+ sandbox_pmbus_support(priv, PMBUS_PAGE, 0);
+ sandbox_pmbus_support(priv, PMBUS_OPERATION, PB_OPERATION_ON);
+ sandbox_pmbus_support(priv, PMBUS_ON_OFF_CONFIG, 0);
+ sandbox_pmbus_support(priv, PMBUS_WRITE_PROTECT, 0);
+ sandbox_pmbus_support(priv, PMBUS_CAPABILITY, 0x30);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_MODE, 0x18); /* LINEAR 2^-8 */
+ sandbox_pmbus_support(priv, PMBUS_VOUT_COMMAND, 0x0200);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_TRIM, 0);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_MAX, 0x0400);
+ sandbox_pmbus_support(priv, PMBUS_VOUT_SCALE_LOOP, 0);
+ sandbox_pmbus_support(priv, PMBUS_REVISION, PMBUS_REV_13);
+
+ /* Status registers, all clean. */
+ sandbox_pmbus_support(priv, PMBUS_STATUS_BYTE, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_WORD, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_VOUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_IOUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_INPUT, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_TEMPERATURE, 0);
+ sandbox_pmbus_support(priv, PMBUS_STATUS_CML, 0);
+
+ /*
+ * Telemetry the emulated chip implements. READ_IIN and READ_POUT
+ * are intentionally absent so callers see the unsupported path.
+ */
+ sandbox_pmbus_support(priv, PMBUS_READ_VIN, 0x0abc);
+ sandbox_pmbus_support(priv, PMBUS_READ_VOUT, 0x0200);
+ sandbox_pmbus_support(priv, PMBUS_READ_IOUT, 0x0123);
+ sandbox_pmbus_support(priv, PMBUS_READ_TEMPERATURE_1, 0x0019);
+
+ return 0;
+}
+
+static struct dm_i2c_ops sandbox_pmbus_emul_ops = {
+ .xfer = sandbox_pmbus_xfer,
+};
+
+static const struct udevice_id sandbox_pmbus_ids[] = {
+ { .compatible = "sandbox,i2c-pmbus" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_pmbus_emul) = {
+ .name = "sandbox_pmbus_emul",
+ .id = UCLASS_I2C_EMUL,
+ .of_match = sandbox_pmbus_ids,
+ .probe = sandbox_pmbus_probe,
+ .priv_auto = sizeof(struct sandbox_pmbus_priv),
+ .ops = &sandbox_pmbus_emul_ops,
+};
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 7d2db1e2bee..aa7b4278260 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -180,7 +180,7 @@ static int scmi_regulator_bind(struct udevice *dev)
ofnode node;
int ret;
- regul_node = ofnode_find_subnode(dev_ofnode(dev), "regulators");
+ regul_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regul_node)) {
dev_err(dev, "no regulators node\n");
return -ENXIO;
diff --git a/drivers/power/regulator/spacemit_p1_regulator.c b/drivers/power/regulator/spacemit_p1_regulator.c
new file mode 100644
index 00000000000..7b5eb710edf
--- /dev/null
+++ b/drivers/power/regulator/spacemit_p1_regulator.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025-2026 RISCstar Ltd.
+ */
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <errno.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/spacemit_p1.h>
+
+struct p1_reg_info {
+ uint min_uv;
+ uint step_uv;
+ u8 vsel_reg;
+ u8 vsel_sleep_reg;
+ u8 config_reg;
+ u8 vsel_mask;
+ u8 min_sel;
+ u8 max_sel;
+};
+
+static const struct p1_reg_info p1_bucks[] = {
+ /* BUCK 1 */
+ { 500000, 5000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(1), P1_BUCK_SVSEL(1), P1_BUCK_CTRL(1),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 2 */
+ { 500000, 5000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(2), P1_BUCK_SVSEL(2), P1_BUCK_CTRL(2),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 3 */
+ { 500000, 5000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(3), P1_BUCK_SVSEL(3), P1_BUCK_CTRL(3),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 4 */
+ { 500000, 5000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(4), P1_BUCK_SVSEL(4), P1_BUCK_CTRL(4),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 5 */
+ { 500000, 5000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(5), P1_BUCK_SVSEL(5), P1_BUCK_CTRL(5),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+ /* BUCK 6 */
+ { 500000, 5000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
+ BUCK_VSEL_MASK, 0x00, 0xaa },
+ { 1375000, 25000, P1_BUCK_VSEL(6), P1_BUCK_SVSEL(6), P1_BUCK_CTRL(6),
+ BUCK_VSEL_MASK, 0xab, 0xfe },
+};
+
+static const struct p1_reg_info p1_aldos[] = {
+ /* ALDO 1 */
+ { 500000, 25000, P1_ALDO_VOLT(1), P1_ALDO_SVOLT(1), P1_ALDO_CTRL(1),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 2 */
+ { 500000, 25000, P1_ALDO_VOLT(2), P1_ALDO_SVOLT(2), P1_ALDO_CTRL(2),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 3 */
+ { 500000, 25000, P1_ALDO_VOLT(3), P1_ALDO_SVOLT(3), P1_ALDO_CTRL(3),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* ALDO 4 */
+ { 500000, 25000, P1_ALDO_VOLT(4), P1_ALDO_SVOLT(4), P1_ALDO_CTRL(4),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+};
+
+static const struct p1_reg_info p1_dldos[] = {
+ /* DLDO 1 */
+ { 500000, 25000, P1_DLDO_VOLT(1), P1_DLDO_SVOLT(1), P1_DLDO_CTRL(1),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 2 */
+ { 500000, 25000, P1_DLDO_VOLT(2), P1_DLDO_SVOLT(2), P1_DLDO_CTRL(2),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 3 */
+ { 500000, 25000, P1_DLDO_VOLT(3), P1_DLDO_SVOLT(3), P1_DLDO_CTRL(3),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 4 */
+ { 500000, 25000, P1_DLDO_VOLT(4), P1_DLDO_SVOLT(4), P1_DLDO_CTRL(4),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 5 */
+ { 500000, 25000, P1_DLDO_VOLT(5), P1_DLDO_SVOLT(5), P1_DLDO_CTRL(5),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 6 */
+ { 500000, 25000, P1_DLDO_VOLT(6), P1_DLDO_SVOLT(6), P1_DLDO_CTRL(6),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+ /* DLDO 7 */
+ { 500000, 25000, P1_DLDO_VOLT(7), P1_DLDO_SVOLT(7), P1_DLDO_CTRL(7),
+ ALDO_VSEL_MASK, 0x0b, 0x7f },
+};
+
+static const struct p1_reg_info *get_buck_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 0)
+ return NULL;
+ if (uvolt < 1375000)
+ return &p1_bucks[(idx - 1) * 2 + 0];
+ return &p1_bucks[(idx - 1) * 2 + 1];
+}
+
+static const struct p1_reg_info *get_aldo_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 1 || idx > ARRAY_SIZE(p1_aldos))
+ return NULL;
+ return &p1_aldos[idx - 1];
+}
+
+static const struct p1_reg_info *get_dldo_reg(struct udevice *pmic,
+ int idx, int uvolt)
+{
+ if (idx < 1 || idx > ARRAY_SIZE(p1_dldos))
+ return NULL;
+ return &p1_dldos[idx - 1];
+}
+
+static int buck_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int buck_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int buck_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & BUCK_EN_MASK;
+}
+
+static int buck_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_buck_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= BUCK_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, BUCK_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_buck_ops = {
+ .get_value = buck_get_value,
+ .set_value = buck_set_value,
+ .get_enable = buck_get_enable,
+ .set_enable = buck_set_enable,
+};
+
+static int p1_buck_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_buck) = {
+ .name = P1_BUCK_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_buck_ops,
+ .probe = p1_buck_probe,
+};
+
+static int aldo_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int aldo_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int aldo_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & ALDO_EN_MASK;
+}
+
+static int aldo_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_aldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= ALDO_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, ALDO_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_aldo_ops = {
+ .get_value = aldo_get_value,
+ .set_value = aldo_set_value,
+ .get_enable = aldo_get_enable,
+ .set_enable = aldo_set_enable,
+};
+
+static int p1_aldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_aldo) = {
+ .name = P1_ALDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_aldo_ops,
+ .probe = p1_aldo_probe,
+};
+
+static int dldo_get_value(struct udevice *dev)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->read)
+ return -ENOSYS;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->vsel_reg);
+ if (ret < 0)
+ return ret;
+
+ val = ret & info->vsel_mask;
+ while (val > info->max_sel)
+ info++;
+
+ return info->min_uv + (val - info->min_sel) * info->step_uv;
+}
+
+static int dldo_set_value(struct udevice *dev, int uvolt)
+{
+ const struct dm_pmic_ops *ops = device_get_ops(dev->parent);
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ if (!ops || !ops->write)
+ return -ENOSYS;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, uvolt);
+ if (!info)
+ return -ENOENT;
+ val = (uvolt - info->min_uv);
+ val = val / info->step_uv;
+ val += info->min_sel;
+ ret = pmic_reg_write(dev->parent, info->vsel_reg, val);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int dldo_get_enable(struct udevice *dev)
+{
+ const struct p1_reg_info *info;
+ int ret;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ return ret & DLDO_EN_MASK;
+}
+
+static int dldo_set_enable(struct udevice *dev, bool enable)
+{
+ const struct p1_reg_info *info;
+ uint val;
+ int ret;
+
+ info = get_dldo_reg(dev->parent, dev->driver_data, 0);
+ if (!info)
+ return -ENOENT;
+
+ ret = pmic_reg_read(dev->parent, info->config_reg);
+ if (ret < 0)
+ return ret;
+ val = (unsigned int)ret;
+ val &= DLDO_EN_MASK;
+
+ if (enable == val)
+ return 0;
+
+ val = enable;
+ ret = pmic_clrsetbits(dev->parent, info->config_reg, DLDO_EN_MASK, val);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops p1_dldo_ops = {
+ .get_value = dldo_get_value,
+ .set_value = dldo_set_value,
+ .get_enable = dldo_get_enable,
+ .set_enable = dldo_set_enable,
+};
+
+static int p1_dldo_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+
+ uc_pdata = dev_get_uclass_plat(dev);
+
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(p1_dldo) = {
+ .name = P1_DLDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &p1_dldo_ops,
+ .probe = p1_dldo_probe,
+};
diff --git a/drivers/power/regulator/tps6287x_regulator.c b/drivers/power/regulator/tps6287x_regulator.c
index 6d185719199..2e6d85f677a 100644
--- a/drivers/power/regulator/tps6287x_regulator.c
+++ b/drivers/power/regulator/tps6287x_regulator.c
@@ -141,7 +141,7 @@ static int tps6287x_regulator_probe(struct udevice *dev)
pdata->config = (void *)dev_get_driver_data(dev);
- slave_id = devfdt_get_addr_index(dev, 0);
+ slave_id = dev_read_addr_index(dev, 0);
ret = i2c_get_chip(dev->parent, slave_id, 1, &pdata->i2c);
if (ret) {
diff --git a/drivers/power/regulator/tps65941_regulator.c b/drivers/power/regulator/tps65941_regulator.c
index 2561d6f4c6c..209968b5718 100644
--- a/drivers/power/regulator/tps65941_regulator.c
+++ b/drivers/power/regulator/tps65941_regulator.c
@@ -63,13 +63,14 @@ static inline int tps65941_get_chip_id(struct udevice *dev)
static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable)
{
- int ret;
+ int ret, idx;
unsigned int adr;
struct dm_regulator_uclass_plat *uc_pdata;
uc_pdata = dev_get_uclass_plat(dev);
adr = uc_pdata->ctrl_reg;
+ idx = dev->driver_data;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
@@ -84,10 +85,18 @@ static int tps65941_buck_enable(struct udevice *dev, int op, bool *enable)
return 0;
} else if (op == PMIC_OP_SET) {
- if (*enable)
+ if (*enable) {
ret |= TPS65941_BUCK_MODE_MASK;
- else
+ /* Enable FPWM */
+ ret |= TPS65941_BUCK_FPWM_MASK;
+ /* If Multiphase enable FPWM_MP */
+ if (idx == TPS65941_BUCK_ID_12 ||
+ idx == TPS65941_BUCK_ID_123 ||
+ idx == TPS65941_BUCK_ID_1234)
+ ret |= TPS65941_BUCK_FPWM_MP_MASK;
+ } else {
ret &= ~TPS65941_BUCK_MODE_MASK;
+ }
ret = pmic_reg_write(dev->parent, adr, ret);
if (ret)
return ret;