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

Robert Beckett bob.beckett at collabora.com
Tue Oct 15 15:53:49 UTC 2019


Add DM driver to support Dialog DA9063.
Currently it support binding regulator children.

Signed-off-by: Robert Beckett <bob.beckett at collabora.com>
---
 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
 
 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
+#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
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_ */
+
-- 
2.20.1



More information about the U-Boot mailing list