[U-Boot] [PATCH 36/37] dm: pmic: add da9063 PMIC driver and regulators

Schrempf Frieder
2019-10-16T07:24:26Z

Hi Robert,

On 15.10.19 17:53, Robert Beckett wrote:
> Add DM driver to support Dialog DA9063.
> Currently it support binding regulator children.
>
> Signed-off-by: Robert Beckett <bob.beckett@collabora.com>

I also have a board with DA9063 and was looking for support in U-Boot. I
found patches from Martin Fuzzey [1] and pulled them in almost as is and
found them to work just fine.

Unfortunately I didn't have time to resend them yet, but you can find
the rebased patches here: [2].

On a first glance your implementation looks different, so it seems you
didn't use Martin's patches. Though the resulting features probably will
be similar.

I only had a very quick look and one difference seems to be that your
regulator implementation supports the AUTO and SYNC mode, while Martin's
version supports AUTO, SYNC and SLEEP. There might be other differences.

What do you think? Which version would be better?

Also find a few comments below, though I didn't do a full review, yet.

Thanks,
Frieder

[1] https://patchwork.ozlabs.org/cover/979346/
[2] https://github.com/fschrempf/u-boot/commits/da9063

> ---
> drivers/power/pmic/Kconfig | 8 +
> drivers/power/pmic/Makefile | 1 +
> drivers/power/pmic/da9063.c | 270 ++++++++++++++++++++++++++
> drivers/power/regulator/Kconfig | 7 +
> drivers/power/regulator/Makefile | 1 +
> drivers/power/regulator/da9063.c | 320 +++++++++++++++++++++++++++++++
> include/power/da9063_pmic.h | 303 +++++++++++++++++++++++++++++
> 7 files changed, 910 insertions(+)
> create mode 100644 drivers/power/pmic/da9063.c
> create mode 100644 drivers/power/regulator/da9063.c
> create mode 100644 include/power/da9063_pmic.h
>
> diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
> index 586772fdec..6dd7b1bf76 100644
> --- a/drivers/power/pmic/Kconfig
> +++ b/drivers/power/pmic/Kconfig
> @@ -267,3 +267,11 @@ config SPL_PMIC_LP87565
> help
> The LP87565 is a PMIC containing a bunch of SMPS.
> This driver binds the pmic children in SPL.
> +
> +config DM_PMIC_DA9063
> + bool "Enable support for Dialog DA9063 PMIC"
> + depends on DM_PMIC && (DM_I2C || DM_SPI)
> + help
> + The DA9063 is a PMIC providing 6 BUCK converters and 11 LDO regulators.
> + It can be accessed via I2C or SPI.
> + This driver binds the pmic children.
> diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
> index 888dbb2857..9be9d5d9a0 100644
> --- a/drivers/power/pmic/Makefile
> +++ b/drivers/power/pmic/Makefile
> @@ -25,6 +25,7 @@ obj-$(CONFIG_$(SPL_)PMIC_PALMAS) += palmas.o
> obj-$(CONFIG_$(SPL_)PMIC_LP873X) += lp873x.o
> obj-$(CONFIG_$(SPL_)PMIC_LP87565) += lp87565.o
> obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o
> +obj-$(CONFIG_DM_PMIC_DA9063) += da9063.o

It would be good to be able to enable the driver for U-Boot proper and
SPL separately. So this should be CONFIG_$(SPL_)DM_PMIC_DA9063.

>
> obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
> obj-$(CONFIG_POWER_MAX77696) += pmic_max77696.o
> diff --git a/drivers/power/pmic/da9063.c b/drivers/power/pmic/da9063.c
> new file mode 100644
> index 0000000000..81a7803b09
> --- /dev/null
> +++ b/drivers/power/pmic/da9063.c
> @@ -0,0 +1,270 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2019 Collabora
> + * (C) Copyright 2019 GE
> + */
> +
> +#define DEBUG 1

This should not be here.

