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

Robert Beckett bob.beckett at collabora.com
Wed Oct 16 11:11:21 UTC 2019


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



More information about the U-Boot mailing list