[PATCH 0/3] power: X-Powers PMIC regulator support

This series adds a driver for the regulators in X-Powers AXPxxx PMICs. It supports everything except regulators shared with GPIO pins. Those have a different register interface, so they may need a separate driver.
Regulator setup in U-Boot proper is needed for Ethernet and HDMI/LCD display output. For some SoCs (H616), this driver could possibly be used for the DRAM regulator setup done in SPL. Older SoCs do all of their regulator setup in SPL. Some of that is probably necessary, but I think a lot of it is not and can be deferred to U-Boot proper or Linux; that's just where it was convenient to initialize the PMIC at the time.
The main goal here is to replace the corresponding code in TF-A, both because of TF-A size constraints, and because the TF-A code is too simple/overzealous and breaks EPHY power sequencing on some boards.
Samuel Holland (3): power: pmic: axp: Provide a variant ID in the driver data power: regulator: Add a driver for AXP PMIC regulators power: pmic: axp: Bind regulators from the DT
drivers/power/pmic/axp.c | 36 ++- drivers/power/regulator/Kconfig | 14 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/axp_regulator.c | 308 ++++++++++++++++++++++++ include/axp_pmic.h | 12 + 5 files changed, 362 insertions(+), 9 deletions(-) create mode 100644 drivers/power/regulator/axp_regulator.c

Subordinate regulator drivers can use this enumerated ID instead of matching the compatible string again.
Signed-off-by: Samuel Holland samuel@sholland.org ---
drivers/power/pmic/axp.c | 18 +++++++++--------- include/axp_pmic.h | 12 ++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 0f2b24a8b5f..e0005994e21 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -64,15 +64,15 @@ static int axp_pmic_bind(struct udevice *dev) }
static const struct udevice_id axp_pmic_ids[] = { - { .compatible = "x-powers,axp152" }, - { .compatible = "x-powers,axp202" }, - { .compatible = "x-powers,axp209" }, - { .compatible = "x-powers,axp221" }, - { .compatible = "x-powers,axp223" }, - { .compatible = "x-powers,axp803" }, - { .compatible = "x-powers,axp806" }, - { .compatible = "x-powers,axp809" }, - { .compatible = "x-powers,axp813" }, + { .compatible = "x-powers,axp152", .data = AXP152_ID }, + { .compatible = "x-powers,axp202", .data = AXP202_ID }, + { .compatible = "x-powers,axp209", .data = AXP209_ID }, + { .compatible = "x-powers,axp221", .data = AXP221_ID }, + { .compatible = "x-powers,axp223", .data = AXP223_ID }, + { .compatible = "x-powers,axp803", .data = AXP803_ID }, + { .compatible = "x-powers,axp806", .data = AXP806_ID }, + { .compatible = "x-powers,axp809", .data = AXP809_ID }, + { .compatible = "x-powers,axp813", .data = AXP813_ID }, { } };
diff --git a/include/axp_pmic.h b/include/axp_pmic.h index 01ebba63479..4ac64865831 100644 --- a/include/axp_pmic.h +++ b/include/axp_pmic.h @@ -26,6 +26,18 @@ #define AXP_PMIC_SEC_DEVICE_ADDR 0x745 #define AXP_PMIC_SEC_RUNTIME_ADDR 0x3a
+enum { + AXP152_ID, + AXP202_ID, + AXP209_ID, + AXP221_ID, + AXP223_ID, + AXP803_ID, + AXP806_ID, + AXP809_ID, + AXP813_ID, +}; + int axp_set_dcdc1(unsigned int mvolt); int axp_set_dcdc2(unsigned int mvolt); int axp_set_dcdc3(unsigned int mvolt);

On Mon, 28 Nov 2022 00:47:54 -0600 Samuel Holland samuel@sholland.org wrote:
Subordinate regulator drivers can use this enumerated ID instead of matching the compatible string again.
Signed-off-by: Samuel Holland samuel@sholland.org
Reviewed-by: Andre Przywara andre.przywara@arm.com
Cheers, Andre
drivers/power/pmic/axp.c | 18 +++++++++--------- include/axp_pmic.h | 12 ++++++++++++ 2 files changed, 21 insertions(+), 9 deletions(-)
diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index 0f2b24a8b5f..e0005994e21 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -64,15 +64,15 @@ static int axp_pmic_bind(struct udevice *dev) }
static const struct udevice_id axp_pmic_ids[] = {
- { .compatible = "x-powers,axp152" },
- { .compatible = "x-powers,axp202" },
- { .compatible = "x-powers,axp209" },
- { .compatible = "x-powers,axp221" },
- { .compatible = "x-powers,axp223" },
- { .compatible = "x-powers,axp803" },
- { .compatible = "x-powers,axp806" },
- { .compatible = "x-powers,axp809" },
- { .compatible = "x-powers,axp813" },
- { .compatible = "x-powers,axp152", .data = AXP152_ID },
- { .compatible = "x-powers,axp202", .data = AXP202_ID },
- { .compatible = "x-powers,axp209", .data = AXP209_ID },
- { .compatible = "x-powers,axp221", .data = AXP221_ID },
- { .compatible = "x-powers,axp223", .data = AXP223_ID },
- { .compatible = "x-powers,axp803", .data = AXP803_ID },
- { .compatible = "x-powers,axp806", .data = AXP806_ID },
- { .compatible = "x-powers,axp809", .data = AXP809_ID },
- { .compatible = "x-powers,axp813", .data = AXP813_ID }, { }
};
diff --git a/include/axp_pmic.h b/include/axp_pmic.h index 01ebba63479..4ac64865831 100644 --- a/include/axp_pmic.h +++ b/include/axp_pmic.h @@ -26,6 +26,18 @@ #define AXP_PMIC_SEC_DEVICE_ADDR 0x745 #define AXP_PMIC_SEC_RUNTIME_ADDR 0x3a
+enum {
- AXP152_ID,
- AXP202_ID,
- AXP209_ID,
- AXP221_ID,
- AXP223_ID,
- AXP803_ID,
- AXP806_ID,
- AXP809_ID,
- AXP813_ID,
+};
int axp_set_dcdc1(unsigned int mvolt); int axp_set_dcdc2(unsigned int mvolt); int axp_set_dcdc3(unsigned int mvolt);