> +#include <common.h>
> +#include <fdtdec.h>
> +#include <errno.h>
> +#include <dm.h>
> +#include <i2c.h>
> +#include <spi.h>
> +#include <linux/bitfield.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <power/da9063_pmic.h>
> +
> +static const struct pmic_child_info pmic_children_info[] = {
> + { .prefix = "bcore", .driver = DA9063_BUCK_DRIVER },
> + { .prefix = "bpro", .driver = DA9063_BUCK_DRIVER },
> + { .prefix = "bmem", .driver = DA9063_BUCK_DRIVER },
> + { .prefix = "bio", .driver = DA9063_BUCK_DRIVER },
> + { .prefix = "bperi", .driver = DA9063_BUCK_DRIVER },
> + { .prefix = "ldo", .driver = DA9063_LDO_DRIVER },
> + { },
> +};
> +
> +static int da9063_reg_count(struct udevice *dev)
> +{
> + return DA9063_NUM_OF_REGS;
> +}
> +
> +#if defined(CONFIG_DM_I2C)
> +static int da9063_i2c_read(struct udevice *dev, uint reg, uint8_t *buff,
> + int len)
> +{
> + int ret;
> +
> + /* only support single reg accesses */
> + if (len != 1)
> + return -EINVAL;
> +
> + ret = dm_i2c_read(dev, reg, buff, len);
> + if (ret) {
> + pr_err("%s: unable to read reg %#x: %d\n", __func__, reg, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int da9063_i2c_write(struct udevice *dev, uint reg, const uint8_t *buff,
> + int len)
> +{
> + int ret;
> +
> + /* only support single reg accesses */
> + if (len != 1)
> + return -EINVAL;
> +
> + ret = dm_i2c_write(dev, reg, buff, len);
> + if (ret) {
> + pr_err("%s: unable to write reg %#x: %d\n", __func__, reg, ret);
> + return ret;
> + }
> +
> + return 0;
> +}
> +#endif
> +
> +#if defined(CONFIG_DM_SPI)
> +static int da9063_spi_read(struct udevice *dev, uint reg, uint8_t *buff,
> + int len)
> +{
> + u8 page;
> + u8 data[2];
> + int ret;
> +
> + /* only support single reg accesses */
> + if (len != 1)
> + return -EINVAL;
> +
> + page = FIELD_GET(DA9063_REG_PAGE_MASK, reg);
> + reg = FIELD_GET(DA9063_REG_ADDR_MASK, reg);
> +
> + ret = dm_spi_claim_bus(dev);
> + if (ret)
> + return ret;
> + /* set page */
> + data[0] = FIELD_PREP(DA9063_PROTO_ADDR_MASK, DA9063_PAGE_CON) |
> + FIELD_PREP(DA9063_PROTO_RW_MASK, DA9063_PROTO_WRITE);
> + data[1] = FIELD_PREP(DA9063_PAGE_CON_PAGE, page);
> + ret = dm_spi_xfer(dev, DA9063_PROTO_LEN, data, NULL, SPI_XFER_ONCE);
> + if (ret) {
> + pr_err("%s: unable to set page: %d\n", __func__, ret);
> + goto err_page;
> + }
> +
> + /* set target reg */
> + data[0] = FIELD_PREP(DA9063_PROTO_ADDR_MASK, reg) |
> + FIELD_PREP(DA9063_PROTO_RW_MASK, DA9063_PROTO_READ);
> + data[1] = 0;
> + ret = dm_spi_xfer(dev, DA9063_PROTO_LEN, data, data, SPI_XFER_ONCE);
> + if (ret) {
> + pr_err("%s: unable to read reg %#x: %d\n", __func__, reg, ret);
> + goto err_reg;
> + }
> + dm_spi_release_bus(dev);
> +
> + *buff = data[1];
> +
> + return 0;
> +
> +err_page:
> +err_reg:
> + dm_spi_release_bus(dev);
> +
> + return ret;
> +}
> +
> +static int da9063_spi_write(struct udevice *dev, uint reg, const uint8_t *buff,
> + int len)
> +{
> + u8 page;
> + u8 data[2];
> + int ret;
> +
> + /* only support single reg accesses */
> + if (len != 1)
> + return -EINVAL;
> +
> + page = FIELD_GET(DA9063_REG_PAGE_MASK, reg);
> + reg = FIELD_GET(DA9063_REG_ADDR_MASK, reg);
> +
> + ret = dm_spi_claim_bus(dev);
> + if (ret)
> + return ret;
> + /* set page */
> + data[0] = FIELD_PREP(DA9063_PROTO_ADDR_MASK, DA9063_PAGE_CON) |
> + FIELD_PREP(DA9063_PROTO_RW_MASK, DA9063_PROTO_WRITE);
> + data[1] = FIELD_PREP(DA9063_PAGE_CON_PAGE, page);
> + ret = dm_spi_xfer(dev, DA9063_PROTO_LEN, data, NULL, SPI_XFER_ONCE);
> + if (ret) {
> + pr_err("%s: unable to set page: %d\n", __func__, ret);
> + goto err_page;
> + }
> +
> + /* set target reg */
> + data[0] = FIELD_PREP(DA9063_PROTO_ADDR_MASK, reg) |
> + FIELD_PREP(DA9063_PROTO_RW_MASK, DA9063_PROTO_WRITE);
> + data[1] = *buff;
> + ret = dm_spi_xfer(dev, DA9063_PROTO_LEN, data, NULL, SPI_XFER_ONCE);
> + if (ret) {
> + pr_err("%s: unable to write reg %#x: %d\n", __func__, reg, ret);
> + goto err_reg;
> + }
> + dm_spi_release_bus(dev);
> +
> + return 0;
> +
> +err_page:
> +err_reg:
> + dm_spi_release_bus(dev);
> +
> + return ret;
> +}
> +#endif
> +
> +struct da9063_priv {
> + int (*read)(struct udevice *dev, uint reg, uint8_t *buffer, int len);
> + int (*write)(struct udevice *dev, uint reg, const uint8_t *buffer,
> + int len);
> +};
> +
> +static int da9063_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
> +{
> + struct da9063_priv *priv = dev_get_priv(dev);
> +
> + return priv->read(dev, reg, buff, len);
> +}
> +
> +static int da9063_write(struct udevice *dev, uint reg, const uint8_t *buff,
> + int len)
> +{
> + struct da9063_priv *priv = dev_get_priv(dev);
> +
> + return priv->write(dev, reg, buff, len);
> +}
> +
> +static struct dm_pmic_ops da9063_ops = {
> + .reg_count = da9063_reg_count,
> + .read = da9063_read,
> + .write = da9063_write,
> +};
> +
> +static int da9063_bind(struct udevice *dev)
> +{
> + ofnode regulators_node;
> + int children;
> +
> + regulators_node = dev_read_subnode(dev, "regulators");
> + if (!ofnode_valid(regulators_node)) {
> + pr_debug("%s: %s regulators subnode not found!\n", __func__,
> + dev->name);
> + return -ENXIO;
> + }
> +
> + pr_debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
> +
> + children = pmic_bind_children(dev, regulators_node, pmic_children_info);
> + if (!children)
> + pr_debug("%s: %s - no child found\n", __func__, dev->name);
> +
> + return 0;
> +}
> +
> +static int da9063_probe(struct udevice *dev)
> +{
> + struct da9063_priv *priv = dev_get_priv(dev);
> + int ret;
> +
> + if (device_get_uclass_id(dev->parent) == UCLASS_I2C) {
> +#if defined(CONFIG_DM_I2C)
> + i2c_set_chip_addr_offset_mask(dev, 0x1);
> + priv->read = da9063_i2c_read;
> + priv->write = da9063_i2c_write;
> +#else
> + return -ENODEV;
> +#endif
> + } else if (device_get_uclass_id(dev->parent) == UCLASS_SPI) {
> +#if defined(CONFIG_DM_SPI)
> + priv->read = da9063_spi_read;
> + priv->write = da9063_spi_write;
> +#else
> + return -ENODEV;
> +#endif
> + } else {
> + pr_err("%s: invalid bus\n", __func__);
> + return -ENODEV;
> + }
> +
> + ret = pmic_reg_read(dev, DA9063_CHIP_ID);
> + if (ret < 0) {
> + pr_debug("%s: unable to read chip id: %d\n", __func__, ret);
> + return ret;
> + }
> +
> + if (ret != DA9063_CHIP_ID_DA9063) {
> + pr_debug("%s: unknown chip id: %#x\n", __func__, ret);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static const struct udevice_id da9063_ids[] = {
> + { .compatible = "dlg,da9063" },
> + { }
> +};
> +
> +U_BOOT_DRIVER(pmic_da9063) = {
> + .name = "da9063_pmic",
> + .id = UCLASS_PMIC,
> + .of_match = da9063_ids,
> + .bind = da9063_bind,
> + .ops = &da9063_ops,
> + .priv_auto_alloc_size = sizeof(struct da9063_priv),
> + .probe = da9063_probe,
> +};
> diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
> index 9aa00fad42..e87cccdb82 100644
> --- a/drivers/power/regulator/Kconfig
> +++ b/drivers/power/regulator/Kconfig
> @@ -313,3 +313,10 @@ config SPL_DM_REGULATOR_LP873X
> This enables implementation of driver-model regulator uclass
> features for REGULATOR LP873X and the family of LP873X PMICs.
> The driver implements get/set api for: value and enable in SPL.
> +
> +config DM_REGULATOR_DA9063
> + bool "Enable support for DA9063 regulators"
> + help
> + Enable support the regulator functions of the DA9063 PMIC.
> + The driver support voltage set/get and enable/disable for LDOs and
> + BUCKs, and mode get/set for BUCKs.
> diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
> index 6a3d4bbee4..f0926614cb 100644
> --- a/drivers/power/regulator/Makefile
> +++ b/drivers/power/regulator/Makefile
> @@ -27,3 +27,4 @@ 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_STPMIC1) += stpmic1.o
> +obj-$(CONFIG_DM_REGULATOR_DA9063) += da9063.o

It would be good to be able to enable the driver for U-Boot proper and
SPL separately. So this should be CONFIG_$(SPL_)DM_REGULATOR_DA9063.

> diff --git a/drivers/power/regulator/da9063.c b/drivers/power/regulator/da9063.c
> new file mode 100644
> index 0000000000..68036a3951
> --- /dev/null
> +++ b/drivers/power/regulator/da9063.c
> @@ -0,0 +1,320 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * (C) Copyright 2019 Collabora
> + * (C) Copyright 2019 GE
> + */
> +
> +#include <common.h>
> +#include <fdtdec.h>
> +#include <dm.h>
> +#include <linux/bitfield.h>
> +#include <power/pmic.h>
> +#include <power/regulator.h>
> +#include <power/da9063_pmic.h>
> +
> +enum {
> + DA9063_ID_BCORE1 = 0,
> + DA9063_ID_BCORE2,
> + DA9063_ID_BPRO,
> + DA9063_ID_BMEM,
> + DA9063_ID_BIO,
> + DA9063_ID_BPERI,
> +};
> +
> +struct buck_info {
> + int min_uV;
> + int max_uV;
> + int step_uV;
> + int ctrl_reg;
> + int volt_reg;
> + int mode_reg;
> +};
> +
> +#define to_buck_info(dev) (struct buck_info *)dev->driver_data
> +
> +#define DA9063_BUCK(name, min_mV, max_mV, step_mV) \
> +{ \
> + .min_uV = min_mV * 1000, \
> + .max_uV = max_mV * 1000, \
> + .step_uV = step_mV * 1000, \
> + .ctrl_reg = DA9063_##name##_CONT, \
> + .volt_reg = DA9063_V##name##_A, \
> + .mode_reg = DA9063_##name##_CFG, \
> +}
> +
> +static const struct buck_info da9063_buck_info[] = {
> + [DA9063_ID_BCORE1] = DA9063_BUCK(BCORE1, 300, 1570, 10),
> + [DA9063_ID_BCORE2] = DA9063_BUCK(BCORE2, 300, 1570, 10),
> + [DA9063_ID_BPRO] = DA9063_BUCK(BPRO, 530, 1800, 10),
> + [DA9063_ID_BMEM] = DA9063_BUCK(BMEM, 800, 3340, 20),
> + [DA9063_ID_BIO] = DA9063_BUCK(BIO, 800, 3340, 20),
> + [DA9063_ID_BPERI] = DA9063_BUCK(BPERI, 800, 3340, 20),
> +};
> +
> +/* Buck converters can either be in auto mode or sync mode.
> + * Auto uses PWM or PFM depending on load current.
> + * Sync mode uses PFM unless output voltage is < 0.7V.
> + */
> +static struct dm_regulator_mode da9063_buck_modes[] = {
> + {
> + .id = DA9063_OPMODE_AUTO,
> + .register_value = DA9063_BUCK_MODE_AUTO,
> + .name = "AUTO",
> + },
> + {
> + .id = DA9063_OPMODE_SYNC,
> + .register_value = DA9063_BUCK_MODE_SYNC,
> + .name = "SYNC",
> + },
> +};
> +
> +static int da9063_buck_get_mode(struct udevice *dev)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int mode = pmic_reg_read(dev_get_parent(dev), info->mode_reg);
> +
> + if (mode < 0)
> + return mode;
> +
> + mode = FIELD_GET(DA9063_BUCK_MODE, mode);
> +
> + switch (mode) {
> + case DA9063_BUCK_MODE_AUTO:
> + return DA9063_OPMODE_AUTO;
> + case DA9063_BUCK_MODE_SYNC:
> + return DA9063_OPMODE_SYNC;
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int da9063_buck_set_mode(struct udevice *dev, int rmode)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int mode = pmic_reg_read(dev_get_parent(dev), info->mode_reg);
> +
> + mode &= ~DA9063_BUCK_MODE;
> +
> + switch (rmode) {
> + case DA9063_OPMODE_AUTO:
> + mode |= FIELD_PREP(DA9063_BUCK_MODE, DA9063_BUCK_MODE_AUTO);
> + break;
> + case DA9063_OPMODE_SYNC:
> + mode |= FIELD_PREP(DA9063_BUCK_MODE, DA9063_BUCK_MODE_SYNC);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + return pmic_reg_write(dev_get_parent(dev), info->mode_reg, mode);
> +}
> +
> +static int da9063_buck_get_value(struct udevice *dev)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int sel = pmic_reg_read(dev_get_parent(dev), info->volt_reg);
> +
> + if (sel < 0)
> + return sel;
> + sel = FIELD_GET(DA9063_BUCK_VSEL, sel);
> + return info->min_uV + (info->step_uV * sel);
> +}
> +
> +static int da9063_buck_set_value(struct udevice *dev, int uV)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int sel = pmic_reg_read(dev_get_parent(dev), info->volt_reg);
> +
> + if (sel < 0)
> + return sel;
> + sel &= ~DA9063_BUCK_VSEL;
> + sel |= FIELD_PREP(DA9063_BUCK_VSEL,
> + DIV_ROUND_UP(uV - info->min_uV, info->step_uV));
> +
> + return pmic_reg_write(dev_get_parent(dev), info->volt_reg, sel);
> +}
> +
> +static int da9063_buck_get_enable(struct udevice *dev)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int cont = pmic_reg_read(dev_get_parent(dev), info->ctrl_reg);
> +
> + if (cont < 0)
> + return cont;
> +
> + return FIELD_GET(DA9063_BUCK_ENABLE, cont);
> +}
> +
> +static int da9063_buck_set_enable(struct udevice *dev, bool enable)
> +{
> + struct buck_info *info = to_buck_info(dev);
> + int cont = pmic_reg_read(dev_get_parent(dev), info->ctrl_reg);
> +
> + if (cont < 0)
> + return cont;
> +
> + cont &= ~DA9063_BUCK_ENABLE;
> + cont |= FIELD_PREP(DA9063_BUCK_ENABLE, enable);
> +
> + return pmic_reg_write(dev_get_parent(dev), info->ctrl_reg, cont);
> +}
> +
> +static int da9063_buck_probe(struct udevice *dev)
> +{
> + struct dm_regulator_uclass_platdata *uc_pdata;
> +
> + uc_pdata = dev_get_uclass_platdata(dev);
> +
> + if (!strcmp(uc_pdata->name, "bcore1"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BCORE1];
> + else if (!strcmp(uc_pdata->name, "bcore2"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BCORE2];
> + else if (!strcmp(uc_pdata->name, "bpro"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BPRO];
> + else if (!strcmp(uc_pdata->name, "bmem"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BMEM];
> + else if (!strcmp(uc_pdata->name, "bio"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BIO];
> + else if (!strcmp(uc_pdata->name, "bperi"))
> + dev->driver_data = (ulong)&da9063_buck_info[DA9063_ID_BPERI];
> + else
> + return -ENODEV;
> +
> + uc_pdata->type = REGULATOR_TYPE_BUCK;
> + uc_pdata->mode_count = ARRAY_SIZE(da9063_buck_modes);
> + uc_pdata->mode = da9063_buck_modes;
> +
> + return 0;
> +}
> +
> +static const struct dm_regulator_ops da9063_buck_ops = {
> + .get_value = da9063_buck_get_value,
> + .set_value = da9063_buck_set_value,
> + .get_enable = da9063_buck_get_enable,
> + .set_enable = da9063_buck_set_enable,
> + .get_mode = da9063_buck_get_mode,
> + .set_mode = da9063_buck_set_mode,
> +};
> +
> +U_BOOT_DRIVER(da9063_buck) = {
> + .name = DA9063_BUCK_DRIVER,
> + .id = UCLASS_REGULATOR,
> + .ops = &da9063_buck_ops,
> + .probe = da9063_buck_probe,
> +};
> +
> +struct ldo_info {
> + int min_uV;
> + int max_uV;
> + int step_uV;
> + int ctrl_reg;
> + int volt_reg;
> +};
> +
> +#define to_ldo_info(dev) (struct ldo_info *)dev->driver_data
> +
> +#define DA9063_LDO(idx, min_mV, max_mV, step_mV) \
> +{ \
> + .min_uV = min_mV * 1000, \
> + .max_uV = max_mV * 1000, \
> + .step_uV = step_mV * 1000, \
> + .ctrl_reg = DA9063_LDO##idx##_CONT, \
> + .volt_reg = DA9063_VLDO##idx##_A, \
> +}
> +
> +static const struct ldo_info da9063_ldo_info[] = {
> + DA9063_LDO(1, 600, 1860, 20),
> + DA9063_LDO(2, 600, 1860, 20),
> + DA9063_LDO(3, 900, 3440, 20),
> + DA9063_LDO(4, 900, 3440, 20),
> + DA9063_LDO(5, 900, 3600, 50),
> + DA9063_LDO(6, 900, 3600, 50),
> + DA9063_LDO(7, 900, 3600, 50),
> + DA9063_LDO(8, 900, 3600, 50),
> + DA9063_LDO(9, 950, 3600, 50),
> + DA9063_LDO(10, 900, 3600, 50),
> + DA9063_LDO(11, 900, 3600, 50),
> +};
> +
> +static int da9063_ldo_get_value(struct udevice *dev)
> +{
> + struct ldo_info *info = to_ldo_info(dev);
> + int sel;
> +
> + sel = pmic_reg_read(dev_get_parent(dev), info->volt_reg);
> + if (sel < 0)
> + return sel;
> + sel = FIELD_GET(DA9063_LDO_VSEL, sel);
> + return info->min_uV + (info->step_uV * sel);
> +}
> +
> +static int da9063_ldo_set_value(struct udevice *dev, int uV)
> +{
> + struct ldo_info *info = to_ldo_info(dev);
> + int sel;
> +
> + sel = pmic_reg_read(dev_get_parent(dev), info->volt_reg);
> + if (sel < 0)
> + return sel;
> + sel &= ~DA9063_LDO_VSEL;
> + sel |= FIELD_PREP(DA9063_LDO_VSEL,
> + DIV_ROUND_UP(uV - info->min_uV, info->step_uV));
> +
> + return pmic_reg_write(dev_get_parent(dev), info->volt_reg, sel);
> +}
> +
> +static int da9063_ldo_get_enable(struct udevice *dev)
> +{
> + struct ldo_info *info = to_ldo_info(dev);
> + int cont = pmic_reg_read(dev_get_parent(dev), info->ctrl_reg);
> +
> + if (cont < 0)
> + return cont;
> +
> + return FIELD_GET(DA9063_LDO_ENABLE, cont);
> +}
> +
> +static int da9063_ldo_set_enable(struct udevice *dev, bool enable)
> +{
> + struct ldo_info *info = to_ldo_info(dev);
> + int cont = pmic_reg_read(dev_get_parent(dev), info->ctrl_reg);
> +
> + if (cont < 0)
> + return cont;
> +
> + cont &= ~DA9063_LDO_ENABLE;
> + cont |= FIELD_PREP(DA9063_LDO_ENABLE, enable);
> +
> + return pmic_reg_write(dev_get_parent(dev), info->ctrl_reg, cont);
> +}
> +
> +static int da9063_ldo_probe(struct udevice *dev)
> +{
> + struct dm_regulator_uclass_platdata *uc_pdata;
> +
> + if (dev->driver_data < 1 ||
> + dev->driver_data > ARRAY_SIZE(da9063_ldo_info))
> + return -EINVAL;
> +
> + uc_pdata = dev_get_uclass_platdata(dev);
> +
> + dev->driver_data = (ulong)&da9063_ldo_info[dev->driver_data - 1];
> + uc_pdata->type = REGULATOR_TYPE_LDO;
> + uc_pdata->mode_count = 0;
> +
> + return 0;
> +}
> +
> +static const struct dm_regulator_ops da9063_ldo_ops = {
> + .get_value = da9063_ldo_get_value,
> + .set_value = da9063_ldo_set_value,
> + .get_enable = da9063_ldo_get_enable,
> + .set_enable = da9063_ldo_set_enable,
> +};
> +
> +U_BOOT_DRIVER(da9063_ldo) = {
> + .name = DA9063_LDO_DRIVER,
> + .id = UCLASS_REGULATOR,
> + .ops = &da9063_ldo_ops,
> + .probe = da9063_ldo_probe,
> +};
> diff --git a/include/power/da9063_pmic.h b/include/power/da9063_pmic.h
> new file mode 100644
> index 0000000000..7258987ae3
> --- /dev/null
> +++ b/include/power/da9063_pmic.h
> @@ -0,0 +1,303 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * (C) Copyright 2019 Collabora
> + * (C) Copyright 2019 GE
> + */
> +
> +#ifndef __DA9063_PMIC_H_
> +#define __DA9063_PMIC_H_
> +
> +#include <linux/bitops.h>
> +#include <power/pmic.h>
> +
> +enum {
> + DA9063_PAGE_CON = 0x0,
> +
> + /* System Control and Event Registers */
> + DA9063_STATUS_A,
> + DA9063_STATUS_B,
> + DA9063_STATUS_C,
> + DA9063_STATUS_D,
> + DA9063_FAULT_LOG,
> + DA9063_EVENT_A,
> + DA9063_EVENT_B,
> + DA9063_EVENT_C,
> + DA9063_EVENT_D,
> + DA9063_IRQ_MASK_A,
> + DA9063_IRQ_MASK_B,
> + DA9063_IRQ_MASK_C,
> + DA9063_IRQ_MASK_D,
> + DA9063_CONTROL_A,
> + DA9063_CONTROL_B,
> + DA9063_CONTROL_C,
> + DA9063_CONTROL_D,
> + DA9063_CONTROL_E,
> + DA9063_CONTROL_F,
> + DA9063_PD_DIS,
> +
> + /* GPIO Control Registers */
> + DA9063_GPIO_0_1,
> + DA9063_GPIO_2_3,
> + DA9063_GPIO_4_5,
> + DA9063_GPIO_6_7,
> + DA9063_GPIO_8_9,
> + DA9063_GPIO_10_11,
> + DA9063_GPIO_12_13,
> + DA9063_GPIO_14_15,
> + DA9063_GPIO_MODE0_7,
> + DA9063_GPIO_MODE8_15,
> + DA9063_SWITCH_CONT,
> +
> + /* Regulator Control Registers */
> + DA9063_BCORE2_CONT,
> + DA9063_BCORE1_CONT,
> + DA9063_BPRO_CONT,
> + DA9063_BMEM_CONT,
> + DA9063_BIO_CONT,
> + DA9063_BPERI_CONT,
> + DA9063_LDO1_CONT,
> + DA9063_LDO2_CONT,
> + DA9063_LDO3_CONT,
> + DA9063_LDO4_CONT,
> + DA9063_LDO5_CONT,
> + DA9063_LDO6_CONT,
> + DA9063_LDO7_CONT,
> + DA9063_LDO8_CONT,
> + DA9063_LDO9_CONT,
> + DA9063_LDO10_CONT,
> + DA9063_LDO11_CONT,
> + DA9063_SUPPLIES,
> + DA9063_DVC_1,
> + DA9063_DVC_2,
> +
> + /* GP-ADC Control Registers */
> + DA9063_ADC_MAN,
> + DA9063_ADC_CONT,
> + DA9063_VSYS_MON,
> + DA9063_ADC_RES_L,
> + DA9063_ADC_RES_H,
> + DA9063_VSYS_RES,
> + DA9063_ADCIN1_RES,
> + DA9063_ADCIN2_RES,
> + DA9063_ADCIN3_RES,
> + DA9063_MON_A8_RES,
> + DA9063_MON_A9_RES,
> + DA9063_MON_A10_RES,
> +
> + /* RTC Calendar and Alarm Registers */
> + DA9063_COUNT_S,
> + DA9063_COUNT_MI,
> + DA9063_COUNT_H,
> + DA9063_COUNT_D,
> + DA9063_COUNT_MO,
> + DA9063_COUNT_Y,
> +
> + DA9063_ALARM_S,
> + DA9063_ALARM_MI,
> + DA9063_ALARM_H,
> + DA9063_ALARM_D,
> + DA9063_ALARM_MO,
> + DA9063_ALARM_Y,
> + DA9063_SECOND_A,
> + DA9063_SECOND_B,
> + DA9063_SECOND_C,
> + DA9063_SECOND_D,
> +
> + /* Sequencer Control Registers */
> + DA9063_SEQ = 0x81,
> + DA9063_SEQ_TIMER,
> + DA9063_ID_2_1,
> + DA9063_ID_4_3,
> + DA9063_ID_6_5,
> + DA9063_ID_8_7,
> + DA9063_ID_10_9,
> + DA9063_ID_12_11,
> + DA9063_ID_14_13,
> + DA9063_ID_16_15,
> + DA9063_ID_18_17,
> + DA9063_ID_20_19,
> + DA9063_ID_22_21,
> + DA9063_ID_24_23,
> + DA9063_ID_26_25,
> + DA9063_ID_28_27,
> + DA9063_ID_30_29,
> + DA9063_ID_32_31,
> + DA9063_SEQ_A,
> + DA9063_SEQ_B,
> + DA9063_WAIT,
> + DA9063_EN_32K,
> + DA9063_RESET,
> +
> + /* Regulator Setting Registers */
> + DA9063_BUCK_ILIM_A,
> + DA9063_BUCK_ILIM_B,
> + DA9063_BUCK_ILIM_C,
> + DA9063_BCORE2_CFG,
> + DA9063_BCORE1_CFG,
> + DA9063_BPRO_CFG,
> + DA9063_BIO_CFG,
> + DA9063_BMEM_CFG,
> + DA9063_BPERI_CFG,
> + DA9063_VBCORE2_A,
> + DA9063_VBCORE1_A,
> + DA9063_VBPRO_A,
> + DA9063_VBMEM_A,
> + DA9063_VBIO_A,
> + DA9063_VBPERI_A,
> + DA9063_VLDO1_A,
> + DA9063_VLDO2_A,
> + DA9063_VLDO3_A,
> + DA9063_VLDO4_A,
> + DA9063_VLDO5_A,
> + DA9063_VLDO6_A,
> + DA9063_VLDO7_A,
> + DA9063_VLDO8_A,
> + DA9063_VLDO9_A,
> + DA9063_VLDO10_A,
> + DA9063_VLDO11_A,
> + DA9063_VBCORE2_B,
> + DA9063_VBCORE1_B,
> + DA9063_VBPRO_B,
> + DA9063_VBMEM_B,
> + DA9063_VBIO_B,
> + DA9063_VBPERI_B,
> + DA9063_VLDO1_B,
> + DA9063_VLDO2_B,
> + DA9063_VLDO3_B,
> + DA9063_VLDO4_B,
> + DA9063_VLDO5_B,
> + DA9063_VLDO6_B,
> + DA9063_VLDO7_B,
> + DA9063_VLDO8_B,
> + DA9063_VLDO9_B,
> + DA9063_VLDO10_B,
> + DA9063_VLDO11_B,
> +
> + /* Backup Battery Charger Control Register */
> + DA9063_BBAT_CONT,
> +
> + /* GPIO PWM (LED) */
> + DA9063_GPO11_LED,
> + DA9063_GPO14_LED,
> + DA9063_GPO15_LED,
> +
> + /* GP-ADC Threshold Registers */
> + DA9063_ADC_CFG,
> + DA9063_AUTO1_HIGH,
> + DA9063_AUTO1_LOW,
> + DA9063_AUTO2_HIGH,
> + DA9063_AUTO2_LOW,
> + DA9063_AUTO3_HIGH,
> + DA9063_AUTO3_LOW,
> +
> + /* DA9063 Configuration registers */
> + /* OTP */
> + DA9063_OTP_CONT = 0x101,
> + DA9063_OTP_ADDR,
> + DA9063_OTP_DATA,
> +
> + /* Customer Trim and Configuration */
> + DA9063_T_OFFSET,
> + DA9063_INTERFACE,
> + DA9063_CONFIG_A,
> + DA9063_CONFIG_B,
> + DA9063_CONFIG_C,
> + DA9063_CONFIG_D,
> + DA9063_CONFIG_E,
> + DA9063_CONFIG_F,
> + DA9063_CONFIG_G,
> + DA9063_CONFIG_H,
> + DA9063_CONFIG_I,
> + DA9063_CONFIG_J,
> + DA9063_CONFIG_K,
> + DA9063_CONFIG_L,
> +
> + DA9063_CONFIG_M,
> + DA9063_CONFIG_N,
> +
> + DA9063_MON_REG_1,
> + DA9063_MON_REG_2,
> + DA9063_MON_REG_3,
> + DA9063_MON_REG_4,
> + DA9063_MON_REG_5 = 0x11E,
> + DA9063_MON_REG_6,
> + DA9063_TRIM_CLDR,
> + /* General Purpose Registers */
> + DA9063_GP_ID_0,
> + DA9063_GP_ID_1,
> + DA9063_GP_ID_2,
> + DA9063_GP_ID_3,
> + DA9063_GP_ID_4,
> + DA9063_GP_ID_5,
> + DA9063_GP_ID_6,
> + DA9063_GP_ID_7,
> + DA9063_GP_ID_8,
> + DA9063_GP_ID_9,
> + DA9063_GP_ID_10,
> + DA9063_GP_ID_11,
> + DA9063_GP_ID_12,
> + DA9063_GP_ID_13,
> + DA9063_GP_ID_14,
> + DA9063_GP_ID_15,
> + DA9063_GP_ID_16,
> + DA9063_GP_ID_17,
> + DA9063_GP_ID_18,
> + DA9063_GP_ID_19,
> +
> + /* Chip ID and variant */
> + DA9063_CHIP_ID = 0x181,
> + DA9063_CHIP_VARIANT,
> +
> + DA9063_NUM_OF_REGS,
> +};
> +
> +#define DA9063_REG_PAGE_MASK GENMASK(8, 7)
> +#define DA9063_REG_ADDR_MASK GENMASK(6, 0)
> +
> +#define DA9063_PROTO_ADDR_MASK GENMASK(7, 1)
> +#define DA9063_PROTO_RW_MASK BIT(0)
> +#define DA9063_PROTO_READ 1
> +#define DA9063_PROTO_WRITE 0
> +#define DA9063_PROTO_LEN 16
> +
> +/* DA9063_PAGE_CON - 0x0 */
> +#define DA9063_PAGE_CON_PAGE GENMASK(2, 0)
> +#define DA9063_PAGE_CON_WRITE_MODE BIT(6)
> +#define DA9063_PAGE_CON_WRITE_MODE_PAGE 0
> +#define DA9063_PAGE_CON_WRITE_MODE_REPEAT 1
> +#define DA9063_PAGE_CON_REVERT BIT(7)
> +
> +/* DA9063_B<x>_CONT - 0x20 - 0x25 */
> +#define DA9063_BUCK_ENABLE BIT(0)
> +
> +/* DA9063_LDO<x>_CONT - 0x26 - 0x30 */
> +#define DA9063_LDO_ENABLE BIT(0)
> +
> +/* DA9063_B<x>_CFG - 0x9D - 0xA2 */
> +#define DA9063_BUCK_MODE GENMASK(7, 6)
> +#define DA9063_BUCK_MODE_MANUAL 0
> +#define DA9063_BUCK_MODE_SLEEP 1
> +#define DA9063_BUCK_MODE_SYNC 2
> +#define DA9063_BUCK_MODE_AUTO 3
> +
> +/* DA9063_VB<x>_A - 0xA3 - 0xA8 */
> +#define DA9063_BUCK_VSEL GENMASK(6, 0)
> +
> +/* DA9063_VLDO<x>_A - 0xA9 - 0xB3 */
> +#define DA9063_LDO_VSEL GENMASK(5, 0)
> +
> +/* DA9063_CHIP_ID - 0x181 */
> +#define DA9063_CHIP_ID_DA9063 0x61
> +
> +/* regulator operating modes */
> +enum {
> + DA9063_OPMODE_AUTO,
> + DA9063_OPMODE_SYNC,
> +};
> +
> +/* Drivers name */
> +#define DA9063_LDO_DRIVER "da9063_ldo"
> +#define DA9063_BUCK_DRIVER "da9063_buck"
> +
> +#endif /* __DA9063_PMIC_H_ */
> +
>