diff options
Diffstat (limited to 'drivers/regulator')
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, ®); + 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, ®ulators[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(®ulator_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"); |