This driver handles most voltage regulators found in X-Powers AXP PMICs. It is based on, and intended to replace, the regulator driver in TF-A.
AXP PMIC regulators can be divided into 6 categories: - Switches without voltage control => fully supported. - Single linear range => fully supported. - Two linear ranges, "step" and "2 * step" => fully supported. - Two linear ranges, "step" and "5 * step" => only the first range is supported. No boards are known to use the second range. - Non-linear voltage values => fully supported. - LDOs shared with GPIO pins => not supported.
Signed-off-by: Samuel Holland samuel@sholland.org ---
drivers/power/regulator/Kconfig | 14 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/axp_regulator.c | 308 ++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 drivers/power/regulator/axp_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index c519e066ef0..de776556ffe 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -43,6 +43,20 @@ config REGULATOR_AS3722 but does not yet support change voltages. Currently this must be done using direct register writes to the PMIC.
+config REGULATOR_AXP + bool "Enable driver for X-Powers AXP PMIC regulators" + depends on DM_REGULATOR && PMIC_AXP + help + Enable support for the regulators (DCDCs, LDOs) in the + X-Powers AXP152, AXP2xx, and AXP8xx PMICs. + +config SPL_REGULATOR_AXP + bool "Enable driver for X-Powers AXP PMIC regulators in SPL" + depends on SPL_DM_REGULATOR && SPL_PMIC_AXP + help + Enable support in SPL for the regulators (DCDCs, LDOs) in the + X-Powers AXP152, AXP2xx, and AXP8xx PMICs. + config DM_REGULATOR_BD71837 bool "Enable Driver Model for ROHM BD71837/BD71847 regulators" depends on DM_REGULATOR && DM_PMIC_BD71837 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index bc736068bca..eb06b85bcd9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o +obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c new file mode 100644 index 00000000000..7af3cccd043 --- /dev/null +++ b/drivers/power/regulator/axp_regulator.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: BSD-3-Clause +/* + * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2022 Samuel Holland samuel@sholland.org + */ + +#include <axp_pmic.h> +#include <dm.h> +#include <errno.h> +#include <dm/device-internal.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define NA 0xff + +struct axp_regulator_plat { + const char *name; + u8 enable_reg; + u8 enable_mask; + u8 volt_reg; + u8 volt_mask; + u16 min_mV; + u16 max_mV; + u8 step_mV; + u8 split; + const u16 *table; +}; + +static int axp_regulator_get_value(struct udevice *dev) +{ + const struct axp_regulator_plat *plat = dev_get_plat(dev); + int mV, sel; + + if (plat->volt_reg == NA) + return -EINVAL; + + sel = pmic_reg_read(dev->parent, plat->volt_reg); + if (sel < 0) + return sel; + + sel &= plat->volt_mask; + sel >>= ffs(plat->volt_mask) - 1; + + if (plat->table) { + mV = plat->table[sel]; + } else { + if (sel > plat->split) + sel = plat->split + (sel - plat->split) * 2; + mV = plat->min_mV + sel * plat->step_mV; + } + + return mV * 1000; +} + +static int axp_regulator_set_value(struct udevice *dev, int uV) +{ + const struct axp_regulator_plat *plat = dev_get_plat(dev); + int mV = uV / 1000; + uint sel, shift; + + if (plat->volt_reg == NA) + return -EINVAL; + if (mV < plat->min_mV || mV > plat->max_mV) + return -EINVAL; + + shift = ffs(plat->volt_mask) - 1; + + if (plat->table) { + sel = plat->volt_mask >> shift; + while (sel && plat->table[sel] > mV) + sel--; + } else { + sel = (mV - plat->min_mV) / plat->step_mV; + if (sel > plat->split) + sel = plat->split + (sel - plat->split) / 2; + } + + return pmic_clrsetbits(dev->parent, plat->volt_reg, + plat->volt_mask, sel << shift); +} + +static int axp_regulator_get_enable(struct udevice *dev) +{ + const struct axp_regulator_plat *plat = dev_get_plat(dev); + int reg; + + reg = pmic_reg_read(dev->parent, plat->enable_reg); + if (reg < 0) + return reg; + + return (reg & plat->enable_mask) == plat->enable_mask; +} + +static int axp_regulator_set_enable(struct udevice *dev, bool enable) +{ + const struct axp_regulator_plat *plat = dev_get_plat(dev); + + return pmic_clrsetbits(dev->parent, plat->enable_reg, + plat->enable_mask, + enable ? plat->enable_mask : 0); +} + +static const struct dm_regulator_ops axp_regulator_ops = { + .get_value = axp_regulator_get_value, + .set_value = axp_regulator_set_value, + .get_enable = axp_regulator_get_enable, + .set_enable = axp_regulator_set_enable, +}; + +static const u16 axp152_dcdc1_table[] = { + 1700, 1800, 1900, 2000, 2100, 2400, 2500, 2600, + 2700, 2800, 3000, 3100, 3200, 3300, 3400, 3500, +}; + +static const u16 axp152_aldo12_table[] = { + 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900, + 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300, +}; + +static const u16 axp152_ldo0_table[] = { + 5000, 3300, 2800, 2500, +}; + +static const struct axp_regulator_plat axp152_regulators[] = { + { "dcdc1", 0x12, BIT(7), 0x26, 0x0f, .table = axp152_dcdc1_table }, + { "dcdc2", 0x12, BIT(6), 0x23, 0x3f, 700, 2275, 25, NA }, + { "dcdc3", 0x12, BIT(5), 0x27, 0x3f, 700, 3500, 50, NA }, + { "dcdc4", 0x12, BIT(4), 0x2b, 0x7f, 700, 3500, 25, NA }, + { "aldo1", 0x12, BIT(3), 0x28, 0xf0, .table = axp152_aldo12_table }, + { "aldo2", 0x12, BIT(2), 0x28, 0x0f, .table = axp152_aldo12_table }, + { "dldo1", 0x12, BIT(1), 0x29, 0x1f, 700, 3500, 100, NA }, + { "dldo2", 0x12, BIT(0), 0x2a, 0x1f, 700, 3500, 100, NA }, + { "ldo0", 0x15, BIT(7), 0x15, 0x30, .table = axp152_ldo0_table }, + { } +}; + +static const u16 axp20x_ldo4_table[] = { + 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900, + 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300, +}; + +static const struct axp_regulator_plat axp20x_regulators[] = { + { "dcdc2", 0x12, BIT(4), 0x23, 0x3f, 700, 2275, 25, NA }, + { "dcdc3", 0x12, BIT(1), 0x27, 0x7f, 700, 3500, 25, NA }, + { "ldo2", 0x12, BIT(2), 0x28, 0xf0, 1800, 3300, 100, NA }, + { "ldo3", 0x12, BIT(6), 0x29, 0x7f, 700, 2275, 25, NA }, + { "ldo4", 0x12, BIT(3), 0x28, 0x0f, .table = axp20x_ldo4_table }, + { } +}; + +static const struct axp_regulator_plat axp22x_regulators[] = { + {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA }, + { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA }, + { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA }, + { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA }, + { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA }, + { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA }, + { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA }, + { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA }, + { "aldo3", 0x13, BIT(0), 0x2a, 0x1f, 700, 3300, 100, NA }, + { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA }, + { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA }, + { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA }, + { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA }, + { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA }, + { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA }, + { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA }, + { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA }, + { } +}; + +static const struct axp_regulator_plat axp803_regulators[] = { + { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, + { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, + { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 }, + { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 }, + { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 }, + { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 }, + { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA }, + { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA }, + { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA }, + { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA }, + { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 }, + { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA }, + { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA }, + { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA }, + { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA }, + { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA }, + { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA }, + { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA }, + { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA }, + { } +}; + +/* + * The "dcdcd" split changes the step size by a factor of 5, not 2; + * disallow values above the split to maintain accuracy. + */ +static const struct axp_regulator_plat axp806_regulators[] = { + { "dcdca", 0x10, BIT(0), 0x12, 0x7f, 600, 1520, 10, 50 }, + { "dcdcb", 0x10, BIT(1), 0x13, 0x1f, 1000, 2550, 50, NA }, + { "dcdcc", 0x10, BIT(2), 0x14, 0x7f, 600, 1520, 10, 50 }, + { "dcdcd", 0x10, BIT(3), 0x15, 0x3f, 600, 1500, 20, NA }, + { "dcdce", 0x10, BIT(4), 0x16, 0x1f, 1100, 3400, 100, NA }, + { "aldo1", 0x10, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA }, + { "aldo2", 0x10, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA }, + { "aldo3", 0x10, BIT(7), 0x19, 0x1f, 700, 3300, 100, NA }, + { "bldo1", 0x11, BIT(0), 0x20, 0x0f, 700, 1900, 100, NA }, + { "bldo2", 0x11, BIT(1), 0x21, 0x0f, 700, 1900, 100, NA }, + { "bldo3", 0x11, BIT(2), 0x22, 0x0f, 700, 1900, 100, NA }, + { "bldo4", 0x11, BIT(3), 0x23, 0x0f, 700, 1900, 100, NA }, + { "cldo1", 0x11, BIT(4), 0x24, 0x1f, 700, 3300, 100, NA }, + { "cldo2", 0x11, BIT(5), 0x25, 0x1f, 700, 4200, 100, 27 }, + { "cldo3", 0x11, BIT(6), 0x26, 0x1f, 700, 3300, 100, NA }, + { "sw", 0x11, BIT(7), NA, NA, NA, NA, NA, NA }, + { } +}; + +/* + * The "dcdc4" split changes the step size by a factor of 5, not 2; + * disallow values above the split to maintain accuracy. + */ +static const struct axp_regulator_plat axp809_regulators[] = { + {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA }, + { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA }, + { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA }, + { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA }, + { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA }, + { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA }, + { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA }, + { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA }, + { "aldo3", 0x12, BIT(5), 0x2a, 0x1f, 700, 3300, 100, NA }, + { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA }, + { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA }, + { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA }, + { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA }, + { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA }, + { "sw", 0x12, BIT(6), NA, NA, NA, NA, NA, NA }, + { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA }, + { } +}; + +static const struct axp_regulator_plat axp813_regulators[] = { + { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA }, + { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 }, + { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 }, + { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 }, + { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 }, + { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 }, + { "dcdc7", 0x10, BIT(6), 0x26, 0x7f, 600, 1520, 10, 50 }, + { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA }, + { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA }, + { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA }, + { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA }, + { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 }, + { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA }, + { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA }, + { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA }, + { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA }, + { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA }, + { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA }, + { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA }, + { "fldo3", 0x13, BIT(4), NA, NA, NA, NA, NA, NA }, + { } +}; + +static const struct axp_regulator_plat *const axp_regulators[] = { + [AXP152_ID] = axp152_regulators, + [AXP202_ID] = axp20x_regulators, + [AXP209_ID] = axp20x_regulators, + [AXP221_ID] = axp22x_regulators, + [AXP223_ID] = axp22x_regulators, + [AXP803_ID] = axp803_regulators, + [AXP806_ID] = axp806_regulators, + [AXP809_ID] = axp809_regulators, + [AXP813_ID] = axp813_regulators, +}; + +static int axp_regulator_bind(struct udevice *dev) +{ + struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev); + ulong id = dev_get_driver_data(dev->parent); + const struct axp_regulator_plat *plat; + + for (plat = axp_regulators[id]; plat && plat->name; plat++) + if (!strcmp(plat->name, dev->name)) + break; + if (!plat || !plat->name) + return -ENODEV; + + dev_set_plat(dev, (void *)plat); + + if (plat->volt_reg == NA) + uc_plat->type = REGULATOR_TYPE_FIXED; + else if (!strncmp(plat->name, "dcdc", strlen("dcdc"))) + uc_plat->type = REGULATOR_TYPE_BUCK; + else + uc_plat->type = REGULATOR_TYPE_LDO; + + return 0; +} + +U_BOOT_DRIVER(axp_regulator) = { + .name = "axp_regulator", + .id = UCLASS_REGULATOR, + .bind = axp_regulator_bind, + .ops = &axp_regulator_ops, +};

