diff options
Diffstat (limited to 'drivers/power/regulator')
| -rw-r--r-- | drivers/power/regulator/Kconfig | 484 | ||||
| -rw-r--r-- | drivers/power/regulator/Makefile | 7 | ||||
| -rw-r--r-- | drivers/power/regulator/anatop_regulator.c | 3 | ||||
| -rw-r--r-- | drivers/power/regulator/mpq8785.c | 494 | ||||
| -rw-r--r-- | drivers/power/regulator/pfuze100.c | 166 | ||||
| -rw-r--r-- | drivers/power/regulator/pmbus_generic.c | 90 | ||||
| -rw-r--r-- | drivers/power/regulator/pmbus_helper.c | 315 | ||||
| -rw-r--r-- | drivers/power/regulator/pmbus_helper.h | 90 | ||||
| -rw-r--r-- | drivers/power/regulator/qcom-rpmh-regulator.c | 146 | ||||
| -rw-r--r-- | drivers/power/regulator/regulator-uclass.c | 27 | ||||
| -rw-r--r-- | drivers/power/regulator/regulator_common.c | 6 | ||||
| -rw-r--r-- | drivers/power/regulator/sandbox.c | 11 | ||||
| -rw-r--r-- | drivers/power/regulator/sandbox_pmbus.c | 171 | ||||
| -rw-r--r-- | drivers/power/regulator/scmi_regulator.c | 2 | ||||
| -rw-r--r-- | drivers/power/regulator/spacemit_p1_regulator.c | 464 | ||||
| -rw-r--r-- | drivers/power/regulator/tps6287x_regulator.c | 2 | ||||
| -rw-r--r-- | drivers/power/regulator/tps65941_regulator.c | 15 |
17 files changed, 2170 insertions, 323 deletions
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; |
