summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-06-24 03:30:20 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-06-24 03:30:20 +0300
commit7fe0bf908d4f8f7d134cab280cac64fe65997ac1 (patch)
treee14095fcaadd69df93b7a0a792001675582a1a1b /drivers
parent5a602e157a9d91d5ce98d07c404097edba8ec9f3 (diff)
parent733ada000f2c9618ccbac7b9ba146113f0a6675b (diff)
downloadlinux-7fe0bf908d4f8f7d134cab280cac64fe65997ac1.tar.xz
Merge tag 'regulator-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator
Pull regulator updates from Mark Brown: "Another fairly quiet release, some new drivers with generic handling for minor features but nothing that makes a substantial difference outside of the subsystem or for most boards: - support for a bunch of new parameters which are present on enough regulators to be worth having generic handling for in the framework. - fixes for some issues with printing constraints during boot which should probably have gone in for v4.1 but didn't. - new drivers for Dialog DA9062, Maxim MAX77621 and Qualcomm SPMI regulators" * tag 'regulator-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (45 commits) regulator: qcom_spmi: Fix calculating number of voltages regulator: qcom_spmi: Add missing braces for aligned code regulator: fix simple_return.cocci warnings regulator: Add QCOM SPMI regulator driver regulator: Add docbook for soft start regulator: Add input current limit support regulator: Add soft start support regulator: Add pull down support regulator: Add system_load constraint regulator: max8973: Fix up ramp_delay for MAX8973_RAMP_25mV_PER_US case regulator: core: replace sprintf with scnprintf regulator: core: fix constraints output buffer regulator: core: Don't corrupt display when printing uV offsets regulator: max8973: add support for MAX77621 regulator: max8973: configure ramp delay through callback regulator: pwm-regulator: Diffientiate between dev (device) and rdev (regulator_dev) regulator: pwm-regulator: Remove superfluous is_enabled check regulator: pwm-regulator: Remove unnecessary descriptor attribute from ddata regulator: core: Don't spew backtraces on duplicate sysfs regulator: da9063: Fix up irq leak ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/regulator/88pm8607.c2
-rw-r--r--drivers/regulator/Kconfig23
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/arizona-ldo1.c15
-rw-r--r--drivers/regulator/core.c76
-rw-r--r--drivers/regulator/da9062-regulator.c842
-rw-r--r--drivers/regulator/da9063-regulator.c21
-rw-r--r--drivers/regulator/fan53555.c1
-rw-r--r--drivers/regulator/helpers.c2
-rw-r--r--drivers/regulator/lp8755.c23
-rw-r--r--drivers/regulator/max14577.c128
-rw-r--r--drivers/regulator/max77686.c8
-rw-r--r--drivers/regulator/max77693.c17
-rw-r--r--drivers/regulator/max77843.c68
-rw-r--r--drivers/regulator/max8973-regulator.c269
-rw-r--r--drivers/regulator/of_regulator.c16
-rw-r--r--drivers/regulator/pwm-regulator.c41
-rw-r--r--drivers/regulator/qcom_spmi-regulator.c1435
-rw-r--r--drivers/regulator/s2mps11.c10
-rw-r--r--drivers/regulator/wm831x-dcdc.c12
-rw-r--r--drivers/regulator/wm831x-isink.c3
-rw-r--r--drivers/regulator/wm831x-ldo.c6
22 files changed, 2727 insertions, 293 deletions
diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c
index c3d15427adc7..b100a63ff3b3 100644
--- a/drivers/regulator/88pm8607.c
+++ b/drivers/regulator/88pm8607.c
@@ -404,7 +404,7 @@ static int pm8607_regulator_probe(struct platform_device *pdev)
return 0;
}
-static struct platform_device_id pm8607_regulator_driver_ids[] = {
+static const struct platform_device_id pm8607_regulator_driver_ids[] = {
{
.name = "88pm860x-regulator",
.driver_data = 0,
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a6f116aa5235..bef3bde6971b 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -178,6 +178,16 @@ config REGULATOR_DA9055
This driver can also be built as a module. If so, the module
will be called da9055-regulator.
+config REGULATOR_DA9062
+ tristate "Dialog Semiconductor DA9062 regulators"
+ depends on MFD_DA9062
+ help
+ Say y here to support the BUCKs and LDOs regulators found on
+ DA9062 PMICs.
+
+ This driver can also be built as a module. If so, the module
+ will be called da9062-regulator.
+
config REGULATOR_DA9063
tristate "Dialog Semiconductor DA9063 regulators"
depends on MFD_DA9063
@@ -233,7 +243,7 @@ config REGULATOR_FAN53555
config REGULATOR_GPIO
tristate "GPIO regulator support"
- depends on GPIOLIB
+ depends on GPIOLIB || COMPILE_TEST
help
This driver provides support for regulators that can be
controlled via gpios.
@@ -512,6 +522,17 @@ config REGULATOR_QCOM_RPM
Qualcomm RPM as a module. The module will be named
"qcom_rpm-regulator".
+config REGULATOR_QCOM_SPMI
+ tristate "Qualcomm SPMI regulator driver"
+ depends on SPMI || COMPILE_TEST
+ help
+ If you say yes to this option, support will be included for the
+ regulators found in Qualcomm SPMI PMICs.
+
+ Say M here if you want to include support for the regulators on the
+ Qualcomm SPMI PMICs as a module. The module will be named
+ "qcom_spmi-regulator".
+
config REGULATOR_RC5T583
tristate "RICOH RC5T583 Power regulators"
depends on MFD_RC5T583
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 2c4da15e1545..91bf76267404 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -25,6 +25,7 @@ obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o
obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index a1d07d347c20..90941632efa9 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -178,6 +178,16 @@ static const struct regulator_init_data arizona_ldo1_default = {
.num_consumer_supplies = 1,
};
+static const struct regulator_init_data arizona_ldo1_wm5110 = {
+ .constraints = {
+ .min_uV = 1175000,
+ .max_uV = 1200000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+ REGULATOR_CHANGE_VOLTAGE,
+ },
+ .num_consumer_supplies = 1,
+};
+
static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
struct regulator_config *config,
const struct regulator_desc *desc)
@@ -243,6 +253,11 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
desc = &arizona_ldo1_hc;
ldo1->init_data = arizona_ldo1_dvfs;
break;
+ case WM5110:
+ case WM8280:
+ desc = &arizona_ldo1;
+ ldo1->init_data = arizona_ldo1_wm5110;
+ break;
default:
desc = &arizona_ldo1;
ldo1->init_data = arizona_ldo1_default;
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 443eaab933fc..c9f72019bd68 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -678,6 +678,8 @@ static int drms_uA_update(struct regulator_dev *rdev)
list_for_each_entry(sibling, &rdev->consumer_list, list)
current_uA += sibling->uA_load;
+ current_uA += rdev->constraints->system_load;
+
if (rdev->desc->ops->set_load) {
/* set the optimum mode for our new total regulator load */
err = rdev->desc->ops->set_load(rdev, current_uA);
@@ -779,59 +781,64 @@ static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)
static void print_constraints(struct regulator_dev *rdev)
{
struct regulation_constraints *constraints = rdev->constraints;
- char buf[80] = "";
+ char buf[160] = "";
+ size_t len = sizeof(buf) - 1;
int count = 0;
int ret;
if (constraints->min_uV && constraints->max_uV) {
if (constraints->min_uV == constraints->max_uV)
- count += sprintf(buf + count, "%d mV ",
- constraints->min_uV / 1000);
+ count += scnprintf(buf + count, len - count, "%d mV ",
+ constraints->min_uV / 1000);
else
- count += sprintf(buf + count, "%d <--> %d mV ",
- constraints->min_uV / 1000,
- constraints->max_uV / 1000);
+ count += scnprintf(buf + count, len - count,
+ "%d <--> %d mV ",
+ constraints->min_uV / 1000,
+ constraints->max_uV / 1000);
}
if (!constraints->min_uV ||
constraints->min_uV != constraints->max_uV) {
ret = _regulator_get_voltage(rdev);
if (ret > 0)
- count += sprintf(buf + count, "at %d mV ", ret / 1000);
+ count += scnprintf(buf + count, len - count,
+ "at %d mV ", ret / 1000);
}
if (constraints->uV_offset)
- count += sprintf(buf, "%dmV offset ",
- constraints->uV_offset / 1000);
+ count += scnprintf(buf + count, len - count, "%dmV offset ",
+ constraints->uV_offset / 1000);
if (constraints->min_uA && constraints->max_uA) {
if (constraints->min_uA == constraints->max_uA)
- count += sprintf(buf + count, "%d mA ",
- constraints->min_uA / 1000);
+ count += scnprintf(buf + count, len - count, "%d mA ",
+ constraints->min_uA / 1000);
else
- count += sprintf(buf + count, "%d <--> %d mA ",
- constraints->min_uA / 1000,
- constraints->max_uA / 1000);
+ count += scnprintf(buf + count, len - count,
+ "%d <--> %d mA ",
+ constraints->min_uA / 1000,
+ constraints->max_uA / 1000);
}
if (!constraints->min_uA ||
constraints->min_uA != constraints->max_uA) {
ret = _regulator_get_current_limit(rdev);
if (ret > 0)
- count += sprintf(buf + count, "at %d mA ", ret / 1000);
+ count += scnprintf(buf + count, len - count,
+ "at %d mA ", ret / 1000);
}
if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
- count += sprintf(buf + count, "fast ");
+ count += scnprintf(buf + count, len - count, "fast ");
if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
- count += sprintf(buf + count, "normal ");
+ count += scnprintf(buf + count, len - count, "normal ");
if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
- count += sprintf(buf + count, "idle ");
+ count += scnprintf(buf + count, len - count, "idle ");
if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
- count += sprintf(buf + count, "standby");
+ count += scnprintf(buf + count, len - count, "standby");
if (!count)
- sprintf(buf, "no parameters");
+ scnprintf(buf, len, "no parameters");
rdev_dbg(rdev, "%s\n", buf);
@@ -1006,6 +1013,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
if (ret != 0)
goto out;
+ if (rdev->constraints->ilim_uA && ops->set_input_current_limit) {
+ ret = ops->set_input_current_limit(rdev,
+ rdev->constraints->ilim_uA);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set input limit\n");
+ goto out;
+ }
+ }
+
/* do we need to setup our suspend state */
if (rdev->constraints->initial_state) {
ret = suspend_prepare(rdev, rdev->constraints->initial_state);
@@ -1049,6 +1065,22 @@ static int set_machine_constraints(struct regulator_dev *rdev,
}
}
+ if (rdev->constraints->pull_down && ops->set_pull_down) {
+ ret = ops->set_pull_down(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set pull down\n");
+ goto out;
+ }
+ }
+
+ if (rdev->constraints->soft_start && ops->set_soft_start) {
+ ret = ops->set_soft_start(rdev);
+ if (ret < 0) {
+ rdev_err(rdev, "failed to set soft start\n");
+ goto out;
+ }
+ }
+
print_constraints(rdev);
return 0;
out:
@@ -1192,10 +1224,10 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
if (regulator->supply_name == NULL)
goto overflow_err;
- err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj,
+ err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj,
buf);
if (err) {
- rdev_warn(rdev, "could not add device link %s err %d\n",
+ rdev_dbg(rdev, "could not add device link %s err %d\n",
dev->kobj.name, err);
/* non-fatal */
}
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644
index 000000000000..dd76da09b3c7
--- /dev/null
+++ b/drivers/regulator/da9062-regulator.c
@@ -0,0 +1,842 @@
+/*
+ * da9062-regulator.c - REGULATOR device driver for DA9062
+ * Copyright (C) 2015 Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+ DA9062_ID_BUCK1,
+ DA9062_ID_BUCK2,
+ DA9062_ID_BUCK3,
+ DA9062_ID_BUCK4,
+ DA9062_ID_LDO1,
+ DA9062_ID_LDO2,
+ DA9062_ID_LDO3,
+ DA9062_ID_LDO4,
+ DA9062_MAX_REGULATORS,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+ struct regulator_desc desc;
+ /* Current limiting */
+ unsigned int n_current_limits;
+ const int *current_limits;
+ /* Main register fields */
+ struct reg_field mode;
+ struct reg_field suspend;
+ struct reg_field sleep;
+ struct reg_field suspend_sleep;
+ unsigned int suspend_vsel_reg;
+ struct reg_field ilimit;
+ /* Event detection bit */
+ struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+ struct regulator_desc desc;
+ struct regulator_dev *rdev;
+ struct da9062 *hw;
+ const struct da9062_regulator_info *info;
+
+ struct regmap_field *mode;
+ struct regmap_field *suspend;
+ struct regmap_field *sleep;
+ struct regmap_field *suspend_sleep;
+ struct regmap_field *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+ int irq_ldo_lim;
+ unsigned n_regulators;
+ /* Array size to be defined during init. Keep at end. */
+ struct da9062_regulator regulator[0];
+};
+
+/* BUCK modes */
+enum {
+ BUCK_MODE_MANUAL, /* 0 */
+ BUCK_MODE_SLEEP, /* 1 */
+ BUCK_MODE_SYNC, /* 2 */
+ BUCK_MODE_AUTO /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) BUCK1 and BUCK3.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_a_limits[] = {
+ 500000, 600000, 700000, 800000, 900000, 1000000, 1100000, 1200000,
+ 1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BUCK2.
+ Entry indexes corresponds to register values. */
+static const int da9062_buck_b_limits[] = {
+ 1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+ 2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+ int min_ua, int max_ua)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int n, tval;
+
+ for (n = 0; n < rinfo->n_current_limits; n++) {
+ tval = rinfo->current_limits[n];
+ if (tval >= min_ua && tval <= max_ua)
+ return regmap_field_write(regl->ilimit, n);
+ }
+
+ return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ unsigned int sel;
+ int ret;
+
+ ret = regmap_field_read(regl->ilimit, &sel);
+ if (ret < 0)
+ return ret;
+
+ if (sel >= rinfo->n_current_limits)
+ sel = rinfo->n_current_limits - 1;
+
+ return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ unsigned int val, mode = 0;
+ int ret;
+
+ ret = regmap_field_read(regl->mode, &val);
+ if (ret < 0)
+ return ret;
+
+ switch (val) {
+ default:
+ case BUCK_MODE_MANUAL:
+ mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+ /* Sleep flag bit decides the mode */
+ break;
+ case BUCK_MODE_SLEEP:
+ return REGULATOR_MODE_STANDBY;
+ case BUCK_MODE_SYNC:
+ return REGULATOR_MODE_FAST;
+ case BUCK_MODE_AUTO:
+ return REGULATOR_MODE_NORMAL;
+ }
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ mode &= REGULATOR_MODE_STANDBY;
+ else
+ mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+ return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ struct regmap_field *field;
+ int ret, val;
+
+ /* Detect current regulator state */
+ ret = regmap_field_read(regl->suspend, &val);
+ if (ret < 0)
+ return 0;
+
+ /* Read regulator mode from proper register, depending on state */
+ if (val)
+ field = regl->suspend_sleep;
+ else
+ field = regl->sleep;
+
+ ret = regmap_field_read(field, &val);
+ if (ret < 0)
+ return 0;
+
+ if (val)
+ return REGULATOR_MODE_STANDBY;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_buck_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+ int ret = regulator_is_enabled_regmap(rdev);
+
+ if (ret == 0) {
+ ret = REGULATOR_STATUS_OFF;
+ } else if (ret > 0) {
+ ret = da9062_ldo_get_mode(rdev);
+ if (ret > 0)
+ ret = regulator_mode_to_status(ret);
+ else if (ret == 0)
+ ret = -EIO;
+ }
+
+ return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ const struct da9062_regulator_info *rinfo = regl->info;
+ int ret, sel;
+
+ sel = regulator_map_voltage_linear(rdev, uv, uv);
+ if (sel < 0)
+ return sel;
+
+ sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+ ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+ rdev->desc->vsel_mask, sel);
+
+ return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+ return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ int val;
+
+ switch (mode) {
+ case REGULATOR_MODE_FAST:
+ val = BUCK_MODE_SYNC;
+ break;
+ case REGULATOR_MODE_NORMAL:
+ val = BUCK_MODE_AUTO;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = BUCK_MODE_SLEEP;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+ unsigned mode)
+{
+ struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+ unsigned val;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ val = 0;
+ break;
+ case REGULATOR_MODE_STANDBY:
+ val = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9062_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_current_limit = da9062_set_current_limit,
+ .get_current_limit = da9062_get_current_limit,
+ .set_mode = da9062_buck_set_mode,
+ .get_mode = da9062_buck_get_mode,
+ .get_status = da9062_buck_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9062_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .set_mode = da9062_ldo_set_mode,
+ .get_mode = da9062_ldo_get_mode,
+ .get_status = da9062_ldo_get_status,
+ .set_suspend_voltage = da9062_set_suspend_voltage,
+ .set_suspend_enable = da9062_suspend_enable,
+ .set_suspend_disable = da9062_suspend_disable,
+ .set_suspend_mode = da9062_ldo_set_suspend_mode,
+};
+
+/* Regulator information */
+static const struct da9062_regulator_info local_regulator_info[] = {
+ {
+ .desc.id = DA9062_ID_BUCK1,
+ .desc.name = "DA9062 BUCK1",
+ .desc.of_match = of_match_ptr("buck1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK1_CONT,
+ .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK1_A,
+ .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+ __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+ .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK2,
+ .desc.name = "DA9062 BUCK2",
+ .desc.of_match = of_match_ptr("buck2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (300) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1570) - (300))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK2_CONT,
+ .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK2_A,
+ .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+ __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+ .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+ __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK3,
+ .desc.name = "DA9062 BUCK3",
+ .desc.of_match = of_match_ptr("buck3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (800) * 1000,
+ .desc.uV_step = (20) * 1000,
+ .desc.n_voltages = ((3340) - (800))/(20) + 1,
+ .current_limits = da9062_buck_b_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+ .desc.enable_reg = DA9062AA_BUCK3_CONT,
+ .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK3_A,
+ .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+ __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+ .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+ __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_BUCK4,
+ .desc.name = "DA9062 BUCK4",
+ .desc.of_match = of_match_ptr("buck4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_buck_ops,
+ .desc.min_uV = (530) * 1000,
+ .desc.uV_step = (10) * 1000,
+ .desc.n_voltages = ((1800) - (530))/(10) + 1,
+ .current_limits = da9062_buck_a_limits,
+ .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+ .desc.enable_reg = DA9062AA_BUCK4_CONT,
+ .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VBUCK4_A,
+ .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+ .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+ __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+ .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+ __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO1,
+ .desc.name = "DA9062 LDO1",
+ .desc.of_match = of_match_ptr("ldo1"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO1_CONT,
+ .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO1_A,
+ .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+ __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO1_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO2,
+ .desc.name = "DA9062 LDO2",
+ .desc.of_match = of_match_ptr("ldo2"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (600))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO2_CONT,
+ .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO2_A,
+ .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+ __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO2_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO3,
+ .desc.name = "DA9062 LDO3",
+ .desc.of_match = of_match_ptr("ldo3"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO3_CONT,
+ .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO3_A,
+ .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+ __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO3_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+ },
+ {
+ .desc.id = DA9062_ID_LDO4,
+ .desc.name = "DA9062 LDO4",
+ .desc.of_match = of_match_ptr("ldo4"),
+ .desc.regulators_node = of_match_ptr("regulators"),
+ .desc.ops = &da9062_ldo_ops,
+ .desc.min_uV = (900) * 1000,
+ .desc.uV_step = (50) * 1000,
+ .desc.n_voltages = ((3600) - (900))/(50) + 1,
+ .desc.enable_reg = DA9062AA_LDO4_CONT,
+ .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+ .desc.vsel_reg = DA9062AA_VLDO4_A,
+ .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+ .desc.linear_min_sel = 0,
+ .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+ .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+ __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+ .suspend_vsel_reg = DA9062AA_VLDO4_B,
+ .suspend = REG_FIELD(DA9062AA_DVC_1,
+ __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+ .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+ __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+ sizeof(unsigned int) * 8 -
+ __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+ },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+ struct da9062_regulators *regulators = data;
+ struct da9062 *hw = regulators->regulator[0].hw;
+ struct da9062_regulator *regl;
+ int handled = IRQ_NONE;
+ int bits, i, ret;
+
+ ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+ if (ret < 0) {
+ dev_err(hw->dev,
+ "Failed to read LDO overcurrent indicator\n");
+ goto ldo_lim_error;
+ }
+
+ for (i = regulators->n_regulators - 1; i >= 0; i--) {
+ regl = &regulators->regulator[i];
+ if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+ continue;
+
+ if (BIT(regl->info->oc_event.lsb) & bits) {
+ regulator_notifier_call_chain(regl->rdev,
+ REGULATOR_EVENT_OVER_CURRENT, NULL);
+ handled = IRQ_HANDLED;
+ }
+ }
+
+ldo_lim_error:
+ return handled;
+}
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+ struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+ struct da9062_regulators *regulators;
+ struct da9062_regulator *regl;
+ struct regulator_config config = { };
+ int irq, n, ret;
+ size_t size;
+
+ /* Allocate memory required by usable regulators */
+ size = sizeof(struct da9062_regulators) +
+ DA9062_MAX_REGULATORS * sizeof(struct da9062_regulator);
+ regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+ if (!regulators)
+ return -ENOMEM;
+
+ regulators->n_regulators = DA9062_MAX_REGULATORS;
+ platform_set_drvdata(pdev, regulators);
+
+ n = 0;
+ while (n < regulators->n_regulators) {
+ /* Initialise regulator structure */
+ regl = &regulators->regulator[n];
+ regl->hw = chip;
+ regl->info = &local_regulator_info[n];
+ regl->desc = regl->info->desc;
+ regl->desc.type = REGULATOR_VOLTAGE;
+ regl->desc.owner = THIS_MODULE;
+
+ if (regl->info->mode.reg)
+ regl->mode = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->mode);
+ if (regl->info->suspend.reg)
+ regl->suspend = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend);
+ if (regl->info->sleep.reg)
+ regl->sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->sleep);
+ if (regl->info->suspend_sleep.reg)
+ regl->suspend_sleep = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->suspend_sleep);
+ if (regl->info->ilimit.reg)
+ regl->ilimit = devm_regmap_field_alloc(
+ &pdev->dev,
+ chip->regmap,
+ regl->info->ilimit);
+
+ /* Register regulator */
+ memset(&config, 0, sizeof(config));
+ config.dev = chip->dev;
+ config.driver_data = regl;
+ config.regmap = chip->regmap;
+
+ regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+ &config);
+ if (IS_ERR(regl->rdev)) {
+ dev_err(&pdev->dev,
+ "Failed to register %s regulator\n",
+ regl->desc.name);
+ return PTR_ERR(regl->rdev);
+ }
+
+ n++;
+ }
+
+ /* LDOs overcurrent event support */
+ irq = platform_get_irq_byname(pdev, "LDO_LIM");
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Failed to get IRQ.\n");
+ return irq;
+ }
+ regulators->irq_ldo_lim = irq;
+
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
+ NULL, da9062_ldo_lim_event,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "LDO_LIM", regulators);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request LDO_LIM IRQ.\n");
+ regulators->irq_ldo_lim = -ENXIO;
+ }
+
+ return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+ .driver = {
+ .name = "da9062-regulators",
+ .owner = THIS_MODULE,
+ },
+ .probe = da9062_regulator_probe,
+};
+
+static int __init da9062_regulator_init(void)
+{
+ return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+ platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-regulators");
diff --git a/drivers/regulator/da9063-regulator.c b/drivers/regulator/da9063-regulator.c
index 31c2c593ae0b..aed1ad3dc964 100644
--- a/drivers/regulator/da9063-regulator.c
+++ b/drivers/regulator/da9063-regulator.c
@@ -117,9 +117,6 @@ struct da9063_regulator {
/* Encapsulates all information for the regulators driver */
struct da9063_regulators {
- int irq_ldo_lim;
- int irq_uvov;
-
unsigned n_regulators;
/* Array size to be defined during init. Keep at end. */
struct da9063_regulator regulator[0];
@@ -867,35 +864,23 @@ static int da9063_regulator_probe(struct platform_device *pdev)
return irq;
}
- ret = request_threaded_irq(irq,
+ ret = devm_request_threaded_irq(&pdev->dev, irq,
NULL, da9063_ldo_lim_event,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"LDO_LIM", regulators);
if (ret) {
- dev_err(&pdev->dev,
- "Failed to request LDO_LIM IRQ.\n");
- regulators->irq_ldo_lim = -ENXIO;
+ dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
+ return ret;
}
return 0;
}
-static int da9063_regulator_remove(struct platform_device *pdev)
-{
- struct da9063_regulators *regulators = platform_get_drvdata(pdev);
-
- free_irq(regulators->irq_ldo_lim, regulators);
- free_irq(regulators->irq_uvov, regulators);
-
- return 0;
-}
-
static struct platform_driver da9063_regulator_driver = {
.driver = {
.name = DA9063_DRVNAME_REGULATORS,
},
.probe = da9063_regulator_probe,
- .remove = da9063_regulator_remove,
};
static int __init da9063_regulator_init(void)
diff --git a/drivers/regulator/fan53555.c b/drivers/regulator/fan53555.c
index 3c25db89a021..42865681c00b 100644
--- a/drivers/regulator/fan53555.c
+++ b/drivers/regulator/fan53555.c
@@ -182,6 +182,7 @@ static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
static struct regulator_ops fan53555_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
.map_voltage = regulator_map_voltage_linear,
.list_voltage = regulator_list_voltage_linear,
.set_suspend_voltage = fan53555_set_suspend_voltage,
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index cbc39096c78d..3bbb32680a94 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -275,7 +275,7 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev,
EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
/**
- * regulator_map_voltage_linear - map_voltage() for multiple linear ranges
+ * regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges
*
* @rdev: Regulator to operate on
* @min_uV: Lower bound for voltage
diff --git a/drivers/regulator/lp8755.c b/drivers/regulator/lp8755.c
index 4a415d4ee463..d6773da925ba 100644
--- a/drivers/regulator/lp8755.c
+++ b/drivers/regulator/lp8755.c
@@ -419,20 +419,16 @@ static int lp8755_int_config(struct lp8755_chip *pchip)
}
ret = lp8755_read(pchip, 0x0F, &regval);
- if (ret < 0)
- goto err_i2c;
- pchip->irqmask = regval;
- ret = request_threaded_irq(pchip->irq, NULL, lp8755_irq_handler,
- IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
- "lp8755-irq", pchip);
- if (ret)
+ if (ret < 0) {
+ dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
return ret;
+ }
- return ret;
-
-err_i2c:
- dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
- return ret;
+ pchip->irqmask = regval;
+ return devm_request_threaded_irq(pchip->dev, pchip->irq, NULL,
+ lp8755_irq_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "lp8755-irq", pchip);
}
static const struct regmap_config lp8755_regmap = {
@@ -514,9 +510,6 @@ static int lp8755_remove(struct i2c_client *client)
for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++)
lp8755_write(pchip, icnt, 0x00);
- if (pchip->irq != 0)
- free_irq(pchip->irq, pchip);
-
return 0;
}
diff --git a/drivers/regulator/max14577.c b/drivers/regulator/max14577.c
index b3678d289619..b2daa6641417 100644
--- a/drivers/regulator/max14577.c
+++ b/drivers/regulator/max14577.c
@@ -100,31 +100,34 @@ static struct regulator_ops max14577_charger_ops = {
.set_current_limit = max14577_reg_set_current_limit,
};
+#define MAX14577_SAFEOUT_REG { \
+ .name = "SAFEOUT", \
+ .of_match = of_match_ptr("SAFEOUT"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_SAFEOUT, \
+ .ops = &max14577_safeout_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = 1, \
+ .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, \
+ .enable_reg = MAX14577_REG_CONTROL2, \
+ .enable_mask = CTRL2_SFOUTORD_MASK, \
+}
+#define MAX14577_CHARGER_REG { \
+ .name = "CHARGER", \
+ .of_match = of_match_ptr("CHARGER"), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX14577_CHARGER, \
+ .ops = &max14577_charger_ops, \
+ .type = REGULATOR_CURRENT, \
+ .owner = THIS_MODULE, \
+ .enable_reg = MAX14577_CHG_REG_CHG_CTRL2, \
+ .enable_mask = CHGCTRL2_MBCHOSTEN_MASK, \
+}
+
static const struct regulator_desc max14577_supported_regulators[] = {
- [MAX14577_SAFEOUT] = {
- .name = "SAFEOUT",
- .of_match = of_match_ptr("SAFEOUT"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_SAFEOUT,
- .ops = &max14577_safeout_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = 1,
- .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
- .enable_reg = MAX14577_REG_CONTROL2,
- .enable_mask = CTRL2_SFOUTORD_MASK,
- },
- [MAX14577_CHARGER] = {
- .name = "CHARGER",
- .of_match = of_match_ptr("CHARGER"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_CHARGER,
- .ops = &max14577_charger_ops,
- .type = REGULATOR_CURRENT,
- .owner = THIS_MODULE,
- .enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
- .enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
- },
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
};
static struct regulator_ops max77836_ldo_ops = {
@@ -138,63 +141,28 @@ static struct regulator_ops max77836_ldo_ops = {
/* TODO: add .set_suspend_mode */
};
+#define MAX77836_LDO_REG(num) { \
+ .name = "LDO" # num, \
+ .of_match = of_match_ptr("LDO" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .id = MAX77836_LDO ## num, \
+ .ops = &max77836_ldo_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, \
+ .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, \
+ .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \
+ .enable_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK, \
+ .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+ .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK, \
+}
+
static const struct regulator_desc max77836_supported_regulators[] = {
- [MAX14577_SAFEOUT] = {
- .name = "SAFEOUT",
- .of_match = of_match_ptr("SAFEOUT"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_SAFEOUT,
- .ops = &max14577_safeout_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = 1,
- .min_uV = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
- .enable_reg = MAX14577_REG_CONTROL2,
- .enable_mask = CTRL2_SFOUTORD_MASK,
- },
- [MAX14577_CHARGER] = {
- .name = "CHARGER",
- .of_match = of_match_ptr("CHARGER"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX14577_CHARGER,
- .ops = &max14577_charger_ops,
- .type = REGULATOR_CURRENT,
- .owner = THIS_MODULE,
- .enable_reg = MAX14577_CHG_REG_CHG_CTRL2,
- .enable_mask = CHGCTRL2_MBCHOSTEN_MASK,
- },
- [MAX77836_LDO1] = {
- .name = "LDO1",
- .of_match = of_match_ptr("LDO1"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX77836_LDO1,
- .ops = &max77836_ldo_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
- .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
- .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
- .enable_reg = MAX77836_LDO_REG_CNFG1_LDO1,
- .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
- .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO1,
- .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
- },
- [MAX77836_LDO2] = {
- .name = "LDO2",
- .of_match = of_match_ptr("LDO2"),
- .regulators_node = of_match_ptr("regulators"),
- .id = MAX77836_LDO2,
- .ops = &max77836_ldo_ops,
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
- .min_uV = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
- .uV_step = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
- .enable_reg = MAX77836_LDO_REG_CNFG1_LDO2,
- .enable_mask = MAX77836_CNFG1_LDO_PWRMD_MASK,
- .vsel_reg = MAX77836_LDO_REG_CNFG1_LDO2,
- .vsel_mask = MAX77836_CNFG1_LDO_TV_MASK,
- },
+ [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+ [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+ [MAX77836_LDO1] = MAX77836_LDO_REG(1),
+ [MAX77836_LDO2] = MAX77836_LDO_REG(2),
};
#ifdef CONFIG_OF
diff --git a/drivers/regulator/max77686.c b/drivers/regulator/max77686.c
index 15fb1416bfbd..17ccf365a9c0 100644
--- a/drivers/regulator/max77686.c
+++ b/drivers/regulator/max77686.c
@@ -2,7 +2,7 @@
* max77686.c - Regulator driver for the Maxim 77686
*
* Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@smasung.com>
+ * Chiwoong Byun <woong.byun@samsung.com>
* Jonghwa Lee <jonghwa3.lee@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -88,7 +88,7 @@ enum max77686_ramp_rate {
};
struct max77686_data {
- u64 gpio_enabled:MAX77686_REGULATORS;
+ DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
/* Array indexed by regulator id */
unsigned int opmode[MAX77686_REGULATORS];
@@ -121,7 +121,7 @@ static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
case MAX77686_BUCK8:
case MAX77686_BUCK9:
case MAX77686_LDO20 ... MAX77686_LDO22:
- if (max77686->gpio_enabled & (1 << id))
+ if (test_bit(id, max77686->gpio_enabled))
return MAX77686_GPIO_CONTROL;
}
@@ -277,7 +277,7 @@ static int max77686_of_parse_cb(struct device_node *np,
}
if (gpio_is_valid(config->ena_gpio)) {
- max77686->gpio_enabled |= (1 << desc->id);
+ set_bit(desc->id, max77686->gpio_enabled);
return regmap_update_bits(config->regmap, desc->enable_reg,
desc->enable_mask,
diff --git a/drivers/regulator/max77693.c b/drivers/regulator/max77693.c
index 9665a488e2f1..38722c8311a5 100644
--- a/drivers/regulator/max77693.c
+++ b/drivers/regulator/max77693.c
@@ -35,20 +35,6 @@
#define CHGIN_ILIM_STEP_20mA 20000
-/* CHARGER regulator ops */
-/* CHARGER regulator uses two bits for enabling */
-static int max77693_chg_is_enabled(struct regulator_dev *rdev)
-{
- int ret;
- unsigned int val;
-
- ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
- if (ret)
- return ret;
-
- return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
/*
* CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
* 0x00, 0x01, 0x2, 0x03 = 60 mA
@@ -118,7 +104,7 @@ static struct regulator_ops max77693_safeout_ops = {
};
static struct regulator_ops max77693_charger_ops = {
- .is_enabled = max77693_chg_is_enabled,
+ .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_current_limit = max77693_chg_get_current_limit,
@@ -155,6 +141,7 @@ static const struct regulator_desc regulators[] = {
.enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
.enable_mask = CHG_CNFG_00_CHG_MASK |
CHG_CNFG_00_BUCK_MASK,
+ .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK,
},
};
diff --git a/drivers/regulator/max77843.c b/drivers/regulator/max77843.c
index c132ef527cdd..f4fd0d3cfa6e 100644
--- a/drivers/regulator/max77843.c
+++ b/drivers/regulator/max77843.c
@@ -33,21 +33,6 @@ static const unsigned int max77843_safeout_voltage_table[] = {
3300000,
};
-static int max77843_reg_is_enabled(struct regulator_dev *rdev)
-{
- struct regmap *regmap = rdev->regmap;
- int ret;
- unsigned int reg;
-
- ret = regmap_read(regmap, rdev->desc->enable_reg, &reg);
- if (ret) {
- dev_err(&rdev->dev, "Fialed to read charger register\n");
- return ret;
- }
-
- return (reg & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
static int max77843_reg_get_current_limit(struct regulator_dev *rdev)
{
struct regmap *regmap = rdev->regmap;
@@ -96,7 +81,7 @@ static int max77843_reg_set_current_limit(struct regulator_dev *rdev,
}
static struct regulator_ops max77843_charger_ops = {
- .is_enabled = max77843_reg_is_enabled,
+ .is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
.get_current_limit = max77843_reg_get_current_limit,
@@ -112,37 +97,25 @@ static struct regulator_ops max77843_regulator_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
+#define MAX77843_SAFEOUT(num) { \
+ .name = "SAFEOUT" # num, \
+ .id = MAX77843_SAFEOUT ## num, \
+ .ops = &max77843_regulator_ops, \
+ .of_match = of_match_ptr("SAFEOUT" # num), \
+ .regulators_node = of_match_ptr("regulators"), \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table), \
+ .volt_table = max77843_safeout_voltage_table, \
+ .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \
+ .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL, \
+ .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \
+}
+
static const struct regulator_desc max77843_supported_regulators[] = {
- [MAX77843_SAFEOUT1] = {
- .name = "SAFEOUT1",
- .id = MAX77843_SAFEOUT1,
- .ops = &max77843_regulator_ops,
- .of_match = of_match_ptr("SAFEOUT1"),
- .regulators_node = of_match_ptr("regulators"),
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table),
- .volt_table = max77843_safeout_voltage_table,
- .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1,
- .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK,
- },
- [MAX77843_SAFEOUT2] = {
- .name = "SAFEOUT2",
- .id = MAX77843_SAFEOUT2,
- .ops = &max77843_regulator_ops,
- .of_match = of_match_ptr("SAFEOUT2"),
- .regulators_node = of_match_ptr("regulators"),
- .type = REGULATOR_VOLTAGE,
- .owner = THIS_MODULE,
- .n_voltages = ARRAY_SIZE(max77843_safeout_voltage_table),
- .volt_table = max77843_safeout_voltage_table,
- .enable_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .enable_mask = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2,
- .vsel_reg = MAX77843_SYS_REG_SAFEOUTCTRL,
- .vsel_mask = MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK,
- },
+ [MAX77843_SAFEOUT1] = MAX77843_SAFEOUT(1),
+ [MAX77843_SAFEOUT2] = MAX77843_SAFEOUT(2),
[MAX77843_CHARGER] = {
.name = "CHARGER",
.id = MAX77843_CHARGER,
@@ -152,7 +125,8 @@ static const struct regulator_desc max77843_supported_regulators[] = {
.type = REGULATOR_CURRENT,
.owner = THIS_MODULE,
.enable_reg = MAX77843_CHG_REG_CHG_CNFG_00,
- .enable_mask = MAX77843_CHG_MASK,
+ .enable_mask = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
+ .enable_val = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
},
};
diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c
index c3d55c2db593..6f2bdad8b4d8 100644
--- a/drivers/regulator/max8973-regulator.c
+++ b/drivers/regulator/max8973-regulator.c
@@ -27,12 +27,14 @@
#include <linux/init.h>
#include <linux/err.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <linux/regulator/max8973-regulator.h>
#include <linux/regulator/of_regulator.h>
#include <linux/gpio.h>
+#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/regmap.h>
@@ -66,6 +68,7 @@
#define MAX8973_RAMP_25mV_PER_US 0x1
#define MAX8973_RAMP_50mV_PER_US 0x2
#define MAX8973_RAMP_200mV_PER_US 0x3
+#define MAX8973_RAMP_MASK 0x3
/* MAX8973_CONTROL2 */
#define MAX8973_WDTMR_ENABLE BIT(6)
@@ -89,19 +92,25 @@
#define MAX8973_VOLATGE_STEP 6250
#define MAX8973_BUCK_N_VOLTAGE 0x80
+enum device_id {
+ MAX8973,
+ MAX77621
+};
+
/* Maxim 8973 chip information */
struct max8973_chip {
struct device *dev;
struct regulator_desc desc;
struct regmap *regmap;
bool enable_external_control;
+ int enable_gpio;
int dvs_gpio;
int lru_index[MAX8973_MAX_VOUT_REG];
int curr_vout_val[MAX8973_MAX_VOUT_REG];
int curr_vout_reg;
int curr_gpio_val;
- bool valid_dvs_gpio;
struct regulator_ops ops;
+ enum device_id id;
};
/*
@@ -174,7 +183,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
* If gpios are available to select the VOUT register then least
* recently used register for new configuration.
*/
- if (max->valid_dvs_gpio)
+ if (gpio_is_valid(max->dvs_gpio))
found = find_voltage_set_register(max, vsel,
&vout_reg, &gpio_val);
@@ -191,7 +200,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
}
/* Select proper VOUT register vio gpios */
- if (max->valid_dvs_gpio) {
+ if (gpio_is_valid(max->dvs_gpio)) {
gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1);
max->curr_gpio_val = gpio_val;
}
@@ -242,12 +251,45 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
}
+static int max8973_set_ramp_delay(struct regulator_dev *rdev,
+ int ramp_delay)
+{
+ struct max8973_chip *max = rdev_get_drvdata(rdev);
+ unsigned int control;
+ int ret;
+ int ret_val;
+
+ /* Set ramp delay */
+ if (ramp_delay < 25000) {
+ control = MAX8973_RAMP_12mV_PER_US;
+ ret_val = 12000;
+ } else if (ramp_delay < 50000) {
+ control = MAX8973_RAMP_25mV_PER_US;
+ ret_val = 25000;
+ } else if (ramp_delay < 200000) {
+ control = MAX8973_RAMP_50mV_PER_US;
+ ret_val = 50000;
+ } else {
+ control = MAX8973_RAMP_200mV_PER_US;
+ ret_val = 200000;
+ }
+
+ ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+ MAX8973_RAMP_MASK, control);
+ if (ret < 0)
+ dev_err(max->dev, "register %d update failed, %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+}
+
static const struct regulator_ops max8973_dcdc_ops = {
.get_voltage_sel = max8973_dcdc_get_voltage_sel,
.set_voltage_sel = max8973_dcdc_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.set_mode = max8973_dcdc_set_mode,
.get_mode = max8973_dcdc_get_mode,
+ .set_voltage_time_sel = regulator_set_voltage_time_sel,
+ .set_ramp_delay = max8973_set_ramp_delay,
};
static int max8973_init_dcdc(struct max8973_chip *max,
@@ -256,6 +298,29 @@ static int max8973_init_dcdc(struct max8973_chip *max,
int ret;
uint8_t control1 = 0;
uint8_t control2 = 0;
+ unsigned int data;
+
+ ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+ if (ret < 0) {
+ dev_err(max->dev, "register %d read failed, err = %d",
+ MAX8973_CONTROL1, ret);
+ return ret;
+ }
+ control1 = data & MAX8973_RAMP_MASK;
+ switch (control1) {
+ case MAX8973_RAMP_12mV_PER_US:
+ max->desc.ramp_delay = 12000;
+ break;
+ case MAX8973_RAMP_25mV_PER_US:
+ max->desc.ramp_delay = 25000;
+ break;
+ case MAX8973_RAMP_50mV_PER_US:
+ max->desc.ramp_delay = 50000;
+ break;
+ case MAX8973_RAMP_200mV_PER_US:
+ max->desc.ramp_delay = 200000;
+ break;
+ }
if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE)
control1 |= MAX8973_SNS_ENABLE;
@@ -266,28 +331,16 @@ static int max8973_init_dcdc(struct max8973_chip *max,
if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE)
control1 |= MAX8973_AD_ENABLE;
- if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE)
+ if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) {
control1 |= MAX8973_BIAS_ENABLE;
+ max->desc.enable_time = 20;
+ } else {
+ max->desc.enable_time = 240;
+ }
if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
control1 |= MAX8973_FREQSHIFT_9PER;
- /* Set ramp delay */
- if (pdata->reg_init_data &&
- pdata->reg_init_data->constraints.ramp_delay) {
- if (pdata->reg_init_data->constraints.ramp_delay < 25000)
- control1 |= MAX8973_RAMP_12mV_PER_US;
- else if (pdata->reg_init_data->constraints.ramp_delay < 50000)
- control1 |= MAX8973_RAMP_25mV_PER_US;
- else if (pdata->reg_init_data->constraints.ramp_delay < 200000)
- control1 |= MAX8973_RAMP_50mV_PER_US;
- else
- control1 |= MAX8973_RAMP_200mV_PER_US;
- } else {
- control1 |= MAX8973_RAMP_12mV_PER_US;
- max->desc.ramp_delay = 12500;
- }
-
if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
control2 |= MAX8973_DISCH_ENBABLE;
@@ -344,7 +397,7 @@ static int max8973_init_dcdc(struct max8973_chip *max,
}
/* If external control is enabled then disable EN bit */
- if (max->enable_external_control) {
+ if (max->enable_external_control && (max->id == MAX8973)) {
ret = regmap_update_bits(max->regmap, MAX8973_VOUT,
MAX8973_VOUT_ENABLE, 0);
if (ret < 0)
@@ -361,22 +414,82 @@ static const struct regmap_config max8973_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
+static struct max8973_regulator_platform_data *max8973_parse_dt(
+ struct device *dev)
+{
+ struct max8973_regulator_platform_data *pdata;
+ struct device_node *np = dev->of_node;
+ int ret;
+ u32 pval;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->enable_ext_control = of_property_read_bool(np,
+ "maxim,externally-enable");
+ pdata->enable_gpio = of_get_named_gpio(np, "maxim,enable-gpio", 0);
+ pdata->dvs_gpio = of_get_named_gpio(np, "maxim,dvs-gpio", 0);
+
+ ret = of_property_read_u32(np, "maxim,dvs-default-state", &pval);
+ if (!ret)
+ pdata->dvs_def_state = pval;
+
+ if (of_property_read_bool(np, "maxim,enable-remote-sense"))
+ pdata->control_flags |= MAX8973_CONTROL_REMOTE_SENSE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-falling-slew-rate"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-active-discharge"))
+ pdata->control_flags |=
+ MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-frequency-shift"))
+ pdata->control_flags |= MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE;
+
+ if (of_property_read_bool(np, "maxim,enable-bias-control"))
+ pdata->control_flags |= MAX8973_BIAS_ENABLE;
+
+ return pdata;
+}
+
+static const struct of_device_id of_max8973_match_tbl[] = {
+ { .compatible = "maxim,max8973", .data = (void *)MAX8973, },
+ { .compatible = "maxim,max77621", .data = (void *)MAX77621, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, of_max8973_match_tbl);
+
static int max8973_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max8973_regulator_platform_data *pdata;
+ struct regulator_init_data *ridata;
struct regulator_config config = { };
struct regulator_dev *rdev;
struct max8973_chip *max;
+ bool pdata_from_dt = false;
+ unsigned int chip_id;
int ret;
pdata = dev_get_platdata(&client->dev);
- if (!pdata && !client->dev.of_node) {
+ if (!pdata && client->dev.of_node) {
+ pdata = max8973_parse_dt(&client->dev);
+ pdata_from_dt = true;
+ }
+
+ if (!pdata) {
dev_err(&client->dev, "No Platform data");
return -EIO;
}
+ if ((pdata->dvs_gpio == -EPROBE_DEFER) ||
+ (pdata->enable_gpio == -EPROBE_DEFER))
+ return -EPROBE_DEFER;
+
max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
if (!max)
return -ENOMEM;
@@ -388,6 +501,27 @@ static int max8973_probe(struct i2c_client *client,
return ret;
}
+ if (client->dev.of_node) {
+ const struct of_device_id *match;
+
+ match = of_match_device(of_match_ptr(of_max8973_match_tbl),
+ &client->dev);
+ if (!match)
+ return -ENODATA;
+ max->id = (u32)((uintptr_t)match->data);
+ } else {
+ max->id = id->driver_data;
+ }
+
+ ret = regmap_read(max->regmap, MAX8973_CHIPID1, &chip_id);
+ if (ret < 0) {
+ dev_err(&client->dev, "register CHIPID1 read failed, %d", ret);
+ return ret;
+ }
+
+ dev_info(&client->dev, "CHIP-ID OTP: 0x%02x ID_M: 0x%02x\n",
+ (chip_id >> 4) & 0xF, (chip_id >> 1) & 0x7);
+
i2c_set_clientdata(client, max);
max->ops = max8973_dcdc_ops;
max->dev = &client->dev;
@@ -400,23 +534,14 @@ static int max8973_probe(struct i2c_client *client,
max->desc.uV_step = MAX8973_VOLATGE_STEP;
max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
- if (!pdata || !pdata->enable_ext_control) {
- max->desc.enable_reg = MAX8973_VOUT;
- max->desc.enable_mask = MAX8973_VOUT_ENABLE;
- max->ops.enable = regulator_enable_regmap;
- max->ops.disable = regulator_disable_regmap;
- max->ops.is_enabled = regulator_is_enabled_regmap;
- }
+ max->dvs_gpio = (pdata->dvs_gpio) ? pdata->dvs_gpio : -EINVAL;
+ max->enable_gpio = (pdata->enable_gpio) ? pdata->enable_gpio : -EINVAL;
+ max->enable_external_control = pdata->enable_ext_control;
+ max->curr_gpio_val = pdata->dvs_def_state;
+ max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
- if (pdata) {
- max->dvs_gpio = pdata->dvs_gpio;
- max->enable_external_control = pdata->enable_ext_control;
- max->curr_gpio_val = pdata->dvs_def_state;
- max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
- } else {
- max->dvs_gpio = -EINVAL;
- max->curr_vout_reg = MAX8973_VOUT;
- }
+ if (gpio_is_valid(max->enable_gpio))
+ max->enable_external_control = true;
max->lru_index[0] = max->curr_vout_reg;
@@ -434,7 +559,6 @@ static int max8973_probe(struct i2c_client *client,
max->dvs_gpio, ret);
return ret;
}
- max->valid_dvs_gpio = true;
/*
* Initialize the lru index with vout_reg id
@@ -444,22 +568,64 @@ static int max8973_probe(struct i2c_client *client,
max->lru_index[i] = i;
max->lru_index[0] = max->curr_vout_reg;
max->lru_index[max->curr_vout_reg] = 0;
- } else {
- max->valid_dvs_gpio = false;
}
- if (pdata) {
- ret = max8973_init_dcdc(max, pdata);
- if (ret < 0) {
- dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
- return ret;
+ if (pdata_from_dt)
+ pdata->reg_init_data = of_get_regulator_init_data(&client->dev,
+ client->dev.of_node, &max->desc);
+
+ ridata = pdata->reg_init_data;
+ switch (max->id) {
+ case MAX8973:
+ if (!pdata->enable_ext_control) {
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ break;
+ }
+
+ if (gpio_is_valid(max->enable_gpio)) {
+ config.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
+ if (ridata && (ridata->constraints.always_on ||
+ ridata->constraints.boot_on))
+ config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+ config.ena_gpio = max->enable_gpio;
}
+ break;
+
+ case MAX77621:
+ if (gpio_is_valid(max->enable_gpio)) {
+ ret = devm_gpio_request_one(&client->dev,
+ max->enable_gpio, GPIOF_OUT_INIT_HIGH,
+ "max8973-en-gpio");
+ if (ret) {
+ dev_err(&client->dev,
+ "gpio_request for gpio %d failed: %d\n",
+ max->enable_gpio, ret);
+ return ret;
+ }
+ }
+
+ max->desc.enable_reg = MAX8973_VOUT;
+ max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+ max->ops.enable = regulator_enable_regmap;
+ max->ops.disable = regulator_disable_regmap;
+ max->ops.is_enabled = regulator_is_enabled_regmap;
+ break;
+ default:
+ break;
+ }
+
+ ret = max8973_init_dcdc(max, pdata);
+ if (ret < 0) {
+ dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
+ return ret;
}
config.dev = &client->dev;
- config.init_data = pdata ? pdata->reg_init_data :
- of_get_regulator_init_data(&client->dev, client->dev.of_node,
- &max->desc);
+ config.init_data = pdata->reg_init_data;
config.driver_data = max;
config.of_node = client->dev.of_node;
config.regmap = max->regmap;
@@ -476,15 +642,16 @@ static int max8973_probe(struct i2c_client *client,
}
static const struct i2c_device_id max8973_id[] = {
- {.name = "max8973",},
+ {.name = "max8973", .driver_data = MAX8973},
+ {.name = "max77621", .driver_data = MAX77621},
{},
};
-
MODULE_DEVICE_TABLE(i2c, max8973_id);
static struct i2c_driver max8973_i2c_driver = {
.driver = {
.name = "max8973",
+ .of_match_table = of_max8973_match_tbl,
.owner = THIS_MODULE,
},
.probe = max8973_probe,
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 24e812c48d93..b1c485b24ab2 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -58,6 +58,10 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
constraints->max_uA = pval;
+ if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
+ &pval))
+ constraints->ilim_uA = pval;
+
/* Current change possible? */
if (constraints->min_uA != constraints->max_uA)
constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
@@ -67,6 +71,8 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!constraints->always_on) /* status change should be possible. */
constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
+ constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+
if (of_property_read_bool(np, "regulator-allow-bypass"))
constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
@@ -82,6 +88,9 @@ static void of_get_regulation_constraints(struct device_node *np,
if (!ret)
constraints->enable_time = pval;
+ constraints->soft_start = of_property_read_bool(np,
+ "regulator-soft-start");
+
if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
if (desc && desc->of_map_mode) {
ret = desc->of_map_mode(pval);
@@ -95,6 +104,9 @@ static void of_get_regulation_constraints(struct device_node *np,
}
}
+ if (!of_property_read_u32(np, "regulator-system-load", &pval))
+ constraints->system_load = pval;
+
for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
switch (i) {
case PM_SUSPEND_MEM:
@@ -108,7 +120,7 @@ static void of_get_regulation_constraints(struct device_node *np,
case PM_SUSPEND_STANDBY:
default:
continue;
- };
+ }
suspend_np = of_get_child_by_name(np, regulator_states[i]);
if (!suspend_np || !suspend_state)
@@ -292,7 +304,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
return NULL;
}
- for_each_child_of_node(search, child) {
+ for_each_available_child_of_node(search, child) {
name = of_get_property(child, "regulator-compatible", NULL);
if (!name)
name = child->name;
diff --git a/drivers/regulator/pwm-regulator.c b/drivers/regulator/pwm-regulator.c
index 253833ae35f3..ffa96124a5e7 100644
--- a/drivers/regulator/pwm-regulator.c
+++ b/drivers/regulator/pwm-regulator.c
@@ -21,10 +21,8 @@
#include <linux/pwm.h>
struct pwm_regulator_data {
- struct regulator_desc desc;
struct pwm_voltages *duty_cycle_table;
struct pwm_device *pwm;
- bool enabled;
int state;
};
@@ -33,17 +31,17 @@ struct pwm_voltages {
unsigned int dutycycle;
};
-static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
return drvdata->state;
}
-static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
unsigned selector)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
unsigned int pwm_reg_period;
int dutycycle;
int ret;
@@ -55,30 +53,27 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
if (ret) {
- dev_err(&dev->dev, "Failed to configure PWM\n");
+ dev_err(&rdev->dev, "Failed to configure PWM\n");
return ret;
}
drvdata->state = selector;
- if (!drvdata->enabled) {
- ret = pwm_enable(drvdata->pwm);
- if (ret) {
- dev_err(&dev->dev, "Failed to enable PWM\n");
- return ret;
- }
- drvdata->enabled = true;
+ ret = pwm_enable(drvdata->pwm);
+ if (ret) {
+ dev_err(&rdev->dev, "Failed to enable PWM\n");
+ return ret;
}
return 0;
}
-static int pwm_regulator_list_voltage(struct regulator_dev *dev,
+static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
unsigned selector)
{
- struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+ struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
- if (selector >= drvdata->desc.n_voltages)
+ if (selector >= rdev->desc->n_voltages)
return -EINVAL;
return drvdata->duty_cycle_table[selector].uV;
@@ -91,7 +86,7 @@ static struct regulator_ops pwm_regulator_voltage_ops = {
.map_voltage = regulator_map_voltage_iterate,
};
-static const struct regulator_desc pwm_regulator_desc = {
+static struct regulator_desc pwm_regulator_desc = {
.name = "pwm-regulator",
.ops = &pwm_regulator_voltage_ops,
.type = REGULATOR_VOLTAGE,
@@ -117,8 +112,6 @@ static int pwm_regulator_probe(struct platform_device *pdev)
if (!drvdata)
return -ENOMEM;
- memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
-
/* determine the number of voltage-table */
prop = of_find_property(np, "voltage-table", &length);
if (!prop) {
@@ -133,7 +126,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
return -EINVAL;
}
- drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
+ pwm_regulator_desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
length, GFP_KERNEL);
@@ -150,7 +143,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
}
config.init_data = of_get_regulator_init_data(&pdev->dev, np,
- &drvdata->desc);
+ &pwm_regulator_desc);
if (!config.init_data)
return -ENOMEM;
@@ -165,10 +158,10 @@ static int pwm_regulator_probe(struct platform_device *pdev)
}
regulator = devm_regulator_register(&pdev->dev,
- &drvdata->desc, &config);
+ &pwm_regulator_desc, &config);
if (IS_ERR(regulator)) {
dev_err(&pdev->dev, "Failed to register regulator %s\n",
- drvdata->desc.name);
+ pwm_regulator_desc.name);
return PTR_ERR(regulator);
}
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644
index 000000000000..850a30a95b5b
--- /dev/null
+++ b/drivers/regulator/qcom_spmi-regulator.c
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+ SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_VS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+ SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+ SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+ SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+ SPMI_REGULATOR_TYPE_BUCK = 0x03,
+ SPMI_REGULATOR_TYPE_LDO = 0x04,
+ SPMI_REGULATOR_TYPE_VS = 0x05,
+ SPMI_REGULATOR_TYPE_BOOST = 0x1b,
+ SPMI_REGULATOR_TYPE_FTS = 0x1c,
+ SPMI_REGULATOR_TYPE_BOOST_BYP = 0x1f,
+ SPMI_REGULATOR_TYPE_ULT_LDO = 0x21,
+ SPMI_REGULATOR_TYPE_ULT_BUCK = 0x22,
+};
+
+enum spmi_regulator_subtype {
+ SPMI_REGULATOR_SUBTYPE_GP_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_RF_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_N50 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_N150 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_N300 = 0x03,
+ SPMI_REGULATOR_SUBTYPE_N600 = 0x04,
+ SPMI_REGULATOR_SUBTYPE_N1200 = 0x05,
+ SPMI_REGULATOR_SUBTYPE_N600_ST = 0x06,
+ SPMI_REGULATOR_SUBTYPE_N1200_ST = 0x07,
+ SPMI_REGULATOR_SUBTYPE_N900_ST = 0x14,
+ SPMI_REGULATOR_SUBTYPE_N300_ST = 0x15,
+ SPMI_REGULATOR_SUBTYPE_P50 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_P150 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_P300 = 0x0a,
+ SPMI_REGULATOR_SUBTYPE_P600 = 0x0b,
+ SPMI_REGULATOR_SUBTYPE_P1200 = 0x0c,
+ SPMI_REGULATOR_SUBTYPE_LN = 0x10,
+ SPMI_REGULATOR_SUBTYPE_LV_P50 = 0x28,
+ SPMI_REGULATOR_SUBTYPE_LV_P150 = 0x29,
+ SPMI_REGULATOR_SUBTYPE_LV_P300 = 0x2a,
+ SPMI_REGULATOR_SUBTYPE_LV_P600 = 0x2b,
+ SPMI_REGULATOR_SUBTYPE_LV_P1200 = 0x2c,
+ SPMI_REGULATOR_SUBTYPE_LV_P450 = 0x2d,
+ SPMI_REGULATOR_SUBTYPE_LV100 = 0x01,
+ SPMI_REGULATOR_SUBTYPE_LV300 = 0x02,
+ SPMI_REGULATOR_SUBTYPE_MV300 = 0x08,
+ SPMI_REGULATOR_SUBTYPE_MV500 = 0x09,
+ SPMI_REGULATOR_SUBTYPE_HDMI = 0x10,
+ SPMI_REGULATOR_SUBTYPE_OTG = 0x11,
+ SPMI_REGULATOR_SUBTYPE_5V_BOOST = 0x01,
+ SPMI_REGULATOR_SUBTYPE_FTS_CTL = 0x08,
+ SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL = 0x09,
+ SPMI_REGULATOR_SUBTYPE_BB_2A = 0x01,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1 = 0x0d,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2 = 0x0e,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3 = 0x0f,
+ SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4 = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+ SPMI_COMMON_REG_DIG_MAJOR_REV = 0x01,
+ SPMI_COMMON_REG_TYPE = 0x04,
+ SPMI_COMMON_REG_SUBTYPE = 0x05,
+ SPMI_COMMON_REG_VOLTAGE_RANGE = 0x40,
+ SPMI_COMMON_REG_VOLTAGE_SET = 0x41,
+ SPMI_COMMON_REG_MODE = 0x45,
+ SPMI_COMMON_REG_ENABLE = 0x46,
+ SPMI_COMMON_REG_PULL_DOWN = 0x48,
+ SPMI_COMMON_REG_SOFT_START = 0x4c,
+ SPMI_COMMON_REG_STEP_CTRL = 0x61,
+};
+
+enum spmi_vs_registers {
+ SPMI_VS_REG_OCP = 0x4a,
+ SPMI_VS_REG_SOFT_START = 0x4c,
+};
+
+enum spmi_boost_registers {
+ SPMI_BOOST_REG_CURRENT_LIMIT = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+ SPMI_BOOST_BYP_REG_CURRENT_LIMIT = 0x4b,
+};
+
+/* Used for indexing into ctrl_reg. These are offets from 0x40 */
+enum spmi_common_control_register_index {
+ SPMI_COMMON_IDX_VOLTAGE_RANGE = 0,
+ SPMI_COMMON_IDX_VOLTAGE_SET = 1,
+ SPMI_COMMON_IDX_MODE = 5,
+ SPMI_COMMON_IDX_ENABLE = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK 0x80
+#define SPMI_COMMON_ENABLE 0x80
+#define SPMI_COMMON_DISABLE 0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK 0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK 0x80
+#define SPMI_COMMON_MODE_AUTO_MASK 0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK 0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK 0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK 0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK 0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK 0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK 0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK 0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK 0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK 0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK 0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE 0x01
+#define SPMI_VS_OCP_NO_OVERRIDE 0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK 0x80
+#define SPMI_VS_SOFT_START_SEL_MASK 0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK 0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK 0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES 10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS 30
+#define SPMI_VS_OCP_FALL_DELAY_US 90
+#define SPMI_VS_OCP_FAULT_DELAY_US 20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK 0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT 3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK 0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT 0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE 19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY 8
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM 4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN 5
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level. It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV: Minimum programmable output voltage resulting from
+ * set point register value 0x00
+ * @max_uV: Maximum programmable output voltage
+ * @step_uV: Output voltage increase resulting from the set point
+ * register value increasing by 1
+ * @set_point_min_uV: Minimum allowed voltage
+ * @set_point_max_uV: Maximum allowed voltage. This may be tweaked in order
+ * to pick which range should be used in the case of
+ * overlapping set points.
+ * @n_voltages: Number of preferred voltage set points present in this
+ * range
+ * @range_sel: Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+ int min_uV;
+ int max_uV;
+ int step_uV;
+ int set_point_min_uV;
+ int set_point_max_uV;
+ unsigned n_voltages;
+ u8 range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+ struct spmi_voltage_range *range;
+ int count;
+ unsigned n_voltages;
+};
+
+struct spmi_regulator {
+ struct regulator_desc desc;
+ struct device *dev;
+ struct delayed_work ocp_work;
+ struct regmap *regmap;
+ struct spmi_voltage_set_points *set_points;
+ enum spmi_regulator_logical_type logical_type;
+ int ocp_irq;
+ int ocp_count;
+ int ocp_max_retries;
+ int ocp_retry_delay_ms;
+ int hpm_min_load;
+ int slew_rate;
+ ktime_t vs_enable_time;
+ u16 base;
+ struct list_head node;
+};
+
+struct spmi_regulator_mapping {
+ enum spmi_regulator_type type;
+ enum spmi_regulator_subtype subtype;
+ enum spmi_regulator_logical_type logical_type;
+ u32 revision_min;
+ u32 revision_max;
+ struct regulator_ops *ops;
+ struct spmi_voltage_set_points *set_points;
+ int hpm_min_load;
+};
+
+struct spmi_regulator_data {
+ const char *name;
+ u16 base;
+ const char *supply;
+ const char *ocp;
+ u16 force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+ _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_##_type, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+ .ops = &spmi_##_ops_val##_ops, \
+ .set_points = &_set_points_val##_set_points, \
+ .hpm_min_load = _hpm_min_load, \
+ }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+ { \
+ .type = SPMI_REGULATOR_TYPE_VS, \
+ .subtype = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+ .revision_min = _dig_major_min, \
+ .revision_max = _dig_major_max, \
+ .logical_type = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+ .ops = &spmi_vs_ops, \
+ }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+ _set_point_max_uV, _max_uV, _step_uV) \
+ { \
+ .min_uV = _min_uV, \
+ .max_uV = _max_uV, \
+ .set_point_min_uV = _set_point_min_uV, \
+ .set_point_max_uV = _set_point_max_uV, \
+ .step_uV = _step_uV, \
+ .range_sel = _range_sel, \
+ }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+ .range = name##_ranges, \
+ .count = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges. Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique. The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+ SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+ SPMI_VOLTAGE_RANGE(2, 750000, 750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 375000, 768750, 768750, 6250),
+ SPMI_VOLTAGE_RANGE(2, 750000, 775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 375000, 0, 0, 1537500, 12500),
+ SPMI_VOLTAGE_RANGE(2, 750000, 0, 0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(1, 690000, 690000, 1110000, 1110000, 60000),
+ SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 0, 350000, 1275000, 1275000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 80000, 350000, 1355000, 1355000, 5000),
+ SPMI_VOLTAGE_RANGE(1, 160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1562500, 1562500, 12500),
+ SPMI_VOLTAGE_RANGE(1, 750000, 0, 0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 375000, 375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+ SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
+ int len)
+{
+ return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+ u8 *buf, int len)
+{
+ return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+ u8 mask)
+{
+ return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
+
+ return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
+}
+
+static int spmi_regulator_common_enable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ if (vreg->ocp_irq) {
+ vreg->ocp_count = 0;
+ vreg->vs_enable_time = ktime_get();
+ }
+
+ return spmi_regulator_common_enable(rdev);
+}
+
+static int spmi_regulator_common_disable(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+ int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+ unsigned *selector)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+ /* Check if request voltage is outside of physically settable range. */
+ lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+ lim_max_uV =
+ vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+ if (uV < lim_min_uV && max_uV >= lim_min_uV)
+ uV = lim_min_uV;
+
+ if (uV < lim_min_uV || uV > lim_max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+ min_uV, max_uV, lim_min_uV, lim_max_uV);
+ return -EINVAL;
+ }
+
+ /* Find the range which uV is inside of. */
+ for (i = vreg->set_points->count - 1; i > 0; i--) {
+ range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+ if (uV > range_max_uV && range_max_uV > 0)
+ break;
+ }
+
+ range_id = i;
+ range = &vreg->set_points->range[range_id];
+ *range_sel = range->range_sel;
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ *voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+ / range->step_uV;
+ uV = *voltage_sel * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ dev_err(vreg->dev,
+ "request v=[%d, %d] cannot be met by any set point; "
+ "next set point: %d\n",
+ min_uV, max_uV, uV);
+ return -EINVAL;
+ }
+
+ *selector = 0;
+ for (i = 0; i < range_id; i++)
+ *selector += vreg->set_points->range[i].n_voltages;
+ *selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+ return 0;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+ u8 range_sel;
+ const struct spmi_voltage_range *range, *end;
+
+ range = vreg->set_points->range;
+ end = range + vreg->set_points->count;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+ for (; range < end; range++)
+ if (range->range_sel == range_sel)
+ return range;
+
+ return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator *vreg,
+ int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+ unsigned *selector)
+{
+ const struct spmi_voltage_range *range;
+ int uV = min_uV;
+ int i;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ goto different_range;
+
+ if (uV < range->min_uV && max_uV >= range->min_uV)
+ uV = range->min_uV;
+
+ if (uV < range->min_uV || uV > range->max_uV) {
+ /* Current range doesn't support the requested voltage. */
+ goto different_range;
+ }
+
+ /*
+ * Force uV to be an allowed set point by applying a ceiling function to
+ * the uV value.
+ */
+ *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+ uV = *voltage_sel * range->step_uV + range->min_uV;
+
+ if (uV > max_uV) {
+ /*
+ * No set point in the current voltage range is within the
+ * requested min_uV to max_uV range.
+ */
+ goto different_range;
+ }
+
+ *selector = 0;
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (uV >= vreg->set_points->range[i].set_point_min_uV
+ && uV <= vreg->set_points->range[i].set_point_max_uV) {
+ *selector +=
+ (uV - vreg->set_points->range[i].set_point_min_uV)
+ / vreg->set_points->range[i].step_uV;
+ break;
+ }
+
+ *selector += vreg->set_points->range[i].n_voltages;
+ }
+
+ if (*selector >= vreg->set_points->n_voltages)
+ goto different_range;
+
+ return 0;
+
+different_range:
+ return spmi_regulator_select_voltage(vreg, min_uV, max_uV,
+ range_sel, voltage_sel, selector);
+}
+
+static int spmi_regulator_common_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 buf[2];
+ u8 range_sel, voltage_sel;
+
+ /*
+ * Favor staying in the current voltage range if possible. This avoids
+ * voltage spikes that occur when changing the voltage range.
+ */
+ ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+ &range_sel, &voltage_sel, selector);
+ if (ret)
+ return ret;
+
+ buf[0] = range_sel;
+ buf[1] = voltage_sel;
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+ unsigned int old_selector, unsigned int new_selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ int diff_uV;
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+ return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return VOLTAGE_UNKNOWN;
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, sel;
+
+ ret = spmi_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+ &sel, selector);
+ if (ret) {
+ dev_err(vreg->dev, "could not set voltage, ret=%d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Certain types of regulators do not have a range select register so
+ * only voltage set register needs to be written.
+ */
+ return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range = vreg->set_points->range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int ret;
+ u8 range_sel, voltage_sel;
+
+ /*
+ * Favor staying in the current voltage range if possible. This avoids
+ * voltage spikes that occur when changing the voltage range.
+ */
+ ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+ &range_sel, &voltage_sel, selector);
+ if (ret)
+ return ret;
+
+ /*
+ * Calculate VSET based on range
+ * In case of range 0: voltage_sel is a 7 bit value, can be written
+ * witout any modification.
+ * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+ * [011].
+ */
+ if (range_sel == 1)
+ voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+ voltage_sel, 0xff);
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ const struct spmi_voltage_range *range;
+ u8 voltage_sel;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return VOLTAGE_UNKNOWN;
+
+ if (range->range_sel == 1)
+ voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+ return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+ unsigned selector)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ int uV = 0;
+ int i;
+
+ if (selector >= vreg->set_points->n_voltages)
+ return 0;
+
+ for (i = 0; i < vreg->set_points->count; i++) {
+ if (selector < vreg->set_points->range[i].n_voltages) {
+ uV = selector * vreg->set_points->range[i].step_uV
+ + vreg->set_points->range[i].set_point_min_uV;
+ break;
+ }
+
+ selector -= vreg->set_points->range[i].n_voltages;
+ }
+
+ return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+ u8 val = 0;
+
+ if (enable)
+ val = mask;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 val;
+ int ret;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+ *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+ return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 reg;
+
+ spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+ if (reg & SPMI_COMMON_MODE_HPM_MASK)
+ return REGULATOR_MODE_NORMAL;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+ u8 val = 0;
+
+ if (mode == REGULATOR_MODE_NORMAL)
+ val = mask;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mode;
+
+ if (load_uA >= vreg->hpm_min_load)
+ mode = REGULATOR_MODE_NORMAL;
+ else
+ mode = REGULATOR_MODE_IDLE;
+
+ return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+ mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+ mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+ struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+ enum spmi_regulator_logical_type type = vreg->logical_type;
+ unsigned int current_reg;
+ u8 reg;
+ u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+ SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+ int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+ if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+ current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+ else
+ current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+ if (ilim_uA > max || ilim_uA <= 0)
+ return -EINVAL;
+
+ reg = (ilim_uA - 1) / 500;
+ reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+ return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+ int ret;
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+ vreg->vs_enable_time = ktime_get();
+
+ ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+ SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+ return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct spmi_regulator *vreg
+ = container_of(dwork, struct spmi_regulator, ocp_work);
+
+ spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+ struct spmi_regulator *vreg = data;
+ ktime_t ocp_irq_time;
+ s64 ocp_trigger_delay_us;
+
+ ocp_irq_time = ktime_get();
+ ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+ vreg->vs_enable_time);
+
+ /*
+ * Reset the OCP count if there is a large delay between switch enable
+ * and when OCP triggers. This is indicative of a hotplug event as
+ * opposed to a fault.
+ */
+ if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+ vreg->ocp_count = 0;
+
+ /* Wait for switch output to settle back to 0 V after OCP triggered. */
+ udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+ vreg->ocp_count++;
+
+ if (vreg->ocp_count == 1) {
+ /* Immediately clear the over current condition. */
+ spmi_regulator_vs_clear_ocp(vreg);
+ } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+ /* Schedule the over current clear task to run later. */
+ schedule_delayed_work(&vreg->ocp_work,
+ msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+ } else {
+ dev_err(vreg->dev,
+ "OCP triggered %d times; no further retries\n",
+ vreg->ocp_count);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static struct regulator_ops spmi_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+ .enable = spmi_regulator_vs_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_common_set_voltage,
+ .set_voltage_time_sel = spmi_regulator_set_voltage_time_sel,
+ .get_voltage = spmi_regulator_common_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_ult_lo_smps_set_voltage,
+ .get_voltage = spmi_regulator_ult_lo_smps_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+ .enable = spmi_regulator_common_enable,
+ .disable = spmi_regulator_common_disable,
+ .is_enabled = spmi_regulator_common_is_enabled,
+ .set_voltage = spmi_regulator_single_range_set_voltage,
+ .get_voltage = spmi_regulator_single_range_get_voltage,
+ .list_voltage = spmi_regulator_common_list_voltage,
+ .set_mode = spmi_regulator_common_set_mode,
+ .get_mode = spmi_regulator_common_get_mode,
+ .set_load = spmi_regulator_common_set_load,
+ .set_bypass = spmi_regulator_common_set_bypass,
+ .get_bypass = spmi_regulator_common_get_bypass,
+ .set_pull_down = spmi_regulator_common_set_pull_down,
+ .set_soft_start = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+ /* type subtype dig_min dig_max ltype ops setpoints hpm_min */
+ SPMI_VREG(BUCK, GP_CTL, 0, INF, SMPS, smps, smps, 100000),
+ SPMI_VREG(LDO, N300, 0, INF, LDO, ldo, nldo1, 10000),
+ SPMI_VREG(LDO, N600, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N600_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N1200_ST, 0, 0, LDO, ldo, nldo2, 10000),
+ SPMI_VREG(LDO, N600_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, N1200_ST, 1, INF, LDO, ldo, nldo3, 10000),
+ SPMI_VREG(LDO, P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LN, 0, INF, LN_LDO, ln_ldo, ln_ldo, 0),
+ SPMI_VREG(LDO, LV_P50, 0, INF, LDO, ldo, pldo, 5000),
+ SPMI_VREG(LDO, LV_P150, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P300, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P600, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG(LDO, LV_P1200, 0, INF, LDO, ldo, pldo, 10000),
+ SPMI_VREG_VS(LV100, 0, INF),
+ SPMI_VREG_VS(LV300, 0, INF),
+ SPMI_VREG_VS(MV300, 0, INF),
+ SPMI_VREG_VS(MV500, 0, INF),
+ SPMI_VREG_VS(HDMI, 0, INF),
+ SPMI_VREG_VS(OTG, 0, INF),
+ SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST, boost, boost, 0),
+ SPMI_VREG(FTS, FTS_CTL, 0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+ SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+ SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+ ult_lo_smps, 100000),
+ SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+ ult_ho_smps, 100000),
+ SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P300, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, LV_P450, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P600, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P150, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+ SPMI_VREG(ULT_LDO, P50, 0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+ unsigned int n;
+ struct spmi_voltage_range *range = points->range;
+
+ for (; range < points->range + points->count; range++) {
+ n = 0;
+ if (range->set_point_max_uV) {
+ n = range->set_point_max_uV - range->set_point_min_uV;
+ n = (n / range->step_uV) + 1;
+ }
+ range->n_voltages = n;
+ points->n_voltages += n;
+ }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+ const struct spmi_regulator_mapping *mapping;
+ int ret, i;
+ u32 dig_major_rev;
+ u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+ u8 type, subtype;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+ ARRAY_SIZE(version));
+ if (ret) {
+ dev_err(vreg->dev, "could not read version registers\n");
+ return ret;
+ }
+ dig_major_rev = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+ - SPMI_COMMON_REG_DIG_MAJOR_REV];
+ if (!force_type) {
+ type = version[SPMI_COMMON_REG_TYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ subtype = version[SPMI_COMMON_REG_SUBTYPE -
+ SPMI_COMMON_REG_DIG_MAJOR_REV];
+ } else {
+ type = force_type >> 8;
+ subtype = force_type;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+ mapping = &supported_regulators[i];
+ if (mapping->type == type && mapping->subtype == subtype
+ && mapping->revision_min <= dig_major_rev
+ && mapping->revision_max >= dig_major_rev)
+ goto found;
+ }
+
+ dev_err(vreg->dev,
+ "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+ vreg->desc.name, type, subtype, dig_major_rev);
+
+ return -ENODEV;
+
+found:
+ vreg->logical_type = mapping->logical_type;
+ vreg->set_points = mapping->set_points;
+ vreg->hpm_min_load = mapping->hpm_min_load;
+ vreg->desc.ops = mapping->ops;
+
+ if (mapping->set_points) {
+ if (!mapping->set_points->n_voltages)
+ spmi_calculate_num_voltages(mapping->set_points);
+ vreg->desc.n_voltages = mapping->set_points->n_voltages;
+ }
+
+ return 0;
+}
+
+static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
+{
+ int ret;
+ u8 reg = 0;
+ int step, delay, slew_rate;
+ const struct spmi_voltage_range *range;
+
+ ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+ if (ret) {
+ dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+ return ret;
+ }
+
+ range = spmi_regulator_find_range(vreg);
+ if (!range)
+ return -EINVAL;
+
+ step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+ step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+ delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+ delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+ /* slew_rate has units of uV/us */
+ slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+ slew_rate /= 1000 * (SPMI_FTSMPS_STEP_DELAY << delay);
+ slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+ slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+ /* Ensure that the slew rate is greater than 0 */
+ vreg->slew_rate = max(slew_rate, 1);
+
+ return ret;
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+ if (mode)
+ return REGULATOR_MODE_NORMAL;
+
+ return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct spmi_regulator *vreg = config->driver_data;
+ struct device *dev = config->dev;
+ int ret;
+
+ vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+ vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+ if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+ ret = spmi_regulator_ftsmps_init_slew_rate(vreg);
+ if (ret)
+ return ret;
+ }
+
+ if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+ vreg->ocp_irq = 0;
+
+ if (vreg->ocp_irq) {
+ ret = devm_request_irq(dev, vreg->ocp_irq,
+ spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+ vreg);
+ if (ret < 0) {
+ dev_err(dev, "failed to request irq %d, ret=%d\n",
+ vreg->ocp_irq, ret);
+ return ret;
+ }
+
+ INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+ }
+
+ return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l11", },
+ { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+ { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+ { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+ { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+ { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+ { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+ { "l11", 0x4a00, "vdd_l4_l11", },
+ { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+ { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+ { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+ { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+ { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+ { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+ { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+ { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+ { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+ { "l21", 0x5400, "vdd_l21", },
+ { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+ { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+ { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+ { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+ { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+ { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+ { "mvs1", 0x8300, "vin_5vs", },
+ { "mvs2", 0x8400, "vin_5vs", },
+ { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+ { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+ { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+ { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+ { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+ { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+ { "s1", 0x1400, "vdd_s1", },
+ { "s2", 0x1700, "vdd_s2", },
+ { "s3", 0x1a00, "vdd_s3", },
+ { "s4", 0x1d00, "vdd_s4", },
+ { "l1", 0x4000, "vdd_l1_l3", },
+ { "l2", 0x4100, "vdd_l2", },
+ { "l3", 0x4200, "vdd_l1_l3", },
+ { "l4", 0x4300, "vdd_l4_l5_l6", },
+ { "l5", 0x4400, "vdd_l4_l5_l6", },
+ { "l6", 0x4500, "vdd_l4_l5_l6", },
+ { "l7", 0x4600, "vdd_l7", },
+ { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+ { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+ { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+ { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+ { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+ { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+ { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+ { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+ { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+ { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+ { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+ { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+ const struct spmi_regulator_data *reg;
+ const struct of_device_id *match;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct spmi_regulator *vreg;
+ struct regmap *regmap;
+ const char *name;
+ struct device *dev = &pdev->dev;
+ int ret;
+ struct list_head *vreg_list;
+
+ vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+ if (!vreg_list)
+ return -ENOMEM;
+ INIT_LIST_HEAD(vreg_list);
+ platform_set_drvdata(pdev, vreg_list);
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+ if (!match)
+ return -ENODEV;
+
+ for (reg = match->data; reg->name; reg++) {
+ vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+ if (!vreg)
+ return -ENOMEM;
+
+ vreg->dev = dev;
+ vreg->base = reg->base;
+ vreg->regmap = regmap;
+
+ if (reg->ocp) {
+ vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+ if (vreg->ocp_irq < 0) {
+ ret = vreg->ocp_irq;
+ goto err;
+ }
+ }
+
+ vreg->desc.id = -1;
+ vreg->desc.owner = THIS_MODULE;
+ vreg->desc.type = REGULATOR_VOLTAGE;
+ vreg->desc.name = name = reg->name;
+ vreg->desc.supply_name = reg->supply;
+ vreg->desc.of_match = reg->name;
+ vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+ vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+ ret = spmi_regulator_match(vreg, reg->force_type);
+ if (ret)
+ goto err;
+
+ config.dev = dev;
+ config.driver_data = vreg;
+ rdev = devm_regulator_register(dev, &vreg->desc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n", name);
+ ret = PTR_ERR(rdev);
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&vreg->node);
+ list_add(&vreg->node, vreg_list);
+ }
+
+ return 0;
+
+err:
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+ return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+ struct spmi_regulator *vreg;
+ struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+ list_for_each_entry(vreg, vreg_list, node)
+ if (vreg->ocp_irq)
+ cancel_delayed_work_sync(&vreg->ocp_work);
+
+ return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+ .driver = {
+ .name = "qcom-spmi-regulator",
+ .of_match_table = qcom_spmi_regulator_match,
+ },
+ .probe = qcom_spmi_regulator_probe,
+ .remove = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index ff828117798f..326ffb553371 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -515,7 +515,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
break;
default:
return -EINVAL;
- };
+ }
return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
rdev->desc->enable_mask, val);
@@ -538,7 +538,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
default:
state = S2MPS14_ENABLE_SUSPEND;
break;
- };
+ }
break;
case S2MPU02:
switch (rdev_id) {
@@ -552,11 +552,11 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
default:
state = S2MPU02_ENABLE_SUSPEND;
break;
- };
+ }
break;
default:
return -EINVAL;
- };
+ }
ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
if (ret < 0)
@@ -977,7 +977,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Invalid device type: %u\n",
s2mps11->dev_type);
return -EINVAL;
- };
+ }
s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev,
sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c
index 0d7e164a5e76..8cbb82ceec40 100644
--- a/drivers/regulator/wm831x-dcdc.c
+++ b/drivers/regulator/wm831x-dcdc.c
@@ -533,7 +533,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);
@@ -543,7 +544,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_oc_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
irq, ret);
@@ -669,7 +671,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name, dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);
@@ -785,7 +788,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_dcdc_uv_irq,
- IRQF_TRIGGER_RISING, dcdc->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dcdc->name,
dcdc);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
diff --git a/drivers/regulator/wm831x-isink.c b/drivers/regulator/wm831x-isink.c
index 1e88391a1628..1442828fcd9a 100644
--- a/drivers/regulator/wm831x-isink.c
+++ b/drivers/regulator/wm831x-isink.c
@@ -204,7 +204,8 @@ static int wm831x_isink_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_isink_irq,
- IRQF_TRIGGER_RISING, isink->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ isink->name,
isink);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c
index 7ae2dc82f636..5a7b65e8a529 100644
--- a/drivers/regulator/wm831x-ldo.c
+++ b/drivers/regulator/wm831x-ldo.c
@@ -287,7 +287,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_ldo_uv_irq,
- IRQF_TRIGGER_RISING, ldo->name,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name,
ldo);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
@@ -496,7 +497,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev)
irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
wm831x_ldo_uv_irq,
- IRQF_TRIGGER_RISING, ldo->name, ldo);
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ ldo->name, ldo);
if (ret != 0) {
dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
irq, ret);