All of lore.kernel.org
 help / color / mirror / Atom feed
From: Martin Fuzzey <martin.fuzzey@flowbird.group>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH 3/3] power: regulator: add driver for Dialog DA9063 PMIC
Date: Fri,  5 Oct 2018 10:08:55 +0200	[thread overview]
Message-ID: <1538726940-3233-4-git-send-email-martin.fuzzey@flowbird.group> (raw)
In-Reply-To: <1538726940-3233-1-git-send-email-martin.fuzzey@flowbird.group>

Add a driver for the regulators in the the DA9063 PMIC.

Signed-off-by: Martin Fuzzey <martin.fuzzey@flowbird.group>
---
 drivers/power/regulator/Kconfig  |  10 +
 drivers/power/regulator/Makefile |   1 +
 drivers/power/regulator/da9063.c | 395 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 406 insertions(+)
 create mode 100644 drivers/power/regulator/da9063.c

diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 414f4a5..0fc6a0d 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -43,6 +43,16 @@ config REGULATOR_AS3722
 	  but does not yet support change voltages. Currently this must be
 	  done using direct register writes to the PMIC.
 
+config DM_REGULATOR_DA9063
+	bool "Enable Driver Model for REGULATOR DA9063"
+	depends on DM_REGULATOR && DM_PMIC_DA9063
+	help
+	  This config enables implementation of driver-model regulator uclass
+	  features for REGULATOR DA9063.
+	  The driver implements get/set api for value, enable and mode for all
+	  regulators. It also implements the get/set api for current for the
+	  buck regulators.
+
 config DM_REGULATOR_PFUZE100
 	bool "Enable Driver Model for REGULATOR PFUZE100"
 	depends on DM_REGULATOR && DM_PMIC_PFUZE100
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 16208af..f0f6541 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -8,6 +8,7 @@
 obj-$(CONFIG_$(SPL_)DM_REGULATOR) += regulator-uclass.o
 obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o
 obj-$(CONFIG_REGULATOR_AS3722)	+= as3722_regulator.o
+obj-$(CONFIG_DM_REGULATOR_DA9063) += da9063.o
 obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
 obj-$(CONFIG_$(SPL_)DM_PMIC_PFUZE100) += pfuze100.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm_regulator.o
