diff options
Diffstat (limited to 'drivers/power')
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; |
