aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/regulator
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/regulator')
-rw-r--r--drivers/regulator/Kconfig40
-rw-r--r--drivers/regulator/Makefile6
-rw-r--r--drivers/regulator/anatop-regulator.c19
-rw-r--r--drivers/regulator/arizona-ldo1.c140
-rw-r--r--drivers/regulator/arizona-micsupp.c119
-rw-r--r--drivers/regulator/bd9571mwv-regulator.c178
-rw-r--r--drivers/regulator/core.c37
-rw-r--r--drivers/regulator/helpers.c36
-rw-r--r--drivers/regulator/hi655x-regulator.c7
-rw-r--r--drivers/regulator/internal.h2
-rw-r--r--drivers/regulator/lm363x-regulator.c4
-rw-r--r--drivers/regulator/ltc3589.c25
-rw-r--r--drivers/regulator/ltc3676.c7
-rw-r--r--drivers/regulator/max1586.c4
-rw-r--r--drivers/regulator/max77693-regulator.c2
-rw-r--r--drivers/regulator/max8660.c4
-rw-r--r--drivers/regulator/of_regulator.c4
-rw-r--r--drivers/regulator/pfuze100-regulator.c24
-rw-r--r--drivers/regulator/rk808-regulator.c2
-rw-r--r--drivers/regulator/s2mpa01.c14
-rw-r--r--drivers/regulator/s2mps11.c16
-rw-r--r--drivers/regulator/s5m8767.c4
-rw-r--r--drivers/regulator/tps65023-regulator.c3
-rw-r--r--drivers/regulator/tps65132-regulator.c284
-rw-r--r--drivers/regulator/twl6030-regulator.c2
-rw-r--r--drivers/regulator/vctrl-regulator.c546
26 files changed, 1363 insertions, 166 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index be06eb29c681..48db87d6dfef 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -125,12 +125,20 @@ config REGULATOR_AB8500
This driver supports the regulators found on the ST-Ericsson mixed
signal AB8500 PMIC
-config REGULATOR_ARIZONA
- tristate "Wolfson Arizona class devices"
+config REGULATOR_ARIZONA_LDO1
+ tristate "Wolfson Arizona class devices LDO1"
depends on MFD_ARIZONA
depends on SND_SOC
help
- Support for the regulators found on Wolfson Arizona class
+ Support for the LDO1 regulators found on Wolfson Arizona class
+ devices.
+
+config REGULATOR_ARIZONA_MICSUPP
+ tristate "Wolfson Arizona class devices MICSUPP"
+ depends on MFD_ARIZONA
+ depends on SND_SOC
+ help
+ Support for the MICSUPP regulators found on Wolfson Arizona class
devices.
config REGULATOR_AS3711
@@ -163,6 +171,17 @@ config REGULATOR_BCM590XX
BCM590xx PMUs. This will enable support for the software
controllable LDO/Switching regulators.
+config REGULATOR_BD9571MWV
+ tristate "ROHM BD9571MWV Regulators"
+ depends on MFD_BD9571MWV
+ help
+ This driver provides support for the voltage regulators on the
+ ROHM BD9571MWV PMIC. This will enable support for the software
+ controllable regulator and voltage sampling units.
+
+ This driver can also be built as a module. If so, the module
+ will be called bd9571mwv-regulator.
+
config REGULATOR_CPCAP
tristate "Motorola CPCAP regulator"
depends on MFD_CPCAP
@@ -788,6 +807,14 @@ config REGULATOR_TPS65090
This driver provides support for the voltage regulators on the
TI TPS65090 PMIC.
+config REGULATOR_TPS65132
+ tristate "TI TPS65132 Dual Output Power regulators"
+ depends on I2C && GPIOLIB
+ select REGMAP_I2C
+ help
+ This driver supports TPS65132 single inductor - dual output
+ power supply specifcally designed for display panels.
+
config REGULATOR_TPS65217
tristate "TI TPS65217 Power regulators"
depends on MFD_TPS65217
@@ -850,6 +877,13 @@ config REGULATOR_TWL4030
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_VCTRL
+ tristate "Voltage controlled regulators"
+ depends on OF
+ help
+ This driver provides support for voltage regulators whose output
+ voltage is controlled by the voltage of another regulator.
+
config REGULATOR_VEXPRESS
tristate "Versatile Express regulators"
depends on VEXPRESS_CONFIG
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index ef7725e2592a..dc3503fb3e30 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -19,11 +19,13 @@ obj-$(CONFIG_REGULATOR_ACT8865) += act8865-regulator.o
obj-$(CONFIG_REGULATOR_ACT8945A) += act8945a-regulator.o
obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o
obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
-obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_ARIZONA_LDO1) += arizona-ldo1.o
+obj-$(CONFIG_REGULATOR_ARIZONA_MICSUPP) += arizona-micsupp.o
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
+obj-$(CONFIG_REGULATOR_BD9571MWV) += bd9571mwv-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
@@ -105,7 +107,9 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o
obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o
+obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o
obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o
diff --git a/drivers/regulator/anatop-regulator.c b/drivers/regulator/anatop-regulator.c
index b041f277a38b..7d6478e6a503 100644
--- a/drivers/regulator/anatop-regulator.c
+++ b/drivers/regulator/anatop-regulator.c
@@ -39,7 +39,6 @@
#define LDO_FET_FULL_ON 0x1f
struct anatop_regulator {
- const char *name;
u32 control_reg;
struct regmap *anatop;
int vol_bit_shift;
@@ -193,13 +192,21 @@ static int anatop_regulator_probe(struct platform_device *pdev)
sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
if (!sreg)
return -ENOMEM;
- sreg->name = of_get_property(np, "regulator-name", NULL);
+
rdesc = &sreg->rdesc;
- rdesc->name = sreg->name;
rdesc->type = REGULATOR_VOLTAGE;
rdesc->owner = THIS_MODULE;
+ of_property_read_string(np, "regulator-name", &rdesc->name);
+ if (!rdesc->name) {
+ dev_err(dev, "failed to get a regulator-name\n");
+ return -EINVAL;
+ }
+
initdata = of_get_regulator_init_data(dev, np, rdesc);
+ if (!initdata)
+ return -ENOMEM;
+
initdata->supply_regulator = "vin";
sreg->initdata = initdata;
@@ -293,9 +300,13 @@ static int anatop_regulator_probe(struct platform_device *pdev)
* a sane default until imx6-cpufreq was probed and changes the
* voltage to the correct value. In this case we set 1.25V.
*/
- if (!sreg->sel && !strcmp(sreg->name, "vddpu"))
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpu"))
sreg->sel = 22;
+ /* set the default voltage of the pcie phy to be 1.100v */
+ if (!sreg->sel && !strcmp(rdesc->name, "vddpcie"))
+ sreg->sel = 0x10;
+
if (!sreg->bypass && !sreg->sel) {
dev_err(&pdev->dev, "Failed to read a valid default voltage selector.\n");
return -EINVAL;
diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c
index e76d094591e7..96fddfff5dc4 100644
--- a/drivers/regulator/arizona-ldo1.c
+++ b/drivers/regulator/arizona-ldo1.c
@@ -25,13 +25,15 @@
#include <linux/gpio.h>
#include <linux/slab.h>
+#include <linux/regulator/arizona-ldo1.h>
+
#include <linux/mfd/arizona/core.h>
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
struct arizona_ldo1 {
struct regulator_dev *regulator;
- struct arizona *arizona;
+ struct regmap *regmap;
struct regulator_consumer_supply supply;
struct regulator_init_data init_data;
@@ -65,7 +67,7 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
unsigned sel)
{
struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
- struct regmap *regmap = ldo->arizona->regmap;
+ struct regmap *regmap = ldo->regmap;
unsigned int val;
int ret;
@@ -91,7 +93,7 @@ static int arizona_ldo1_hc_set_voltage_sel(struct regulator_dev *rdev,
static int arizona_ldo1_hc_get_voltage_sel(struct regulator_dev *rdev)
{
struct arizona_ldo1 *ldo = rdev_get_drvdata(rdev);
- struct regmap *regmap = ldo->arizona->regmap;
+ struct regmap *regmap = ldo->regmap;
unsigned int val;
int ret;
@@ -186,19 +188,19 @@ static const struct regulator_init_data arizona_ldo1_wm5110 = {
.num_consumer_supplies = 1,
};
-static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
+static int arizona_ldo1_of_get_pdata(struct arizona_ldo1_pdata *pdata,
struct regulator_config *config,
- const struct regulator_desc *desc)
+ const struct regulator_desc *desc,
+ bool *external_dcvdd)
{
- struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_ldo1 *ldo1 = config->driver_data;
- struct device_node *np = arizona->dev->of_node;
+ struct device_node *np = config->dev->of_node;
struct device_node *init_node, *dcvdd_node;
struct regulator_init_data *init_data;
pdata->ldoena = of_get_named_gpio(np, "wlf,ldoena", 0);
if (pdata->ldoena < 0) {
- dev_warn(arizona->dev,
+ dev_warn(config->dev,
"LDOENA GPIO property missing/malformed: %d\n",
pdata->ldoena);
pdata->ldoena = 0;
@@ -212,20 +214,19 @@ static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
if (init_node) {
config->of_node = init_node;
- init_data = of_get_regulator_init_data(arizona->dev, init_node,
+ init_data = of_get_regulator_init_data(config->dev, init_node,
desc);
-
if (init_data) {
init_data->consumer_supplies = &ldo1->supply;
init_data->num_consumer_supplies = 1;
if (dcvdd_node && dcvdd_node != init_node)
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
- pdata->ldo1 = init_data;
+ pdata->init_data = init_data;
}
} else if (dcvdd_node) {
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
}
of_node_put(dcvdd_node);
@@ -233,66 +234,40 @@ static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
return 0;
}
-static int arizona_ldo1_probe(struct platform_device *pdev)
+static int arizona_ldo1_common_init(struct platform_device *pdev,
+ struct arizona_ldo1 *ldo1,
+ const struct regulator_desc *desc,
+ struct arizona_ldo1_pdata *pdata,
+ bool *external_dcvdd)
{
- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- const struct regulator_desc *desc;
+ struct device *parent_dev = pdev->dev.parent;
struct regulator_config config = { };
- struct arizona_ldo1 *ldo1;
int ret;
- arizona->external_dcvdd = false;
-
- ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
- if (!ldo1)
- return -ENOMEM;
-
- ldo1->arizona = arizona;
-
- /*
- * Since the chip usually supplies itself we provide some
- * default init_data for it. This will be overridden with
- * platform data if provided.
- */
- switch (arizona->type) {
- case WM5102:
- case WM8997:
- case WM8998:
- case WM1814:
- 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;
- break;
- }
+ *external_dcvdd = false;
- ldo1->init_data.consumer_supplies = &ldo1->supply;
ldo1->supply.supply = "DCVDD";
- ldo1->supply.dev_name = dev_name(arizona->dev);
+ ldo1->init_data.consumer_supplies = &ldo1->supply;
+ ldo1->supply.dev_name = dev_name(parent_dev);
- config.dev = arizona->dev;
+ config.dev = parent_dev;
config.driver_data = ldo1;
- config.regmap = arizona->regmap;
+ config.regmap = ldo1->regmap;
if (IS_ENABLED(CONFIG_OF)) {
- if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_ldo1_of_get_pdata(arizona, &config, desc);
+ if (!dev_get_platdata(parent_dev)) {
+ ret = arizona_ldo1_of_get_pdata(pdata,
+ &config, desc,
+ external_dcvdd);
if (ret < 0)
return ret;
}
}
- config.ena_gpio = arizona->pdata.ldoena;
+ config.ena_gpio = pdata->ldoena;
- if (arizona->pdata.ldo1)
- config.init_data = arizona->pdata.ldo1;
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
else
config.init_data = &ldo1->init_data;
@@ -301,7 +276,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
* consumers then DCVDD is supplied externally.
*/
if (config.init_data->num_consumer_supplies == 0)
- arizona->external_dcvdd = true;
+ *external_dcvdd = true;
ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config);
@@ -309,7 +284,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
if (IS_ERR(ldo1->regulator)) {
ret = PTR_ERR(ldo1->regulator);
- dev_err(arizona->dev, "Failed to register LDO1 supply: %d\n",
+ dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
ret);
return ret;
}
@@ -319,6 +294,53 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
return 0;
}
+static int arizona_ldo1_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ struct arizona_ldo1 *ldo1;
+ const struct regulator_desc *desc;
+ bool external_dcvdd;
+ int ret;
+
+ ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL);
+ if (!ldo1)
+ return -ENOMEM;
+
+ ldo1->regmap = arizona->regmap;
+
+ /*
+ * Since the chip usually supplies itself we provide some
+ * default init_data for it. This will be overridden with
+ * platform data if provided.
+ */
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ case WM8998:
+ case WM1814:
+ 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;
+ break;
+ }
+
+ ret = arizona_ldo1_common_init(pdev, ldo1, desc,
+ &arizona->pdata.ldo1,
+ &external_dcvdd);
+ if (ret == 0)
+ arizona->external_dcvdd = external_dcvdd;
+
+ return ret;
+}
+
static struct platform_driver arizona_ldo1_driver = {
.probe = arizona_ldo1_probe,
.driver = {
diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c
index 22bd71407622..120de94caf02 100644
--- a/drivers/regulator/arizona-micsupp.c
+++ b/drivers/regulator/arizona-micsupp.c
@@ -30,9 +30,14 @@
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
+#include <linux/regulator/arizona-micsupp.h>
+
struct arizona_micsupp {
struct regulator_dev *regulator;
- struct arizona *arizona;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context **dapm;
+ unsigned int enable_reg;
+ struct device *dev;
struct regulator_consumer_supply supply;
struct regulator_init_data init_data;
@@ -44,21 +49,22 @@ static void arizona_micsupp_check_cp(struct work_struct *work)
{
struct arizona_micsupp *micsupp =
container_of(work, struct arizona_micsupp, check_cp_work);
- struct snd_soc_dapm_context *dapm = micsupp->arizona->dapm;
- struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
- struct arizona *arizona = micsupp->arizona;
- struct regmap *regmap = arizona->regmap;
- unsigned int reg;
+ struct snd_soc_dapm_context *dapm = *micsupp->dapm;
+ struct snd_soc_component *component;
+ unsigned int val;
int ret;
- ret = regmap_read(regmap, ARIZONA_MIC_CHARGE_PUMP_1, &reg);
+ ret = regmap_read(micsupp->regmap, micsupp->enable_reg, &val);
if (ret != 0) {
- dev_err(arizona->dev, "Failed to read CP state: %d\n", ret);
+ dev_err(micsupp->dev,
+ "Failed to read CP state: %d\n", ret);
return;
}
if (dapm) {
- if ((reg & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
+ component = snd_soc_dapm_to_component(dapm);
+
+ if ((val & (ARIZONA_CPMIC_ENA | ARIZONA_CPMIC_BYPASS)) ==
ARIZONA_CPMIC_ENA)
snd_soc_component_force_enable_pin(component,
"MICSUPP");
@@ -199,89 +205,67 @@ static const struct regulator_init_data arizona_micsupp_ext_default = {
.num_consumer_supplies = 1,
};
-static int arizona_micsupp_of_get_pdata(struct arizona *arizona,
+static int arizona_micsupp_of_get_pdata(struct arizona_micsupp_pdata *pdata,
struct regulator_config *config,
const struct regulator_desc *desc)
{
- struct arizona_pdata *pdata = &arizona->pdata;
struct arizona_micsupp *micsupp = config->driver_data;
struct device_node *np;
struct regulator_init_data *init_data;
- np = of_get_child_by_name(arizona->dev->of_node, "micvdd");
+ np = of_get_child_by_name(config->dev->of_node, "micvdd");
if (np) {
config->of_node = np;
- init_data = of_get_regulator_init_data(arizona->dev, np, desc);
+ init_data = of_get_regulator_init_data(config->dev, np, desc);
if (init_data) {
init_data->consumer_supplies = &micsupp->supply;
init_data->num_consumer_supplies = 1;
- pdata->micvdd = init_data;
+ pdata->init_data = init_data;
}
}
return 0;
}
-static int arizona_micsupp_probe(struct platform_device *pdev)
+static int arizona_micsupp_common_init(struct platform_device *pdev,
+ struct arizona_micsupp *micsupp,
+ const struct regulator_desc *desc,
+ struct arizona_micsupp_pdata *pdata)
{
- struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
- const struct regulator_desc *desc;
struct regulator_config config = { };
- struct arizona_micsupp *micsupp;
int ret;
- micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
- if (!micsupp)
- return -ENOMEM;
-
- micsupp->arizona = arizona;
INIT_WORK(&micsupp->check_cp_work, arizona_micsupp_check_cp);
- /*
- * Since the chip usually supplies itself we provide some
- * default init_data for it. This will be overridden with
- * platform data if provided.
- */
- switch (arizona->type) {
- case WM5110:
- case WM8280:
- desc = &arizona_micsupp_ext;
- micsupp->init_data = arizona_micsupp_ext_default;
- break;
- default:
- desc = &arizona_micsupp;
- micsupp->init_data = arizona_micsupp_default;
- break;
- }
-
micsupp->init_data.consumer_supplies = &micsupp->supply;
micsupp->supply.supply = "MICVDD";
- micsupp->supply.dev_name = dev_name(arizona->dev);
+ micsupp->supply.dev_name = dev_name(micsupp->dev);
+ micsupp->enable_reg = desc->enable_reg;
- config.dev = arizona->dev;
+ config.dev = micsupp->dev;
config.driver_data = micsupp;
- config.regmap = arizona->regmap;
+ config.regmap = micsupp->regmap;
if (IS_ENABLED(CONFIG_OF)) {
- if (!dev_get_platdata(arizona->dev)) {
- ret = arizona_micsupp_of_get_pdata(arizona, &config,
+ if (!dev_get_platdata(micsupp->dev)) {
+ ret = arizona_micsupp_of_get_pdata(pdata, &config,
desc);
if (ret < 0)
return ret;
}
}
- if (arizona->pdata.micvdd)
- config.init_data = arizona->pdata.micvdd;
+ if (pdata->init_data)
+ config.init_data = pdata->init_data;
else
config.init_data = &micsupp->init_data;
- /* Default to regulated mode until the API supports bypass */
- regmap_update_bits(arizona->regmap, ARIZONA_MIC_CHARGE_PUMP_1,
+ /* Default to regulated mode */
+ regmap_update_bits(micsupp->regmap, micsupp->enable_reg,
ARIZONA_CPMIC_BYPASS, 0);
micsupp->regulator = devm_regulator_register(&pdev->dev,
@@ -292,7 +276,7 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
if (IS_ERR(micsupp->regulator)) {
ret = PTR_ERR(micsupp->regulator);
- dev_err(arizona->dev, "Failed to register mic supply: %d\n",
+ dev_err(micsupp->dev, "Failed to register mic supply: %d\n",
ret);
return ret;
}
@@ -302,6 +286,41 @@ static int arizona_micsupp_probe(struct platform_device *pdev)
return 0;
}
+static int arizona_micsupp_probe(struct platform_device *pdev)
+{
+ struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+ const struct regulator_desc *desc;
+ struct arizona_micsupp *micsupp;
+
+ micsupp = devm_kzalloc(&pdev->dev, sizeof(*micsupp), GFP_KERNEL);
+ if (!micsupp)
+ return -ENOMEM;
+
+ micsupp->regmap = arizona->regmap;
+ micsupp->dapm = &arizona->dapm;
+ micsupp->dev = arizona->dev;
+
+ /*
+ * Since the chip usually supplies itself we provide some
+ * default init_data for it. This will be overridden with
+ * platform data if provided.
+ */
+ switch (arizona->type) {
+ case WM5110:
+ case WM8280:
+ desc = &arizona_micsupp_ext;
+ micsupp->init_data = arizona_micsupp_ext_default;
+ break;
+ default:
+ desc = &arizona_micsupp;
+ micsupp->init_data = arizona_micsupp_default;
+ break;
+ }
+
+ return arizona_micsupp_common_init(pdev, micsupp, desc,
+ &arizona->pdata.micvdd);
+}
+
static struct platform_driver arizona_micsupp_driver = {
.probe = arizona_micsupp_probe,
.driver = {
diff --git a/drivers/regulator/bd9571mwv-regulator.c b/drivers/regulator/bd9571mwv-regulator.c
new file mode 100644
index 000000000000..8ba206fec31e
--- /dev/null
+++ b/drivers/regulator/bd9571mwv-regulator.c
@@ -0,0 +1,178 @@
+/*
+ * ROHM BD9571MWV-M regulator driver
+ *
+ * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether expressed or implied; without even the implied warranty
+ * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License version 2 for more details.
+ *
+ * Based on the TPS65086 driver
+ *
+ * NOTE: VD09 is missing
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+
+#include <linux/mfd/bd9571mwv.h>
+
+enum bd9571mwv_regulators { VD09, VD18, VD25, VD33, DVFS };
+
+#define BD9571MWV_REG(_name, _of, _id, _ops, _vr, _vm, _nv, _min, _step, _lmin)\
+ { \
+ .name = _name, \
+ .of_match = of_match_ptr(_of), \
+ .regulators_node = "regulators", \
+ .id = _id, \
+ .ops = &_ops, \
+ .n_voltages = _nv, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ .vsel_reg = _vr, \
+ .vsel_mask = _vm, \
+ .min_uV = _min, \
+ .uV_step = _step, \
+ .linear_min_sel = _lmin, \
+ }
+
+int bd9571mwv_avs_get_moni_state(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_SET_MONI, &val);
+ if (ret != 0)
+ return ret;
+
+ return val & BD9571MWV_AVS_SET_MONI_MASK;
+}
+
+int bd9571mwv_avs_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write_bits(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret),
+ rdev->desc->vsel_mask, sel);
+}
+
+int bd9571mwv_avs_get_voltage_sel_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = bd9571mwv_avs_get_moni_state(rdev);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(rdev->regmap, BD9571MWV_AVS_VD09_VID(ret), &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->vsel_mask;
+ val >>= ffs(rdev->desc->vsel_mask) - 1;
+
+ return val;
+}
+
+int bd9571mwv_reg_set_voltage_sel_regmap(struct regulator_dev *rdev,
+ unsigned int sel)
+{
+ return regmap_write_bits(rdev->regmap, BD9571MWV_DVFS_SETVID,
+ rdev->desc->vsel_mask, sel);
+}
+
+/* Operations permitted on AVS voltage regulator */
+static struct regulator_ops avs_ops = {
+ .set_voltage_sel = bd9571mwv_avs_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = bd9571mwv_avs_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage regulators */
+static struct regulator_ops reg_ops = {
+ .set_voltage_sel = bd9571mwv_reg_set_voltage_sel_regmap,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+/* Operations permitted on voltage monitors */
+static struct regulator_ops vid_ops = {
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+};
+
+static struct regulator_desc regulators[] = {
+ BD9571MWV_REG("VD09", "vd09", VD09, avs_ops, 0, 0x7f,
+ 0x80, 600000, 10000, 0x3c),
+ BD9571MWV_REG("VD18", "vd18", VD18, vid_ops, BD9571MWV_VD18_VID, 0xf,
+ 16, 1625000, 25000, 0),
+ BD9571MWV_REG("VD25", "vd25", VD25, vid_ops, BD9571MWV_VD25_VID, 0xf,
+ 16, 2150000, 50000, 0),
+ BD9571MWV_REG("VD33", "vd33", VD33, vid_ops, BD9571MWV_VD33_VID, 0xf,
+ 11, 2800000, 100000, 0),
+ BD9571MWV_REG("DVFS", "dvfs", DVFS, reg_ops,
+ BD9571MWV_DVFS_MONIVDAC, 0x7f,
+ 0x80, 600000, 10000, 0x3c),
+};
+
+static int bd9571mwv_regulator_probe(struct platform_device *pdev)
+{
+ struct bd9571mwv *bd = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ int i;
+
+ platform_set_drvdata(pdev, bd);
+
+ config.dev = &pdev->dev;
+ config.dev->of_node = bd->dev->of_node;
+ config.driver_data = bd;
+ config.regmap = bd->regmap;
+
+ for (i = 0; i < ARRAY_SIZE(regulators); i++) {
+ rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+ &config);
+ if (IS_ERR(rdev)) {
+ dev_err(bd->dev, "failed to register %s regulator\n",
+ pdev->name);
+ return PTR_ERR(rdev);
+ }
+ }
+
+ return 0;
+}
+
+static const struct platform_device_id bd9571mwv_regulator_id_table[] = {
+ { "bd9571mwv-regulator", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, bd9571mwv_regulator_id_table);
+
+static struct platform_driver bd9571mwv_regulator_driver = {
+ .driver = {
+ .name = "bd9571mwv-regulator",
+ },
+ .probe = bd9571mwv_regulator_probe,
+ .id_table = bd9571mwv_regulator_id_table,
+};
+module_platform_driver(bd9571mwv_regulator_driver);
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut+renesas@gmail.com>");
+MODULE_DESCRIPTION("BD9571MWV Regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 53d4fc70dbd0..c0d9ae8d0860 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1326,8 +1326,8 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
regulator->dev = dev;
/* Add a link to the device sysfs entry */
- size = scnprintf(buf, REG_STR_SIZE, "%s-%s",
- dev->kobj.name, supply_name);
+ size = snprintf(buf, REG_STR_SIZE, "%s-%s",
+ dev->kobj.name, supply_name);
if (size >= REG_STR_SIZE)
goto overflow_err;
@@ -1343,7 +1343,7 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
/* non-fatal */
}
} else {
- regulator->supply_name = kstrdup(supply_name, GFP_KERNEL);
+ regulator->supply_name = kstrdup_const(supply_name, GFP_KERNEL);
if (regulator->supply_name == NULL)
goto overflow_err;
}
@@ -1451,8 +1451,6 @@ static struct regulator_dev *regulator_lookup_by_name(const char *name)
* regulator_dev_lookup - lookup a regulator device.
* @dev: device for regulator "consumer".
* @supply: Supply name or regulator ID.
- * @ret: 0 on success, -ENODEV if lookup fails permanently, -EPROBE_DEFER if
- * lookup could succeed in the future.
*
* If successful, returns a struct regulator_dev that corresponds to the name
* @supply and with the embedded struct device refcount incremented by one.
@@ -1534,14 +1532,6 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
if (IS_ERR(r)) {
ret = PTR_ERR(r);
- if (ret == -ENODEV) {
- /*
- * No supply was specified for this regulator and
- * there will never be one.
- */
- return 0;
- }
-
/* Did the lookup explicitly defer for us? */
if (ret == -EPROBE_DEFER)
return ret;
@@ -1799,7 +1789,7 @@ static void _regulator_put(struct regulator *regulator)
put_device(&rdev->dev);
mutex_unlock(&rdev->mutex);
- kfree(regulator->supply_name);
+ kfree_const(regulator->supply_name);
kfree(regulator);
module_put(rdev->owner);
@@ -2182,6 +2172,8 @@ static int _regulator_enable(struct regulator_dev *rdev)
if (ret < 0)
return ret;
+ _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE,
+ NULL);
} else if (ret < 0) {
rdev_err(rdev, "is_enabled() failed: %d\n", ret);
return ret;
@@ -2486,7 +2478,7 @@ static int _regulator_list_voltage(struct regulator *regulator,
ret = ops->list_voltage(rdev, selector);
if (lock)
mutex_unlock(&rdev->mutex);
- } else if (rdev->supply) {
+ } else if (rdev->is_switch && rdev->supply) {
ret = _regulator_list_voltage(rdev->supply, selector, lock);
} else {
return -EINVAL;
@@ -2544,7 +2536,7 @@ int regulator_count_voltages(struct regulator *regulator)
if (rdev->desc->n_voltages)
return rdev->desc->n_voltages;
- if (!rdev->supply)
+ if (!rdev->is_switch || !rdev->supply)
return -EINVAL;
return regulator_count_voltages(rdev->supply);
@@ -2773,6 +2765,8 @@ static int _regulator_set_voltage_time(struct regulator_dev *rdev,
ramp_delay = rdev->constraints->ramp_delay;
else if (rdev->desc->ramp_delay)
ramp_delay = rdev->desc->ramp_delay;
+ else if (rdev->constraints->settling_time)
+ return rdev->constraints->settling_time;
if (ramp_delay == 0) {
rdev_dbg(rdev, "ramp_delay not set\n");
@@ -2941,8 +2935,10 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
if (ret < 0)
goto out2;
- if (rdev->supply && (rdev->desc->min_dropout_uV ||
- !rdev->desc->ops->get_voltage)) {
+ if (rdev->supply &&
+ regulator_ops_is_valid(rdev->supply->rdev,
+ REGULATOR_CHANGE_VOLTAGE) &&
+ (rdev->desc->min_dropout_uV || !rdev->desc->ops->get_voltage)) {
int current_supply_uV;
int selector;
@@ -4099,6 +4095,11 @@ regulator_register(const struct regulator_desc *regulator_desc,
mutex_unlock(&regulator_list_mutex);
}
+ if (!rdev->desc->ops->get_voltage &&
+ !rdev->desc->ops->list_voltage &&
+ !rdev->desc->fixed_uV)
+ rdev->is_switch = true;
+
ret = device_register(&rdev->dev);
if (ret != 0) {
put_device(&rdev->dev);
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index 379cdacc05d8..2ae7c3ac5940 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -446,6 +446,42 @@ int regulator_set_bypass_regmap(struct regulator_dev *rdev, bool enable)
EXPORT_SYMBOL_GPL(regulator_set_bypass_regmap);
/**
+ * regulator_set_soft_start_regmap - Default set_soft_start() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_soft_start_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->soft_start_val_on;
+ if (!val)
+ val = rdev->desc->soft_start_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->soft_start_reg,
+ rdev->desc->soft_start_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_soft_start_regmap);
+
+/**
+ * regulator_set_pull_down_regmap - Default set_pull_down() using regmap
+ *
+ * @rdev: device to operate on.
+ */
+int regulator_set_pull_down_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+
+ val = rdev->desc->pull_down_val_on;
+ if (!val)
+ val = rdev->desc->pull_down_mask;
+
+ return regmap_update_bits(rdev->regmap, rdev->desc->pull_down_reg,
+ rdev->desc->pull_down_mask, val);
+}
+EXPORT_SYMBOL_GPL(regulator_set_pull_down_regmap);
+
+/**
* regulator_get_bypass_regmap - Default get_bypass() using regmap
*
* @rdev: device to operate on.
diff --git a/drivers/regulator/hi655x-regulator.c b/drivers/regulator/hi655x-regulator.c
index 065c100e9a03..36ae54b53814 100644
--- a/drivers/regulator/hi655x-regulator.c
+++ b/drivers/regulator/hi655x-regulator.c
@@ -214,7 +214,14 @@ static int hi655x_regulator_probe(struct platform_device *pdev)
return 0;
}
+static const struct platform_device_id hi655x_regulator_table[] = {
+ { .name = "hi655x-regulator" },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, hi655x_regulator_table);
+
static struct platform_driver hi655x_regulator_driver = {
+ .id_table = hi655x_regulator_table,
.driver = {
.name = "hi655x-regulator",
},
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
index 1dd575b28564..66a8ea0c8386 100644
--- a/drivers/regulator/internal.h
+++ b/drivers/regulator/internal.h
@@ -29,7 +29,7 @@ struct regulator {
int uA_load;
int min_uV;
int max_uV;
- char *supply_name;
+ const char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
struct dentry *debugfs;
diff --git a/drivers/regulator/lm363x-regulator.c b/drivers/regulator/lm363x-regulator.c
index f53e63301a20..ce5f7d9ad475 100644
--- a/drivers/regulator/lm363x-regulator.c
+++ b/drivers/regulator/lm363x-regulator.c
@@ -227,9 +227,9 @@ static int lm363x_regulator_of_get_enable_gpio(struct device_node *np, int id)
*/
switch (id) {
case LM3632_LDO_POS:
- return of_get_named_gpio(np, "ti,lcm-en1-gpio", 0);
+ return of_get_named_gpio(np, "enable-gpios", 0);
case LM3632_LDO_NEG:
- return of_get_named_gpio(np, "ti,lcm-en2-gpio", 0);
+ return of_get_named_gpio(np, "enable-gpios", 1);
default:
return -EINVAL;
}
diff --git a/drivers/regulator/ltc3589.c b/drivers/regulator/ltc3589.c
index a7a1a0313bbf..853a06ad86d6 100644
--- a/drivers/regulator/ltc3589.c
+++ b/drivers/regulator/ltc3589.c
@@ -22,6 +22,7 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/of_regulator.h>
@@ -470,7 +471,11 @@ static int ltc3589_probe(struct i2c_client *client,
return -ENOMEM;
i2c_set_clientdata(client, ltc3589);
- ltc3589->variant = id->driver_data;
+ if (client->dev.of_node)
+ ltc3589->variant = (enum ltc3589_variant)
+ of_device_get_match_data(&client->dev);
+ else
+ ltc3589->variant = id->driver_data;
ltc3589->dev = dev;
descs = ltc3589->regulator_descs;
@@ -542,9 +547,27 @@ static struct i2c_device_id ltc3589_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc3589_i2c_id);
+static const struct of_device_id ltc3589_of_match[] = {
+ {
+ .compatible = "lltc,ltc3589",
+ .data = (void *)LTC3589,
+ },
+ {
+ .compatible = "lltc,ltc3589-1",
+ .data = (void *)LTC3589_1,
+ },
+ {
+ .compatible = "lltc,ltc3589-2",
+ .data = (void *)LTC3589_2,
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3589_of_match);
+
static struct i2c_driver ltc3589_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3589_of_match),
},
.probe = ltc3589_probe,
.id_table = ltc3589_i2c_id,
diff --git a/drivers/regulator/ltc3676.c b/drivers/regulator/ltc3676.c
index 503cd90eba39..662ee05ea44d 100644
--- a/drivers/regulator/ltc3676.c
+++ b/drivers/regulator/ltc3676.c
@@ -406,9 +406,16 @@ static const struct i2c_device_id ltc3676_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, ltc3676_i2c_id);
+static const struct of_device_id ltc3676_of_match[] = {
+ { .compatible = "lltc,ltc3676" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ltc3676_of_match);
+
static struct i2c_driver ltc3676_driver = {
.driver = {
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(ltc3676_of_match),
},
.probe = ltc3676_regulator_probe,
.id_table = ltc3676_i2c_id,
diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c
index 2c1228d5796a..6779c2b53674 100644
--- a/drivers/regulator/max1586.c
+++ b/drivers/regulator/max1586.c
@@ -126,14 +126,14 @@ static int max1586_v6_set_voltage_sel(struct regulator_dev *rdev,
* The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back
* the set up value.
*/
-static struct regulator_ops max1586_v3_ops = {
+static const struct regulator_ops max1586_v3_ops = {
.get_voltage_sel = max1586_v3_get_voltage_sel,
.set_voltage_sel = max1586_v3_set_voltage_sel,
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
};
-static struct regulator_ops max1586_v6_ops = {
+static const struct regulator_ops max1586_v6_ops = {
.get_voltage_sel = max1586_v6_get_voltage_sel,
.set_voltage_sel = max1586_v6_set_voltage_sel,
.list_voltage = regulator_list_voltage_table,
diff --git a/drivers/regulator/max77693-regulator.c b/drivers/regulator/max77693-regulator.c
index 3fce67982682..e7000e777292 100644
--- a/drivers/regulator/max77693-regulator.c
+++ b/drivers/regulator/max77693-regulator.c
@@ -150,7 +150,7 @@ static const struct regulator_ops max77693_safeout_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
-static struct regulator_ops max77693_charger_ops = {
+static const struct regulator_ops max77693_charger_ops = {
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
diff --git a/drivers/regulator/max8660.c b/drivers/regulator/max8660.c
index b87f62dd484e..a6183425f27d 100644
--- a/drivers/regulator/max8660.c
+++ b/drivers/regulator/max8660.c
@@ -194,7 +194,7 @@ static int max8660_ldo5_set_voltage_sel(struct regulator_dev *rdev,
return max8660_write(max8660, MAX8660_VCC1, 0xff, 0xc0);
}
-static struct regulator_ops max8660_ldo5_ops = {
+static const struct regulator_ops max8660_ldo5_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.set_voltage_sel = max8660_ldo5_set_voltage_sel,
@@ -252,7 +252,7 @@ static int max8660_ldo67_set_voltage_sel(struct regulator_dev *rdev,
selector << 4);
}
-static struct regulator_ops max8660_ldo67_ops = {
+static const struct regulator_ops max8660_ldo67_ops = {
.is_enabled = max8660_ldo67_is_enabled,
.enable = max8660_ldo67_enable,
.disable = max8660_ldo67_disable,
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 4f613ec99500..09d677d5d3f0 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -86,6 +86,10 @@ static void of_get_regulation_constraints(struct device_node *np,
constraints->ramp_disable = true;
}
+ ret = of_property_read_u32(np, "regulator-settling-time-us", &pval);
+ if (!ret)
+ constraints->settling_time = pval;
+
ret = of_property_read_u32(np, "regulator-enable-ramp-delay", &pval);
if (!ret)
constraints->enable_time = pval;
diff --git a/drivers/regulator/pfuze100-regulator.c b/drivers/regulator/pfuze100-regulator.c
index e193bbbb8ffc..63922a2167e5 100644
--- a/drivers/regulator/pfuze100-regulator.c
+++ b/drivers/regulator/pfuze100-regulator.c
@@ -40,6 +40,7 @@
#define PFUZE100_REVID 0x3
#define PFUZE100_FABID 0x4
+#define PFUZE100_COINVOL 0x1a
#define PFUZE100_SW1ABVOL 0x20
#define PFUZE100_SW1CVOL 0x2e
#define PFUZE100_SW2VOL 0x35
@@ -81,6 +82,10 @@ static const int pfuze100_vsnvs[] = {
1000000, 1100000, 1200000, 1300000, 1500000, 1800000, 3000000,
};
+static const int pfuze100_coin[] = {
+ 2500000, 2700000, 2800000, 2900000, 3000000, 3100000, 3200000, 3300000,
+};
+
static const int pfuze3000_sw2lo[] = {
1500000, 1550000, 1600000, 1650000, 1700000, 1750000, 1800000, 1850000,
};
@@ -230,6 +235,23 @@ static const struct regulator_ops pfuze100_swb_regulator_ops = {
.stby_mask = 0x20, \
}
+#define PFUZE100_COIN_REG(_chip, _name, base, mask, voltages) \
+ [_chip ## _ ## _name] = { \
+ .desc = { \
+ .name = #_name, \
+ .n_voltages = ARRAY_SIZE(voltages), \
+ .ops = &pfuze100_swb_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = _chip ## _ ## _name, \
+ .owner = THIS_MODULE, \
+ .volt_table = voltages, \
+ .vsel_reg = (base), \
+ .vsel_mask = (mask), \
+ .enable_reg = (base), \
+ .enable_mask = 0x8, \
+ }, \
+ }
+
#define PFUZE3000_VCC_REG(_chip, _name, base, min, max, step) { \
.desc = { \
.name = #_name, \
@@ -317,6 +339,7 @@ static struct pfuze_regulator pfuze200_regulators[] = {
PFUZE100_VGEN_REG(PFUZE200, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
PFUZE100_VGEN_REG(PFUZE200, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
PFUZE100_VGEN_REG(PFUZE200, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+ PFUZE100_COIN_REG(PFUZE200, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin),
};
static struct pfuze_regulator pfuze3000_regulators[] = {
@@ -371,6 +394,7 @@ static struct of_regulator_match pfuze200_matches[] = {
{ .name = "vgen4", },
{ .name = "vgen5", },
{ .name = "vgen6", },
+ { .name = "coin", },
};
/* PFUZE3000 */
diff --git a/drivers/regulator/rk808-regulator.c b/drivers/regulator/rk808-regulator.c
index fb44d5215e30..a16d81420612 100644
--- a/drivers/regulator/rk808-regulator.c
+++ b/drivers/regulator/rk808-regulator.c
@@ -519,7 +519,7 @@ static const struct regulator_desc rk818_reg[] = {
RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(0), 400),
RK8XX_DESC(RK818_ID_LDO2, "LDO_REG2", "vcc6", 1800, 3400, 100,
- RK818_LDO1_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
+ RK818_LDO2_ON_VSEL_REG, RK818_LDO_VSEL_MASK, RK818_LDO_EN_REG,
BIT(1), 400),
{
.name = "LDO_REG3",
diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c
index 38ee97a085f9..48f0ca90743c 100644
--- a/drivers/regulator/s2mpa01.c
+++ b/drivers/regulator/s2mpa01.c
@@ -213,7 +213,7 @@ ramp_disable:
1 << enable_shift, 0);
}
-static struct regulator_ops s2mpa01_ldo_ops = {
+static const struct regulator_ops s2mpa01_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -224,7 +224,7 @@ static struct regulator_ops s2mpa01_ldo_ops = {
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops s2mpa01_buck_ops = {
+static const struct regulator_ops s2mpa01_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -359,11 +359,11 @@ static int s2mpa01_pmic_probe(struct platform_device *pdev)
if (iodev->dev->of_node) {
reg_np = of_get_child_by_name(iodev->dev->of_node,
"regulators");
- if (!reg_np) {
- dev_err(&pdev->dev,
- "could not find regulators sub-node\n");
- return -EINVAL;
- }
+ if (!reg_np) {
+ dev_err(&pdev->dev,
+ "could not find regulators sub-node\n");
+ return -EINVAL;
+ }
of_regulator_match(&pdev->dev, reg_np, rdata,
S2MPA01_REGULATOR_MAX);
diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c
index d838e77dd947..7726b874e539 100644
--- a/drivers/regulator/s2mps11.c
+++ b/drivers/regulator/s2mps11.c
@@ -238,7 +238,7 @@ ramp_disable:
1 << enable_shift, 0);
}
-static struct regulator_ops s2mps11_ldo_ops = {
+static const struct regulator_ops s2mps11_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -249,7 +249,7 @@ static struct regulator_ops s2mps11_ldo_ops = {
.set_voltage_time_sel = regulator_set_voltage_time_sel,
};
-static struct regulator_ops s2mps11_buck_ops = {
+static const struct regulator_ops s2mps11_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -392,7 +392,7 @@ static const struct regulator_desc s2mps11_regulators[] = {
regulator_desc_s2mps11_buck67810(10, MIN_750_MV, STEP_12_5_MV),
};
-static struct regulator_ops s2mps14_reg_ops;
+static const struct regulator_ops s2mps14_reg_ops;
#define regulator_desc_s2mps13_ldo(num, min, step, min_sel) { \
.name = "LDO"#num, \
@@ -599,7 +599,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
rdev->desc->enable_mask, state);
}
-static struct regulator_ops s2mps14_reg_ops = {
+static const struct regulator_ops s2mps14_reg_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -681,7 +681,7 @@ static const struct regulator_desc s2mps14_regulators[] = {
S2MPS14_BUCK1235_START_SEL),
};
-static struct regulator_ops s2mps15_reg_ldo_ops = {
+static const struct regulator_ops s2mps15_reg_ldo_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.is_enabled = regulator_is_enabled_regmap,
@@ -691,7 +691,7 @@ static struct regulator_ops s2mps15_reg_ldo_ops = {
.set_voltage_sel = regulator_set_voltage_sel_regmap,
};
-static struct regulator_ops s2mps15_reg_buck_ops = {
+static const struct regulator_ops s2mps15_reg_buck_ops = {
.list_voltage = regulator_list_voltage_linear_range,
.map_voltage = regulator_map_voltage_linear_range,
.is_enabled = regulator_is_enabled_regmap,
@@ -886,7 +886,7 @@ static int s2mpu02_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay)
ramp_val << ramp_shift);
}
-static struct regulator_ops s2mpu02_ldo_ops = {
+static const struct regulator_ops s2mpu02_ldo_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
@@ -898,7 +898,7 @@ static struct regulator_ops s2mpu02_ldo_ops = {
.set_suspend_disable = s2mps14_regulator_set_suspend_disable,
};
-static struct regulator_ops s2mpu02_buck_ops = {
+static const struct regulator_ops s2mpu02_buck_ops = {
.list_voltage = regulator_list_voltage_linear,
.map_voltage = regulator_map_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c
index 27343e1c43ef..383cd7533721 100644
--- a/drivers/regulator/s5m8767.c
+++ b/drivers/regulator/s5m8767.c
@@ -357,7 +357,7 @@ static int s5m8767_set_voltage_time_sel(struct regulator_dev *rdev,
return 0;
}
-static struct regulator_ops s5m8767_ops = {
+static const struct regulator_ops s5m8767_ops = {
.list_voltage = regulator_list_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
@@ -367,7 +367,7 @@ static struct regulator_ops s5m8767_ops = {
.set_voltage_time_sel = s5m8767_set_voltage_time_sel,
};
-static struct regulator_ops s5m8767_buck78_ops = {
+static const struct regulator_ops s5m8767_buck78_ops = {
.list_voltage = regulator_list_voltage_linear,
.is_enabled = regulator_is_enabled_regmap,
.enable = regulator_enable_regmap,
diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c
index d2c3d7cc35f5..5ca6d2130593 100644
--- a/drivers/regulator/tps65023-regulator.c
+++ b/drivers/regulator/tps65023-regulator.c
@@ -311,8 +311,7 @@ static int tps_65023_probe(struct i2c_client *client,
/* Enable setting output voltage by I2C */
regmap_update_bits(tps->regmap, TPS65023_REG_CON_CTRL2,
- TPS65023_REG_CTRL2_CORE_ADJ,
- TPS65023_REG_CTRL2_CORE_ADJ);
+ TPS65023_REG_CTRL2_CORE_ADJ, 0);
return 0;
}
diff --git a/drivers/regulator/tps65132-regulator.c b/drivers/regulator/tps65132-regulator.c
new file mode 100644
index 000000000000..73978dd440f7
--- /dev/null
+++ b/drivers/regulator/tps65132-regulator.c
@@ -0,0 +1,284 @@
+/*
+ * TI TPS65132 Regulator driver
+ *
+ * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
+ *
+ * Author: Venkat Reddy Talla <vreddytalla@nvidia.com>
+ * Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * 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 "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#define TPS65132_REG_VPOS 0x00
+#define TPS65132_REG_VNEG 0x01
+#define TPS65132_REG_APPS_DISP_DISN 0x03
+#define TPS65132_REG_CONTROL 0x0FF
+
+#define TPS65132_VOUT_MASK 0x1F
+#define TPS65132_VOUT_N_VOLTAGE 0x15
+#define TPS65132_VOUT_VMIN 4000000
+#define TPS65132_VOUT_VMAX 6000000
+#define TPS65132_VOUT_STEP 100000
+
+#define TPS65132_REG_APPS_DIS_VPOS BIT(0)
+#define TPS65132_REG_APPS_DIS_VNEG BIT(1)
+
+#define TPS65132_REGULATOR_ID_VPOS 0
+#define TPS65132_REGULATOR_ID_VNEG 1
+#define TPS65132_MAX_REGULATORS 2
+
+#define TPS65132_ACT_DIS_TIME_SLACK 1000
+
+struct tps65132_reg_pdata {
+ struct gpio_desc *en_gpiod;
+ struct gpio_desc *act_dis_gpiod;
+ unsigned int act_dis_time_us;
+ int ena_gpio_state;
+};
+
+struct tps65132_regulator {
+ struct device *dev;
+ struct regmap *rmap;
+ struct regulator_desc *rdesc[TPS65132_MAX_REGULATORS];
+ struct tps65132_reg_pdata reg_pdata[TPS65132_MAX_REGULATORS];
+ struct regulator_dev *rdev[TPS65132_MAX_REGULATORS];
+};
+
+static int tps65132_regulator_enable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+ int ret;
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 1);
+ rpdata->ena_gpio_state = 1;
+ }
+
+ /* Hardware automatically enable discharge bit in enable */
+ if (rdev->constraints->active_discharge ==
+ REGULATOR_ACTIVE_DISCHARGE_DISABLE) {
+ ret = regulator_set_active_discharge_regmap(rdev, false);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to disable active discharge: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_disable(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->en_gpiod, 0);
+ rpdata->ena_gpio_state = 0;
+ }
+
+ if (!IS_ERR(rpdata->act_dis_gpiod)) {
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 1);
+ usleep_range(rpdata->act_dis_time_us, rpdata->act_dis_time_us +
+ TPS65132_ACT_DIS_TIME_SLACK);
+ gpiod_set_value_cansleep(rpdata->act_dis_gpiod, 0);
+ }
+
+ return 0;
+}
+
+static int tps65132_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct tps65132_regulator *tps = rdev_get_drvdata(rdev);
+ int id = rdev_get_id(rdev);
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[id];
+
+ if (!IS_ERR(rpdata->en_gpiod))
+ return rpdata->ena_gpio_state;
+
+ return 1;
+}
+
+static struct regulator_ops tps65132_regulator_ops = {
+ .enable = tps65132_regulator_enable,
+ .disable = tps65132_regulator_disable,
+ .is_enabled = tps65132_regulator_is_enabled,
+ .list_voltage = regulator_list_voltage_linear,
+ .map_voltage = regulator_map_voltage_linear,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .set_active_discharge = regulator_set_active_discharge_regmap,
+};
+
+static int tps65132_of_parse_cb(struct device_node *np,
+ const struct regulator_desc *desc,
+ struct regulator_config *config)
+{
+ struct tps65132_regulator *tps = config->driver_data;
+ struct tps65132_reg_pdata *rpdata = &tps->reg_pdata[desc->id];
+ int ret;
+
+ rpdata->en_gpiod = devm_fwnode_get_index_gpiod_from_child(tps->dev,
+ "enable", 0, &np->fwnode, 0, "enable");
+ if (IS_ERR(rpdata->en_gpiod)) {
+ ret = PTR_ERR(rpdata->en_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+ return 0;
+ }
+
+ rpdata->act_dis_gpiod = devm_fwnode_get_index_gpiod_from_child(
+ tps->dev, "active-discharge", 0,
+ &np->fwnode, 0, "active-discharge");
+ if (IS_ERR(rpdata->act_dis_gpiod)) {
+ ret = PTR_ERR(rpdata->act_dis_gpiod);
+
+ /* Ignore the error other than probe defer */
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
+ return 0;
+ }
+
+ ret = of_property_read_u32(np, "ti,active-discharge-time-us",
+ &rpdata->act_dis_time_us);
+ if (ret < 0) {
+ dev_err(tps->dev, "Failed to read active discharge time:%d\n",
+ ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+#define TPS65132_REGULATOR_DESC(_id, _name) \
+ [TPS65132_REGULATOR_ID_##_id] = { \
+ .name = "tps65132-"#_name, \
+ .supply_name = "vin", \
+ .id = TPS65132_REGULATOR_ID_##_id, \
+ .of_match = of_match_ptr(#_name), \
+ .of_parse_cb = tps65132_of_parse_cb, \
+ .ops = &tps65132_regulator_ops, \
+ .n_voltages = TPS65132_VOUT_N_VOLTAGE, \
+ .min_uV = TPS65132_VOUT_VMIN, \
+ .uV_step = TPS65132_VOUT_STEP, \
+ .enable_time = 500, \
+ .vsel_mask = TPS65132_VOUT_MASK, \
+ .vsel_reg = TPS65132_REG_##_id, \
+ .active_discharge_off = 0, \
+ .active_discharge_on = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_mask = TPS65132_REG_APPS_DIS_##_id, \
+ .active_discharge_reg = TPS65132_REG_APPS_DISP_DISN, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+static struct regulator_desc tps_regs_desc[TPS65132_MAX_REGULATORS] = {
+ TPS65132_REGULATOR_DESC(VPOS, outp),
+ TPS65132_REGULATOR_DESC(VNEG, outn),
+};
+
+static const struct regmap_range tps65132_no_reg_ranges[] = {
+ regmap_reg_range(TPS65132_REG_APPS_DISP_DISN + 1,
+ TPS65132_REG_CONTROL - 1),
+};
+
+static const struct regmap_access_table tps65132_no_reg_table = {
+ .no_ranges = tps65132_no_reg_ranges,
+ .n_no_ranges = ARRAY_SIZE(tps65132_no_reg_ranges),
+};
+
+static const struct regmap_config tps65132_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TPS65132_REG_CONTROL,
+ .cache_type = REGCACHE_NONE,
+ .rd_table = &tps65132_no_reg_table,
+ .wr_table = &tps65132_no_reg_table,
+};
+
+static int tps65132_probe(struct i2c_client *client,
+ const struct i2c_device_id *client_id)
+{
+ struct device *dev = &client->dev;
+ struct tps65132_regulator *tps;
+ struct regulator_config config = { };
+ int id;
+ int ret;
+
+ tps = devm_kzalloc(dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps)
+ return -ENOMEM;
+
+ tps->rmap = devm_regmap_init_i2c(client, &tps65132_regmap_config);
+ if (IS_ERR(tps->rmap)) {
+ ret = PTR_ERR(tps->rmap);
+ dev_err(dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ i2c_set_clientdata(client, tps);
+ tps->dev = dev;
+
+ for (id = 0; id < TPS65132_MAX_REGULATORS; ++id) {
+ tps->rdesc[id] = &tps_regs_desc[id];
+
+ config.regmap = tps->rmap;
+ config.dev = dev;
+ config.driver_data = tps;
+
+ tps->rdev[id] = devm_regulator_register(dev,
+ tps->rdesc[id], &config);
+ if (IS_ERR(tps->rdev[id])) {
+ ret = PTR_ERR(tps->rdev[id]);
+ dev_err(dev, "regulator %s register failed: %d\n",
+ tps->rdesc[id]->name, ret);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static const struct i2c_device_id tps65132_id[] = {
+ {.name = "tps65132",},
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, tps65132_id);
+
+static struct i2c_driver tps65132_i2c_driver = {
+ .driver = {
+ .name = "tps65132",
+ },
+ .probe = tps65132_probe,
+ .id_table = tps65132_id,
+};
+
+module_i2c_driver(tps65132_i2c_driver);
+
+MODULE_DESCRIPTION("tps65132 regulator driver");
+MODULE_AUTHOR("Venkat Reddy Talla <vreddytalla@nvidia.com>");
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/twl6030-regulator.c b/drivers/regulator/twl6030-regulator.c
index 716191046a70..56aada387887 100644
--- a/drivers/regulator/twl6030-regulator.c
+++ b/drivers/regulator/twl6030-regulator.c
@@ -456,8 +456,6 @@ static int twl6030smps_map_voltage(struct regulator_dev *rdev, int min_uV,
vsel = 60;
else if ((min_uV > 1350000) && (min_uV <= 1500000))
vsel = 59;
- else if ((min_uV > 1300000) && (min_uV <= 1350000))
- vsel = 58;
else
return -EINVAL;
break;
diff --git a/drivers/regulator/vctrl-regulator.c b/drivers/regulator/vctrl-regulator.c
new file mode 100644
index 000000000000..78de002037c7
--- /dev/null
+++ b/drivers/regulator/vctrl-regulator.c
@@ -0,0 +1,546 @@
+/*
+ * Driver for voltage controller regulators
+ *
+ * Copyright (C) 2017 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/sort.h>
+
+struct vctrl_voltage_range {
+ int min_uV;
+ int max_uV;
+};
+
+struct vctrl_voltage_ranges {
+ struct vctrl_voltage_range ctrl;
+ struct vctrl_voltage_range out;
+};
+
+struct vctrl_voltage_table {
+ int ctrl;
+ int out;
+ int ovp_min_sel;
+};
+
+struct vctrl_data {
+ struct regulator_dev *rdev;
+ struct regulator_desc desc;
+ struct regulator *ctrl_reg;
+ bool enabled;
+ unsigned int min_slew_down_rate;
+ unsigned int ovp_threshold;
+ struct vctrl_voltage_ranges vrange;
+ struct vctrl_voltage_table *vtable;
+ unsigned int sel;
+};
+
+static int vctrl_calc_ctrl_voltage(struct vctrl_data *vctrl, int out_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ return ctrl->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(out_uV - out->min_uV) *
+ (ctrl->max_uV - ctrl->min_uV),
+ out->max_uV - out->min_uV);
+}
+
+static int vctrl_calc_output_voltage(struct vctrl_data *vctrl, int ctrl_uV)
+{
+ struct vctrl_voltage_range *ctrl = &vctrl->vrange.ctrl;
+ struct vctrl_voltage_range *out = &vctrl->vrange.out;
+
+ if (ctrl_uV < 0) {
+ pr_err("vctrl: failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ if (ctrl_uV < ctrl->min_uV)
+ return out->min_uV;
+
+ if (ctrl_uV > ctrl->max_uV)
+ return out->max_uV;
+
+ return out->min_uV +
+ DIV_ROUND_CLOSEST_ULL((s64)(ctrl_uV - ctrl->min_uV) *
+ (out->max_uV - out->min_uV),
+ ctrl->max_uV - ctrl->min_uV);
+}
+
+static int vctrl_get_voltage(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+
+ return vctrl_calc_output_voltage(vctrl, ctrl_uV);
+}
+
+static int vctrl_set_voltage(struct regulator_dev *rdev,
+ int req_min_uV, int req_max_uV,
+ unsigned int *selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ int orig_ctrl_uV = regulator_get_voltage(ctrl_reg);
+ int uV = vctrl_calc_output_voltage(vctrl, orig_ctrl_uV);
+ int ret;
+
+ if (req_min_uV >= uV || !vctrl->ovp_threshold)
+ /* voltage rising or no OVP */
+ return regulator_set_voltage(
+ ctrl_reg,
+ vctrl_calc_ctrl_voltage(vctrl, req_min_uV),
+ vctrl_calc_ctrl_voltage(vctrl, req_max_uV));
+
+ while (uV > req_min_uV) {
+ int max_drop_uV = (uV * vctrl->ovp_threshold) / 100;
+ int next_uV;
+ int next_ctrl_uV;
+ int delay;
+
+ /* Make sure no infinite loop even in crazy cases */
+ if (max_drop_uV == 0)
+ max_drop_uV = 1;
+
+ next_uV = max_t(int, req_min_uV, uV - max_drop_uV);
+ next_ctrl_uV = vctrl_calc_ctrl_voltage(vctrl, next_uV);
+
+ ret = regulator_set_voltage(ctrl_reg,
+ next_ctrl_uV,
+ next_ctrl_uV);
+ if (ret)
+ goto err;
+
+ delay = DIV_ROUND_UP(uV - next_uV, vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+
+ uV = next_uV;
+ }
+
+ return 0;
+
+err:
+ /* Try to go back to original voltage */
+ regulator_set_voltage(ctrl_reg, orig_ctrl_uV, orig_ctrl_uV);
+
+ return ret;
+}
+
+static int vctrl_get_voltage_sel(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->sel;
+}
+
+static int vctrl_set_voltage_sel(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ unsigned int orig_sel = vctrl->sel;
+ int ret;
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ if (selector >= vctrl->sel || !vctrl->ovp_threshold) {
+ /* voltage rising or no OVP */
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[selector].ctrl,
+ vctrl->vtable[selector].ctrl);
+ if (!ret)
+ vctrl->sel = selector;
+
+ return ret;
+ }
+
+ while (vctrl->sel != selector) {
+ unsigned int next_sel;
+ int delay;
+
+ if (selector >= vctrl->vtable[vctrl->sel].ovp_min_sel)
+ next_sel = selector;
+ else
+ next_sel = vctrl->vtable[vctrl->sel].ovp_min_sel;
+
+ ret = regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[next_sel].ctrl,
+ vctrl->vtable[next_sel].ctrl);
+ if (ret) {
+ dev_err(&rdev->dev,
+ "failed to set control voltage to %duV\n",
+ vctrl->vtable[next_sel].ctrl);
+ goto err;
+ }
+ vctrl->sel = next_sel;
+
+ delay = DIV_ROUND_UP(vctrl->vtable[vctrl->sel].out -
+ vctrl->vtable[next_sel].out,
+ vctrl->min_slew_down_rate);
+ usleep_range(delay, delay + DIV_ROUND_UP(delay, 10));
+ }
+
+ return 0;
+
+err:
+ if (vctrl->sel != orig_sel) {
+ /* Try to go back to original voltage */
+ if (!regulator_set_voltage(ctrl_reg,
+ vctrl->vtable[orig_sel].ctrl,
+ vctrl->vtable[orig_sel].ctrl))
+ vctrl->sel = orig_sel;
+ else
+ dev_warn(&rdev->dev,
+ "failed to restore original voltage\n");
+ }
+
+ return ret;
+}
+
+static int vctrl_list_voltage(struct regulator_dev *rdev,
+ unsigned int selector)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ if (selector >= rdev->desc->n_voltages)
+ return -EINVAL;
+
+ return vctrl->vtable[selector].out;
+}
+
+static int vctrl_parse_dt(struct platform_device *pdev,
+ struct vctrl_data *vctrl)
+{
+ int ret;
+ struct device_node *np = pdev->dev.of_node;
+ u32 pval;
+ u32 vrange_ctrl[2];
+
+ vctrl->ctrl_reg = devm_regulator_get(&pdev->dev, "ctrl");
+ if (IS_ERR(vctrl->ctrl_reg))
+ return PTR_ERR(vctrl->ctrl_reg);
+
+ ret = of_property_read_u32(np, "ovp-threshold-percent", &pval);
+ if (!ret) {
+ vctrl->ovp_threshold = pval;
+ if (vctrl->ovp_threshold > 100) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent (%u) > 100\n",
+ vctrl->ovp_threshold);
+ return -EINVAL;
+ }
+ }
+
+ ret = of_property_read_u32(np, "min-slew-down-rate", &pval);
+ if (!ret) {
+ vctrl->min_slew_down_rate = pval;
+
+ /* We use the value as int and as divider; sanity check */
+ if (vctrl->min_slew_down_rate == 0) {
+ dev_err(&pdev->dev,
+ "min-slew-down-rate must not be 0\n");
+ return -EINVAL;
+ } else if (vctrl->min_slew_down_rate > INT_MAX) {
+ dev_err(&pdev->dev, "min-slew-down-rate (%u) too big\n",
+ vctrl->min_slew_down_rate);
+ return -EINVAL;
+ }
+ }
+
+ if (vctrl->ovp_threshold && !vctrl->min_slew_down_rate) {
+ dev_err(&pdev->dev,
+ "ovp-threshold-percent requires min-slew-down-rate\n");
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(np, "regulator-min-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-min-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.min_uV = pval;
+
+ ret = of_property_read_u32(np, "regulator-max-microvolt", &pval);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "failed to read regulator-max-microvolt: %d\n", ret);
+ return ret;
+ }
+ vctrl->vrange.out.max_uV = pval;
+
+ ret = of_property_read_u32_array(np, "ctrl-voltage-range", vrange_ctrl,
+ 2);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read ctrl-voltage-range: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (vrange_ctrl[0] >= vrange_ctrl[1]) {
+ dev_err(&pdev->dev, "ctrl-voltage-range is invalid: %d-%d\n",
+ vrange_ctrl[0], vrange_ctrl[1]);
+ return -EINVAL;
+ }
+
+ vctrl->vrange.ctrl.min_uV = vrange_ctrl[0];
+ vctrl->vrange.ctrl.max_uV = vrange_ctrl[1];
+
+ return 0;
+}
+
+static int vctrl_cmp_ctrl_uV(const void *a, const void *b)
+{
+ const struct vctrl_voltage_table *at = a;
+ const struct vctrl_voltage_table *bt = b;
+
+ return at->ctrl - bt->ctrl;
+}
+
+static int vctrl_init_vtable(struct platform_device *pdev)
+{
+ struct vctrl_data *vctrl = platform_get_drvdata(pdev);
+ struct regulator_desc *rdesc = &vctrl->desc;
+ struct regulator *ctrl_reg = vctrl->ctrl_reg;
+ struct vctrl_voltage_range *vrange_ctrl = &vctrl->vrange.ctrl;
+ int n_voltages;
+ int ctrl_uV;
+ int i, idx_vt;
+
+ n_voltages = regulator_count_voltages(ctrl_reg);
+
+ rdesc->n_voltages = n_voltages;
+
+ /* determine number of steps within the range of the vctrl regulator */
+ for (i = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV) {
+ rdesc->n_voltages--;
+ continue;
+ }
+ }
+
+ if (rdesc->n_voltages == 0) {
+ dev_err(&pdev->dev, "invalid configuration\n");
+ return -EINVAL;
+ }
+
+ vctrl->vtable = devm_kcalloc(&pdev->dev, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table),
+ GFP_KERNEL);
+ if (!vctrl->vtable)
+ return -ENOMEM;
+
+ /* create mapping control <=> output voltage */
+ for (i = 0, idx_vt = 0; i < n_voltages; i++) {
+ ctrl_uV = regulator_list_voltage(ctrl_reg, i);
+
+ if (ctrl_uV < vrange_ctrl->min_uV ||
+ ctrl_uV > vrange_ctrl->max_uV)
+ continue;
+
+ vctrl->vtable[idx_vt].ctrl = ctrl_uV;
+ vctrl->vtable[idx_vt].out =
+ vctrl_calc_output_voltage(vctrl, ctrl_uV);
+ idx_vt++;
+ }
+
+ /* we rely on the table to be ordered by ascending voltage */
+ sort(vctrl->vtable, rdesc->n_voltages,
+ sizeof(struct vctrl_voltage_table), vctrl_cmp_ctrl_uV,
+ NULL);
+
+ /* pre-calculate OVP-safe downward transitions */
+ for (i = rdesc->n_voltages - 1; i > 0; i--) {
+ int j;
+ int ovp_min_uV = (vctrl->vtable[i].out *
+ (100 - vctrl->ovp_threshold)) / 100;
+
+ for (j = 0; j < i; j++) {
+ if (vctrl->vtable[j].out >= ovp_min_uV) {
+ vctrl->vtable[i].ovp_min_sel = j;
+ break;
+ }
+ }
+
+ if (j == i) {
+ dev_warn(&pdev->dev, "switching down from %duV may cause OVP shutdown\n",
+ vctrl->vtable[i].out);
+ /* use next lowest voltage */
+ vctrl->vtable[i].ovp_min_sel = i - 1;
+ }
+ }
+
+ return 0;
+}
+
+static int vctrl_enable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_enable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = true;
+
+ return ret;
+}
+
+static int vctrl_disable(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+ int ret = regulator_disable(vctrl->ctrl_reg);
+
+ if (!ret)
+ vctrl->enabled = false;
+
+ return ret;
+}
+
+static int vctrl_is_enabled(struct regulator_dev *rdev)
+{
+ struct vctrl_data *vctrl = rdev_get_drvdata(rdev);
+
+ return vctrl->enabled;
+}
+
+static const struct regulator_ops vctrl_ops_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .get_voltage = vctrl_get_voltage,
+ .set_voltage = vctrl_set_voltage,
+};
+
+static const struct regulator_ops vctrl_ops_non_cont = {
+ .enable = vctrl_enable,
+ .disable = vctrl_disable,
+ .is_enabled = vctrl_is_enabled,
+ .set_voltage_sel = vctrl_set_voltage_sel,
+ .get_voltage_sel = vctrl_get_voltage_sel,
+ .list_voltage = vctrl_list_voltage,
+ .map_voltage = regulator_map_voltage_iterate,
+};
+
+static int vctrl_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct vctrl_data *vctrl;
+ const struct regulator_init_data *init_data;
+ struct regulator_desc *rdesc;
+ struct regulator_config cfg = { };
+ struct vctrl_voltage_range *vrange_ctrl;
+ int ctrl_uV;
+ int ret;
+
+ vctrl = devm_kzalloc(&pdev->dev, sizeof(struct vctrl_data),
+ GFP_KERNEL);
+ if (!vctrl)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, vctrl);
+
+ ret = vctrl_parse_dt(pdev, vctrl);
+ if (ret)
+ return ret;
+
+ vrange_ctrl = &vctrl->vrange.ctrl;
+
+ rdesc = &vctrl->desc;
+ rdesc->name = "vctrl";
+ rdesc->type = REGULATOR_VOLTAGE;
+ rdesc->owner = THIS_MODULE;
+
+ if ((regulator_get_linear_step(vctrl->ctrl_reg) == 1) ||
+ (regulator_count_voltages(vctrl->ctrl_reg) == -EINVAL)) {
+ rdesc->continuous_voltage_range = true;
+ rdesc->ops = &vctrl_ops_cont;
+ } else {
+ rdesc->ops = &vctrl_ops_non_cont;
+ }
+
+ init_data = of_get_regulator_init_data(&pdev->dev, np, rdesc);
+ if (!init_data)
+ return -ENOMEM;
+
+ cfg.of_node = np;
+ cfg.dev = &pdev->dev;
+ cfg.driver_data = vctrl;
+ cfg.init_data = init_data;
+
+ if (!rdesc->continuous_voltage_range) {
+ ret = vctrl_init_vtable(pdev);
+ if (ret)
+ return ret;
+
+ ctrl_uV = regulator_get_voltage(vctrl->ctrl_reg);
+ if (ctrl_uV < 0) {
+ dev_err(&pdev->dev, "failed to get control voltage\n");
+ return ctrl_uV;
+ }
+
+ /* determine current voltage selector from control voltage */
+ if (ctrl_uV < vrange_ctrl->min_uV) {
+ vctrl->sel = 0;
+ } else if (ctrl_uV > vrange_ctrl->max_uV) {
+ vctrl->sel = rdesc->n_voltages - 1;
+ } else {
+ int i;
+
+ for (i = 0; i < rdesc->n_voltages; i++) {
+ if (ctrl_uV == vctrl->vtable[i].ctrl) {
+ vctrl->sel = i;
+ break;
+ }
+ }
+ }
+ }
+
+ vctrl->rdev = devm_regulator_register(&pdev->dev, rdesc, &cfg);
+ if (IS_ERR(vctrl->rdev)) {
+ ret = PTR_ERR(vctrl->rdev);
+ dev_err(&pdev->dev, "failed to register regulator: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id vctrl_of_match[] = {
+ { .compatible = "vctrl-regulator", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vctrl_of_match);
+
+static struct platform_driver vctrl_driver = {
+ .probe = vctrl_probe,
+ .driver = {
+ .name = "vctrl-regulator",
+ .of_match_table = of_match_ptr(vctrl_of_match),
+ },
+};
+
+module_platform_driver(vctrl_driver);
+
+MODULE_DESCRIPTION("Voltage Controlled Regulator Driver");
+MODULE_AUTHOR("Matthias Kaehlcke <mka@chromium.org>");
+MODULE_LICENSE("GPL v2");