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

Robert Beckett
2019-10-16T11:11:21Z

On Wed, 2019-10-16 at 07:24 +0000, Schrempf Frieder wrote:
> 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?

(+cc Martin Fuzzey)

Huh, I had not seen that.
Martin's versions looks more complete than mine, so I would say go with
that one.

Martin: any objections to including your patches in here? I dont mind
pushing it through and handling any review comments etc. I am keen to
get get this upstreamed.

>
> 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_ */
> > +