[PATCH v3 0/7] Add support for PMICs used on Tegra 3 devices

Patch set adds basic support for PMICs used in many Tegra 3 devices. All PMIC drivers are based on datasheets provided by vendors. Implemented API include regulator set/get voltage enable/disable and basic PMIC r/w capabilities.
Drivers were tested by me on LG P895 (max77663), HTC One X (TPS80032) and ASUS TF300T (tps65911). Calculations and behavior were correct, predictable and reproducible.
Poweroff functional will be added after this basic drivers are merged.
--- Changes from v2: - removed all previously made poweroff API in favour to use sysreset after this patchset is accepted
Changes from v1: - created poweroff API for PMIC and integrated it call into do_poweroff - added poweroff function to submitted max77663, tps80032 and tps65911 drivers - fixed registers asignments for regulators with non-numeric name (ldoln, ldousb, vddctrl) ---
Svyatoslav Ryhel (7): power: pmic: palmas: support TI TPS65913 PMIC power: pmic: add the base MAX77663 PMIC support power: regulator: max77663: add regulator support power: pmic: add the base TPS80031 PMIC support power: regulator: tps80031: add regulator support power: pmic: tps65910: add TPS65911 PMIC support power: regulator: tps65911: add regulator support
doc/device-tree-bindings/pmic/max77663.txt | 84 +++++ doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++ doc/device-tree-bindings/pmic/tps80031.txt | 76 ++++ drivers/power/pmic/Kconfig | 12 + drivers/power/pmic/Makefile | 2 + drivers/power/pmic/max77663.c | 81 ++++ drivers/power/pmic/palmas.c | 1 + drivers/power/pmic/pmic_tps65910_dm.c | 26 +- drivers/power/pmic/tps80031.c | 82 ++++ drivers/power/regulator/Kconfig | 24 ++ drivers/power/regulator/Makefile | 3 + drivers/power/regulator/max77663_regulator.c | 358 ++++++++++++++++++ drivers/power/regulator/tps65911_regulator.c | 377 +++++++++++++++++++ drivers/power/regulator/tps80031_regulator.c | 296 +++++++++++++++ include/power/max77663.h | 40 ++ include/power/tps65910_pmic.h | 47 +++ include/power/tps80031.h | 42 +++ 17 files changed, 1626 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/max77663.txt create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt create mode 100644 doc/device-tree-bindings/pmic/tps80031.txt create mode 100644 drivers/power/pmic/max77663.c create mode 100644 drivers/power/pmic/tps80031.c create mode 100644 drivers/power/regulator/max77663_regulator.c create mode 100644 drivers/power/regulator/tps65911_regulator.c create mode 100644 drivers/power/regulator/tps80031_regulator.c create mode 100644 include/power/max77663.h create mode 100644 include/power/tps80031.h

Existing PALMAS PMIC driver is fully compatible with TI TPS65913 PMIC found in many Tegra 4 devices, like Tegra Note 7 and ASUS TF701T. TPS65913 shares same structure of regulators like TPS659038 so data can be reused.
Tested-by: Svyatoslav Ryhel clamor95@gmail.com # NVIDIA Tegratab Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/pmic/palmas.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/drivers/power/pmic/palmas.c b/drivers/power/pmic/palmas.c index 6080cbff0b..d6eb9cb433 100644 --- a/drivers/power/pmic/palmas.c +++ b/drivers/power/pmic/palmas.c @@ -87,6 +87,7 @@ static struct dm_pmic_ops palmas_ops = {
static const struct udevice_id palmas_ids[] = { { .compatible = "ti,tps659038", .data = TPS659038 }, + { .compatible = "ti,tps65913" , .data = TPS659038 }, { .compatible = "ti,tps65917" , .data = TPS65917 }, { } };

