[PATCH v6 07/13] power: regulator: Add AB8500 AUX3 support
Linus Walleij <[email protected]>
| Newsgroups | org.u-boot-project.lists.u-boot |
|---|---|
| Message-ID | <[email protected]> |
Add regulator support for LDO AUX3 on AB8500 and AB8505 PMICs. AUX3 supplies the removable SD card on the upstream Ux500 Samsung device trees. Support the AB8505-specific 3.05 V selector override and clear it after programming an ordinary voltage. Treat both normal and low-power modes as enabled and select the supported voltage closest to a requested target. AB8500 cut 2.0 or later is assumed. Imply the regulator core for ARCH_U8500 so the driver can instantiate from the upstream device trees. Signed-off-by: Linus Walleij <[email protected]> --- MAINTAINERS | 1 + arch/arm/Kconfig | 2 + drivers/power/pmic/ab8500.c | 1 + drivers/power/regulator/Kconfig | 7 ++ drivers/power/regulator/Makefile | 1 + drivers/power/regulator/ab8500.c | 171 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 183 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index a5d6b5be29dc..8e78a7f224fa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -790,6 +790,7 @@ F: drivers/gpio/nmk_gpio.c F: drivers/pinctrl/pinctrl-nomadik.c F: drivers/phy/phy-ab8500-usb.c F: drivers/power/pmic/ab8500.c +F: drivers/power/regulator/ab8500.c F: drivers/timer/nomadik-mtu-timer.c F: drivers/usb/musb-new/ux500.c F: drivers/video/mcde_simple.c diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 5b02ff18bd0e..62aa9e8cc565 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1275,6 +1275,7 @@ config ARCH_U8500 imply ARM_PL180_MMCI imply CLK imply DM_PMIC + imply DM_REGULATOR imply DM_RTC imply NOMADIK_GPIO imply NOMADIK_MTU_TIMER @@ -1283,6 +1284,7 @@ config ARCH_U8500 imply PHY imply PL01X_SERIAL imply PMIC_AB8500 + imply REGULATOR_AB8500 imply RTC_PL031 imply SYS_THUMB_BUILD imply SYSRESET_SYSCON diff --git a/drivers/power/pmic/ab8500.c b/drivers/power/pmic/ab8500.c index 9ba096711e14..b9fae7000b78 100644 --- a/drivers/power/pmic/ab8500.c +++ b/drivers/power/pmic/ab8500.c @@ -253,6 +253,7 @@ static int ab8500_probe(struct udevice *dev) static const struct udevice_id ab8500_ids[] = { { .compatible = "stericsson,ab8500" }, + { .compatible = "stericsson,ab8505" }, { } }; diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 00a25acfdf99..a27fe4925cf9 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -16,6 +16,13 @@ config DM_REGULATOR for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_dev() otherwise. Detailed information can be found in the header file. +config REGULATOR_AB8500 + bool "ST-Ericsson AB8500/AB8505 regulator support" + depends on DM_REGULATOR && PMIC_AB8500 + help + Enable LDO AUX3 regulator support for AB8500 and AB8505 PMICs. + This supply is used to power removable SD cards on Ux500 boards. + config SPL_DM_REGULATOR bool "Enable regulators for SPL" depends on DM_REGULATOR && SPL_POWER diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index a40c9d340d98..a500a3da98d4 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -5,6 +5,7 @@ # obj-$(CONFIG_$(PHASE_)DM_REGULATOR) += regulator-uclass.o +obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o obj-$(CONFIG_$(PHASE_)REGULATOR_AXP) += axp_regulator.o diff --git a/drivers/power/regulator/ab8500.c b/drivers/power/regulator/ab8500.c new file mode 100644 index 000000000000..4cd15c5fcf86 --- /dev/null +++ b/drivers/power/regulator/ab8500.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* ST-Ericsson AB8500/AB8505 LDO AUX3 regulator */ + +#include <dm.h> +#include <dm/lists.h> +#include <linux/errno.h> +#include <power/ab8500.h> +#include <power/pmic.h> +#include <power/regulator.h> + +#define AB8500_VAUX3_REGU AB8500_REGU_CTRL2(0x0a) +#define AB8500_VAUX3_REGU_EN_MASK GENMASK(1, 0) +#define AB8500_VAUX3_REGU_EN BIT(0) + +#define AB8500_VAUX3_SEL AB8500_REGU_CTRL2(0x21) +#define AB8500_VAUX3_SEL_MASK GENMASK(2, 0) + +#define AB8505_VAUX3_SEL3 AB8500_REGU_CTRL2(0x01) + +#define AB8505_VAUX3_SEL3_MASK BIT(4) +#define AB8505_VAUX3_SEL3_UV 3050000 + +static const int ab8500_vaux3_voltages[] = { + 1200000, 1500000, 1800000, 2100000, + 2500000, 2750000, 2790000, 2910000, +}; + +static struct udevice *ab8500_regulator_pmic(struct udevice *dev) +{ + return dev->parent->parent; +} + +static int ab8500_regulator_get_value(struct udevice *dev) +{ + struct udevice *pmic = ab8500_regulator_pmic(dev); + int ret; + + if (device_is_compatible(pmic, "stericsson,ab8505")) { + ret = pmic_reg_read(pmic, AB8505_VAUX3_SEL3); + if (ret < 0) + return ret; + if (ret & AB8505_VAUX3_SEL3_MASK) + return AB8505_VAUX3_SEL3_UV; + } + + ret = pmic_reg_read(pmic, AB8500_VAUX3_SEL); + if (ret < 0) + return ret; + ret &= AB8500_VAUX3_SEL_MASK; + + return ab8500_vaux3_voltages[ret]; +} + +static int ab8500_regulator_set_value(struct udevice *dev, int uV) +{ + struct udevice *pmic = ab8500_regulator_pmic(dev); + bool is_ab8505 = device_is_compatible(pmic, "stericsson,ab8505"); + int ret; + int i; + + if (uV == AB8505_VAUX3_SEL3_UV) { + if (!is_ab8505) + return -EINVAL; + + return pmic_clrsetbits(pmic, AB8505_VAUX3_SEL3, + AB8505_VAUX3_SEL3_MASK, + AB8505_VAUX3_SEL3_MASK); + } + + for (i = 0; i < ARRAY_SIZE(ab8500_vaux3_voltages); i++) { + if (ab8500_vaux3_voltages[i] != uV) + continue; + + ret = pmic_clrsetbits(pmic, AB8500_VAUX3_SEL, + AB8500_VAUX3_SEL_MASK, i); + if (ret || !is_ab8505) + return ret; + + return pmic_clrsetbits(pmic, AB8505_VAUX3_SEL3, + AB8505_VAUX3_SEL3_MASK, 0); + } + + return -EINVAL; +} + +static int ab8500_regulator_set_value_clamp(struct udevice *dev, int min_uV, + int target_uV, int max_uV) +{ + struct udevice *pmic = ab8500_regulator_pmic(dev); + int best_uV = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(ab8500_vaux3_voltages); i++) { + int uV = ab8500_vaux3_voltages[i]; + + if (uV < min_uV || uV > max_uV) + continue; + if (best_uV < 0 || abs(uV - target_uV) < + abs(best_uV - target_uV)) + best_uV = uV; + } + + if (device_is_compatible(pmic, "stericsson,ab8505") && + min_uV <= AB8505_VAUX3_SEL3_UV && + max_uV >= AB8505_VAUX3_SEL3_UV && + (best_uV < 0 || abs(AB8505_VAUX3_SEL3_UV - target_uV) < + abs(best_uV - target_uV))) + best_uV = AB8505_VAUX3_SEL3_UV; + + if (best_uV < 0) + return best_uV; + + return ab8500_regulator_set_value(dev, best_uV); +} + +static int ab8500_regulator_get_enable(struct udevice *dev) +{ + int ret; + + ret = pmic_reg_read(ab8500_regulator_pmic(dev), AB8500_VAUX3_REGU); + if (ret < 0) + return ret; + + return !!(ret & AB8500_VAUX3_REGU_EN_MASK); +} + +static int ab8500_regulator_set_enable(struct udevice *dev, bool enable) +{ + return pmic_clrsetbits(ab8500_regulator_pmic(dev), AB8500_VAUX3_REGU, + AB8500_VAUX3_REGU_EN_MASK, + enable ? AB8500_VAUX3_REGU_EN : 0); +} + +static const struct dm_regulator_ops ab8500_regulator_ops = { + .get_value = ab8500_regulator_get_value, + .set_value = ab8500_regulator_set_value, + .set_value_clamp = ab8500_regulator_set_value_clamp, + .get_enable = ab8500_regulator_get_enable, + .set_enable = ab8500_regulator_set_enable, +}; + +U_BOOT_DRIVER(ab8500_ldo_aux3) = { + .name = "ab8500_ldo_aux3", + .id = UCLASS_REGULATOR, + .ops = &ab8500_regulator_ops, +}; + +static int ab8500_regulators_bind(struct udevice *dev) +{ + ofnode node; + + node = dev_read_subnode(dev, "ab8500_ldo_aux3"); + if (!ofnode_valid(node)) + return -ENODEV; + + return device_bind_driver_to_node(dev, "ab8500_ldo_aux3", + ofnode_get_name(node), node, NULL); +} + +static const struct udevice_id ab8500_regulator_ids[] = { + { .compatible = "stericsson,ab8500-regulator" }, + { .compatible = "stericsson,ab8505-regulator" }, + { } +}; + +U_BOOT_DRIVER(ab8500_regulators) = { + .name = "ab8500_regulators", + .id = UCLASS_NOP, + .of_match = ab8500_regulator_ids, + .bind = ab8500_regulators_bind, +}; -- 2.55.0