On Mon, 28 Nov 2022 00:47:55 -0600 Samuel Holland samuel@sholland.org wrote:
Hi Samuel,
This driver handles most voltage regulators found in X-Powers AXP PMICs. It is based on, and intended to replace, the regulator driver in TF-A.
Many thanks for putting this together! That's much appreciated.
AXP PMIC regulators can be divided into 6 categories:
- Switches without voltage control => fully supported.
- Single linear range => fully supported.
- Two linear ranges, "step" and "2 * step" => fully supported.
- Two linear ranges, "step" and "5 * step" => only the first range is supported. No boards are known to use the second range.
- Non-linear voltage values => fully supported.
- LDOs shared with GPIO pins => not supported.
Quite an impressive feature list!
So I first scratched my head about the ffs and shift dance, but then realised that some of those older PMICs stuffed two voltage controls in one register. Neat solution for that!
I compared the tables against all datasheets, just found one bug (plus apparently one documentation bug). And one question about the license, see below.
Otherwise I am ready to take that driver now, just need to test it on some boards.
Signed-off-by: Samuel Holland samuel@sholland.org
drivers/power/regulator/Kconfig | 14 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/axp_regulator.c | 308 ++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 drivers/power/regulator/axp_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index c519e066ef0..de776556ffe 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -43,6 +43,20 @@ config REGULATOR_AS3722 but does not yet support change voltages. Currently this must be done using direct register writes to the PMIC.
+config REGULATOR_AXP
- bool "Enable driver for X-Powers AXP PMIC regulators"
- depends on DM_REGULATOR && PMIC_AXP
- help
Enable support for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
+config SPL_REGULATOR_AXP
- bool "Enable driver for X-Powers AXP PMIC regulators in SPL"
- depends on SPL_DM_REGULATOR && SPL_PMIC_AXP
- help
Enable support in SPL for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
config DM_REGULATOR_BD71837 bool "Enable Driver Model for ROHM BD71837/BD71847 regulators" depends on DM_REGULATOR && DM_PMIC_BD71837 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index bc736068bca..eb06b85bcd9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o +obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c new file mode 100644 index 00000000000..7af3cccd043 --- /dev/null +++ b/drivers/power/regulator/axp_regulator.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: BSD-3-Clause
So just BSD-3 looks a bit odd. I guess this comes because you copied some code (actually not that much?) from TF-A? Looking at the history there, it was just you and me contributing. I see one small patch from someone else, but you didn't copy that code. So I am fine with dual licensing this as "GPL-2.0 OR BSD-3-Clause". If you agree as well, I would appreciate if you would change the license.
+/*
- Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
- Copyright (c) 2018-2022 Samuel Holland samuel@sholland.org
- */
+#include <axp_pmic.h> +#include <dm.h> +#include <errno.h> +#include <dm/device-internal.h> +#include <power/pmic.h> +#include <power/regulator.h>
+#define NA 0xff
+struct axp_regulator_plat {
- const char *name;
- u8 enable_reg;
- u8 enable_mask;
- u8 volt_reg;
- u8 volt_mask;
- u16 min_mV;
- u16 max_mV;
- u8 step_mV;
- u8 split;
- const u16 *table;
+};
+static int axp_regulator_get_value(struct udevice *dev) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int mV, sel;
- if (plat->volt_reg == NA)
return -EINVAL;
- sel = pmic_reg_read(dev->parent, plat->volt_reg);
- if (sel < 0)
return sel;
- sel &= plat->volt_mask;
- sel >>= ffs(plat->volt_mask) - 1;
- if (plat->table) {
mV = plat->table[sel];
- } else {
if (sel > plat->split)
sel = plat->split + (sel - plat->split) * 2;
mV = plat->min_mV + sel * plat->step_mV;
- }
- return mV * 1000;
+}
+static int axp_regulator_set_value(struct udevice *dev, int uV) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int mV = uV / 1000;
- uint sel, shift;
- if (plat->volt_reg == NA)
return -EINVAL;
- if (mV < plat->min_mV || mV > plat->max_mV)
return -EINVAL;
- shift = ffs(plat->volt_mask) - 1;
- if (plat->table) {
sel = plat->volt_mask >> shift;
while (sel && plat->table[sel] > mV)
sel--;
I wonder if it deserves some documentation/comment somewhere that tables need to span the whole bit range, as you scan from the end.
- } else {
sel = (mV - plat->min_mV) / plat->step_mV;
if (sel > plat->split)
sel = plat->split + (sel - plat->split) / 2;
- }
- return pmic_clrsetbits(dev->parent, plat->volt_reg,
plat->volt_mask, sel << shift);
+}
+static int axp_regulator_get_enable(struct udevice *dev) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int reg;
- reg = pmic_reg_read(dev->parent, plat->enable_reg);
- if (reg < 0)
return reg;
- return (reg & plat->enable_mask) == plat->enable_mask;
+}
+static int axp_regulator_set_enable(struct udevice *dev, bool enable) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- return pmic_clrsetbits(dev->parent, plat->enable_reg,
plat->enable_mask,
enable ? plat->enable_mask : 0);
+}
+static const struct dm_regulator_ops axp_regulator_ops = {
- .get_value = axp_regulator_get_value,
- .set_value = axp_regulator_set_value,
- .get_enable = axp_regulator_get_enable,
- .set_enable = axp_regulator_set_enable,
+};
+static const u16 axp152_dcdc1_table[] = {
- 1700, 1800, 1900, 2000, 2100, 2400, 2500, 2600,
- 2700, 2800, 3000, 3100, 3200, 3300, 3400, 3500,
+};
+static const u16 axp152_aldo12_table[] = {
- 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
- 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300,
+};
+static const u16 axp152_ldo0_table[] = {
- 5000, 3300, 2800, 2500,
+};
+static const struct axp_regulator_plat axp152_regulators[] = {
- { "dcdc1", 0x12, BIT(7), 0x26, 0x0f, .table = axp152_dcdc1_table },
- { "dcdc2", 0x12, BIT(6), 0x23, 0x3f, 700, 2275, 25, NA },
- { "dcdc3", 0x12, BIT(5), 0x27, 0x3f, 700, 3500, 50, NA },
- { "dcdc4", 0x12, BIT(4), 0x2b, 0x7f, 700, 3500, 25, NA },
- { "aldo1", 0x12, BIT(3), 0x28, 0xf0, .table = axp152_aldo12_table },
- { "aldo2", 0x12, BIT(2), 0x28, 0x0f, .table = axp152_aldo12_table },
- { "dldo1", 0x12, BIT(1), 0x29, 0x1f, 700, 3500, 100, NA },
- { "dldo2", 0x12, BIT(0), 0x2a, 0x1f, 700, 3500, 100, NA },
- { "ldo0", 0x15, BIT(7), 0x15, 0x30, .table = axp152_ldo0_table },
- { }
+};
+static const u16 axp20x_ldo4_table[] = {
- 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
- 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300,
+};
+static const struct axp_regulator_plat axp20x_regulators[] = {
- { "dcdc2", 0x12, BIT(4), 0x23, 0x3f, 700, 2275, 25, NA },
- { "dcdc3", 0x12, BIT(1), 0x27, 0x7f, 700, 3500, 25, NA },
- { "ldo2", 0x12, BIT(2), 0x28, 0xf0, 1800, 3300, 100, NA },
- { "ldo3", 0x12, BIT(6), 0x29, 0x7f, 700, 2275, 25, NA },
- { "ldo4", 0x12, BIT(3), 0x28, 0x0f, .table = axp20x_ldo4_table },
- { }
+};
+static const struct axp_regulator_plat axp22x_regulators[] = {
- {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA },
- { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA },
- { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA },
- { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA },
- { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA },
The AXP221 datasheet lists this as 0.6->1.54mV @ 20mV/step. However there are not enough bits to encode the whole range, and the Linux driver also uses the above parameters. So I am inclined to believe in a manual copy&paste incident (from DCDC4), and the above entry being correct.
- { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(0), 0x2a, 0x1f, 700, 3300, 100, NA },
According to the AXP221 datasheet and the Linux driver, the enable bit is bit 7 in reg 13h.
The rest looks fine, just from reading through the code. As mentioned, I still need to test it, but I am quite hopeful we can take this.
Thanks again! Andre
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat axp803_regulators[] = {
- { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
- { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 },
- { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 },
- { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 },
- { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 },
- { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA },
- { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA },
- { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+/*
- The "dcdcd" split changes the step size by a factor of 5, not 2;
- disallow values above the split to maintain accuracy.
- */
+static const struct axp_regulator_plat axp806_regulators[] = {
- { "dcdca", 0x10, BIT(0), 0x12, 0x7f, 600, 1520, 10, 50 },
- { "dcdcb", 0x10, BIT(1), 0x13, 0x1f, 1000, 2550, 50, NA },
- { "dcdcc", 0x10, BIT(2), 0x14, 0x7f, 600, 1520, 10, 50 },
- { "dcdcd", 0x10, BIT(3), 0x15, 0x3f, 600, 1500, 20, NA },
- { "dcdce", 0x10, BIT(4), 0x16, 0x1f, 1100, 3400, 100, NA },
- { "aldo1", 0x10, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x10, BIT(7), 0x19, 0x1f, 700, 3300, 100, NA },
- { "bldo1", 0x11, BIT(0), 0x20, 0x0f, 700, 1900, 100, NA },
- { "bldo2", 0x11, BIT(1), 0x21, 0x0f, 700, 1900, 100, NA },
- { "bldo3", 0x11, BIT(2), 0x22, 0x0f, 700, 1900, 100, NA },
- { "bldo4", 0x11, BIT(3), 0x23, 0x0f, 700, 1900, 100, NA },
- { "cldo1", 0x11, BIT(4), 0x24, 0x1f, 700, 3300, 100, NA },
- { "cldo2", 0x11, BIT(5), 0x25, 0x1f, 700, 4200, 100, 27 },
- { "cldo3", 0x11, BIT(6), 0x26, 0x1f, 700, 3300, 100, NA },
- { "sw", 0x11, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+/*
- The "dcdc4" split changes the step size by a factor of 5, not 2;
- disallow values above the split to maintain accuracy.
- */
+static const struct axp_regulator_plat axp809_regulators[] = {
- {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA },
- { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA },
- { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA },
- { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA },
- { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA },
- { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x12, BIT(5), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA },
- { "sw", 0x12, BIT(6), NA, NA, NA, NA, NA, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat axp813_regulators[] = {
- { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
- { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 },
- { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 },
- { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 },
- { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 },
- { "dcdc7", 0x10, BIT(6), 0x26, 0x7f, 600, 1520, 10, 50 },
- { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA },
- { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA },
- { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA },
- { "fldo3", 0x13, BIT(4), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat *const axp_regulators[] = {
- [AXP152_ID] = axp152_regulators,
- [AXP202_ID] = axp20x_regulators,
- [AXP209_ID] = axp20x_regulators,
- [AXP221_ID] = axp22x_regulators,
- [AXP223_ID] = axp22x_regulators,
- [AXP803_ID] = axp803_regulators,
- [AXP806_ID] = axp806_regulators,
- [AXP809_ID] = axp809_regulators,
- [AXP813_ID] = axp813_regulators,
+};
+static int axp_regulator_bind(struct udevice *dev) +{
- struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev);
- ulong id = dev_get_driver_data(dev->parent);
- const struct axp_regulator_plat *plat;
- for (plat = axp_regulators[id]; plat && plat->name; plat++)
if (!strcmp(plat->name, dev->name))
break;
- if (!plat || !plat->name)
return -ENODEV;
- dev_set_plat(dev, (void *)plat);
- if (plat->volt_reg == NA)
uc_plat->type = REGULATOR_TYPE_FIXED;
- else if (!strncmp(plat->name, "dcdc", strlen("dcdc")))
uc_plat->type = REGULATOR_TYPE_BUCK;
- else
uc_plat->type = REGULATOR_TYPE_LDO;
- return 0;
+}
+U_BOOT_DRIVER(axp_regulator) = {
- .name = "axp_regulator",
- .id = UCLASS_REGULATOR,
- .bind = axp_regulator_bind,
- .ops = &axp_regulator_ops,
+};

Hi Andre,
On 1/11/23 17:08, Andre Przywara wrote:
On Mon, 28 Nov 2022 00:47:55 -0600 Samuel Holland samuel@sholland.org wrote:
Hi Samuel,
This driver handles most voltage regulators found in X-Powers AXP PMICs. It is based on, and intended to replace, the regulator driver in TF-A.
Many thanks for putting this together! That's much appreciated.
AXP PMIC regulators can be divided into 6 categories:
- Switches without voltage control => fully supported.
- Single linear range => fully supported.
- Two linear ranges, "step" and "2 * step" => fully supported.
- Two linear ranges, "step" and "5 * step" => only the first range is supported. No boards are known to use the second range.
- Non-linear voltage values => fully supported.
- LDOs shared with GPIO pins => not supported.
Quite an impressive feature list!
So I first scratched my head about the ffs and shift dance, but then realised that some of those older PMICs stuffed two voltage controls in one register. Neat solution for that!
I compared the tables against all datasheets, just found one bug (plus apparently one documentation bug). And one question about the license, see below.
Otherwise I am ready to take that driver now, just need to test it on some boards.
Signed-off-by: Samuel Holland samuel@sholland.org
drivers/power/regulator/Kconfig | 14 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/axp_regulator.c | 308 ++++++++++++++++++++++++ 3 files changed, 323 insertions(+) create mode 100644 drivers/power/regulator/axp_regulator.c
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index c519e066ef0..de776556ffe 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -43,6 +43,20 @@ config REGULATOR_AS3722 but does not yet support change voltages. Currently this must be done using direct register writes to the PMIC.
+config REGULATOR_AXP
- bool "Enable driver for X-Powers AXP PMIC regulators"
- depends on DM_REGULATOR && PMIC_AXP
- help
Enable support for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
+config SPL_REGULATOR_AXP
- bool "Enable driver for X-Powers AXP PMIC regulators in SPL"
- depends on SPL_DM_REGULATOR && SPL_PMIC_AXP
- help
Enable support in SPL for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
config DM_REGULATOR_BD71837 bool "Enable Driver Model for ROHM BD71837/BD71847 regulators" depends on DM_REGULATOR && DM_PMIC_BD71837 diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index bc736068bca..eb06b85bcd9 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_$(SPL_)DM_REGULATOR) += regulator-uclass.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o +obj-$(CONFIG_$(SPL_)REGULATOR_AXP) += axp_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_DA9063) += da9063.o obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o diff --git a/drivers/power/regulator/axp_regulator.c b/drivers/power/regulator/axp_regulator.c new file mode 100644 index 00000000000..7af3cccd043 --- /dev/null +++ b/drivers/power/regulator/axp_regulator.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: BSD-3-Clause
So just BSD-3 looks a bit odd. I guess this comes because you copied some code (actually not that much?) from TF-A? Looking at the history there, it was just you and me contributing. I see one small patch from someone else, but you didn't copy that code. So I am fine with dual licensing this as "GPL-2.0 OR BSD-3-Clause". If you agree as well, I would appreciate if you would change the license.
Yes, the license is because I took the code from TF-A. I am fine with dual-licensing as well.
+/*
- Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
- Copyright (c) 2018-2022 Samuel Holland samuel@sholland.org
- */
+#include <axp_pmic.h> +#include <dm.h> +#include <errno.h> +#include <dm/device-internal.h> +#include <power/pmic.h> +#include <power/regulator.h>
+#define NA 0xff
+struct axp_regulator_plat {
- const char *name;
- u8 enable_reg;
- u8 enable_mask;
- u8 volt_reg;
- u8 volt_mask;
- u16 min_mV;
- u16 max_mV;
- u8 step_mV;
- u8 split;
- const u16 *table;
+};
+static int axp_regulator_get_value(struct udevice *dev) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int mV, sel;
- if (plat->volt_reg == NA)
return -EINVAL;
- sel = pmic_reg_read(dev->parent, plat->volt_reg);
- if (sel < 0)
return sel;
- sel &= plat->volt_mask;
- sel >>= ffs(plat->volt_mask) - 1;
- if (plat->table) {
mV = plat->table[sel];
- } else {
if (sel > plat->split)
sel = plat->split + (sel - plat->split) * 2;
mV = plat->min_mV + sel * plat->step_mV;
- }
- return mV * 1000;
+}
+static int axp_regulator_set_value(struct udevice *dev, int uV) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int mV = uV / 1000;
- uint sel, shift;
- if (plat->volt_reg == NA)
return -EINVAL;
- if (mV < plat->min_mV || mV > plat->max_mV)
return -EINVAL;
- shift = ffs(plat->volt_mask) - 1;
- if (plat->table) {
sel = plat->volt_mask >> shift;
while (sel && plat->table[sel] > mV)
sel--;
I wonder if it deserves some documentation/comment somewhere that tables need to span the whole bit range, as you scan from the end.
I will add a comment above this block.
- } else {
sel = (mV - plat->min_mV) / plat->step_mV;
if (sel > plat->split)
sel = plat->split + (sel - plat->split) / 2;
- }
- return pmic_clrsetbits(dev->parent, plat->volt_reg,
plat->volt_mask, sel << shift);
+}
+static int axp_regulator_get_enable(struct udevice *dev) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- int reg;
- reg = pmic_reg_read(dev->parent, plat->enable_reg);
- if (reg < 0)
return reg;
- return (reg & plat->enable_mask) == plat->enable_mask;
+}
+static int axp_regulator_set_enable(struct udevice *dev, bool enable) +{
- const struct axp_regulator_plat *plat = dev_get_plat(dev);
- return pmic_clrsetbits(dev->parent, plat->enable_reg,
plat->enable_mask,
enable ? plat->enable_mask : 0);
+}
+static const struct dm_regulator_ops axp_regulator_ops = {
- .get_value = axp_regulator_get_value,
- .set_value = axp_regulator_set_value,
- .get_enable = axp_regulator_get_enable,
- .set_enable = axp_regulator_set_enable,
+};
+static const u16 axp152_dcdc1_table[] = {
- 1700, 1800, 1900, 2000, 2100, 2400, 2500, 2600,
- 2700, 2800, 3000, 3100, 3200, 3300, 3400, 3500,
+};
+static const u16 axp152_aldo12_table[] = {
- 1200, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
- 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300,
+};
+static const u16 axp152_ldo0_table[] = {
- 5000, 3300, 2800, 2500,
+};
+static const struct axp_regulator_plat axp152_regulators[] = {
- { "dcdc1", 0x12, BIT(7), 0x26, 0x0f, .table = axp152_dcdc1_table },
- { "dcdc2", 0x12, BIT(6), 0x23, 0x3f, 700, 2275, 25, NA },
- { "dcdc3", 0x12, BIT(5), 0x27, 0x3f, 700, 3500, 50, NA },
- { "dcdc4", 0x12, BIT(4), 0x2b, 0x7f, 700, 3500, 25, NA },
- { "aldo1", 0x12, BIT(3), 0x28, 0xf0, .table = axp152_aldo12_table },
- { "aldo2", 0x12, BIT(2), 0x28, 0x0f, .table = axp152_aldo12_table },
- { "dldo1", 0x12, BIT(1), 0x29, 0x1f, 700, 3500, 100, NA },
- { "dldo2", 0x12, BIT(0), 0x2a, 0x1f, 700, 3500, 100, NA },
- { "ldo0", 0x15, BIT(7), 0x15, 0x30, .table = axp152_ldo0_table },
- { }
+};
+static const u16 axp20x_ldo4_table[] = {
- 1250, 1300, 1400, 1500, 1600, 1700, 1800, 1900,
- 2000, 2500, 2700, 2800, 3000, 3100, 3200, 3300,
+};
+static const struct axp_regulator_plat axp20x_regulators[] = {
- { "dcdc2", 0x12, BIT(4), 0x23, 0x3f, 700, 2275, 25, NA },
- { "dcdc3", 0x12, BIT(1), 0x27, 0x7f, 700, 3500, 25, NA },
- { "ldo2", 0x12, BIT(2), 0x28, 0xf0, 1800, 3300, 100, NA },
- { "ldo3", 0x12, BIT(6), 0x29, 0x7f, 700, 2275, 25, NA },
- { "ldo4", 0x12, BIT(3), 0x28, 0x0f, .table = axp20x_ldo4_table },
- { }
+};
+static const struct axp_regulator_plat axp22x_regulators[] = {
- {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA },
- { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA },
- { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA },
- { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA },
- { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA },
The AXP221 datasheet lists this as 0.6->1.54mV @ 20mV/step. However there are not enough bits to encode the whole range, and the Linux driver also uses the above parameters. So I am inclined to believe in a manual copy&paste incident (from DCDC4), and the above entry being correct.
These parameters match the range given for VDC5OUT elsewhere in the datasheet. Also, DCDC5 is intended for DRAM, so it would need to support at least 1.8V for DDR2.
- { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(0), 0x2a, 0x1f, 700, 3300, 100, NA },
According to the AXP221 datasheet and the Linux driver, the enable bit is bit 7 in reg 13h.
I will fix this in v2.
The rest looks fine, just from reading through the code. As mentioned, I still need to test it, but I am quite hopeful we can take this.
Do you have any thoughts about the best way to minimize the code/data size (if needed) for where this driver is used in SPL, like on H616? Of course, optimization could come later as well.
Regards, Samuel
Thanks again! Andre
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat axp803_regulators[] = {
- { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
- { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 },
- { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 },
- { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 },
- { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 },
- { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA },
- { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA },
- { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+/*
- The "dcdcd" split changes the step size by a factor of 5, not 2;
- disallow values above the split to maintain accuracy.
- */
+static const struct axp_regulator_plat axp806_regulators[] = {
- { "dcdca", 0x10, BIT(0), 0x12, 0x7f, 600, 1520, 10, 50 },
- { "dcdcb", 0x10, BIT(1), 0x13, 0x1f, 1000, 2550, 50, NA },
- { "dcdcc", 0x10, BIT(2), 0x14, 0x7f, 600, 1520, 10, 50 },
- { "dcdcd", 0x10, BIT(3), 0x15, 0x3f, 600, 1500, 20, NA },
- { "dcdce", 0x10, BIT(4), 0x16, 0x1f, 1100, 3400, 100, NA },
- { "aldo1", 0x10, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x10, BIT(7), 0x19, 0x1f, 700, 3300, 100, NA },
- { "bldo1", 0x11, BIT(0), 0x20, 0x0f, 700, 1900, 100, NA },
- { "bldo2", 0x11, BIT(1), 0x21, 0x0f, 700, 1900, 100, NA },
- { "bldo3", 0x11, BIT(2), 0x22, 0x0f, 700, 1900, 100, NA },
- { "bldo4", 0x11, BIT(3), 0x23, 0x0f, 700, 1900, 100, NA },
- { "cldo1", 0x11, BIT(4), 0x24, 0x1f, 700, 3300, 100, NA },
- { "cldo2", 0x11, BIT(5), 0x25, 0x1f, 700, 4200, 100, 27 },
- { "cldo3", 0x11, BIT(6), 0x26, 0x1f, 700, 3300, 100, NA },
- { "sw", 0x11, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+/*
- The "dcdc4" split changes the step size by a factor of 5, not 2;
- disallow values above the split to maintain accuracy.
- */
+static const struct axp_regulator_plat axp809_regulators[] = {
- {"dc5ldo", 0x10, BIT(0), 0x1c, 0x07, 700, 1400, 100, NA },
- { "dcdc1", 0x10, BIT(1), 0x21, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(2), 0x22, 0x3f, 600, 1540, 20, NA },
- { "dcdc3", 0x10, BIT(3), 0x23, 0x3f, 600, 1860, 20, NA },
- { "dcdc4", 0x10, BIT(4), 0x24, 0x3f, 600, 1540, 20, NA },
- { "dcdc5", 0x10, BIT(5), 0x25, 0x1f, 1000, 2550, 50, NA },
- { "aldo1", 0x10, BIT(6), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x10, BIT(7), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x12, BIT(5), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 3300, 100, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 3300, 100, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 3300, 100, NA },
- { "sw", 0x12, BIT(6), NA, NA, NA, NA, NA, NA },
- { "dc1sw", 0x12, BIT(7), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat axp813_regulators[] = {
- { "dcdc1", 0x10, BIT(0), 0x20, 0x1f, 1600, 3400, 100, NA },
- { "dcdc2", 0x10, BIT(1), 0x21, 0x7f, 500, 1300, 10, 70 },
- { "dcdc3", 0x10, BIT(2), 0x22, 0x7f, 500, 1300, 10, 70 },
- { "dcdc4", 0x10, BIT(3), 0x23, 0x7f, 500, 1300, 10, 70 },
- { "dcdc5", 0x10, BIT(4), 0x24, 0x7f, 800, 1840, 10, 32 },
- { "dcdc6", 0x10, BIT(5), 0x25, 0x7f, 600, 1520, 10, 50 },
- { "dcdc7", 0x10, BIT(6), 0x26, 0x7f, 600, 1520, 10, 50 },
- { "aldo1", 0x13, BIT(5), 0x28, 0x1f, 700, 3300, 100, NA },
- { "aldo2", 0x13, BIT(6), 0x29, 0x1f, 700, 3300, 100, NA },
- { "aldo3", 0x13, BIT(7), 0x2a, 0x1f, 700, 3300, 100, NA },
- { "dldo1", 0x12, BIT(3), 0x15, 0x1f, 700, 3300, 100, NA },
- { "dldo2", 0x12, BIT(4), 0x16, 0x1f, 700, 4200, 100, 27 },
- { "dldo3", 0x12, BIT(5), 0x17, 0x1f, 700, 3300, 100, NA },
- { "dldo4", 0x12, BIT(6), 0x18, 0x1f, 700, 3300, 100, NA },
- { "eldo1", 0x12, BIT(0), 0x19, 0x1f, 700, 1900, 50, NA },
- { "eldo2", 0x12, BIT(1), 0x1a, 0x1f, 700, 1900, 50, NA },
- { "eldo3", 0x12, BIT(2), 0x1b, 0x1f, 700, 1900, 50, NA },
- { "fldo1", 0x13, BIT(2), 0x1c, 0x0f, 700, 1450, 50, NA },
- { "fldo2", 0x13, BIT(3), 0x1d, 0x0f, 700, 1450, 50, NA },
- { "fldo3", 0x13, BIT(4), NA, NA, NA, NA, NA, NA },
- { }
+};
+static const struct axp_regulator_plat *const axp_regulators[] = {
- [AXP152_ID] = axp152_regulators,
- [AXP202_ID] = axp20x_regulators,
- [AXP209_ID] = axp20x_regulators,
- [AXP221_ID] = axp22x_regulators,
- [AXP223_ID] = axp22x_regulators,
- [AXP803_ID] = axp803_regulators,
- [AXP806_ID] = axp806_regulators,
- [AXP809_ID] = axp809_regulators,
- [AXP813_ID] = axp813_regulators,
+};
+static int axp_regulator_bind(struct udevice *dev) +{
- struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev);
- ulong id = dev_get_driver_data(dev->parent);
- const struct axp_regulator_plat *plat;
- for (plat = axp_regulators[id]; plat && plat->name; plat++)
if (!strcmp(plat->name, dev->name))
break;
- if (!plat || !plat->name)
return -ENODEV;
- dev_set_plat(dev, (void *)plat);
- if (plat->volt_reg == NA)
uc_plat->type = REGULATOR_TYPE_FIXED;
- else if (!strncmp(plat->name, "dcdc", strlen("dcdc")))
uc_plat->type = REGULATOR_TYPE_BUCK;
- else
uc_plat->type = REGULATOR_TYPE_LDO;
- return 0;
+}
+U_BOOT_DRIVER(axp_regulator) = {
- .name = "axp_regulator",
- .id = UCLASS_REGULATOR,
- .bind = axp_regulator_bind,
- .ops = &axp_regulator_ops,
+};

Now that a regulator driver exists for this PMIC, hook it up to the device tree "regulators" subnodes.
Signed-off-by: Samuel Holland samuel@sholland.org ---
drivers/power/pmic/axp.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c index e0005994e21..025dac24f28 100644 --- a/drivers/power/pmic/axp.c +++ b/drivers/power/pmic/axp.c @@ -45,14 +45,32 @@ static struct dm_pmic_ops axp_pmic_ops = { .write = dm_i2c_write, };
+static const struct pmic_child_info axp_pmic_child_info[] = { + { "aldo", "axp_regulator" }, + { "bldo", "axp_regulator" }, + { "cldo", "axp_regulator" }, + { "dc", "axp_regulator" }, + { "dldo", "axp_regulator" }, + { "eldo", "axp_regulator" }, + { "fldo", "axp_regulator" }, + { "ldo", "axp_regulator" }, + { "sw", "axp_regulator" }, + { } +}; + static int axp_pmic_bind(struct udevice *dev) { + ofnode regulators_node; int ret;
ret = dm_scan_fdt_dev(dev); if (ret) return ret;
+ regulators_node = dev_read_subnode(dev, "regulators"); + if (ofnode_valid(regulators_node)) + pmic_bind_children(dev, regulators_node, axp_pmic_child_info); + if (CONFIG_IS_ENABLED(SYSRESET)) { ret = device_bind_driver_to_node(dev, "axp_sysreset", "axp_sysreset", dev_ofnode(dev), NULL);
participants (2)
-
Andre Przywara
-
Samuel Holland