From patchwork Tue Oct 15 15:53:49 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Robert Beckett X-Patchwork-Id: 1177150 X-Patchwork-Delegate: sbabic@denx.de Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=lists.denx.de (client-ip=81.169.180.215; helo=lists.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dmarc=fail (p=none dis=none) header.from=collabora.com Received: from lists.denx.de (dione.denx.de [81.169.180.215]) by ozlabs.org (Postfix) with ESMTP id 46t0qm1C0Cz9sP7 for ; Wed, 16 Oct 2019 03:14:28 +1100 (AEDT) Received: by lists.denx.de (Postfix, from userid 105) id C470AC21F1D; Tue, 15 Oct 2019 16:07:17 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on lists.denx.de X-Spam-Level: X-Spam-Status: No, score=0.0 required=5.0 tests=SPF_HELO_PASS, UNPARSEABLE_RELAY autolearn=unavailable autolearn_force=no version=3.4.0 Received: from lists.denx.de (localhost [IPv6:::1]) by lists.denx.de (Postfix) with ESMTP id D1617C21E68; Tue, 15 Oct 2019 16:07:11 +0000 (UTC) Received: by lists.denx.de (Postfix, from userid 105) id 70314C21E68; Tue, 15 Oct 2019 15:55:34 +0000 (UTC) Received: from bhuna.collabora.co.uk (bhuna.collabora.co.uk [46.235.227.227]) by lists.denx.de (Postfix) with ESMTPS id 1BF0DC21CB1 for ; Tue, 15 Oct 2019 15:55:31 +0000 (UTC) Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: bbeckett) with ESMTPSA id 8D4EC28A1A1 From: Robert Beckett To: U-Boot Mailing List Date: Tue, 15 Oct 2019 16:53:49 +0100 Message-Id: <302df8994af8e969b256a76024afee0af1cd3b55.1571150958.git.bob.beckett@collabora.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: References: MIME-Version: 1.0 Cc: Sven Schwermer , Christoph Muellner Subject: [U-Boot] [PATCH 36/37] dm: pmic: add da9063 PMIC driver and regulators X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.18 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" Add DM driver to support Dialog DA9063. Currently it support binding regulator children. Signed-off-by: Robert Beckett --- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 +#include +#include +#include +#include +#include +#include + +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 +#include + +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_CONT - 0x20 - 0x25 */ +#define DA9063_BUCK_ENABLE BIT(0) + +/* DA9063_LDO_CONT - 0x26 - 0x30 */ +#define DA9063_LDO_ENABLE BIT(0) + +/* DA9063_B_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_A - 0xA3 - 0xA8 */ +#define DA9063_BUCK_VSEL GENMASK(6, 0) + +/* DA9063_VLDO_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_ */ +