diff --git a/drivers/power/regulator/da9063.c b/drivers/power/regulator/da9063.c
new file mode 100644
index 0000000..98cb7ac
--- /dev/null
+++ b/drivers/power/regulator/da9063.c
@@ -0,0 +1,395 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright (C) 2018 Flowbird
+ *  Martin Fuzzey  <martin.fuzzey@flowbird.group>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <power/da9063_pmic.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+#define	DA9063_BUCK_EN		0x01
+#define	DA9063_LDO_EN		0x01
+#define DA9063_VBUCK_MASK	0x7F
+#define DA9063_BUCK_SL		0x80
+#define DA9063_LDO_SL		0x80
+
+#define DA9063_VLDO1_MASK	0x3F
+#define DA9063_VLDO2_MASK	0x3F
+#define DA9063_VLDO3_MASK	0x7F
+#define DA9063_VLDO4_MASK	0x7F
+#define DA9063_VLDO5_MASK	0x3F
+#define DA9063_VLDO6_MASK	0x3F
+#define DA9063_VLDO7_MASK	0x3F
+#define DA9063_VLDO8_MASK	0x3F
+#define DA9063_VLDO9_MASK	0x3F
+#define DA9063_VLDO10_MASK	0x3F
+#define DA9063_VLDO11_MASK	0x3F
+
+#define DA9063_BUCK_MODE_MASK	0xC0
+#define	DA9063_BUCK_MODE_MANUAL	0x00
+#define	DA9063_BUCK_MODE_SLEEP	0x40
+#define	DA9063_BUCK_MODE_SYNC	0x80
+#define	DA9063_BUCK_MODE_AUTO	0xC0
+
+#define DA9063_BIO_ILIM_MASK	0x0F
+#define DA9063_BMEM_ILIM_MASK	0xF0
+#define DA9063_BPRO_ILIM_MASK	0x0F
+#define DA9063_BPERI_ILIM_MASK	0xF0
+#define DA9063_BCORE1_ILIM_MASK	0x0F
+#define DA9063_BCORE2_ILIM_MASK	0xF0
+
+struct da9063_reg_info {
+	uint min_uV;
+	uint step_uV;
+	uint max_uV;
+	uint min_uA;
+	uint step_uA;
+	uint max_uA;
+	uint en_reg;
+	uint vsel_reg;
+	uint mode_reg;
+	uint ilim_reg;
+	u8 en_mask;
+	u8 vsel_mask;
+	u8 ilim_mask;
+	const char *dt_node_name;
+	const int *current_limits;
+};
+
+struct da9063_priv {
+	const struct da9063_reg_info *reg_info;
+};
+
+enum {
+	LDOMODE_SLEEP,
+	LDOMODE_NORMAL
+};
+
+static struct dm_regulator_mode da9063_ldo_modes[] = {
+	{ .id = LDOMODE_SLEEP,
+		.register_value = DA9063_LDO_SL, .name = "SLEEP" },
+	{ .id = LDOMODE_NORMAL,
+		.register_value = 0, .name = "NORMAL" },
+};
+
+#define DA9063_LDO(regl_name, min_mV, step_mV, max_mV) \
+	.min_uV = (min_mV) * 1000, \
+	.step_uV = (step_mV) * 1000, \
+	.max_uV = (max_mV) * 1000, \
+	.en_reg = DA9063_REG_##regl_name##_CONT, \
+	.en_mask = DA9063_LDO_EN, \
+	.vsel_reg = DA9063_REG_V##regl_name##_A, \
+	.vsel_mask = DA9063_V##regl_name##_MASK, \
+	.mode_reg = DA9063_REG_V##regl_name##_A \
+
+/* This array is directly indexed so must stay in numerical order */
+static const struct da9063_reg_info da9063_ldo_info[] = {
+	{ DA9063_LDO(LDO1, 600, 20, 1860) },
+	{ DA9063_LDO(LDO2, 600, 20, 1860) },
+	{ DA9063_LDO(LDO3, 900, 20, 3440) },
+	{ DA9063_LDO(LDO4, 900, 20, 3440) },
+	{ DA9063_LDO(LDO5, 900, 50, 3600) },
+	{ DA9063_LDO(LDO6, 900, 50, 3600) },
+	{ DA9063_LDO(LDO7, 900, 50, 3600) },
+	{ DA9063_LDO(LDO8, 900, 50, 3600) },
+	{ DA9063_LDO(LDO9, 950, 50, 3600) },
+	{ DA9063_LDO(LDO10, 900, 50, 3600) },
+	{ DA9063_LDO(LDO11, 900, 50, 3600) },
+};
+
+enum {
+	BUCKMODE_MANUAL, /* SLEEP / SYNC depending on bit in VSEL */
+	BUCKMODE_SLEEP,
+	BUCKMODE_SYNC,
+	BUCKMODE_AUTO,
+};
+
+static struct dm_regulator_mode da9063_buck_modes[] = {
+	{ .id = BUCKMODE_SLEEP,
+		.register_value = DA9063_BUCK_MODE_SLEEP, .name = "SLEEP" },
+	{ .id = BUCKMODE_SYNC,
+		.register_value = DA9063_BUCK_MODE_SYNC, .name = "SYNC" },
+	{ .id = BUCKMODE_AUTO,
+		.register_value = DA9063_BUCK_MODE_AUTO, .name = "AUTO" },
+};
+
+#define DA9063_BUCK(regl_name, dt_name, \
+		    min_mV, step_mV, max_mV, \
+		    min_mA, step_mA, max_mA, _ilim_reg) \
+	.dt_node_name = dt_name, \
+	.min_uV = (min_mV) * 1000, \
+	.step_uV = (step_mV) * 1000, \
+	.max_uV = (max_mV) * 1000, \
+	.min_uA = (min_mA) * 1000, \
+	.step_uA = (step_mA) * 1000, \
+	.max_uA = (max_mA) * 1000, \
+	.en_reg = DA9063_REG_##regl_name##_CONT, \
+	.en_mask = DA9063_BUCK_EN, \
+	.vsel_reg = DA9063_REG_V##regl_name##_A, \
+	.vsel_mask = DA9063_VBUCK_MASK, \
+	.mode_reg = DA9063_REG_##regl_name##_CFG, \
+	.ilim_reg = DA9063_REG_BUCK_ILIM_##_ilim_reg, \
+	.ilim_mask = DA9063_##regl_name##_ILIM_MASK
+
+static const struct da9063_reg_info da9063_buck_info[] = {
+	/*				mV		mA */
+	{ DA9063_BUCK(BCORE1, "bcore1",	300, 10, 1570,	500, 100, 2000,	C) },
+	{ DA9063_BUCK(BCORE2, "bcore2", 300, 10, 1570,	500, 100, 2000, C) },
+	{ DA9063_BUCK(BPRO, "bpro",	530, 10, 1800,	500, 100, 2000, B) },
+	{ DA9063_BUCK(BMEM, "bmem",	800, 20, 3340,	1500, 100, 3000, A) },
+	{ DA9063_BUCK(BIO, "bio",	800, 20, 3340,	1500, 100, 3000, A) },
+	{ DA9063_BUCK(BPERI, "bperi",	800, 20, 3340,	1500, 100, 3000, B) },
+};
+
+static int da9063_get_enable(struct udevice *dev)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int ret;
+
+	ret = pmic_reg_read(dev->parent, info->en_reg);
+	if (ret < 0)
+		return ret;
+
+	return ret & info->en_mask ? true : false;
+}
+
+static int da9063_set_enable(struct udevice *dev, bool enable)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+
+	return pmic_clrsetbits(dev->parent, info->en_reg,
+			       info->en_mask, enable ? info->en_mask : 0);
+}
+
+static int da9063_get_voltage(struct udevice *dev)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int ret;
+
+	ret = pmic_reg_read(dev->parent, info->vsel_reg);
+	if (ret < 0)
+		return ret;
+
+	return info->min_uV + (ret & info->vsel_mask) * info->step_uV;
+}
+
+static int da9063_set_voltage(struct udevice *dev, int uV)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	uint sel;
+
+	if (uV < info->min_uV || uV > info->max_uV)
+		return -EINVAL;
+
+	sel = (uV - info->min_uV) / info->step_uV;
+
+	return pmic_clrsetbits(dev->parent, info->vsel_reg,
+			       info->vsel_mask, sel);
+}
+
+static const struct dm_regulator_mode
+	*da9063_find_mode_by_id(int id,
+				const struct dm_regulator_mode *modes,
+				uint mode_count)
+{
+	for (; mode_count; mode_count--) {
+		if (modes->id == id)
+			return modes;
+		modes++;
+	}
+	return NULL;
+}
+
+static int ldo_get_mode(struct udevice *dev)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int val;
+
+	val = pmic_reg_read(dev->parent, info->mode_reg);
+	if (val < 0)
+		return val;
+
+	return val & DA9063_LDO_SL ? LDOMODE_SLEEP : LDOMODE_NORMAL;
+}
+
+static int ldo_set_mode(struct udevice *dev, int mode_id)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	const struct dm_regulator_mode *mode;
+
+	mode = da9063_find_mode_by_id(mode_id,
+				      da9063_ldo_modes,
+				      ARRAY_SIZE(da9063_ldo_modes));
+	if (!mode)
+		return -EINVAL;
+
+	return pmic_clrsetbits(dev->parent, info->mode_reg,
+			       LDOMODE_SLEEP, mode->register_value);
+}
+
+static int buck_get_mode(struct udevice *dev)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int i;
+	int val;
+
+	val = pmic_reg_read(dev->parent, info->mode_reg);
+	if (val < 0)
+		return val;
+
+	val &= DA9063_BUCK_MODE_MASK;
+	if (val == DA9063_BUCK_MODE_MANUAL) {
+		val = pmic_reg_read(dev->parent, info->vsel_reg);
+		if (val < 0)
+			return val;
+
+		return val & DA9063_BUCK_SL ?
+			DA9063_BUCK_MODE_SLEEP : DA9063_BUCK_MODE_SYNC;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(da9063_buck_modes); i++) {
+		if (da9063_buck_modes[i].register_value == val)
+			return da9063_buck_modes[i].id;
+	}
+
+	return -EINVAL;
+}
+
+static int buck_set_mode(struct udevice *dev, int mode_id)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	const struct dm_regulator_mode *mode;
+
+	mode = da9063_find_mode_by_id(mode_id,
+				      da9063_buck_modes,
+				      ARRAY_SIZE(da9063_buck_modes));
+	if (!mode)
+		return -EINVAL;
+
+	return pmic_clrsetbits(dev->parent, info->mode_reg,
+			       DA9063_BUCK_MODE_MASK, mode->register_value);
+}
+
+static int buck_get_current_limit(struct udevice *dev)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int val;
+
+	val = pmic_reg_read(dev->parent, info->ilim_reg);
+	if (val < 0)
+		return val;
+
+	val &= info->ilim_mask;
+	val >>= (ffs(info->ilim_mask) - 1);
+
+	return info->min_uA + val * info->step_uA;
+}
+
+static int buck_set_current_limit(struct udevice *dev, int uA)
+{
+	const struct da9063_priv *priv = dev->priv;
+	const struct da9063_reg_info *info = priv->reg_info;
+	int val;
+
+	if (uA < info->min_uA || uA > info->max_uA)
+		return -EINVAL;
+
+	val = (uA - info->min_uA) / info->step_uA;
+	val <<= (ffs(info->ilim_mask) - 1);
+
+	return pmic_clrsetbits(dev->parent, info->ilim_reg,
+			       info->ilim_mask, val);
+}
+
+static int da9063_ldo_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct da9063_priv *priv = dev->priv;
+
+	/* LDOs are named numerically in DT so can directly index */
+	if (dev->driver_data < 1 ||
+	    dev->driver_data > ARRAY_SIZE(da9063_ldo_info))
+		return -EINVAL;
+	priv->reg_info = &da9063_ldo_info[dev->driver_data - 1];
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	uc_pdata->type = REGULATOR_TYPE_LDO;
+	uc_pdata->mode = da9063_ldo_modes;
+	uc_pdata->mode_count = ARRAY_SIZE(da9063_ldo_modes);
+
+	return 0;
+}
+
+static int da9063_buck_probe(struct udevice *dev)
+{
+	struct dm_regulator_uclass_platdata *uc_pdata;
+	struct da9063_priv *priv = dev->priv;
+	int i;
+
+	/* Bucks have names rather than numbers so need to match with DT */
+	for (i = 0; i < ARRAY_SIZE(da9063_buck_info); i++) {
+		const struct da9063_reg_info *info = &da9063_buck_info[i];
+
+		if (!strcmp(info->dt_node_name, dev->name)) {
+			priv->reg_info = info;
+			break;
+		}
+	}
+	if (!priv->reg_info)
+		return -ENODEV;
+
+	uc_pdata = dev_get_uclass_platdata(dev);
+	uc_pdata->type = REGULATOR_TYPE_BUCK;
+	uc_pdata->mode = da9063_buck_modes;
+	uc_pdata->mode_count = ARRAY_SIZE(da9063_buck_modes);
+
+	return 0;
+}
+
+static const struct dm_regulator_ops da9063_ldo_ops = {
+	.get_value  = da9063_get_voltage,
+	.set_value  = da9063_set_voltage,
+	.get_enable = da9063_get_enable,
+	.set_enable = da9063_set_enable,
+	.get_mode   = ldo_get_mode,
+	.set_mode   = ldo_set_mode,
+};
+
+U_BOOT_DRIVER(da9063_ldo) = {
+	.name = DA9063_LDO_DRIVER,
+	.id = UCLASS_REGULATOR,
+	.ops = &da9063_ldo_ops,
+	.probe = da9063_ldo_probe,
+	.priv_auto_alloc_size = sizeof(struct da9063_priv),
+};
+
+static const struct dm_regulator_ops da9063_buck_ops = {
+	.get_value  = da9063_get_voltage,
+	.set_value  = da9063_set_voltage,
+	.get_enable = da9063_get_enable,
+	.set_enable = da9063_set_enable,
+	.get_mode   = buck_get_mode,
+	.set_mode   = buck_set_mode,
+	.get_current = buck_get_current_limit,
+	.set_current = buck_set_current_limit,
+};
+
+U_BOOT_DRIVER(da9063_buck) = {
+	.name = DA9063_BUCK_DRIVER,
+	.id = UCLASS_REGULATOR,
+	.ops = &da9063_buck_ops,
+	.probe = da9063_buck_probe,
+	.priv_auto_alloc_size = sizeof(struct da9063_priv),
+};
-- 
1.9.1

      parent reply	other threads:[~2018-10-05  8:08 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-10-05  8:08 [U-Boot] [PATCH 0/3] power: Add support for the Dialog DA9063 PMIC Martin Fuzzey
2018-10-05  8:08 ` [U-Boot] [PATCH 1/3] pmic: allow dump command for non contiguous register maps Martin Fuzzey
2018-10-05  8:08 ` [U-Boot] [PATCH 2/3] power: pmic: add driver for Dialog DA9063 PMIC Martin Fuzzey
2018-10-05  8:08 ` Martin Fuzzey [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1538726940-3233-4-git-send-email-martin.fuzzey@flowbird.group \
    --to=martin.fuzzey@flowbird.group \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.