On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
Existing PALMAS PMIC driver is fully compatible with TI TPS65913 PMIC found in many Tegra 4 devices, like Tegra Note 7 and ASUS TF701T. TPS65913 shares same structure of regulators like TPS659038 so data can be reused.
Tested-by: Svyatoslav Ryhel clamor95@gmail.com # NVIDIA Tegratab Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/pmic/palmas.c | 1 + 1 file changed, 1 insertion(+)
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/drivers/power/pmic/palmas.c b/drivers/power/pmic/palmas.c index 6080cbff0b..d6eb9cb433 100644 --- a/drivers/power/pmic/palmas.c +++ b/drivers/power/pmic/palmas.c @@ -87,6 +87,7 @@ static struct dm_pmic_ops palmas_ops = {
static const struct udevice_id palmas_ids[] = { { .compatible = "ti,tps659038", .data = TPS659038 },
{ .compatible = "ti,tps65913" , .data = TPS659038 }, { .compatible = "ti,tps65917" , .data = TPS65917 }, { }
};
2.39.2

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- doc/device-tree-bindings/pmic/max77663.txt | 84 ++++++++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/max77663.c | 81 +++++++++++++++++++++ include/power/max77663.h | 40 +++++++++++ 5 files changed, 212 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/max77663.txt create mode 100644 drivers/power/pmic/max77663.c create mode 100644 include/power/max77663.h
diff --git a/doc/device-tree-bindings/pmic/max77663.txt b/doc/device-tree-bindings/pmic/max77663.txt new file mode 100644 index 0000000000..ddb7d3eb14 --- /dev/null +++ b/doc/device-tree-bindings/pmic/max77663.txt @@ -0,0 +1,84 @@ +MAXIM, MAX77663 PMIC + +This device uses two drivers: +- drivers/power/pmic/max77663.c (for parent device) +- drivers/power/regulator/max77663_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "maxim,max77663" +- reg: usually 0x1c or 0x3c + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for max77663: +- sd0, sd1, sd2, sd3, ldo0, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8 + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Linux driver binding for this driver is compatible. + +Example: + +max77663@1c { + compatible = "maxim,max77663"; + reg = <0x1c>; + + regulators { + sd0 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1250000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo0 { + regulator-name = "avdd_pll"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + + ... + + ldo2 { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo3 { + regulator-name = "vdd_sdmmc3"; + regulator-min-microvolt = <3000000>; + regulator-max-microvolt = <3000000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo8 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + }; + }; +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 176fb07c65..abea0fe4ed 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -184,6 +184,12 @@ config SPL_DM_PMIC_PFUZE100 This config enables implementation of driver-model pmic uclass features for PMIC PFUZE100 in SPL. The driver implements read/write operations.
+config DM_PMIC_MAX77663 + bool "Enable Driver Model for PMIC MAX77663" + ---help--- + This config enables implementation of driver-model pmic uclass features + for PMIC MAX77663. The driver implements read/write operations. + config DM_PMIC_MAX77686 bool "Enable Driver Model for PMIC MAX77686" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 0b3b3d62d0..414a9d8225 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_PMIC) += pmic-uclass.o obj-$(CONFIG_$(SPL_)DM_PMIC_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_PMIC_MAX77663) += max77663.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o diff --git a/drivers/power/pmic/max77663.c b/drivers/power/pmic/max77663.c new file mode 100644 index 0000000000..482b646901 --- /dev/null +++ b/drivers/power/pmic/max77663.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> +#include <dm/device.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = MAX77663_LDO_DRIVER }, + { .prefix = "sd", .driver = MAX77663_SD_DRIVER }, + { }, +}; + +static int max77663_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + log_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77663_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + log_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int max77663_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops max77663_ops = { + .read = max77663_read, + .write = max77663_write, +}; + +static const struct udevice_id max77663_ids[] = { + { .compatible = "maxim,max77663" }, + { } +}; + +U_BOOT_DRIVER(pmic_max77663) = { + .name = "max77663_pmic", + .id = UCLASS_PMIC, + .of_match = max77663_ids, + .bind = max77663_bind, + .ops = &max77663_ops, +}; diff --git a/include/power/max77663.h b/include/power/max77663.h new file mode 100644 index 0000000000..53a0976346 --- /dev/null +++ b/include/power/max77663.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#ifndef _MAX77663_H_ +#define _MAX77663_H_ + +#define MAX77663_LDO_NUM 9 +#define MAX77663_SD_NUM 5 + +/* Drivers name */ +#define MAX77663_LDO_DRIVER "max77663_ldo" +#define MAX77663_SD_DRIVER "max77663_sd" + +#define MAX77663_SD_STATUS_MASK 0x30 + +#define MAX77663_SD0_VOLT_MAX_HEX 0x40 +#define MAX77663_SD1_VOLT_MAX_HEX 0X4C +#define MAX77663_SD_VOLT_MAX_HEX 0XFF +#define MAX77663_SD_VOLT_MIN_HEX 0x02 + +#define MAX77663_SD0_VOLT_MAX 1400000 +#define MAX77663_SD1_VOLT_MAX 1550000 +#define MAX77663_SD_VOLT_MAX 3787500 +#define MAX77663_SD_VOLT_MIN 625000 + +#define MAX77663_SD_VOLT_BASE 600000 + +#define MAX77663_LDO_STATUS_MASK 0xC0 +#define MAX77663_LDO_VOLT_MASK 0x3F +#define MAX77663_LDO_VOLT_MAX_HEX 0x3F + +#define MAX77663_LDO01_VOLT_MAX 2375000 +#define MAX77663_LDO4_VOLT_MAX 1587500 +#define MAX77663_LDO_VOLT_MAX 3950000 + +#define MAX77663_LDO_VOLT_BASE 800000 + +#endif /* _MAX77663_H_ */

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
doc/device-tree-bindings/pmic/max77663.txt | 84 ++++++++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/max77663.c | 81 +++++++++++++++++++++ include/power/max77663.h | 40 +++++++++++ 5 files changed, 212 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/max77663.txt create mode 100644 drivers/power/pmic/max77663.c create mode 100644 include/power/max77663.h
Reviewed-by: Simon Glass sjg@chromium.org
but see below
diff --git a/doc/device-tree-bindings/pmic/max77663.txt b/doc/device-tree-bindings/pmic/max77663.txt new file mode 100644 index 0000000000..ddb7d3eb14 --- /dev/null +++ b/doc/device-tree-bindings/pmic/max77663.txt @@ -0,0 +1,84 @@ +MAXIM, MAX77663 PMIC
+This device uses two drivers: +- drivers/power/pmic/max77663.c (for parent device) +- drivers/power/regulator/max77663_regulator.c (for child regulators)
+This chapter describes the binding info for the PMIC driver and regulators.
+Required properties for PMIC: +- compatible: "maxim,max77663" +- reg: usually 0x1c or 0x3c
+With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists.
+Optional subnode: +- name: regulators (subnode list of each device's regulator)
+Regulators subnode contains set on supported regulators.
+Required properties: +- regulator-name: used for regulator uclass platform data '.name',
+List of supported regulator nodes names for max77663: +- sd0, sd1, sd2, sd3, ldo0, ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8
+Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader
+Linux driver binding for this driver is compatible.
+Example:
+max77663@1c {
compatible = "maxim,max77663";
reg = <0x1c>;
regulators {
sd0 {
regulator-name = "vdd_cpu";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1250000>;
regulator-always-on;
regulator-boot-on;
};
...
ldo0 {
regulator-name = "avdd_pll";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
};
...
ldo2 {
regulator-name = "avdd_usb";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-always-on;
regulator-boot-on;
};
ldo3 {
regulator-name = "vdd_sdmmc3";
regulator-min-microvolt = <3000000>;
regulator-max-microvolt = <3000000>;
regulator-always-on;
regulator-boot-on;
};
...
ldo8 {
regulator-name = "avdd_dsi_csi";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
};
};
+}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index 176fb07c65..abea0fe4ed 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -184,6 +184,12 @@ config SPL_DM_PMIC_PFUZE100 This config enables implementation of driver-model pmic uclass features for PMIC PFUZE100 in SPL. The driver implements read/write operations.
+config DM_PMIC_MAX77663
bool "Enable Driver Model for PMIC MAX77663"
---help---
This config enables implementation of driver-model pmic uclass features
for PMIC MAX77663. The driver implements read/write operations.
You could mention a few features such as number of LDO, etc.
config DM_PMIC_MAX77686 bool "Enable Driver Model for PMIC MAX77686" ---help--- diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 0b3b3d62d0..414a9d8225 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_$(SPL_TPL_)DM_PMIC) += pmic-uclass.o obj-$(CONFIG_$(SPL_)DM_PMIC_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_PMIC_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_PMIC_MAX77663) += max77663.o obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o diff --git a/drivers/power/pmic/max77663.c b/drivers/power/pmic/max77663.c new file mode 100644 index 0000000000..482b646901 --- /dev/null +++ b/drivers/power/pmic/max77663.c @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> +#include <dm/device.h>
That should go after log.h
+static const struct pmic_child_info pmic_children_info[] = {
{ .prefix = "ldo", .driver = MAX77663_LDO_DRIVER },
{ .prefix = "sd", .driver = MAX77663_SD_DRIVER },
{ },
+};
+static int max77663_write(struct udevice *dev, uint reg, const uint8_t *buff,
int len)
+{
if (dm_i2c_write(dev, reg, buff, len)) {
log_err("write error to device: %p register: %#x!\n", dev, reg);
return -EIO;
Please return the error provided by dm_i2c_write()
}
return 0;
+}
+static int max77663_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{
if (dm_i2c_read(dev, reg, buff, len)) {
log_err("read error from device: %p register: %#x!\n", dev, reg);
return -EIO;
ditto
Also you might consider log_debug() here since log_err does add to code size, and this problem is unlikely.
}
return 0;
+}
+static int max77663_bind(struct udevice *dev) +{
ofnode regulators_node;
int children;
regulators_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regulators_node)) {
log_err("%s regulators subnode not found!\n", dev->name);
return -ENXIO;
}
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
if (!children)
log_err("%s - no child found\n", dev->name);
/* Always return success for this device */
return 0;
+}
+static struct dm_pmic_ops max77663_ops = {
.read = max77663_read,
.write = max77663_write,
+};
+static const struct udevice_id max77663_ids[] = {
{ .compatible = "maxim,max77663" },
{ }
+};
+U_BOOT_DRIVER(pmic_max77663) = {
.name = "max77663_pmic",
.id = UCLASS_PMIC,
.of_match = max77663_ids,
.bind = max77663_bind,
.ops = &max77663_ops,
+}; diff --git a/include/power/max77663.h b/include/power/max77663.h new file mode 100644 index 0000000000..53a0976346 --- /dev/null +++ b/include/power/max77663.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#ifndef _MAX77663_H_ +#define _MAX77663_H_
+#define MAX77663_LDO_NUM 9 +#define MAX77663_SD_NUM 5
+/* Drivers name */ +#define MAX77663_LDO_DRIVER "max77663_ldo" +#define MAX77663_SD_DRIVER "max77663_sd"
+#define MAX77663_SD_STATUS_MASK 0x30
+#define MAX77663_SD0_VOLT_MAX_HEX 0x40 +#define MAX77663_SD1_VOLT_MAX_HEX 0X4C +#define MAX77663_SD_VOLT_MAX_HEX 0XFF +#define MAX77663_SD_VOLT_MIN_HEX 0x02
+#define MAX77663_SD0_VOLT_MAX 1400000 +#define MAX77663_SD1_VOLT_MAX 1550000 +#define MAX77663_SD_VOLT_MAX 3787500 +#define MAX77663_SD_VOLT_MIN 625000
+#define MAX77663_SD_VOLT_BASE 600000
+#define MAX77663_LDO_STATUS_MASK 0xC0 +#define MAX77663_LDO_VOLT_MASK 0x3F +#define MAX77663_LDO_VOLT_MAX_HEX 0x3F
lower-case hex
+#define MAX77663_LDO01_VOLT_MAX 2375000 +#define MAX77663_LDO4_VOLT_MAX 1587500 +#define MAX77663_LDO_VOLT_MAX 3950000
+#define MAX77663_LDO_VOLT_BASE 800000
+#endif /* _MAX77663_H_ */
2.39.2
Regads, Simon

The driver provides regulator set/get voltage enable/disable functions for MAXIM MAX77663 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/max77663_regulator.c | 358 +++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/power/regulator/max77663_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index eb5aa38c1c..571debd54e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -141,6 +141,14 @@ config SPL_REGULATOR_PWM This config enables implementation of driver-model regulator uclass features for PWM regulators in SPL.
+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 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 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index d9e0cd5949..8d73169b50 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/max77663_regulator.c b/drivers/power/regulator/max77663_regulator.c new file mode 100644 index 0000000000..cbc275b30b --- /dev/null +++ b/drivers/power/regulator/max77663_regulator.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h> + +/* fist row is control registers, second is voltage registers */ +static const char max77663_sd_reg[][MAX77663_SD_NUM] = { + { 0x1d, 0x1e, 0x1f, 0x20, 0x21 }, + { 0x16, 0x17, 0x18, 0x19, 0x2a }, +}; + +static const char max77663_ldo_reg[MAX77663_LDO_NUM] = { + 0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33 +}; + +static int max77663_sd_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & MAX77663_SD_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(MAX77663_SD_STATUS_MASK); + + if (*enable) + ret |= MAX77663_SD_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int max77663_sd_volt2hex(int sd, int uV) +{ + switch (sd) { + case 0: + /* SD0 has max voltage 1.4V */ + if (uV > MAX77663_SD0_VOLT_MAX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (uV > MAX77663_SD1_VOLT_MAX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (uV > MAX77663_SD_VOLT_MAX) + return -EINVAL; + break; + }; + + if (uV < MAX77663_SD_VOLT_MIN) + uV = MAX77663_SD_VOLT_MIN; + + return (uV - MAX77663_SD_VOLT_BASE) / 12500; +} + +static int max77663_sd_hex2volt(int sd, int hex) +{ + switch (sd) { + case 0: + /* SD0 has max voltage 1.4V */ + if (hex > MAX77663_SD0_VOLT_MAX_HEX) + return -EINVAL; + break; + case 1: + /* SD1 has max voltage 1.55V */ + if (hex > MAX77663_SD1_VOLT_MAX_HEX) + return -EINVAL; + break; + default: + /* SD2 and SD3 have max voltage 3.79V */ + if (hex > MAX77663_SD_VOLT_MAX_HEX) + return -EINVAL; + break; + }; + + if (hex < MAX77663_SD_VOLT_MIN_HEX) + hex = MAX77663_SD_VOLT_MIN_HEX; + + return MAX77663_SD_VOLT_BASE + hex * 12500; +} + +static int max77663_sd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int id = dev->driver_data; + int ret; + + if (op == PMIC_OP_GET) { + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + *uV = 0; + + ret = max77663_sd_hex2volt(id, ret); + if (ret < 0) + return ret; + *uV = ret; + + return 0; + } + + /* SD regulators use entire register for voltage */ + ret = max77663_sd_volt2hex(id, *uV); + if (ret < 0) + return ret; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int max77663_sd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + uc_pdata->ctrl_reg = max77663_sd_reg[0][dev->driver_data]; + uc_pdata->volt_reg = max77663_sd_reg[1][dev->driver_data]; + + return 0; +} + +static int sd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_sd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int sd_set_value(struct udevice *dev, int uV) +{ + return max77663_sd_val(dev, PMIC_OP_SET, &uV); +} + +static int sd_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_sd_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int sd_set_enable(struct udevice *dev, bool enable) +{ + return max77663_sd_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_sd_ops = { + .get_value = sd_get_value, + .set_value = sd_set_value, + .get_enable = sd_get_enable, + .set_enable = sd_set_enable, +}; + +U_BOOT_DRIVER(max77663_sd) = { + .name = MAX77663_SD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_sd_ops, + .probe = max77663_sd_probe, +}; + +static int max77663_ldo_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & MAX77663_LDO_STATUS_MASK) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(MAX77663_LDO_STATUS_MASK); + + if (*enable) + ret |= MAX77663_LDO_STATUS_MASK; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int max77663_ldo_volt2hex(int ldo, int uV) +{ + switch (ldo) { + case 0: + case 1: + if (uV > MAX77663_LDO01_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 25000; + case 4: + if (uV > MAX77663_LDO4_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 12500; + default: + if (uV > MAX77663_LDO_VOLT_MAX) + return -EINVAL; + + return (uV - MAX77663_LDO_VOLT_BASE) / 50000; + }; +} + +static int max77663_ldo_hex2volt(int ldo, int hex) +{ + if (hex > MAX77663_LDO_VOLT_MAX_HEX) + return -EINVAL; + + switch (ldo) { + case 0: + case 1: + return (hex * 25000) + MAX77663_LDO_VOLT_BASE; + case 4: + return (hex * 12500) + MAX77663_LDO_VOLT_BASE; + default: + return (hex * 50000) + MAX77663_LDO_VOLT_BASE; + }; +} + +static int max77663_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int id = dev->driver_data; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = max77663_ldo_hex2volt(id, ret & MAX77663_LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = max77663_ldo_volt2hex(id, *uV); + if (hex < 0) + return hex; + + ret &= ~(MAX77663_LDO_VOLT_MASK); + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int max77663_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = max77663_ldo_reg[dev->driver_data]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = max77663_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return max77663_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static int ldo_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = max77663_ldo_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int ldo_set_enable(struct udevice *dev, bool enable) +{ + return max77663_ldo_enable(dev, PMIC_OP_SET, &enable); +} + +static const struct dm_regulator_ops max77663_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = ldo_get_enable, + .set_enable = ldo_set_enable, +}; + +U_BOOT_DRIVER(max77663_ldo) = { + .name = MAX77663_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &max77663_ldo_ops, + .probe = max77663_ldo_probe, +};

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
The driver provides regulator set/get voltage enable/disable functions for MAXIM MAX77663 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/max77663_regulator.c | 358 +++++++++++++++++++ 3 files changed, 367 insertions(+) create mode 100644 drivers/power/regulator/max77663_regulator.c
Reviewed-by: Simon Glass sjg@chromium.org
nits below
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index eb5aa38c1c..571debd54e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -141,6 +141,14 @@ config SPL_REGULATOR_PWM This config enables implementation of driver-model regulator uclass features for PWM regulators in SPL.
+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 implements get/set api for:
value and enable.
any thoughts on the feature set of this regulator?
config DM_REGULATOR_MAX77686 bool "Enable Driver Model for REGULATOR MAX77686" depends on DM_REGULATOR && DM_PMIC_MAX77686 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index d9e0cd5949..8d73169b50 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/max77663_regulator.c b/drivers/power/regulator/max77663_regulator.c new file mode 100644 index 0000000000..cbc275b30b --- /dev/null +++ b/drivers/power/regulator/max77663_regulator.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/max77663.h>
+/* fist row is control registers, second is voltage registers */ +static const char max77663_sd_reg[][MAX77663_SD_NUM] = {
{ 0x1d, 0x1e, 0x1f, 0x20, 0x21 },
{ 0x16, 0x17, 0x18, 0x19, 0x2a },
+};
+static const char max77663_ldo_reg[MAX77663_LDO_NUM] = {
0x23, 0x25, 0x27, 0x29, 0x2b, 0x2d, 0x2f, 0x31, 0x33
+};
+static int max77663_sd_enable(struct udevice *dev, int op, bool *enable) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->ctrl_reg;
int ret;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
if (ret & MAX77663_SD_STATUS_MASK)
*enable = true;
else
*enable = false;
return 0;
} else if (op == PMIC_OP_SET) {
ret &= ~(MAX77663_SD_STATUS_MASK);
If that macro is set up correct you should not need the ()
if (*enable)
ret |= MAX77663_SD_STATUS_MASK;
ret = pmic_reg_write(dev->parent, adr, ret);
if (ret)
return ret;
}
return 0;
+}
+static int max77663_sd_volt2hex(int sd, int uV) +{
switch (sd) {
case 0:
/* SD0 has max voltage 1.4V */
if (uV > MAX77663_SD0_VOLT_MAX)
return -EINVAL;
break;
case 1:
/* SD1 has max voltage 1.55V */
if (uV > MAX77663_SD1_VOLT_MAX)
return -EINVAL;
break;
default:
/* SD2 and SD3 have max voltage 3.79V */
if (uV > MAX77663_SD_VOLT_MAX)
return -EINVAL;
break;
};
if (uV < MAX77663_SD_VOLT_MIN)
uV = MAX77663_SD_VOLT_MIN;
return (uV - MAX77663_SD_VOLT_BASE) / 12500;
If the header file with these defines is only included by this driver, you can drop the MAX77663_ prefix and make everything shorter.
+}
+static int max77663_sd_hex2volt(int sd, int hex)
What is sd? What is hex? Please add a function comment.
+{
switch (sd) {
case 0:
/* SD0 has max voltage 1.4V */
if (hex > MAX77663_SD0_VOLT_MAX_HEX)
return -EINVAL;
break;
case 1:
/* SD1 has max voltage 1.55V */
if (hex > MAX77663_SD1_VOLT_MAX_HEX)
return -EINVAL;
break;
default:
/* SD2 and SD3 have max voltage 3.79V */
if (hex > MAX77663_SD_VOLT_MAX_HEX)
return -EINVAL;
break;
};
if (hex < MAX77663_SD_VOLT_MIN_HEX)
hex = MAX77663_SD_VOLT_MIN_HEX;
return MAX77663_SD_VOLT_BASE + hex * 12500;
+}
+static int max77663_sd_val(struct udevice *dev, int op, int *uV) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->volt_reg;
int id = dev->driver_data;
int ret;
if (op == PMIC_OP_GET) {
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
*uV = 0;
ret = max77663_sd_hex2volt(id, ret);
if (ret < 0)
return ret;
*uV = ret;
return 0;
}
/* SD regulators use entire register for voltage */
ret = max77663_sd_volt2hex(id, *uV);
if (ret < 0)
return ret;
return pmic_reg_write(dev->parent, adr, ret);
+}
+static int max77663_sd_probe(struct udevice *dev) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_BUCK;
uc_pdata->ctrl_reg = max77663_sd_reg[0][dev->driver_data];
uc_pdata->volt_reg = max77663_sd_reg[1][dev->driver_data];
return 0;
+}
+static int sd_get_value(struct udevice *dev) +{
int uV;
int ret;
ret = max77663_sd_val(dev, PMIC_OP_GET, &uV);
if (ret)
return ret;
return uV;
+}
+static int sd_set_value(struct udevice *dev, int uV) +{
return max77663_sd_val(dev, PMIC_OP_SET, &uV);
+}
+static int sd_get_enable(struct udevice *dev) +{
bool enable = false;
int ret;
ret = max77663_sd_enable(dev, PMIC_OP_GET, &enable);
if (ret)
return ret;
return enable;
+}
+static int sd_set_enable(struct udevice *dev, bool enable) +{
return max77663_sd_enable(dev, PMIC_OP_SET, &enable);
+}
+static const struct dm_regulator_ops max77663_sd_ops = {
.get_value = sd_get_value,
.set_value = sd_set_value,
.get_enable = sd_get_enable,
.set_enable = sd_set_enable,
+};
+U_BOOT_DRIVER(max77663_sd) = {
.name = MAX77663_SD_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &max77663_sd_ops,
.probe = max77663_sd_probe,
+};
+static int max77663_ldo_enable(struct udevice *dev, int op, bool *enable) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->ctrl_reg;
int ret;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
if (ret & MAX77663_LDO_STATUS_MASK)
*enable = true;
else
*enable = false;
return 0;
} else if (op == PMIC_OP_SET) {
ret &= ~(MAX77663_LDO_STATUS_MASK);
if (*enable)
ret |= MAX77663_LDO_STATUS_MASK;
ret = pmic_reg_write(dev->parent, adr, ret);
if (ret)
return ret;
}
return 0;
+}
+static int max77663_ldo_volt2hex(int ldo, int uV) +{
switch (ldo) {
case 0:
case 1:
if (uV > MAX77663_LDO01_VOLT_MAX)
return -EINVAL;
return (uV - MAX77663_LDO_VOLT_BASE) / 25000;
case 4:
if (uV > MAX77663_LDO4_VOLT_MAX)
return -EINVAL;
return (uV - MAX77663_LDO_VOLT_BASE) / 12500;
default:
if (uV > MAX77663_LDO_VOLT_MAX)
return -EINVAL;
return (uV - MAX77663_LDO_VOLT_BASE) / 50000;
};
+}
+static int max77663_ldo_hex2volt(int ldo, int hex) +{
if (hex > MAX77663_LDO_VOLT_MAX_HEX)
return -EINVAL;
switch (ldo) {
case 0:
case 1:
return (hex * 25000) + MAX77663_LDO_VOLT_BASE;
case 4:
return (hex * 12500) + MAX77663_LDO_VOLT_BASE;
default:
return (hex * 50000) + MAX77663_LDO_VOLT_BASE;
};
+}
+static int max77663_ldo_val(struct udevice *dev, int op, int *uV)
We tend to add a 'p' suffix to return values and use snake case, i.e int *uvp
+{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->ctrl_reg;
int id = dev->driver_data;
int hex, ret;
hex is a strange varname. Does it have an actual meaning? Perhaps it is the code for the microvolts?
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
*uV = 0;
ret = max77663_ldo_hex2volt(id, ret & MAX77663_LDO_VOLT_MASK);
if (ret < 0)
return ret;
*uV = ret;
return 0;
}
hex = max77663_ldo_volt2hex(id, *uV);
if (hex < 0)
return hex;
ret &= ~(MAX77663_LDO_VOLT_MASK);
return pmic_reg_write(dev->parent, adr, ret | hex);
+}
+static int max77663_ldo_probe(struct udevice *dev) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_LDO;
uc_pdata->ctrl_reg = max77663_ldo_reg[dev->driver_data];
return 0;
+}
+static int ldo_get_value(struct udevice *dev) +{
int uV;
int ret;
ret = max77663_ldo_val(dev, PMIC_OP_GET, &uV);
if (ret)
return ret;
return uV;
+}
+static int ldo_set_value(struct udevice *dev, int uV) +{
return max77663_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+static int ldo_get_enable(struct udevice *dev) +{
bool enable = false;
int ret;
ret = max77663_ldo_enable(dev, PMIC_OP_GET, &enable);
if (ret)
return ret;
return enable;
+}
+static int ldo_set_enable(struct udevice *dev, bool enable) +{
return max77663_ldo_enable(dev, PMIC_OP_SET, &enable);
+}
+static const struct dm_regulator_ops max77663_ldo_ops = {
.get_value = ldo_get_value,
.set_value = ldo_set_value,
.get_enable = ldo_get_enable,
.set_enable = ldo_set_enable,
+};
+U_BOOT_DRIVER(max77663_ldo) = {
.name = MAX77663_LDO_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &max77663_ldo_ops,
.probe = max77663_ldo_probe,
+};
2.39.2
Regards, Simon

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- doc/device-tree-bindings/pmic/tps80031.txt | 76 ++++++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/tps80031.c | 82 ++++++++++++++++++++++ include/power/tps80031.h | 42 +++++++++++ 5 files changed, 207 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/tps80031.txt create mode 100644 drivers/power/pmic/tps80031.c create mode 100644 include/power/tps80031.h
diff --git a/doc/device-tree-bindings/pmic/tps80031.txt b/doc/device-tree-bindings/pmic/tps80031.txt new file mode 100644 index 0000000000..577e6de1c1 --- /dev/null +++ b/doc/device-tree-bindings/pmic/tps80031.txt @@ -0,0 +1,76 @@ +Texas Instruments, TPS80031/TPS80032 PMIC + +This device uses two drivers: +- drivers/power/pmic/tps80031.c (for parent device) +- drivers/power/regulator/tps80031_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "ti,tps80031" or "ti,tps80032" +- reg: 0x48 + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for tps80031/tps80032: +- smps1, smps2, smps3, smps4, smps5 +- ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldoln, ldousb + +SMPS5 in Linux 3.1.10 is referred as vio, but datasheet clearly names it SMPS5. + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Example: + +tps80032@48 { + compatible = "ti,tps80032"; + reg = <0x48>; + + regulators { + smps1 { + regulator-name = "vdd_cpu"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1250000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + smps5 { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo1 { + regulator-name = "avdd_dsi_csi"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-boot-on; + }; + + ... + + ldousb { + regulator-name = "avdd_usb"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + }; +}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index abea0fe4ed..d25fe1ce0d 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -347,6 +347,12 @@ config DM_PMIC_TPS65910 DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO pmic children.
+config DM_PMIC_TPS80031 + bool "Enable driver for Texas Instruments TPS80031/TPS80032 PMIC" + ---help--- + The TPS80031/TPS80032 are PMIC's containing several LDOs, SMPS. + This driver binds the pmic children. + config PMIC_STPMIC1 bool "Enable support for STMicroelectronics STPMIC1 PMIC" depends on DM_I2C diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 414a9d8225..55ee614364 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_)PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o +obj-$(CONFIG_$(SPL_)DM_PMIC_TPS80031) += tps80031.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o diff --git a/drivers/power/pmic/tps80031.c b/drivers/power/pmic/tps80031.c new file mode 100644 index 0000000000..24c8a2c613 --- /dev/null +++ b/drivers/power/pmic/tps80031.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> +#include <dm/device.h> + +static const struct pmic_child_info pmic_children_info[] = { + { .prefix = "ldo", .driver = TPS80031_LDO_DRIVER }, + { .prefix = "smps", .driver = TPS80031_SMPS_DRIVER }, + { }, +}; + +static int tps80031_write(struct udevice *dev, uint reg, const uint8_t *buff, + int len) +{ + if (dm_i2c_write(dev, reg, buff, len)) { + log_err("write error to device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps80031_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{ + if (dm_i2c_read(dev, reg, buff, len)) { + log_err("read error from device: %p register: %#x!\n", dev, reg); + return -EIO; + } + + return 0; +} + +static int tps80031_bind(struct udevice *dev) +{ + ofnode regulators_node; + int children; + + regulators_node = dev_read_subnode(dev, "regulators"); + if (!ofnode_valid(regulators_node)) { + log_err("%s regulators subnode not found!\n", dev->name); + return -ENXIO; + } + + debug("%s: '%s' - found regulators subnode\n", __func__, dev->name); + + children = pmic_bind_children(dev, regulators_node, pmic_children_info); + if (!children) + log_err("%s - no child found\n", dev->name); + + /* Always return success for this device */ + return 0; +} + +static struct dm_pmic_ops tps80031_ops = { + .read = tps80031_read, + .write = tps80031_write, +}; + +static const struct udevice_id tps80031_ids[] = { + { .compatible = "ti,tps80031" }, + { .compatible = "ti,tps80032" }, + { } +}; + +U_BOOT_DRIVER(pmic_tps80031) = { + .name = "tps80031_pmic", + .id = UCLASS_PMIC, + .of_match = tps80031_ids, + .bind = tps80031_bind, + .ops = &tps80031_ops, +}; diff --git a/include/power/tps80031.h b/include/power/tps80031.h new file mode 100644 index 0000000000..f514db910b --- /dev/null +++ b/include/power/tps80031.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#ifndef _TPS80031_H_ +#define _TPS80031_H_ + +#define TPS80031_LDO_NUM 9 +#define TPS80031_SMPS_NUM 5 + +/* Drivers name */ +#define TPS80031_LDO_DRIVER "tps80031_ldo" +#define TPS80031_SMPS_DRIVER "tps80031_smps" + +#define TPS80031_SMPS_OFFSET 0xE0 +#define TPS80031_OFFSET_FLAG BIT(3) + +#define TPS80031_REGULATOR_STATUS_MASK 0x3 +#define TPS80031_REGULATOR_MODE_ON 0x1 + +#define TPS80031_SMPS_VOLT_MASK 0x3F +#define TPS80031_SMPS_VOLT_MAX_HEX 0x39 +#define TPS80031_SMPS_VOLT_MAX 1400000 +#define TPS80031_SMPS_VOLT_BASE 600000 +#define TPS80031_SMPS_VOLT_BASE_OFFSET 700000 + +#define TPS80031_LDO_VOLT_MASK 0x3F +#define TPS80031_LDO_VOLT_MAX_HEX 0x18 +#define TPS80031_LDO_VOLT_MIN_HEX 0x01 +#define TPS80031_LDO_VOLT_MAX 3360000 +#define TPS80031_LDO_VOLT_MIN 1018000 +#define TPS80031_LDO_VOLT_BASE 916000 + +/* register groups */ +enum { + CTRL, + VOLT, + OFFSET, +}; + +#endif /* _TPS80031_H_ */

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
doc/device-tree-bindings/pmic/tps80031.txt | 76 ++++++++++++++++++++ drivers/power/pmic/Kconfig | 6 ++ drivers/power/pmic/Makefile | 1 + drivers/power/pmic/tps80031.c | 82 ++++++++++++++++++++++ include/power/tps80031.h | 42 +++++++++++ 5 files changed, 207 insertions(+) create mode 100644 doc/device-tree-bindings/pmic/tps80031.txt create mode 100644 drivers/power/pmic/tps80031.c create mode 100644 include/power/tps80031.h
Reviewed-by: Simon Glass sjg@chromium.org
diff --git a/doc/device-tree-bindings/pmic/tps80031.txt b/doc/device-tree-bindings/pmic/tps80031.txt new file mode 100644 index 0000000000..577e6de1c1 --- /dev/null +++ b/doc/device-tree-bindings/pmic/tps80031.txt @@ -0,0 +1,76 @@ +Texas Instruments, TPS80031/TPS80032 PMIC
+This device uses two drivers: +- drivers/power/pmic/tps80031.c (for parent device) +- drivers/power/regulator/tps80031_regulator.c (for child regulators)
+This chapter describes the binding info for the PMIC driver and regulators.
+Required properties for PMIC: +- compatible: "ti,tps80031" or "ti,tps80032" +- reg: 0x48
+With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists.
+Optional subnode: +- name: regulators (subnode list of each device's regulator)
+Regulators subnode contains set on supported regulators.
+Required properties: +- regulator-name: used for regulator uclass platform data '.name',
+List of supported regulator nodes names for tps80031/tps80032: +- smps1, smps2, smps3, smps4, smps5 +- ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldoln, ldousb
+SMPS5 in Linux 3.1.10 is referred as vio, but datasheet clearly names it SMPS5.
+Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader
+Example:
+tps80032@48 {
compatible = "ti,tps80032";
reg = <0x48>;
regulators {
smps1 {
regulator-name = "vdd_cpu";
regulator-min-microvolt = <800000>;
regulator-max-microvolt = <1250000>;
regulator-always-on;
regulator-boot-on;
};
...
smps5 {
regulator-name = "vdd_1v8_gen";
regulator-min-microvolt = <1800000>;
regulator-max-microvolt = <1800000>;
regulator-always-on;
regulator-boot-on;
};
ldo1 {
regulator-name = "avdd_dsi_csi";
regulator-min-microvolt = <1200000>;
regulator-max-microvolt = <1200000>;
regulator-boot-on;
};
...
ldousb {
regulator-name = "avdd_usb";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
};
};
+}; diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig index abea0fe4ed..d25fe1ce0d 100644 --- a/drivers/power/pmic/Kconfig +++ b/drivers/power/pmic/Kconfig @@ -347,6 +347,12 @@ config DM_PMIC_TPS65910 DC-DC converter, 8 LDOs and a RTC. This driver binds the SMPS and LDO pmic children.
+config DM_PMIC_TPS80031
bool "Enable driver for Texas Instruments TPS80031/TPS80032 PMIC"
---help---
The TPS80031/TPS80032 are PMIC's containing several LDOs, SMPS.
This driver binds the pmic children.
SMPs ? What is that? Perhaps add a little more detail?
config PMIC_STPMIC1 bool "Enable support for STMicroelectronics STPMIC1 PMIC" depends on DM_I2C diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile index 414a9d8225..55ee614364 100644 --- a/drivers/power/pmic/Makefile +++ b/drivers/power/pmic/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_$(SPL_)PMIC_RN5T567) += rn5t567.o obj-$(CONFIG_PMIC_TPS65090) += tps65090.o obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o +obj-$(CONFIG_$(SPL_)DM_PMIC_TPS80031) += tps80031.o obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o diff --git a/drivers/power/pmic/tps80031.c b/drivers/power/pmic/tps80031.c new file mode 100644 index 0000000000..24c8a2c613 --- /dev/null +++ b/drivers/power/pmic/tps80031.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <i2c.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> +#include <dm/device.h>
That should be included by dm.h
+static const struct pmic_child_info pmic_children_info[] = {
{ .prefix = "ldo", .driver = TPS80031_LDO_DRIVER },
{ .prefix = "smps", .driver = TPS80031_SMPS_DRIVER },
{ },
+};
+static int tps80031_write(struct udevice *dev, uint reg, const uint8_t *buff,
u8
int len)
+{
if (dm_i2c_write(dev, reg, buff, len)) {
log_err("write error to device: %p register: %#x!\n", dev, reg);
return -EIO;
}
return 0;
+}
+static int tps80031_read(struct udevice *dev, uint reg, uint8_t *buff, int len) +{
if (dm_i2c_read(dev, reg, buff, len)) {
log_err("read error from device: %p register: %#x!\n", dev, reg);
return -EIO;
}
return 0;
+}
+static int tps80031_bind(struct udevice *dev) +{
ofnode regulators_node;
int children;
regulators_node = dev_read_subnode(dev, "regulators");
if (!ofnode_valid(regulators_node)) {
log_err("%s regulators subnode not found!\n", dev->name);
return -ENXIO;
}
debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
children = pmic_bind_children(dev, regulators_node, pmic_children_info);
if (!children)
log_err("%s - no child found\n", dev->name);
/* Always return success for this device */
return 0;
+}
+static struct dm_pmic_ops tps80031_ops = {
.read = tps80031_read,
.write = tps80031_write,
+};
+static const struct udevice_id tps80031_ids[] = {
{ .compatible = "ti,tps80031" },
{ .compatible = "ti,tps80032" },
{ }
+};
+U_BOOT_DRIVER(pmic_tps80031) = {
.name = "tps80031_pmic",
.id = UCLASS_PMIC,
.of_match = tps80031_ids,
.bind = tps80031_bind,
.ops = &tps80031_ops,
+}; diff --git a/include/power/tps80031.h b/include/power/tps80031.h new file mode 100644 index 0000000000..f514db910b --- /dev/null +++ b/include/power/tps80031.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#ifndef _TPS80031_H_ +#define _TPS80031_H_
+#define TPS80031_LDO_NUM 9 +#define TPS80031_SMPS_NUM 5
+/* Drivers name */ +#define TPS80031_LDO_DRIVER "tps80031_ldo" +#define TPS80031_SMPS_DRIVER "tps80031_smps"
+#define TPS80031_SMPS_OFFSET 0xE0 +#define TPS80031_OFFSET_FLAG BIT(3)
+#define TPS80031_REGULATOR_STATUS_MASK 0x3 +#define TPS80031_REGULATOR_MODE_ON 0x1
+#define TPS80031_SMPS_VOLT_MASK 0x3F +#define TPS80031_SMPS_VOLT_MAX_HEX 0x39 +#define TPS80031_SMPS_VOLT_MAX 1400000 +#define TPS80031_SMPS_VOLT_BASE 600000 +#define TPS80031_SMPS_VOLT_BASE_OFFSET 700000
+#define TPS80031_LDO_VOLT_MASK 0x3F +#define TPS80031_LDO_VOLT_MAX_HEX 0x18 +#define TPS80031_LDO_VOLT_MIN_HEX 0x01 +#define TPS80031_LDO_VOLT_MAX 3360000 +#define TPS80031_LDO_VOLT_MIN 1018000 +#define TPS80031_LDO_VOLT_BASE 916000
Similar point about the TPS80031_ prefix
+/* register groups */ +enum {
CTRL,
VOLT,
OFFSET,
+};
+#endif /* _TPS80031_H_ */
2.39.2
Regards, Simon

The driver provides regulator set/get voltage enable/disable functions for TI TPS80031/TPS80032 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps80031_regulator.c | 296 +++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/power/regulator/tps80031_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 571debd54e..f297e408cd 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -355,6 +355,14 @@ config DM_REGULATOR_TPS62360 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. + config DM_REGULATOR_STPMIC1 bool "Enable driver for STPMIC1 regulators" depends on DM_REGULATOR && PMIC_STPMIC1 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 8d73169b50..3ef55dc534 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o diff --git a/drivers/power/regulator/tps80031_regulator.c b/drivers/power/regulator/tps80031_regulator.c new file mode 100644 index 0000000000..56b6a7b684 --- /dev/null +++ b/drivers/power/regulator/tps80031_regulator.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <fdtdec.h> +#include <errno.h> +#include <dm.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps80031.h> + +static const char tps80031_smps_reg[][TPS80031_SMPS_NUM] = { + { 0x54, 0x5a, 0x66, 0x42, 0x48 }, + { 0x56, 0x5c, 0x68, 0x44, 0x4a }, + { BIT(3), BIT(4), BIT(6), BIT(0), BIT(1) }, +}; + +static const char tps80031_ldo_reg[][TPS80031_LDO_NUM] = { + { 0x9e, 0x86, 0x8e, 0x8a, 0x9a, 0x92, 0xa6, 0x96, 0xa2 }, + { 0x9f, 0x87, 0x8f, 0x8b, 0x9b, 0x93, 0xa7, 0x97, 0xa3 }, +}; + +static int tps80031_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & TPS80031_REGULATOR_MODE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(TPS80031_REGULATOR_STATUS_MASK); + + if (*enable) + ret |= TPS80031_REGULATOR_MODE_ON; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps80031_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps80031_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps80031_set_enable(struct udevice *dev, bool enable) +{ + return tps80031_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +static int tps80031_ldo_volt2hex(int uV) +{ + if (uV > TPS80031_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < TPS80031_LDO_VOLT_MIN) + uV = TPS80031_LDO_VOLT_MIN; + + return DIV_ROUND_UP(uV - TPS80031_LDO_VOLT_BASE, 102000); +} + +static int tps80031_ldo_hex2volt(int hex) +{ + if (hex > TPS80031_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < TPS80031_LDO_VOLT_MIN_HEX) + hex = TPS80031_LDO_VOLT_MIN_HEX; + + return TPS80031_LDO_VOLT_BASE + hex * 102000; +} + +static int tps80031_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_ldo_hex2volt(ret & TPS80031_LDO_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_ldo_volt2hex(*uV); + if (hex < 0) + return hex; + + ret &= ~TPS80031_LDO_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps80031_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_LDO; + + /* check for ldoln and ldousb cases */ + if (!strcmp("ldoln", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][7]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][7]; + return 0; + } + + if (!strcmp("ldousb", dev->name)) { + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][8]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][8]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps80031_ldo_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_ldo_reg[VOLT][idx]; + } + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps80031_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_ldo) = { + .name = TPS80031_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_ldo_ops, + .probe = tps80031_ldo_probe, +}; + +static int tps80031_smps_volt2hex(u32 base, int uV) +{ + if (uV > TPS80031_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < base) + return 1; + + return DIV_ROUND_UP(uV - base, 12500); +} + +static int tps80031_smps_hex2volt(u32 base, int hex) +{ + if (hex > TPS80031_LDO_VOLT_MAX_HEX) + return -EINVAL; + + if (!hex) + return 0; + + return base + hex * 12500; +} + +static int tps80031_smps_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int base, hex, ret; + + /* If offset flag was set then base voltage is higher */ + if (uc_pdata->flags & TPS80031_OFFSET_FLAG) + base = TPS80031_SMPS_VOLT_BASE_OFFSET; + else + base = TPS80031_SMPS_VOLT_BASE; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps80031_smps_hex2volt(base, ret & TPS80031_SMPS_VOLT_MASK); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps80031_smps_volt2hex(base, *uV); + if (hex < 0) + return hex; + + ret &= ~TPS80031_SMPS_VOLT_MASK; + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps80031_smps_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + int idx = dev->driver_data - 1; + int ret; + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + uc_pdata->ctrl_reg = tps80031_smps_reg[CTRL][idx]; + uc_pdata->volt_reg = tps80031_smps_reg[VOLT][idx]; + + /* Determine if smps regulator uses higher voltage */ + ret = pmic_reg_read(dev->parent, TPS80031_SMPS_OFFSET); + if (ret & tps80031_smps_reg[OFFSET][idx]) + uc_pdata->flags |= TPS80031_OFFSET_FLAG; + + return 0; +} + +static int smps_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps80031_smps_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int smps_set_value(struct udevice *dev, int uV) +{ + return tps80031_smps_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps80031_smps_ops = { + .get_value = smps_get_value, + .set_value = smps_set_value, + .get_enable = tps80031_get_enable, + .set_enable = tps80031_set_enable, +}; + +U_BOOT_DRIVER(tps80031_smps) = { + .name = TPS80031_SMPS_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps80031_smps_ops, + .probe = tps80031_smps_probe, +};

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
The driver provides regulator set/get voltage enable/disable functions for TI TPS80031/TPS80032 PMICs.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps80031_regulator.c | 296 +++++++++++++++++++ 3 files changed, 305 insertions(+) create mode 100644 drivers/power/regulator/tps80031_regulator.c
Reviewed-by: Simon Glass sjg@chromium.org
This is quite nice code. Consider the 'p' suffix for return vars and also the meaning of 'hex'.
Regards, Simon

Add support to bind the regulators/child nodes with the pmic. Also adds the pmic i2c based read/write functions to access pmic registers.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com Reviewed-by: Simon Glass sjg@chromium.org --- doc/device-tree-bindings/pmic/tps65911.txt | 78 ++++++++++++++++++++++ drivers/power/pmic/pmic_tps65910_dm.c | 26 +++++++- include/power/tps65910_pmic.h | 47 +++++++++++++ 3 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 doc/device-tree-bindings/pmic/tps65911.txt
diff --git a/doc/device-tree-bindings/pmic/tps65911.txt b/doc/device-tree-bindings/pmic/tps65911.txt new file mode 100644 index 0000000000..29270efbfe --- /dev/null +++ b/doc/device-tree-bindings/pmic/tps65911.txt @@ -0,0 +1,78 @@ +Texas Instruments, TPS65911 PMIC + +This device uses two drivers: +- drivers/power/pmic/tps65910.c (for parent device) +- drivers/power/regulator/tps65911_regulator.c (for child regulators) + +This chapter describes the binding info for the PMIC driver and regulators. + +Required properties for PMIC: +- compatible: "ti,tps65911" +- reg: 0x2d + +With those two properties, the pmic device can be used for read/write only. +To bind each regulator, the optional regulators subnode should exists. + +Optional subnode: +- name: regulators (subnode list of each device's regulator) + +Regulators subnode contains set on supported regulators. + +Required properties: +- regulator-name: used for regulator uclass platform data '.name', + +List of supported regulator nodes names for tps65911: +- vdd1, vdd2, vddctrl, vddio +- ldo1, ldo2, ldo3, ldo4, ldo5, ldo6, ldo7, ldo8 + +vddio in datasheet is referred as vio, but for reduction of code and +unification of smps regulators it is named vddio. + +Optional: +- regulator-min-microvolt: minimum allowed Voltage to set +- regulator-max-microvolt: minimum allowed Voltage to set +- regulator-always-on: regulator should be never disabled +- regulator-boot-on: regulator should be enabled by the bootloader + +Example: + +tps65911@2d { + compatible = "ti,tps65911"; + reg = <0x2d>; + + regulators { + vdd1 { + regulator-name = "vdd_1v2_backlight"; + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <1200000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + vddio { + regulator-name = "vdd_1v8_gen"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + regulator-boot-on; + }; + + ldo1 { + regulator-name = "vdd_emmc_core"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-always-on; + regulator-boot-on; + }; + + ... + + ldo8 { + regulator-name = "vdd_ddr_hs"; + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <1000000>; + }; + }; +}; diff --git a/drivers/power/pmic/pmic_tps65910_dm.c b/drivers/power/pmic/pmic_tps65910_dm.c index e03ddc98d7..ff63c502d0 100644 --- a/drivers/power/pmic/pmic_tps65910_dm.c +++ b/drivers/power/pmic/pmic_tps65910_dm.c @@ -11,13 +11,19 @@ #include <power/regulator.h> #include <power/tps65910_pmic.h>
-static const struct pmic_child_info pmic_children_info[] = { +static const struct pmic_child_info tps65910_children_info[] = { { .prefix = "ldo_", .driver = TPS65910_LDO_DRIVER }, { .prefix = "buck_", .driver = TPS65910_BUCK_DRIVER }, { .prefix = "boost_", .driver = TPS65910_BOOST_DRIVER }, { }, };
+static const struct pmic_child_info tps65911_children_info[] = { + { .prefix = "ldo", .driver = TPS65911_LDO_DRIVER }, + { .prefix = "vdd", .driver = TPS65911_VDD_DRIVER }, + { }, +}; + static int pmic_tps65910_reg_count(struct udevice *dev) { return TPS65910_NUM_REGS; @@ -51,6 +57,7 @@ static int pmic_tps65910_bind(struct udevice *dev) { ofnode regulators_node; int children; + int type = dev_get_driver_data(dev);
regulators_node = dev_read_subnode(dev, "regulators"); if (!ofnode_valid(regulators_node)) { @@ -58,7 +65,19 @@ static int pmic_tps65910_bind(struct udevice *dev) return -EINVAL; }
- children = pmic_bind_children(dev, regulators_node, pmic_children_info); + switch (type) { + case TPS65910: + children = pmic_bind_children(dev, regulators_node, + tps65910_children_info); + break; + case TPS65911: + children = pmic_bind_children(dev, regulators_node, + tps65911_children_info); + break; + default: + log_err("unknown PMIC type\n"); + } + if (!children) debug("%s has no children (regulators)\n", dev->name);
@@ -82,7 +101,8 @@ static struct dm_pmic_ops pmic_tps65910_ops = { };
static const struct udevice_id pmic_tps65910_match[] = { - { .compatible = "ti,tps65910" }, + { .compatible = "ti,tps65910", .data = TPS65910 }, + { .compatible = "ti,tps65911", .data = TPS65911 }, { /* sentinel */ } };
diff --git a/include/power/tps65910_pmic.h b/include/power/tps65910_pmic.h index 66214786d3..10fe02b81a 100644 --- a/include/power/tps65910_pmic.h +++ b/include/power/tps65910_pmic.h @@ -6,6 +6,9 @@ #ifndef __TPS65910_PMIC_H_ #define __TPS65910_PMIC_H_
+#define TPS65910 1 +#define TPS65911 2 + #define TPS65910_I2C_SEL_MASK (0x1 << 4) #define TPS65910_VDD_SR_MASK (0x1 << 7) #define TPS65910_GAIN_SEL_MASK (0x3 << 6) @@ -126,4 +129,48 @@ struct tps65910_regulator_pdata { #define TPS65910_BOOST_DRIVER "tps65910_boost" #define TPS65910_LDO_DRIVER "tps65910_ldo"
+/* tps65911 i2c registers */ +enum { + TPS65911_REG_VIO = 0x20, + TPS65911_REG_VDD1, + TPS65911_REG_VDD1_OP, + TPS65911_REG_VDD1_SR, + TPS65911_REG_VDD2, + TPS65911_REG_VDD2_OP, + TPS65911_REG_VDD2_SR, + TPS65911_REG_VDDCTRL, + TPS65911_REG_VDDCTRL_OP, + TPS65911_REG_VDDCTRL_SR, + TPS65911_REG_LDO1 = 0x30, + TPS65911_REG_LDO2, + TPS65911_REG_LDO5, + TPS65911_REG_LDO8, + TPS65911_REG_LDO7, + TPS65911_REG_LDO6, + TPS65911_REG_LDO4, + TPS65911_REG_LDO3, +}; + +#define TPS65911_VDD_NUM 4 +#define TPS65911_LDO_NUM 8 + +#define TPS65911_VDD_VOLT_MAX 1500000 +#define TPS65911_VDD_VOLT_MIN 600000 +#define TPS65911_VDD_VOLT_BASE 562500 + +#define TPS65911_LDO_VOLT_MAX 3300000 +#define TPS65911_LDO_VOLT_BASE 800000 + +#define TPS65911_LDO_SEL_MASK (0x3f << 2) + +#define TPS65911_LDO124_VOLT_MAX_HEX 0x32 +#define TPS65911_LDO358_VOLT_MAX_HEX 0x19 +#define TPS65911_LDO358_VOLT_MIN_HEX 0x02 + +#define TPS65911_LDO124_VOLT_STEP 50000 +#define TPS65911_LDO358_VOLT_STEP 100000 + +#define TPS65911_VDD_DRIVER "tps65911_vdd" +#define TPS65911_LDO_DRIVER "tps65911_ldo" + #endif /* __TPS65910_PMIC_H_ */

The driver provides regulator set/get voltage enable/disable functions for TI TPS5911 PMIC.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com --- drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps65911_regulator.c | 377 +++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 drivers/power/regulator/tps65911_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index f297e408cd..3398f0fbee 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -345,6 +345,14 @@ config DM_REGULATOR_TPS65910 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--- + The TPS65911 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all + regulator types of the TPS65911. It implements the get/set api for value + and enable. + config DM_REGULATOR_TPS62360 bool "Enable driver for TPS6236x Power Regulator" depends on DM_REGULATOR diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 3ef55dc534..f79932d833 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o diff --git a/drivers/power/regulator/tps65911_regulator.c b/drivers/power/regulator/tps65911_regulator.c new file mode 100644 index 0000000000..f1b3a8295e --- /dev/null +++ b/drivers/power/regulator/tps65911_regulator.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65910_pmic.h> + +/* fist row is control registers, second is voltage registers */ +static const char tps65911_vdd_reg[][TPS65911_VDD_NUM] = { + { TPS65911_REG_VDD1, TPS65911_REG_VDD2, + TPS65911_REG_VDDCTRL, TPS65911_REG_VIO }, + { TPS65911_REG_VDD1_OP, TPS65911_REG_VDD2_OP, + TPS65911_REG_VDDCTRL_OP, 0x00 }, +}; + +static const u32 tps65911_vio_range[] = { + 1500000, 1800000, 2500000, 3300000 +}; + +static const char tps65911_ldo_reg[TPS65911_LDO_NUM] = { + TPS65911_REG_LDO1, TPS65911_REG_LDO2, TPS65911_REG_LDO3, + TPS65911_REG_LDO4, TPS65911_REG_LDO5, TPS65911_REG_LDO6, + TPS65911_REG_LDO7, TPS65911_REG_LDO8 +}; + +static int tps65911_regulator_enable(struct udevice *dev, int op, bool *enable) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + if (ret & TPS65910_SUPPLY_STATE_ON) + *enable = true; + else + *enable = false; + + return 0; + } else if (op == PMIC_OP_SET) { + ret &= ~(TPS65910_SUPPLY_STATE_MASK); + + if (*enable) + ret |= TPS65910_SUPPLY_STATE_ON; + + ret = pmic_reg_write(dev->parent, adr, ret); + if (ret) + return ret; + } + + return 0; +} + +static int tps65911_get_enable(struct udevice *dev) +{ + bool enable = false; + int ret; + + ret = tps65911_regulator_enable(dev, PMIC_OP_GET, &enable); + if (ret) + return ret; + + return enable; +} + +static int tps65911_set_enable(struct udevice *dev, bool enable) +{ + return tps65911_regulator_enable(dev, PMIC_OP_SET, &enable); +} + +static int tps65911_vdd_volt2hex(int uV) +{ + if (uV > TPS65911_VDD_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_VDD_VOLT_MIN) + uV = TPS65911_VDD_VOLT_MIN; + + return (uV - TPS65911_VDD_VOLT_BASE) / 12500; +} + +static int tps65911_vdd_hex2volt(int hex) +{ + if (hex > TPS65910_VDD_SEL_MAX) + return -EINVAL; + + if (hex < TPS65910_VDD_SEL_MIN) + hex = TPS65910_VDD_SEL_MIN; + + return TPS65911_VDD_VOLT_BASE + hex * 12500; +} + +static int tps65911_vio_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret &= TPS65910_SEL_MASK; + + *uV = tps65911_vio_range[ret >> 2]; + + return 0; + } + + ret &= ~TPS65910_SEL_MASK; + + /* vio has only 4 voltages available */ + switch (*uV) { + case 1500000: + ret |= 0x00 << 2; + break; + case 1800000: + ret |= 0x01 << 2; + break; + case 2500000: + ret |= 0x02 << 2; + break; + case 3300000: + ret |= 0x03 << 2; + break; + default: + return -EINVAL; + }; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int tps65911_vdd_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->volt_reg; + int ret; + + /* in case vdd is vio */ + if (!adr) + return tps65911_vio_val(dev, op, uV); + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + + ret = tps65911_vdd_hex2volt(ret); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + ret = tps65911_vdd_volt2hex(*uV); + if (ret < 0) + return ret; + + return pmic_reg_write(dev->parent, adr, ret); +} + +static int tps65911_vdd_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + + uc_pdata->type = REGULATOR_TYPE_BUCK; + + /* check for vddctrl and vddio cases */ + if (!strcmp("vddctrl", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][2]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][2]; + return 0; + } + + if (!strcmp("vddio", dev->name)) { + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][3]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][3]; + return 0; + } + + if (dev->driver_data > 0) { + u8 idx = dev->driver_data - 1; + + uc_pdata->ctrl_reg = tps65911_vdd_reg[0][idx]; + uc_pdata->volt_reg = tps65911_vdd_reg[1][idx]; + } + + return 0; +} + +static int vdd_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_vdd_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int vdd_set_value(struct udevice *dev, int uV) +{ + return tps65911_vdd_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_vdd_ops = { + .get_value = vdd_get_value, + .set_value = vdd_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_vdd) = { + .name = TPS65911_VDD_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_vdd_ops, + .probe = tps65911_vdd_probe, +}; + +static int tps65911_ldo_volt2hex(int id, int uV) +{ + int step; + + if (uV > TPS65911_LDO_VOLT_MAX) + return -EINVAL; + + if (uV < TPS65911_LDO_VOLT_BASE) + uV = TPS65911_LDO_VOLT_BASE; + + switch (id) { + case 1: + case 2: + case 4: + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return ((uV - TPS65911_LDO_VOLT_BASE) / step) << 2; +} + +static int tps65911_ldo_hex2volt(int id, int hex) +{ + int step; + + switch (id) { + case 1: + case 2: + case 4: + if (hex > TPS65911_LDO124_VOLT_MAX_HEX) + return -EINVAL; + + step = TPS65911_LDO124_VOLT_STEP; + break; + case 3: + case 5: + case 6: + case 7: + case 8: + if (hex > TPS65911_LDO358_VOLT_MAX_HEX) + return -EINVAL; + + if (hex < TPS65911_LDO358_VOLT_MIN_HEX) + hex = TPS65911_LDO358_VOLT_MIN_HEX; + + step = TPS65911_LDO358_VOLT_STEP; + break; + default: + return -EINVAL; + }; + + return TPS65911_LDO_VOLT_BASE + hex * step; +} + +static int tps65911_ldo_val(struct udevice *dev, int op, int *uV) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u32 adr = uc_pdata->ctrl_reg; + int id = dev->driver_data; + int hex, ret; + + ret = pmic_reg_read(dev->parent, adr); + if (ret < 0) + return ret; + + if (op == PMIC_OP_GET) { + *uV = 0; + ret &= TPS65911_LDO_SEL_MASK; + + ret = tps65911_ldo_hex2volt(id, ret >> 2); + if (ret < 0) + return ret; + + *uV = ret; + return 0; + } + + hex = tps65911_ldo_volt2hex(id, *uV); + if (hex < 0) + return hex; + + ret &= ~(TPS65911_LDO_SEL_MASK); + + return pmic_reg_write(dev->parent, adr, ret | hex); +} + +static int tps65911_ldo_probe(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_pdata = + dev_get_uclass_plat(dev); + u8 idx = dev->driver_data - 1; + + uc_pdata->type = REGULATOR_TYPE_LDO; + uc_pdata->ctrl_reg = tps65911_ldo_reg[idx]; + + return 0; +} + +static int ldo_get_value(struct udevice *dev) +{ + int uV; + int ret; + + ret = tps65911_ldo_val(dev, PMIC_OP_GET, &uV); + if (ret) + return ret; + + return uV; +} + +static int ldo_set_value(struct udevice *dev, int uV) +{ + return tps65911_ldo_val(dev, PMIC_OP_SET, &uV); +} + +static const struct dm_regulator_ops tps65911_ldo_ops = { + .get_value = ldo_get_value, + .set_value = ldo_set_value, + .get_enable = tps65911_get_enable, + .set_enable = tps65911_set_enable, +}; + +U_BOOT_DRIVER(tps65911_ldo) = { + .name = TPS65911_LDO_DRIVER, + .id = UCLASS_REGULATOR, + .ops = &tps65911_ldo_ops, + .probe = tps65911_ldo_probe, +};

Hi Svyatoslav,
On Sun, 23 Jul 2023 at 06:28, Svyatoslav Ryhel clamor95@gmail.com wrote:
The driver provides regulator set/get voltage enable/disable functions for TI TPS5911 PMIC.
Signed-off-by: Svyatoslav Ryhel clamor95@gmail.com
drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/tps65911_regulator.c | 377 +++++++++++++++++++ 3 files changed, 386 insertions(+) create mode 100644 drivers/power/regulator/tps65911_regulator.c
Similar comments to previous patches
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index f297e408cd..3398f0fbee 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -345,6 +345,14 @@ config DM_REGULATOR_TPS65910 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---
The TPS65911 PMIC provides 4 SMPSs and 8 LDOs. This driver supports all
regulator types of the TPS65911. It implements the get/set api for value
and enable.
docs about the actual regulator are helpful
config DM_REGULATOR_TPS62360 bool "Enable driver for TPS6236x Power Regulator" depends on DM_REGULATOR diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index 3ef55dc534..f79932d833 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o +obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_STPMIC1) += stpmic1.o diff --git a/drivers/power/regulator/tps65911_regulator.c b/drivers/power/regulator/tps65911_regulator.c new file mode 100644 index 0000000000..f1b3a8295e --- /dev/null +++ b/drivers/power/regulator/tps65911_regulator.c @@ -0,0 +1,377 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright(C) 2023 Svyatoslav Ryhel clamor95@gmail.com
- */
+#include <common.h> +#include <dm.h> +#include <log.h> +#include <power/pmic.h> +#include <power/regulator.h> +#include <power/tps65910_pmic.h>
+/* fist row is control registers, second is voltage registers */ +static const char tps65911_vdd_reg[][TPS65911_VDD_NUM] = {
{ TPS65911_REG_VDD1, TPS65911_REG_VDD2,
TPS65911_REG_VDDCTRL, TPS65911_REG_VIO },
{ TPS65911_REG_VDD1_OP, TPS65911_REG_VDD2_OP,
TPS65911_REG_VDDCTRL_OP, 0x00 },
+};
+static const u32 tps65911_vio_range[] = {
1500000, 1800000, 2500000, 3300000
+};
+static const char tps65911_ldo_reg[TPS65911_LDO_NUM] = {
TPS65911_REG_LDO1, TPS65911_REG_LDO2, TPS65911_REG_LDO3,
TPS65911_REG_LDO4, TPS65911_REG_LDO5, TPS65911_REG_LDO6,
TPS65911_REG_LDO7, TPS65911_REG_LDO8
+};
+static int tps65911_regulator_enable(struct udevice *dev, int op, bool *enable)
enablep ?
+{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->ctrl_reg;
int ret;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
if (ret & TPS65910_SUPPLY_STATE_ON)
*enable = true;
else
*enable = false;
return 0;
} else if (op == PMIC_OP_SET) {
ret &= ~(TPS65910_SUPPLY_STATE_MASK);
drop () ?
if (*enable)
ret |= TPS65910_SUPPLY_STATE_ON;
ret = pmic_reg_write(dev->parent, adr, ret);
if (ret)
return ret;
}
return 0;
+}
+static int tps65911_get_enable(struct udevice *dev) +{
bool enable = false;
int ret;
ret = tps65911_regulator_enable(dev, PMIC_OP_GET, &enable);
if (ret)
return ret;
return enable;
+}
+static int tps65911_set_enable(struct udevice *dev, bool enable) +{
return tps65911_regulator_enable(dev, PMIC_OP_SET, &enable);
+}
+static int tps65911_vdd_volt2hex(int uV)
uvolt
+{
if (uV > TPS65911_VDD_VOLT_MAX)
return -EINVAL;
if (uV < TPS65911_VDD_VOLT_MIN)
uV = TPS65911_VDD_VOLT_MIN;
return (uV - TPS65911_VDD_VOLT_BASE) / 12500;
+}
+static int tps65911_vdd_hex2volt(int hex)
hex doesn't mean much - perhaps it is a code or a regval?
+{
if (hex > TPS65910_VDD_SEL_MAX)
return -EINVAL;
if (hex < TPS65910_VDD_SEL_MIN)
hex = TPS65910_VDD_SEL_MIN;
return TPS65911_VDD_VOLT_BASE + hex * 12500;
+}
+static int tps65911_vio_val(struct udevice *dev, int op, int *uV) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->volt_reg;
int ret;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
*uV = 0;
ret &= TPS65910_SEL_MASK;
Please avoid resusing the 'ret' var. It is only for return values, not for internal computation. The compiler will optimise things anyway.
*uV = tps65911_vio_range[ret >> 2];
return 0;
}
ret &= ~TPS65910_SEL_MASK;
/* vio has only 4 voltages available */
switch (*uV) {
case 1500000:
ret |= 0x00 << 2;
break;
case 1800000:
ret |= 0x01 << 2;
break;
case 2500000:
ret |= 0x02 << 2;
break;
case 3300000:
ret |= 0x03 << 2;
break;
default:
return -EINVAL;
};
return pmic_reg_write(dev->parent, adr, ret);
+}
+static int tps65911_vdd_val(struct udevice *dev, int op, int *uV) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->volt_reg;
int ret;
/* in case vdd is vio */
if (!adr)
return tps65911_vio_val(dev, op, uV);
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
*uV = 0;
ret = tps65911_vdd_hex2volt(ret);
if (ret < 0)
return ret;
*uV = ret;
return 0;
}
ret = tps65911_vdd_volt2hex(*uV);
if (ret < 0)
return ret;
return pmic_reg_write(dev->parent, adr, ret);
+}
+static int tps65911_vdd_probe(struct udevice *dev) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
uc_pdata->type = REGULATOR_TYPE_BUCK;
/* check for vddctrl and vddio cases */
if (!strcmp("vddctrl", dev->name)) {
uc_pdata->ctrl_reg = tps65911_vdd_reg[0][2];
uc_pdata->volt_reg = tps65911_vdd_reg[1][2];
return 0;
}
if (!strcmp("vddio", dev->name)) {
uc_pdata->ctrl_reg = tps65911_vdd_reg[0][3];
uc_pdata->volt_reg = tps65911_vdd_reg[1][3];
return 0;
}
if (dev->driver_data > 0) {
u8 idx = dev->driver_data - 1;
uc_pdata->ctrl_reg = tps65911_vdd_reg[0][idx];
uc_pdata->volt_reg = tps65911_vdd_reg[1][idx];
}
return 0;
+}
+static int vdd_get_value(struct udevice *dev) +{
int uV;
int ret;
ret = tps65911_vdd_val(dev, PMIC_OP_GET, &uV);
if (ret)
return ret;
return uV;
+}
+static int vdd_set_value(struct udevice *dev, int uV) +{
return tps65911_vdd_val(dev, PMIC_OP_SET, &uV);
+}
+static const struct dm_regulator_ops tps65911_vdd_ops = {
.get_value = vdd_get_value,
.set_value = vdd_set_value,
.get_enable = tps65911_get_enable,
.set_enable = tps65911_set_enable,
+};
+U_BOOT_DRIVER(tps65911_vdd) = {
.name = TPS65911_VDD_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &tps65911_vdd_ops,
.probe = tps65911_vdd_probe,
+};
+static int tps65911_ldo_volt2hex(int id, int uV) +{
int step;
if (uV > TPS65911_LDO_VOLT_MAX)
return -EINVAL;
if (uV < TPS65911_LDO_VOLT_BASE)
uV = TPS65911_LDO_VOLT_BASE;
switch (id) {
case 1:
case 2:
case 4:
step = TPS65911_LDO124_VOLT_STEP;
break;
case 3:
case 5:
case 6:
case 7:
case 8:
step = TPS65911_LDO358_VOLT_STEP;
break;
default:
return -EINVAL;
};
return ((uV - TPS65911_LDO_VOLT_BASE) / step) << 2;
+}
+static int tps65911_ldo_hex2volt(int id, int hex) +{
int step;
switch (id) {
case 1:
case 2:
case 4:
if (hex > TPS65911_LDO124_VOLT_MAX_HEX)
return -EINVAL;
step = TPS65911_LDO124_VOLT_STEP;
break;
case 3:
case 5:
case 6:
case 7:
case 8:
if (hex > TPS65911_LDO358_VOLT_MAX_HEX)
return -EINVAL;
if (hex < TPS65911_LDO358_VOLT_MIN_HEX)
hex = TPS65911_LDO358_VOLT_MIN_HEX;
step = TPS65911_LDO358_VOLT_STEP;
break;
default:
return -EINVAL;
};
return TPS65911_LDO_VOLT_BASE + hex * step;
+}
+static int tps65911_ldo_val(struct udevice *dev, int op, int *uV) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u32 adr = uc_pdata->ctrl_reg;
int id = dev->driver_data;
int hex, ret;
ret = pmic_reg_read(dev->parent, adr);
if (ret < 0)
return ret;
if (op == PMIC_OP_GET) {
*uV = 0;
ret &= TPS65911_LDO_SEL_MASK;
ret = tps65911_ldo_hex2volt(id, ret >> 2);
if (ret < 0)
return ret;
*uV = ret;
return 0;
}
hex = tps65911_ldo_volt2hex(id, *uV);
if (hex < 0)
return hex;
ret &= ~(TPS65911_LDO_SEL_MASK);
return pmic_reg_write(dev->parent, adr, ret | hex);
+}
+static int tps65911_ldo_probe(struct udevice *dev) +{
struct dm_regulator_uclass_plat *uc_pdata =
dev_get_uclass_plat(dev);
u8 idx = dev->driver_data - 1;
uc_pdata->type = REGULATOR_TYPE_LDO;
uc_pdata->ctrl_reg = tps65911_ldo_reg[idx];
return 0;
+}
+static int ldo_get_value(struct udevice *dev) +{
int uV;
int ret;
ret = tps65911_ldo_val(dev, PMIC_OP_GET, &uV);
if (ret)
return ret;
return uV;
+}
+static int ldo_set_value(struct udevice *dev, int uV) +{
return tps65911_ldo_val(dev, PMIC_OP_SET, &uV);
+}
+static const struct dm_regulator_ops tps65911_ldo_ops = {
.get_value = ldo_get_value,
.set_value = ldo_set_value,
.get_enable = tps65911_get_enable,
.set_enable = tps65911_set_enable,
+};
+U_BOOT_DRIVER(tps65911_ldo) = {
.name = TPS65911_LDO_DRIVER,
.id = UCLASS_REGULATOR,
.ops = &tps65911_ldo_ops,
.probe = tps65911_ldo_probe,
+};
2.39.2
Regards, Simon
participants (2)
-
Simon Glass
-
Svyatoslav Ryhel