From 0c57067430a2b729bc08c92b17eb4f29d9bbfaae Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sat, 6 Oct 2012 20:47:46 +0530 Subject: regulator: tps51632: Add tps51632 regulator driver The TPS51632 is a driverless step down controller with serial control. Advanced features such as D-Cap+ architecture with overlapping pulse support and OSR overshoot reduction provide fast transient response, lowest output capacitance and high efficiency. The TPS51632 supports both I2C and DVFS interfaces (through PWM) for dynamic control of the output voltage and current monitor telemetry. Add regulator driver for TPS51632. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 11 ++ drivers/regulator/Makefile | 1 + drivers/regulator/tps51632-regulator.c | 332 +++++++++++++++++++++++++++++++++ 3 files changed, 344 insertions(+) create mode 100644 drivers/regulator/tps51632-regulator.c (limited to 'drivers/regulator') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67d47b59a66d..aa9e8a18262d 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -335,6 +335,17 @@ config REGULATOR_PALMAS on the muxing. This is handled automatically in the driver by reading the mux info from OTP. +config REGULATOR_TPS51632 + tristate "TI TPS51632 Power Regulator" + depends on I2C + select REGMAP_I2C + help + This driver supports TPS51632 voltage regulator chip. + The TPS52632 is 3-2-1 Phase D-Cap+ Step Down Driverless Controller + with Serial VID control and DVFS. + The voltage output can be configure through I2C interface or PWM + interface. + config REGULATOR_TPS6105X tristate "TI TPS6105X Power regulators" depends on TPS6105X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e431eed8a878..ec1aec460bf6 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -41,6 +41,7 @@ obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o obj-$(CONFIG_REGULATOR_MC13XXX_CORE) += mc13xxx-regulator-core.o obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o +obj-$(CONFIG_REGULATOR_TPS51632) += tps51632-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_RC5T583) += rc5t583-regulator.o diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c new file mode 100644 index 000000000000..34603640d6d9 --- /dev/null +++ b/drivers/regulator/tps51632-regulator.c @@ -0,0 +1,332 @@ +/* + * tps51632-regulator.c -- TI TPS51632 + * + * Regulator driver for TPS51632 3-2-1 Phase D-Cap Step Down Driverless + * Controller with serial VID control and DVFS. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * 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 version 2. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ +#define TPS51632_VOLTAGE_SELECT_REG 0x0 +#define TPS51632_VOLTAGE_BASE_REG 0x1 +#define TPS51632_OFFSET_REG 0x2 +#define TPS51632_IMON_REG 0x3 +#define TPS51632_VMAX_REG 0x4 +#define TPS51632_DVFS_CONTROL_REG 0x5 +#define TPS51632_POWER_STATE_REG 0x6 +#define TPS51632_SLEW_REGS 0x7 +#define TPS51632_FAULT_REG 0x14 + +#define TPS51632_MAX_REG 0x15 + +#define TPS51632_VOUT_MASK 0x7F +#define TPS51632_VOUT_OFFSET_MASK 0x1F +#define TPS51632_VMAX_MASK 0x7F +#define TPS51632_VMAX_LOCK 0x80 + +/* TPS51632_DVFS_CONTROL_REG */ +#define TPS51632_DVFS_PWMEN 0x1 +#define TPS51632_DVFS_STEP_20 0x2 +#define TPS51632_DVFS_VMAX_PG 0x4 +#define TPS51632_DVFS_PWMRST 0x8 +#define TPS51632_DVFS_OCA_EN 0x10 +#define TPS51632_DVFS_FCCM 0x20 + +/* TPS51632_POWER_STATE_REG */ +#define TPS51632_POWER_STATE_MASK 0x03 +#define TPS51632_POWER_STATE_MULTI_PHASE_CCM 0x0 +#define TPS51632_POWER_STATE_SINGLE_PHASE_CCM 0x1 +#define TPS51632_POWER_STATE_SINGLE_PHASE_DCM 0x2 + +#define TPS51632_MIN_VOLATGE 500000 +#define TPS51632_MAX_VOLATGE 1520000 +#define TPS51632_VOLATGE_STEP_10mV 10000 +#define TPS51632_VOLATGE_STEP_20mV 20000 +#define TPS51632_MAX_VSEL 0x7F +#define TPS51632_MIN_VSEL 0x19 +#define TPS51632_DEFAULT_RAMP_DELAY 6000 +#define TPS51632_VOLT_VSEL(uV) \ + (DIV_ROUND_UP(uV - TPS51632_MIN_VOLATGE, \ + TPS51632_VOLATGE_STEP_10mV) + \ + TPS51632_MIN_VSEL) + +/* TPS51632 chip information */ +struct tps51632_chip { + struct device *dev; + struct regulator_desc desc; + struct regulator_dev *rdev; + struct regmap *regmap; + bool enable_pwm_dvfs; +}; + +static int tps51632_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps51632_chip *tps = rdev_get_drvdata(rdev); + unsigned int data; + int ret; + unsigned int reg = TPS51632_VOLTAGE_SELECT_REG; + int vsel; + + if (tps->enable_pwm_dvfs) + reg = TPS51632_VOLTAGE_BASE_REG; + + ret = regmap_read(tps->regmap, reg, &data); + if (ret < 0) { + dev_err(tps->dev, "reg read failed, err %d\n", ret); + return ret; + } + + vsel = data & TPS51632_VOUT_MASK; + + if (vsel < TPS51632_MIN_VSEL) + return 0; + else + return vsel - TPS51632_MIN_VSEL; +} + +static int tps51632_dcdc_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct tps51632_chip *tps = rdev_get_drvdata(rdev); + int vsel; + int ret; + unsigned int reg = TPS51632_VOLTAGE_SELECT_REG; + + if (tps->enable_pwm_dvfs) + reg = TPS51632_VOLTAGE_BASE_REG; + + vsel = selector + TPS51632_MIN_VSEL; + if (vsel > TPS51632_MAX_VSEL) + return -EINVAL; + + ret = regmap_write(tps->regmap, TPS51632_VOLTAGE_SELECT_REG, vsel); + if (ret < 0) + dev_err(tps->dev, "reg write failed, err %d\n", ret); + return ret; +} + +static int tps51632_dcdc_set_ramp_delay(struct regulator_dev *rdev, + int ramp_delay) +{ + struct tps51632_chip *tps = rdev_get_drvdata(rdev); + int bit = ramp_delay/6000; + int ret; + + if (bit) + bit--; + ret = regmap_write(tps->regmap, TPS51632_SLEW_REGS, BIT(bit)); + if (ret < 0) + dev_err(tps->dev, "SLEW reg write failed, err %d\n", ret); + return ret; +} + +static struct regulator_ops tps51632_dcdc_ops = { + .get_voltage_sel = tps51632_dcdc_get_voltage_sel, + .set_voltage_sel = tps51632_dcdc_set_voltage_sel, + .list_voltage = regulator_list_voltage_linear, + .set_voltage_time_sel = regulator_set_voltage_time_sel, + .set_ramp_delay = tps51632_dcdc_set_ramp_delay, +}; + +static int __devinit tps51632_init_dcdc(struct tps51632_chip *tps, + struct tps51632_regulator_platform_data *pdata) +{ + int ret; + uint8_t control = 0; + int vsel; + + if (!pdata->enable_pwm_dvfs) + goto skip_pwm_config; + + control |= TPS51632_DVFS_PWMEN; + tps->enable_pwm_dvfs = pdata->enable_pwm_dvfs; + vsel = TPS51632_VOLT_VSEL(pdata->base_voltage_uV); + ret = regmap_write(tps->regmap, TPS51632_VOLTAGE_BASE_REG, vsel); + if (ret < 0) { + dev_err(tps->dev, "BASE reg write failed, err %d\n", ret); + return ret; + } + + if (pdata->dvfs_step_20mV) + control |= TPS51632_DVFS_STEP_20; + + if (pdata->max_voltage_uV) { + unsigned int vmax; + /** + * TPS51632 hw behavior: VMAX register can be write only + * once as it get locked after first write. The lock get + * reset only when device is power-reset. + * Write register only when lock bit is not enabled. + */ + ret = regmap_read(tps->regmap, TPS51632_VMAX_REG, &vmax); + if (ret < 0) { + dev_err(tps->dev, "VMAX read failed, err %d\n", ret); + return ret; + } + if (!(vmax & TPS51632_VMAX_LOCK)) { + vsel = TPS51632_VOLT_VSEL(pdata->max_voltage_uV); + ret = regmap_write(tps->regmap, TPS51632_VMAX_REG, + vsel); + if (ret < 0) { + dev_err(tps->dev, + "VMAX write failed, err %d\n", ret); + return ret; + } + } + } + +skip_pwm_config: + ret = regmap_write(tps->regmap, TPS51632_DVFS_CONTROL_REG, control); + if (ret < 0) + dev_err(tps->dev, "DVFS reg write failed, err %d\n", ret); + return ret; +} + +static bool rd_wr_reg(struct device *dev, unsigned int reg) +{ + if ((reg >= 0x8) && (reg <= 0x10)) + return false; + return true; +} + +static const struct regmap_config tps51632_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = rd_wr_reg, + .readable_reg = rd_wr_reg, + .max_register = TPS51632_MAX_REG - 1, + .cache_type = REGCACHE_RBTREE, +}; + +static int __devinit tps51632_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tps51632_regulator_platform_data *pdata; + struct regulator_dev *rdev; + struct tps51632_chip *tps; + int ret; + struct regulator_config config = { }; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "No Platform data\n"); + return -EINVAL; + } + + tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); + if (!tps) { + dev_err(&client->dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + tps->dev = &client->dev; + tps->desc.name = id->name; + tps->desc.id = 0; + tps->desc.ramp_delay = TPS51632_DEFAULT_RAMP_DELAY; + tps->desc.min_uV = TPS51632_MIN_VOLATGE; + tps->desc.uV_step = TPS51632_VOLATGE_STEP_10mV; + tps->desc.n_voltages = (TPS51632_MAX_VSEL - TPS51632_MIN_VSEL) + 1; + tps->desc.ops = &tps51632_dcdc_ops; + tps->desc.type = REGULATOR_VOLTAGE; + tps->desc.owner = THIS_MODULE; + + tps->regmap = devm_regmap_init_i2c(client, &tps51632_regmap_config); + if (IS_ERR(tps->regmap)) { + ret = PTR_ERR(tps->regmap); + dev_err(&client->dev, "regmap init failed, err %d\n", ret); + return ret; + } + i2c_set_clientdata(client, tps); + + ret = tps51632_init_dcdc(tps, pdata); + if (ret < 0) { + dev_err(tps->dev, "Init failed, err = %d\n", ret); + return ret; + } + + /* Register the regulators */ + config.dev = &client->dev; + config.init_data = pdata->reg_init_data; + config.driver_data = tps; + config.regmap = tps->regmap; + config.of_node = client->dev.of_node; + + rdev = regulator_register(&tps->desc, &config); + if (IS_ERR(rdev)) { + dev_err(tps->dev, "regulator register failed\n"); + return PTR_ERR(rdev); + } + + tps->rdev = rdev; + return 0; +} + +static int __devexit tps51632_remove(struct i2c_client *client) +{ + struct tps51632_chip *tps = i2c_get_clientdata(client); + + regulator_unregister(tps->rdev); + return 0; +} + +static const struct i2c_device_id tps51632_id[] = { + {.name = "tps51632",}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tps51632_id); + +static struct i2c_driver tps51632_i2c_driver = { + .driver = { + .name = "tps51632", + .owner = THIS_MODULE, + }, + .probe = tps51632_probe, + .remove = __devexit_p(tps51632_remove), + .id_table = tps51632_id, +}; + +static int __init tps51632_init(void) +{ + return i2c_add_driver(&tps51632_i2c_driver); +} +subsys_initcall(tps51632_init); + +static void __exit tps51632_cleanup(void) +{ + i2c_del_driver(&tps51632_i2c_driver); +} +module_exit(tps51632_cleanup); + +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_DESCRIPTION("TPS51632 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bd7a2b600ace90c8819495b639a744c8f5c68feb Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 18:56:53 +0100 Subject: regulator: core: Support for continuous voltage range Some regulators can set any voltage within the constraints range, not being limited to specified operating points. This patch makes it possible to describe such regulator and makes the regulator_is_supported_voltage() function behave correctly. Signed-off-by: Pawel Moll Signed-off-by: Mark Brown --- drivers/regulator/core.c | 5 +++++ include/linux/regulator/driver.h | 3 +++ 2 files changed, 8 insertions(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 5c4829cba6a6..f7c74db7465c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1979,6 +1979,11 @@ int regulator_is_supported_voltage(struct regulator *regulator, return ret; } + /* Any voltage within constrains range is fine? */ + if (rdev->desc->continuous_voltage_range) + return min_uV >= rdev->constraints->min_uV && + max_uV <= rdev->constraints->max_uV; + ret = regulator_count_voltages(regulator); if (ret < 0) return ret; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 7932a3bf21bd..f2b72b230b9b 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -181,6 +181,8 @@ enum regulator_type { * @type: Indicates if the regulator is a voltage or current regulator. * @owner: Module providing the regulator, used for refcounting. * + * @continuous_voltage_range: Indicates if the regulator can set any + * voltage within constrains range. * @n_voltages: Number of selectors available for ops.list_voltage(). * * @min_uV: Voltage given by the lowest selector (if linear mapping) @@ -199,6 +201,7 @@ struct regulator_desc { const char *name; const char *supply_name; int id; + bool continuous_voltage_range; unsigned n_voltages; struct regulator_ops *ops; int irq; -- cgit v1.2.3 From 31e54086dd7bb86ad40f1d76a9063f2a95866b87 Mon Sep 17 00:00:00 2001 From: Pawel Moll Date: Mon, 24 Sep 2012 18:56:54 +0100 Subject: regulator: Versatile Express regulator driver Implementation of the regulator framework driver for the Versatile Express voltage control. Devices without voltage constraints (ie. "regulator-[min|max]-microvolt" properties in the DT node) are treated as fixed (or rather read-only) regulators. Signed-off-by: Pawel Moll Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/vexpress.txt | 32 +++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/vexpress.c | 146 +++++++++++++++++++++ 4 files changed, 186 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/vexpress.txt create mode 100644 drivers/regulator/vexpress.c (limited to 'drivers/regulator') diff --git a/Documentation/devicetree/bindings/regulator/vexpress.txt b/Documentation/devicetree/bindings/regulator/vexpress.txt new file mode 100644 index 000000000000..d775f72487aa --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/vexpress.txt @@ -0,0 +1,32 @@ +Versatile Express voltage regulators +------------------------------------ + +Requires node properties: +- "compatible" value: "arm,vexpress-volt" +- "arm,vexpress-sysreg,func" when controlled via vexpress-sysreg + (see Documentation/devicetree/bindings/arm/vexpress-sysreg.txt + for more details) + +Required regulator properties: +- "regulator-name" +- "regulator-always-on" + +Optional regulator properties: +- "regulator-min-microvolt" +- "regulator-max-microvolt" + +See Documentation/devicetree/bindings/regulator/regulator.txt +for more details about the regulator properties. + +When no "regulator-[min|max]-microvolt" properties are defined, +the device is treated as fixed (or rather "read-only") regulator. + +Example: + volt@0 { + compatible = "arm,vexpress-volt"; + arm,vexpress-sysreg,func = <2 0>; + regulator-name = "Cores"; + regulator-min-microvolt = <800000>; + regulator-max-microvolt = <1050000>; + regulator-always-on; + }; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67d47b59a66d..b44b019b9433 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -422,6 +422,13 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_VEXPRESS + tristate "Versatile Express regulators" + depends on VEXPRESS_CONFIG + help + This driver provides support for voltage regulators available + on the ARM Ltd's Versatile Express platform. + config REGULATOR_WM831X tristate "Wolfson Microelectronics WM831x PMIC regulators" depends on MFD_WM831X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e431eed8a878..9fa7a7bc42d8 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_REGULATOR_TPS6586X) += tps6586x-regulator.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c new file mode 100644 index 000000000000..1702945a93a9 --- /dev/null +++ b/drivers/regulator/vexpress.c @@ -0,0 +1,146 @@ +/* + * 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 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. + * + * Copyright (C) 2012 ARM Limited + */ + +#define DRVNAME "vexpress-regulator" +#define pr_fmt(fmt) DRVNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct vexpress_regulator { + struct regulator_desc desc; + struct regulator_dev *regdev; + struct vexpress_config_func *func; +}; + +static int vexpress_regulator_get_voltage(struct regulator_dev *regdev) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + u32 uV; + int err = vexpress_config_read(reg->func, 0, &uV); + + return err ? err : uV; +} + +static int vexpress_regulator_set_voltage(struct regulator_dev *regdev, + int min_uV, int max_uV, unsigned *selector) +{ + struct vexpress_regulator *reg = rdev_get_drvdata(regdev); + + return vexpress_config_write(reg->func, 0, min_uV); +} + +static struct regulator_ops vexpress_regulator_ops_ro = { + .get_voltage = vexpress_regulator_get_voltage, +}; + +static struct regulator_ops vexpress_regulator_ops = { + .get_voltage = vexpress_regulator_get_voltage, + .set_voltage = vexpress_regulator_set_voltage, +}; + +static int vexpress_regulator_probe(struct platform_device *pdev) +{ + int err; + struct vexpress_regulator *reg; + struct regulator_init_data *init_data; + struct regulator_config config = { }; + + reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL); + if (!reg) { + err = -ENOMEM; + goto error_kzalloc; + } + + reg->func = vexpress_config_func_get_by_dev(&pdev->dev); + if (!reg->func) { + err = -ENXIO; + goto error_get_func; + } + + reg->desc.name = dev_name(&pdev->dev); + reg->desc.type = REGULATOR_VOLTAGE; + reg->desc.owner = THIS_MODULE; + reg->desc.continuous_voltage_range = true; + + init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node); + if (!init_data) { + err = -EINVAL; + goto error_get_regulator_init_data; + } + + init_data->constraints.apply_uV = 0; + if (init_data->constraints.min_uV && init_data->constraints.max_uV) + reg->desc.ops = &vexpress_regulator_ops; + else + reg->desc.ops = &vexpress_regulator_ops_ro; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.driver_data = reg; + config.of_node = pdev->dev.of_node; + + reg->regdev = regulator_register(®->desc, &config); + if (IS_ERR(reg->regdev)) { + err = PTR_ERR(reg->regdev); + goto error_regulator_register; + } + + platform_set_drvdata(pdev, reg); + + return 0; + +error_regulator_register: +error_get_regulator_init_data: + vexpress_config_func_put(reg->func); +error_get_func: +error_kzalloc: + return err; +} + +static int __devexit vexpress_regulator_remove(struct platform_device *pdev) +{ + struct vexpress_regulator *reg = platform_get_drvdata(pdev); + + vexpress_config_func_put(reg->func); + regulator_unregister(reg->regdev); + + return 0; +} + +static struct of_device_id vexpress_regulator_of_match[] = { + { .compatible = "arm,vexpress-volt", }, +}; + +static struct platform_driver vexpress_regulator_driver = { + .probe = vexpress_regulator_probe, + .remove = __devexit_p(vexpress_regulator_remove), + .driver = { + .name = DRVNAME, + .owner = THIS_MODULE, + .of_match_table = vexpress_regulator_of_match, + }, +}; + +module_platform_driver(vexpress_regulator_driver); + +MODULE_AUTHOR("Pawel Moll "); +MODULE_DESCRIPTION("Versatile Express regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:vexpress-regulator"); -- cgit v1.2.3 From 9f4e45f77e8a532c8a7e39268e844821dbf7fe91 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Wed, 17 Oct 2012 09:00:20 +0800 Subject: regulator: vexpress: Add terminating entry for vexpress_regulator_of_match table The of_device_id table is supposed to be zero-terminated. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/vexpress.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c index 1702945a93a9..1d558111d60f 100644 --- a/drivers/regulator/vexpress.c +++ b/drivers/regulator/vexpress.c @@ -126,6 +126,7 @@ static int __devexit vexpress_regulator_remove(struct platform_device *pdev) static struct of_device_id vexpress_regulator_of_match[] = { { .compatible = "arm,vexpress-volt", }, + { } }; static struct platform_driver vexpress_regulator_driver = { -- cgit v1.2.3 From 24282a1ca33b4a2cdfb907fb7a3ba4d0f6e93311 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:18:59 +0530 Subject: regulator: tps65090: Register all regulators in single probe call MFD driver registers the regulator driver once per device and hence it is require to register all regulators in single probe call. Following are details of changes done to achieve this: - Move the regulator enums to mfd header and remove the tps65090-regulator.h as it does not contain more info. - Add max regulator and register all regulators even if there is no regulator init data from platform. - Convert regulator init data to pointer type in platform data. - Add input supply name in regulator desc to provide input supply. - Separate desc information from driver information. - Disable external control bit to have control through register write. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 195 +++++++++++++++++++-------- include/linux/mfd/tps65090.h | 28 ++++ include/linux/regulator/tps65090-regulator.h | 50 ------- 3 files changed, 165 insertions(+), 108 deletions(-) delete mode 100644 include/linux/regulator/tps65090-regulator.h (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 001ad554ac62..5f7f9311024e 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -24,15 +24,11 @@ #include #include #include -#include struct tps65090_regulator { - int id; - /* used by regulator core */ - struct regulator_desc desc; - - /* Device */ struct device *dev; + struct regulator_desc *desc; + struct regulator_dev *rdev; }; static struct regulator_ops tps65090_ops = { @@ -41,44 +37,80 @@ static struct regulator_ops tps65090_ops = { .is_enabled = regulator_is_enabled_regmap, }; -#define tps65090_REG(_id) \ +#define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ { \ - .id = TPS65090_ID_##_id, \ - .desc = { \ - .name = tps65090_rails(_id), \ - .id = TPS65090_ID_##_id, \ - .ops = &tps65090_ops, \ - .type = REGULATOR_VOLTAGE, \ - .owner = THIS_MODULE, \ - .enable_reg = (TPS65090_ID_##_id) + 12, \ - .enable_mask = BIT(0), \ - }, \ + .name = "TPS65090_RAILS"#_id, \ + .supply_name = _sname, \ + .id = TPS65090_ID_##_id, \ + .ops = &_ops, \ + .enable_reg = _en_reg, \ + .enable_mask = BIT(0), \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ } -static struct tps65090_regulator TPS65090_regulator[] = { - tps65090_REG(DCDC1), - tps65090_REG(DCDC2), - tps65090_REG(DCDC3), - tps65090_REG(FET1), - tps65090_REG(FET2), - tps65090_REG(FET3), - tps65090_REG(FET4), - tps65090_REG(FET5), - tps65090_REG(FET6), - tps65090_REG(FET7), +static struct regulator_desc tps65090_regulator_desc[] = { + tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_ops), + tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_ops), + tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_ops), + tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_ops), + tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_ops), + tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_ops), + tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_ops), + tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), + tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), + tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), }; -static inline struct tps65090_regulator *find_regulator_info(int id) +static inline bool is_dcdc(int id) { - struct tps65090_regulator *ri; - int i; + switch (id) { + case TPS65090_ID_DCDC1: + case TPS65090_ID_DCDC2: + case TPS65090_ID_DCDC3: + return true; + default: + return false; + } +} - for (i = 0; i < ARRAY_SIZE(TPS65090_regulator); i++) { - ri = &TPS65090_regulator[i]; - if (ri->desc.id == id) - return ri; +static int __devinit tps65090_config_ext_control( + struct tps65090_regulator *ri, bool enable) +{ + int ret; + struct device *parent = ri->dev->parent; + unsigned int reg_en_reg = ri->desc->enable_reg; + + if (enable) + ret = tps65090_set_bits(parent, reg_en_reg, 1); + else + ret = tps65090_clr_bits(parent, reg_en_reg, 1); + if (ret < 0) + dev_err(ri->dev, "Error in updating reg 0x%x\n", reg_en_reg); + return ret; +} + +static int __devinit tps65090_regulator_disable_ext_control( + struct tps65090_regulator *ri, + struct tps65090_regulator_plat_data *tps_pdata) +{ + int ret = 0; + struct device *parent = ri->dev->parent; + unsigned int reg_en_reg = ri->desc->enable_reg; + + /* + * First enable output for internal control if require. + * And then disable external control. + */ + if (tps_pdata->reg_init_data->constraints.always_on || + tps_pdata->reg_init_data->constraints.boot_on) { + ret = tps65090_set_bits(parent, reg_en_reg, 0); + if (ret < 0) { + dev_err(ri->dev, "Error in set reg 0x%x\n", reg_en_reg); + return ret; + } } - return NULL; + return tps65090_config_ext_control(ri, false); } static int __devinit tps65090_regulator_probe(struct platform_device *pdev) @@ -87,40 +119,87 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) struct tps65090_regulator *ri = NULL; struct regulator_config config = { }; struct regulator_dev *rdev; - struct tps65090_regulator_platform_data *tps_pdata; - int id = pdev->id; + struct tps65090_regulator_plat_data *tps_pdata; + struct tps65090_regulator *pmic; + struct tps65090_platform_data *tps65090_pdata; + int num; + int ret; - dev_dbg(&pdev->dev, "Probing regulator %d\n", id); + dev_dbg(&pdev->dev, "Probing regulator\n"); - ri = find_regulator_info(id); - if (ri == NULL) { - dev_err(&pdev->dev, "invalid regulator ID specified\n"); + tps65090_pdata = dev_get_platdata(pdev->dev.parent); + if (!tps65090_pdata) { + dev_err(&pdev->dev, "Platform data missing\n"); return -EINVAL; } - tps_pdata = pdev->dev.platform_data; - ri->dev = &pdev->dev; - - config.dev = &pdev->dev; - config.init_data = &tps_pdata->regulator; - config.driver_data = ri; - config.regmap = tps65090_mfd->rmap; - - rdev = regulator_register(&ri->desc, &config); - if (IS_ERR(rdev)) { - dev_err(&pdev->dev, "failed to register regulator %s\n", - ri->desc.name); - return PTR_ERR(rdev); + + pmic = devm_kzalloc(&pdev->dev, TPS65090_ID_MAX * sizeof(*pmic), + GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + return -ENOMEM; + } + + for (num = 0; num < TPS65090_ID_MAX; num++) { + tps_pdata = tps65090_pdata->reg_pdata[num]; + + ri = &pmic[num]; + ri->dev = &pdev->dev; + ri->desc = &tps65090_regulator_desc[num]; + + /* + * TPS5090 DCDC support the control from external digital input. + * It may be possible that during boot, the external control is + * enabled. Disabling external control for DCDC. + */ + if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { + ret = tps65090_regulator_disable_ext_control( + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "failed disable ext control\n"); + goto scrub; + } + } + config.dev = &pdev->dev; + config.driver_data = ri; + config.regmap = tps65090_mfd->rmap; + if (tps_pdata) + config.init_data = tps_pdata->reg_init_data; + else + config.init_data = NULL; + + rdev = regulator_register(ri->desc, &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "failed to register regulator %s\n", + ri->desc->name); + ret = PTR_ERR(rdev); + goto scrub; + } + ri->rdev = rdev; } - platform_set_drvdata(pdev, rdev); + platform_set_drvdata(pdev, pmic); return 0; + +scrub: + while (--num >= 0) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return ret; } static int __devexit tps65090_regulator_remove(struct platform_device *pdev) { - struct regulator_dev *rdev = platform_get_drvdata(pdev); + struct tps65090_regulator *pmic = platform_get_drvdata(pdev); + struct tps65090_regulator *ri; + int num; - regulator_unregister(rdev); + for (num = 0; num < TPS65090_ID_MAX; ++num) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } return 0; } diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 6bc31d854626..d06c63335a2a 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -24,6 +24,23 @@ #include +/* TPS65090 Regulator ID */ +enum { + TPS65090_ID_DCDC1, + TPS65090_ID_DCDC2, + TPS65090_ID_DCDC3, + TPS65090_ID_FET1, + TPS65090_ID_FET2, + TPS65090_ID_FET3, + TPS65090_ID_FET4, + TPS65090_ID_FET5, + TPS65090_ID_FET6, + TPS65090_ID_FET7, + + /* Last entry for maximum ID */ + TPS65090_ID_MAX, +}; + struct tps65090 { struct mutex lock; struct device *dev; @@ -41,10 +58,21 @@ struct tps65090_subdev_info { void *platform_data; }; +/* + * struct tps65090_regulator_plat_data + * + * @reg_init_data: The regulator init data. + */ + +struct tps65090_regulator_plat_data { + struct regulator_init_data *reg_init_data; +}; + struct tps65090_platform_data { int irq_base; int num_subdevs; struct tps65090_subdev_info *subdevs; + struct tps65090_regulator_plat_data *reg_pdata[TPS65090_ID_MAX]; }; /* diff --git a/include/linux/regulator/tps65090-regulator.h b/include/linux/regulator/tps65090-regulator.h deleted file mode 100644 index 0fa04b64db3e..000000000000 --- a/include/linux/regulator/tps65090-regulator.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Regulator driver interface for TI TPS65090 PMIC family - * - * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. - - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - - * This program is distributed in the hope 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. - - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef __REGULATOR_TPS65090_H -#define __REGULATOR_TPS65090_H - -#include - -#define tps65090_rails(_name) "tps65090_"#_name - -enum { - TPS65090_ID_DCDC1, - TPS65090_ID_DCDC2, - TPS65090_ID_DCDC3, - TPS65090_ID_FET1, - TPS65090_ID_FET2, - TPS65090_ID_FET3, - TPS65090_ID_FET4, - TPS65090_ID_FET5, - TPS65090_ID_FET6, - TPS65090_ID_FET7, -}; - -/* - * struct tps65090_regulator_platform_data - * - * @regulator: The regulator init data. - * @slew_rate_uV_per_us: Slew rate microvolt per microsec. - */ - -struct tps65090_regulator_platform_data { - struct regulator_init_data regulator; -}; - -#endif /* __REGULATOR_TPS65090_H */ -- cgit v1.2.3 From 8620ca9f77b71a0069a6151e859b988117ef1fa5 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:00 +0530 Subject: regulator: tps65090: rename driver name and regulator name To make the names proper and more appropriate: Rename the driver name from tps65090-regulator to tps65090-pmic. Rename the regulators from TPS65090_ID_* to TPS65090_REGULATOR_* Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 16 ++++++++-------- include/linux/mfd/tps65090.h | 24 ++++++++++++------------ 2 files changed, 20 insertions(+), 20 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 5f7f9311024e..584a185f89e1 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -41,7 +41,7 @@ static struct regulator_ops tps65090_ops = { { \ .name = "TPS65090_RAILS"#_id, \ .supply_name = _sname, \ - .id = TPS65090_ID_##_id, \ + .id = TPS65090_REGULATOR_##_id, \ .ops = &_ops, \ .enable_reg = _en_reg, \ .enable_mask = BIT(0), \ @@ -65,9 +65,9 @@ static struct regulator_desc tps65090_regulator_desc[] = { static inline bool is_dcdc(int id) { switch (id) { - case TPS65090_ID_DCDC1: - case TPS65090_ID_DCDC2: - case TPS65090_ID_DCDC3: + case TPS65090_REGULATOR_DCDC1: + case TPS65090_REGULATOR_DCDC2: + case TPS65090_REGULATOR_DCDC3: return true; default: return false; @@ -133,14 +133,14 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) return -EINVAL; } - pmic = devm_kzalloc(&pdev->dev, TPS65090_ID_MAX * sizeof(*pmic), + pmic = devm_kzalloc(&pdev->dev, TPS65090_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); if (!pmic) { dev_err(&pdev->dev, "mem alloc for pmic failed\n"); return -ENOMEM; } - for (num = 0; num < TPS65090_ID_MAX; num++) { + for (num = 0; num < TPS65090_REGULATOR_MAX; num++) { tps_pdata = tps65090_pdata->reg_pdata[num]; ri = &pmic[num]; @@ -196,7 +196,7 @@ static int __devexit tps65090_regulator_remove(struct platform_device *pdev) struct tps65090_regulator *ri; int num; - for (num = 0; num < TPS65090_ID_MAX; ++num) { + for (num = 0; num < TPS65090_REGULATOR_MAX; ++num) { ri = &pmic[num]; regulator_unregister(ri->rdev); } @@ -205,7 +205,7 @@ static int __devexit tps65090_regulator_remove(struct platform_device *pdev) static struct platform_driver tps65090_regulator_driver = { .driver = { - .name = "tps65090-regulator", + .name = "tps65090-pmic", .owner = THIS_MODULE, }, .probe = tps65090_regulator_probe, diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index d06c63335a2a..958bf155097c 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -26,19 +26,19 @@ /* TPS65090 Regulator ID */ enum { - TPS65090_ID_DCDC1, - TPS65090_ID_DCDC2, - TPS65090_ID_DCDC3, - TPS65090_ID_FET1, - TPS65090_ID_FET2, - TPS65090_ID_FET3, - TPS65090_ID_FET4, - TPS65090_ID_FET5, - TPS65090_ID_FET6, - TPS65090_ID_FET7, + TPS65090_REGULATOR_DCDC1, + TPS65090_REGULATOR_DCDC2, + TPS65090_REGULATOR_DCDC3, + TPS65090_REGULATOR_FET1, + TPS65090_REGULATOR_FET2, + TPS65090_REGULATOR_FET3, + TPS65090_REGULATOR_FET4, + TPS65090_REGULATOR_FET5, + TPS65090_REGULATOR_FET6, + TPS65090_REGULATOR_FET7, /* Last entry for maximum ID */ - TPS65090_ID_MAX, + TPS65090_REGULATOR_MAX, }; struct tps65090 { @@ -72,7 +72,7 @@ struct tps65090_platform_data { int irq_base; int num_subdevs; struct tps65090_subdev_info *subdevs; - struct tps65090_regulator_plat_data *reg_pdata[TPS65090_ID_MAX]; + struct tps65090_regulator_plat_data *reg_pdata[TPS65090_REGULATOR_MAX]; }; /* -- cgit v1.2.3 From 3a81ef8c27cea5c749a45765da4e06a7af75be2b Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:01 +0530 Subject: regulator: tps65090: Add support for LDO regulators TPS65090 supports the two LDOs, LDO1 and LDO2. These are always ON regulators. The output on these LDOs are available once the input voltage available for these LDOs. Add support for these LDOs regulators. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 5 +++++ include/linux/mfd/tps65090.h | 2 ++ 2 files changed, 7 insertions(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 584a185f89e1..0732d9ba3f40 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -37,6 +37,9 @@ static struct regulator_ops tps65090_ops = { .is_enabled = regulator_is_enabled_regmap, }; +static struct regulator_ops tps65090_ldo_ops = { +}; + #define tps65090_REG_DESC(_id, _sname, _en_reg, _ops) \ { \ .name = "TPS65090_RAILS"#_id, \ @@ -60,6 +63,8 @@ static struct regulator_desc tps65090_regulator_desc[] = { tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), + tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops), + tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops), }; static inline bool is_dcdc(int id) diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 958bf155097c..598921221ff0 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -36,6 +36,8 @@ enum { TPS65090_REGULATOR_FET5, TPS65090_REGULATOR_FET6, TPS65090_REGULATOR_FET7, + TPS65090_REGULATOR_LDO1, + TPS65090_REGULATOR_LDO2, /* Last entry for maximum ID */ TPS65090_REGULATOR_MAX, -- cgit v1.2.3 From f329b1755b475f64f0472cda1ae9602e092f6f05 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Tue, 9 Oct 2012 15:19:02 +0530 Subject: regulator: tps65090: add external control support for DCDC The TPS65090's DCDC output can also be enable/disable through the external digital input signal. Add support for enable/disable either through register access via I2C or through external control inputs. The external control inputs can be driven through GPIOs also and hence adding support for passing the GPIO number. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 77 +++++++++++++++++++++++++--------- include/linux/mfd/tps65090.h | 7 +++- 2 files changed, 63 insertions(+), 21 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 0732d9ba3f40..412413838e12 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -31,10 +32,13 @@ struct tps65090_regulator { struct regulator_dev *rdev; }; -static struct regulator_ops tps65090_ops = { - .enable = regulator_enable_regmap, - .disable = regulator_disable_regmap, - .is_enabled = regulator_is_enabled_regmap, +static struct regulator_ops tps65090_ext_control_ops = { +}; + +static struct regulator_ops tps65090_reg_contol_ops = { + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, }; static struct regulator_ops tps65090_ldo_ops = { @@ -53,16 +57,16 @@ static struct regulator_ops tps65090_ldo_ops = { } static struct regulator_desc tps65090_regulator_desc[] = { - tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_ops), - tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_ops), - tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_ops), - tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_ops), - tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_ops), - tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_ops), - tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_ops), - tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_ops), - tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_ops), - tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_ops), + tps65090_REG_DESC(DCDC1, "vsys1", 0x0C, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC2, "vsys2", 0x0D, tps65090_reg_contol_ops), + tps65090_REG_DESC(DCDC3, "vsys3", 0x0E, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET1, "infet1", 0x0F, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET2, "infet2", 0x10, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET3, "infet3", 0x11, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET4, "infet4", 0x12, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET5, "infet5", 0x13, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET6, "infet6", 0x14, tps65090_reg_contol_ops), + tps65090_REG_DESC(FET7, "infet7", 0x15, tps65090_reg_contol_ops), tps65090_REG_DESC(LDO1, "vsys_l1", 0, tps65090_ldo_ops), tps65090_REG_DESC(LDO2, "vsys_l2", 0, tps65090_ldo_ops), }; @@ -118,6 +122,22 @@ static int __devinit tps65090_regulator_disable_ext_control( return tps65090_config_ext_control(ri, false); } +static void __devinit tps65090_configure_regulator_config( + struct tps65090_regulator_plat_data *tps_pdata, + struct regulator_config *config) +{ + if (gpio_is_valid(tps_pdata->gpio)) { + int gpio_flag = GPIOF_OUT_INIT_LOW; + + if (tps_pdata->reg_init_data->constraints.always_on || + tps_pdata->reg_init_data->constraints.boot_on) + gpio_flag = GPIOF_OUT_INIT_HIGH; + + config->ena_gpio = tps_pdata->gpio; + config->ena_gpio_flags = gpio_flag; + } +} + static int __devinit tps65090_regulator_probe(struct platform_device *pdev) { struct tps65090 *tps65090_mfd = dev_get_drvdata(pdev->dev.parent); @@ -154,18 +174,24 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) /* * TPS5090 DCDC support the control from external digital input. - * It may be possible that during boot, the external control is - * enabled. Disabling external control for DCDC. + * Configure it as per platform data. */ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data) { - ret = tps65090_regulator_disable_ext_control( + if (tps_pdata->enable_ext_control) { + tps65090_configure_regulator_config( + tps_pdata, &config); + ri->desc->ops = &tps65090_ext_control_ops; + } else { + ret = tps65090_regulator_disable_ext_control( ri, tps_pdata); - if (ret < 0) { - dev_err(&pdev->dev, + if (ret < 0) { + dev_err(&pdev->dev, "failed disable ext control\n"); - goto scrub; + goto scrub; + } } } + config.dev = &pdev->dev; config.driver_data = ri; config.regmap = tps65090_mfd->rmap; @@ -182,6 +208,17 @@ static int __devinit tps65090_regulator_probe(struct platform_device *pdev) goto scrub; } ri->rdev = rdev; + + /* Enable external control if it is require */ + if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && + tps_pdata->enable_ext_control) { + ret = tps65090_config_ext_control(ri, true); + if (ret < 0) { + /* Increment num to get unregister rdev */ + num++; + goto scrub; + } + } } platform_set_drvdata(pdev, pmic); diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 598921221ff0..804e280c1e1d 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -64,10 +64,15 @@ struct tps65090_subdev_info { * struct tps65090_regulator_plat_data * * @reg_init_data: The regulator init data. + * @enable_ext_control: Enable extrenal control or not. Only available for + * DCDC1, DCDC2 and DCDC3. + * @gpio: Gpio number if external control is enabled and controlled through + * gpio. */ - struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; + bool enable_ext_control; + int gpio; }; struct tps65090_platform_data { -- cgit v1.2.3 From 006694d099e86e5a928fb39bdea280ab42c9d59c Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 15 Oct 2012 14:16:59 +0100 Subject: regulator: gpio-regulator: Allow use of GPIO controlled regulators though DT Here we provide the GPIO Regulator driver with Device Tree capability, so that when a platform is booting with DT instead of platform data we can still make full use of it. Signed-off-by: Lee Jones Reviewed-by: Stephen Warren Signed-off-by: Mark Brown --- .../bindings/regulator/gpio-regulator.txt | 36 +++++++++ drivers/regulator/gpio-regulator.c | 94 ++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/gpio-regulator.txt (limited to 'drivers/regulator') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt new file mode 100644 index 000000000000..3703be2ab821 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -0,0 +1,36 @@ +GPIO controlled regulators + +Required properties: +- compatible : Must be "regulator-gpio". + +Optional properties: +- gpio-enable : GPIO to use to enable/disable the regulator. +- gpios : GPIO group used to control voltage. +- states : Selection of available voltages and GPIO configs. +- startup-delay-us : Startup time in microseconds. +- enable-active-high : Polarity of GPIO is active high (default is low). + +Any property defined as part of the core regulator binding defined in +regulator.txt can also be used. + +Example: + + mmciv: gpio-regulator { + compatible = "regulator-gpio"; + + regulator-name = "mmci-gpio-supply"; + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2600000>; + regulator-boot-on; + + gpio-enable = <&gpio0 23 0x4>; + gpios = <&gpio0 24 0x4 + &gpio0 25 0x4>; + states = <1800000 0x3 + 2200000 0x2 + 2600000 0x1 + 2900000 0x0>; + + startup-delay-us = <100000>; + enable-active-high; + }; diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index 8b5944f2d7d1..e467d0ac8705 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -28,9 +28,12 @@ #include #include #include +#include #include #include #include +#include +#include struct gpio_regulator_data { struct regulator_desc desc; @@ -129,6 +132,84 @@ static struct regulator_ops gpio_regulator_voltage_ops = { .list_voltage = gpio_regulator_list_voltage, }; +struct gpio_regulator_config * +of_get_gpio_regulator_config(struct device *dev, struct device_node *np) +{ + struct gpio_regulator_config *config; + struct property *prop; + const char *regtype; + int proplen, gpio, i; + + config = devm_kzalloc(dev, + sizeof(struct gpio_regulator_config), + GFP_KERNEL); + if (!config) + return ERR_PTR(-ENOMEM); + + config->init_data = of_get_regulator_init_data(dev, np); + if (!config->init_data) + return ERR_PTR(-EINVAL); + + config->supply_name = config->init_data->constraints.name; + + if (of_property_read_bool(np, "enable-active-high")) + config->enable_high = true; + + if (of_property_read_bool(np, "enable-at-boot")) + config->enabled_at_boot = true; + + of_property_read_u32(np, "startup-delay-us", &config->startup_delay); + + config->enable_gpio = of_get_named_gpio(np, "enable-gpio", 0); + + /* Fetch GPIOs. */ + for (i = 0; ; i++) + if (of_get_named_gpio(np, "gpios", i) < 0) + break; + config->nr_gpios = i; + + config->gpios = devm_kzalloc(dev, + sizeof(struct gpio) * config->nr_gpios, + GFP_KERNEL); + if (!config->gpios) + return ERR_PTR(-ENOMEM); + + for (i = 0; config->nr_gpios; i++) { + gpio = of_get_named_gpio(np, "gpios", i); + if (gpio < 0) + break; + config->gpios[i].gpio = gpio; + } + + /* Fetch states. */ + prop = of_find_property(np, "states", NULL); + proplen = prop->length / sizeof(int); + + config->states = devm_kzalloc(dev, + sizeof(struct gpio_regulator_state) + * (proplen / 2), + GFP_KERNEL); + if (!config->states) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < proplen / 2; i++) { + config->states[i].value = + be32_to_cpup((int *)prop->value + (i * 2)); + config->states[i].gpios = + be32_to_cpup((int *)prop->value + (i * 2 + 1)); + } + config->nr_states = i; + + of_property_read_string(np, "regulator-type", ®type); + + if (!strncmp("voltage", regtype, 7)) + config->type = REGULATOR_VOLTAGE; + else if (!strncmp("current", regtype, 7)) + config->type = REGULATOR_CURRENT; + + return config; +} + static struct regulator_ops gpio_regulator_current_ops = { .get_current_limit = gpio_regulator_get_value, .set_current_limit = gpio_regulator_set_current_limit, @@ -137,10 +218,17 @@ static struct regulator_ops gpio_regulator_current_ops = { static int __devinit gpio_regulator_probe(struct platform_device *pdev) { struct gpio_regulator_config *config = pdev->dev.platform_data; + struct device_node *np = pdev->dev.of_node; struct gpio_regulator_data *drvdata; struct regulator_config cfg = { }; int ptr, ret, state; + if (np) { + config = of_get_gpio_regulator_config(&pdev->dev, np); + if (IS_ERR(config)) + return PTR_ERR(config); + } + drvdata = devm_kzalloc(&pdev->dev, sizeof(struct gpio_regulator_data), GFP_KERNEL); if (drvdata == NULL) { @@ -270,12 +358,18 @@ static int __devexit gpio_regulator_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id regulator_gpio_of_match[] __devinitconst = { + { .compatible = "regulator-gpio", }, + {}, +}; + static struct platform_driver gpio_regulator_driver = { .probe = gpio_regulator_probe, .remove = __devexit_p(gpio_regulator_remove), .driver = { .name = "gpio-regulator", .owner = THIS_MODULE, + .of_match_table = regulator_gpio_of_match, }, }; -- cgit v1.2.3 From 7bde76726fa296defc0eafc602284a7676817348 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Thu, 1 Nov 2012 14:02:36 +0800 Subject: regulator: tps51632: Fix trivial typo for TPS51632 Kconfig help text Signed-off-by: Axel Lin Acked-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index aa9e8a18262d..2fd5808bf668 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -341,7 +341,7 @@ config REGULATOR_TPS51632 select REGMAP_I2C help This driver supports TPS51632 voltage regulator chip. - The TPS52632 is 3-2-1 Phase D-Cap+ Step Down Driverless Controller + The TPS51632 is 3-2-1 Phase D-Cap+ Step Down Driverless Controller with Serial VID control and DVFS. The voltage output can be configure through I2C interface or PWM interface. -- cgit v1.2.3 From 45b6f8e8fc014fe404d155a657a04b25b861001d Mon Sep 17 00:00:00 2001 From: Qing Xu Date: Wed, 7 Nov 2012 18:09:53 +0800 Subject: regulator: max8925: support dt for regulator Signed-off-by: Qing Xu Signed-off-by: Mark Brown --- .../bindings/regulator/max8925-regulator.txt | 29 +++++++++ drivers/regulator/max8925-regulator.c | 72 +++++++++++++++++++++- 2 files changed, 98 insertions(+), 3 deletions(-) create mode 100644 Documentation/devicetree/bindings/regulator/max8925-regulator.txt (limited to 'drivers/regulator') diff --git a/Documentation/devicetree/bindings/regulator/max8925-regulator.txt b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt new file mode 100644 index 000000000000..073b41d90f2c --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/max8925-regulator.txt @@ -0,0 +1,29 @@ +Max8925 Voltage regulators + +max8925 regulator device register is still handled by mfd_add_devices, not by +of_xxx, so, it is not necessary to add compatible name. Also, those reg +offset and id info is stored in mfd_cell(see max8925-core.c), as a result +there is not private properties in dts. + +node's name should match with the definition in max8925_regulator_matches +(see max8925-regulator.c) + + +Optional properties: +- Any optional property defined in bindings/regulator/regulator.txt + + +Example: + + + regulators { + SDV1 { + regulator-min-microvolt = <637500>; + regulator-max-microvolt = <1425000>; + regulator-boot-on; + regulator-always-on; + }; + + ... + ... + } diff --git a/drivers/regulator/max8925-regulator.c b/drivers/regulator/max8925-regulator.c index 9bb0be37495f..2b549791c975 100644 --- a/drivers/regulator/max8925-regulator.c +++ b/drivers/regulator/max8925-regulator.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #define SD1_DVM_VMIN 850000 #define SD1_DVM_VMAX 1000000 @@ -187,6 +189,34 @@ static struct regulator_ops max8925_regulator_ldo_ops = { .enable_reg = MAX8925_LDOCTL##_id, \ } +#ifdef CONFIG_OF +static struct of_regulator_match max8925_regulator_matches[] = { + { .name = "SDV1",}, + { .name = "SDV2",}, + { .name = "SDV3",}, + { .name = "LDO1",}, + { .name = "LDO2",}, + { .name = "LDO3",}, + { .name = "LDO4",}, + { .name = "LDO5",}, + { .name = "LDO6",}, + { .name = "LDO7",}, + { .name = "LDO8",}, + { .name = "LDO9",}, + { .name = "LDO10",}, + { .name = "LDO11",}, + { .name = "LDO12",}, + { .name = "LDO13",}, + { .name = "LDO14",}, + { .name = "LDO15",}, + { .name = "LDO16",}, + { .name = "LDO17",}, + { .name = "LDO18",}, + { .name = "LDO19",}, + { .name = "LDO20",}, +}; +#endif + static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_SDV(1, 637.5, 1425, 12.5), MAX8925_SDV(2, 650, 2225, 25), @@ -214,6 +244,36 @@ static struct max8925_regulator_info max8925_regulator_info[] = { MAX8925_LDO(20, 750, 3900, 50), }; +#ifdef CONFIG_OF +static int max8925_regulator_dt_init(struct platform_device *pdev, + struct max8925_regulator_info *info, + struct regulator_config *config, + int ridx) +{ + struct device_node *nproot, *np; + int rcount; + nproot = pdev->dev.parent->of_node; + if (!nproot) + return -ENODEV; + np = of_find_node_by_name(nproot, "regulators"); + if (!np) { + dev_err(&pdev->dev, "failed to find regulators node\n"); + return -ENODEV; + } + + rcount = of_regulator_match(&pdev->dev, np, + &max8925_regulator_matches[ridx], 1); + if (rcount < 0) + return -ENODEV; + config->init_data = max8925_regulator_matches[ridx].init_data; + config->of_node = max8925_regulator_matches[ridx].of_node; + + return 0; +} +#else +#define max8925_regulator_dt_init(w, x, y, z) (-1) +#endif + static int __devinit max8925_regulator_probe(struct platform_device *pdev) { struct max8925_chip *chip = dev_get_drvdata(pdev->dev.parent); @@ -222,7 +282,7 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) struct max8925_regulator_info *ri; struct resource *res; struct regulator_dev *rdev; - int i; + int i, regulator_idx; res = platform_get_resource(pdev, IORESOURCE_REG, 0); if (!res) { @@ -231,9 +291,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) } for (i = 0; i < ARRAY_SIZE(max8925_regulator_info); i++) { ri = &max8925_regulator_info[i]; - if (ri->vol_reg == res->start) + if (ri->vol_reg == res->start) { + regulator_idx = i; break; + } } + if (i == ARRAY_SIZE(max8925_regulator_info)) { dev_err(&pdev->dev, "Failed to find regulator %llu\n", (unsigned long long)res->start); @@ -243,9 +306,12 @@ static int __devinit max8925_regulator_probe(struct platform_device *pdev) ri->chip = chip; config.dev = &pdev->dev; - config.init_data = pdata; config.driver_data = ri; + if (max8925_regulator_dt_init(pdev, ri, &config, regulator_idx)) + if (pdata) + config.init_data = pdata; + rdev = regulator_register(&ri->desc, &config); if (IS_ERR(rdev)) { dev_err(&pdev->dev, "failed to register regulator %s\n", -- cgit v1.2.3 From 1a0bb679bcee5233dc5134e4f5fbf97d8caf8bbb Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Sun, 11 Nov 2012 20:42:01 +0530 Subject: regulator: tps80031: add regulator driver for tps80031 Add regulator driver for Texas Instrument TPS80031/TPS80032 device. TPS80031/ TPS80032 Fully Integrated Power Management with Power Path and Battery Charger. It has 5 configurable step-down converters, 11 general purpose LDOs, VBUS generator and digital output to control regulators. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 9 + drivers/regulator/Makefile | 1 + drivers/regulator/tps80031-regulator.c | 789 +++++++++++++++++++++++++++++++++ 3 files changed, 799 insertions(+) create mode 100644 drivers/regulator/tps80031-regulator.c (limited to 'drivers/regulator') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 67d47b59a66d..2a1554b57104 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -415,6 +415,15 @@ config REGULATOR_TPS65912 help This driver supports TPS65912 voltage regulator chip. +config REGULATOR_TPS80031 + tristate "TI TPS80031/TPS80032 power regualtor driver" + depends on MFD_TPS80031 + help + TPS80031/ TPS80032 Fully Integrated Power Management with Power + Path and Battery Charger. It has 5 configurable step-down + converters, 11 general purpose LDOs, VBUS generator and digital + output to control regulators. + config REGULATOR_TWL4030 bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 PMIC" depends on TWL4030_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index e431eed8a878..e5882668618f 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o 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_TWL4030) += twl-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c new file mode 100644 index 000000000000..04844780b56c --- /dev/null +++ b/drivers/regulator/tps80031-regulator.c @@ -0,0 +1,789 @@ +/* + * tps80031-regulator.c -- TI TPS80031 regulator driver. + * + * Regulator driver for TITPS80031/TPS80032 Fully Integrated Power + * Management with Power Path and Battery Charger. + * + * Copyright (c) 2012, NVIDIA Corporation. + * + * Author: Laxman Dewangan + * + * 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 version 2. + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Flags for DCDC Voltage reading */ +#define DCDC_OFFSET_EN BIT(0) +#define DCDC_EXTENDED_EN BIT(1) +#define TRACK_MODE_ENABLE BIT(2) + +#define SMPS_MULTOFFSET_VIO BIT(1) +#define SMPS_MULTOFFSET_SMPS1 BIT(3) +#define SMPS_MULTOFFSET_SMPS2 BIT(4) +#define SMPS_MULTOFFSET_SMPS3 BIT(6) +#define SMPS_MULTOFFSET_SMPS4 BIT(0) + +#define SMPS_CMD_MASK 0xC0 +#define SMPS_VSEL_MASK 0x3F +#define LDO_VSEL_MASK 0x1F +#define LDO_TRACK_VSEL_MASK 0x3F + +#define MISC2_LDOUSB_IN_VSYS BIT(4) +#define MISC2_LDOUSB_IN_PMID BIT(3) +#define MISC2_LDOUSB_IN_MASK 0x18 + +#define MISC2_LDO3_SEL_VIB_VAL BIT(0) +#define MISC2_LDO3_SEL_VIB_MASK 0x1 + +#define BOOST_HW_PWR_EN BIT(5) +#define BOOST_HW_PWR_EN_MASK BIT(5) + +#define OPA_MODE_EN BIT(6) +#define OPA_MODE_EN_MASK BIT(6) + +#define USB_VBUS_CTRL_SET 0x04 +#define USB_VBUS_CTRL_CLR 0x05 +#define VBUS_DISCHRG 0x20 + +struct tps80031_regulator_info { + /* Regulator register address.*/ + u8 trans_reg; + u8 state_reg; + u8 force_reg; + u8 volt_reg; + u8 volt_id; + + /*Power request bits */ + int preq_bit; + + /* used by regulator core */ + struct regulator_desc desc; + +}; + +struct tps80031_regulator { + struct device *dev; + struct regulator_dev *rdev; + struct tps80031_regulator_info *rinfo; + + u8 device_flags; + unsigned int config_flags; + unsigned int ext_ctrl_flag; +}; + +static inline struct device *to_tps80031_dev(struct regulator_dev *rdev) +{ + return rdev_get_dev(rdev)->parent->parent; +} + +static int tps80031_reg_is_enabled(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + u8 reg_val; + int ret; + + if (ri->ext_ctrl_flag & EXT_PWR_REQ) + return true; + + ret = tps80031_read(parent, SLAVE_ID1, ri->rinfo->state_reg, ®_val); + if (ret < 0) { + dev_err(&rdev->dev, "Reg 0x%02x read failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; + } + return ((reg_val & STATE_MASK) == STATE_ON); +} + +static int tps80031_reg_enable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + if (ri->ext_ctrl_flag & EXT_PWR_REQ) + return 0; + + ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->state_reg, + STATE_ON, STATE_MASK); + if (ret < 0) { + dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; + } + return ret; +} + +static int tps80031_reg_disable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + if (ri->ext_ctrl_flag & EXT_PWR_REQ) + return 0; + + ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->state_reg, + STATE_OFF, STATE_MASK); + if (ret < 0) + dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", + ri->rinfo->state_reg, ret); + return ret; +} + +/* DCDC voltages for the selector of 58 to 63 */ +static int tps80031_dcdc_voltages[4][5] = { + { 1350, 1500, 1800, 1900, 2100}, + { 1350, 1500, 1800, 1900, 2100}, + { 2084, 2315, 2778, 2932, 3241}, + { 4167, 2315, 2778, 2932, 3241}, +}; + +static int tps80031_dcdc_list_voltage(struct regulator_dev *rdev, unsigned sel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + int volt_index = ri->device_flags & 0x3; + + if (sel == 0) + return 0; + else if (sel < 58) + return regulator_list_voltage_linear(rdev, sel - 1); + else + return tps80031_dcdc_voltages[volt_index][sel - 58] * 1000; +} + +static int tps80031_dcdc_set_voltage_sel(struct regulator_dev *rdev, + unsigned vsel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + u8 reg_val; + + if (ri->rinfo->force_reg) { + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, ®_val); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + if (!(reg_val & SMPS_CMD_MASK)) { + ret = tps80031_update(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, vsel, SMPS_VSEL_MASK); + if (ret < 0) + dev_err(ri->dev, + "reg 0x%02x update failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + } + ret = tps80031_update(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, vsel, SMPS_VSEL_MASK); + if (ret < 0) + dev_err(ri->dev, "reg 0x%02x update failed, e = %d\n", + ri->rinfo->volt_reg, ret); + return ret; +} + +static int tps80031_dcdc_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + uint8_t vsel = 0; + int ret; + + if (ri->rinfo->force_reg) { + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->force_reg, &vsel); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->force_reg, ret); + return ret; + } + + if (!(vsel & SMPS_CMD_MASK)) + return vsel & SMPS_VSEL_MASK; + } + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, &vsel); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + ri->rinfo->volt_reg, ret); + return ret; + } + return vsel & SMPS_VSEL_MASK; +} + +static int tps80031_ldo_set_voltage_sel(struct regulator_dev *rdev, + unsigned sel) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + /* Check for valid setting for TPS80031 or TPS80032-ES1.0 */ + if ((ri->rinfo->desc.id == TPS80031_REGULATOR_LDO2) && + (ri->device_flags & TRACK_MODE_ENABLE)) { + unsigned nvsel = (sel) & 0x1F; + if (((tps80031_get_chip_info(parent) == TPS80031) || + ((tps80031_get_chip_info(parent) == TPS80032) && + (tps80031_get_pmu_version(parent) == 0x0))) && + ((nvsel == 0x0) || (nvsel >= 0x19 && nvsel <= 0x1F))) { + dev_err(ri->dev, + "Invalid sel %d in track mode LDO2\n", + nvsel); + return -EINVAL; + } + } + + ret = tps80031_write(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, sel); + if (ret < 0) + dev_err(ri->dev, "Error in writing reg 0x%02x, e = %d\n", + ri->rinfo->volt_reg, ret); + return ret; +} + +static int tps80031_ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + uint8_t vsel; + int ret; + + ret = tps80031_read(parent, ri->rinfo->volt_id, + ri->rinfo->volt_reg, &vsel); + if (ret < 0) { + dev_err(ri->dev, "Error in writing the Voltage register\n"); + return ret; + } + return vsel & rdev->desc->vsel_mask; +} + +static int tps80031_ldo_list_voltage(struct regulator_dev *rdev, unsigned sel) +{ + if (sel == 0) + return 0; + else + return regulator_list_voltage_linear(rdev, sel - 1); +} + +static int tps80031_vbus_is_enabled(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret = -EIO; + uint8_t ctrl1 = 0; + uint8_t ctrl3 = 0; + + ret = tps80031_read(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, &ctrl1); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + ret = tps80031_read(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, &ctrl3); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + if ((ctrl1 & OPA_MODE_EN) && (ctrl3 & BOOST_HW_PWR_EN)) + return 1; + return ret; +} + +static int tps80031_vbus_enable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret; + + ret = tps80031_set_bits(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + + ret = tps80031_set_bits(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL3, ret); + return ret; + } + return ret; +} + +static int tps80031_vbus_disable(struct regulator_dev *rdev) +{ + struct tps80031_regulator *ri = rdev_get_drvdata(rdev); + struct device *parent = to_tps80031_dev(rdev); + int ret = 0; + + if (ri->config_flags & VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, SLAVE_ID2, + USB_VBUS_CTRL_SET, VBUS_DISCHRG); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", + USB_VBUS_CTRL_SET, ret); + return ret; + } + } + + ret = tps80031_clr_bits(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL1, ret); + return ret; + } + + ret = tps80031_clr_bits(parent, SLAVE_ID2, + TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", + TPS80031_CHARGERUSB_CTRL3, ret); + return ret; + } + + mdelay(DIV_ROUND_UP(ri->rinfo->desc.enable_time, 1000)); + if (ri->config_flags & VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, SLAVE_ID2, + USB_VBUS_CTRL_CLR, VBUS_DISCHRG); + if (ret < 0) { + dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", + USB_VBUS_CTRL_CLR, ret); + return ret; + } + } + return ret; +} + +static struct regulator_ops tps80031_dcdc_ops = { + .list_voltage = tps80031_dcdc_list_voltage, + .set_voltage_sel = tps80031_dcdc_set_voltage_sel, + .get_voltage_sel = tps80031_dcdc_get_voltage_sel, + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +static struct regulator_ops tps80031_ldo_ops = { + .list_voltage = tps80031_ldo_list_voltage, + .set_voltage_sel = tps80031_ldo_set_voltage_sel, + .get_voltage_sel = tps80031_ldo_get_voltage_sel, + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +static struct regulator_ops tps80031_vbus_sw_ops = { + .enable = tps80031_vbus_enable, + .disable = tps80031_vbus_disable, + .is_enabled = tps80031_vbus_is_enabled, +}; + +static struct regulator_ops tps80031_vbus_hw_ops = { +}; + +static struct regulator_ops tps80031_ext_reg_ops = { + .enable = tps80031_reg_enable, + .disable = tps80031_reg_disable, + .is_enabled = tps80031_reg_is_enabled, +}; + +/* Non-exiting default definition for some register */ +#define TPS80031_SMPS3_CFG_FORCE 0 +#define TPS80031_SMPS4_CFG_FORCE 0 + +#define TPS80031_VBUS_CFG_TRANS 0 +#define TPS80031_VBUS_CFG_STATE 0 + +#define TPS80031_REG_SMPS(_id, _volt_id, _pbit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .force_reg = TPS80031_##_id##_CFG_FORCE, \ + .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ + .volt_id = SLAVE_##_volt_id, \ + .preq_bit = _pbit, \ + .desc = { \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .n_voltages = 63, \ + .ops = &tps80031_dcdc_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = 500, \ + }, \ +} + +#define TPS80031_REG_LDO(_id, _preq_bit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ + .volt_id = SLAVE_ID1, \ + .preq_bit = _preq_bit, \ + .desc = { \ + .owner = THIS_MODULE, \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .ops = &tps80031_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .min_uV = 1000000, \ + .uV_step = 100000, \ + .n_voltages = 25, \ + .vsel_mask = LDO_VSEL_MASK, \ + .enable_time = 500, \ + }, \ +} + +#define TPS80031_REG_FIXED(_id, max_mV, _ops, _delay, _pbit) \ +{ \ + .trans_reg = TPS80031_##_id##_CFG_TRANS, \ + .state_reg = TPS80031_##_id##_CFG_STATE, \ + .volt_id = SLAVE_ID1, \ + .preq_bit = _pbit, \ + .desc = { \ + .name = "tps80031_"#_id, \ + .id = TPS80031_REGULATOR_##_id, \ + .n_voltages = 2, \ + .ops = &_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + .enable_time = _delay, \ + }, \ +} + +static struct tps80031_regulator_info tps80031_rinfo[TPS80031_REGULATOR_MAX] = { + TPS80031_REG_SMPS(VIO, ID0, 4), + TPS80031_REG_SMPS(SMPS1, ID0, 0), + TPS80031_REG_SMPS(SMPS2, ID0, 1), + TPS80031_REG_SMPS(SMPS3, ID1, 2), + TPS80031_REG_SMPS(SMPS4, ID1, 3), + TPS80031_REG_LDO(VANA, -1), + TPS80031_REG_LDO(LDO1, 8), + TPS80031_REG_LDO(LDO2, 9), + TPS80031_REG_LDO(LDO3, 10), + TPS80031_REG_LDO(LDO4, 11), + TPS80031_REG_LDO(LDO5, 12), + TPS80031_REG_LDO(LDO6, 13), + TPS80031_REG_LDO(LDO7, 14), + TPS80031_REG_LDO(LDOLN, 15), + TPS80031_REG_LDO(LDOUSB, 5), + TPS80031_REG_FIXED(VBUS, 5000, tps80031_vbus_hw_ops, 100000, -1), + TPS80031_REG_FIXED(REGEN1, 3300, tps80031_ext_reg_ops, 0, 16), + TPS80031_REG_FIXED(REGEN2, 3300, tps80031_ext_reg_ops, 0, 17), + TPS80031_REG_FIXED(SYSEN, 3300, tps80031_ext_reg_ops, 0, 18), +}; + +static int tps80031_power_req_config(struct device *parent, + struct tps80031_regulator *ri, + struct tps80031_regulator_platform_data *tps80031_pdata) +{ + int ret = 0; + + if (ri->rinfo->preq_bit < 0) + goto skip_pwr_req_config; + + ret = tps80031_ext_power_req_config(parent, ri->ext_ctrl_flag, + ri->rinfo->preq_bit, ri->rinfo->state_reg, + ri->rinfo->trans_reg); + if (ret < 0) { + dev_err(ri->dev, "ext powerreq config failed, err = %d\n", ret); + return ret; + } + +skip_pwr_req_config: + if (tps80031_pdata->ext_ctrl_flag & PWR_ON_ON_SLEEP) { + ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->trans_reg, + TRANS_SLEEP_ON, TRANS_SLEEP_MASK); + if (ret < 0) { + dev_err(ri->dev, "Reg 0x%02x update failed, e %d\n", + ri->rinfo->trans_reg, ret); + return ret; + } + } + return ret; +} + +static int tps80031_regulator_config(struct device *parent, + struct tps80031_regulator *ri, + struct tps80031_regulator_platform_data *tps80031_pdata) +{ + int ret = 0; + + switch (ri->rinfo->desc.id) { + case TPS80031_REGULATOR_LDOUSB: + if (ri->config_flags & + (USBLDO_INPUT_VSYS | USBLDO_INPUT_PMID)) { + unsigned val = 0; + if (ri->config_flags & USBLDO_INPUT_VSYS) + val = MISC2_LDOUSB_IN_VSYS; + else + val = MISC2_LDOUSB_IN_PMID; + + ret = tps80031_update(parent, SLAVE_ID1, + TPS80031_MISC2, val, MISC2_LDOUSB_IN_MASK); + if (ret < 0) { + dev_err(ri->dev, + "LDOUSB config failed, e= %d\n", ret); + return ret; + } + } + break; + + case TPS80031_REGULATOR_LDO3: + if (ri->config_flags & LDO3_OUTPUT_VIB) { + ret = tps80031_update(parent, SLAVE_ID1, + TPS80031_MISC2, MISC2_LDO3_SEL_VIB_VAL, + MISC2_LDO3_SEL_VIB_MASK); + if (ret < 0) { + dev_err(ri->dev, + "LDO3 config failed, e = %d\n", ret); + return ret; + } + } + break; + + case TPS80031_REGULATOR_VBUS: + /* Provide SW control Ops if VBUS is SW control */ + if (!(ri->config_flags & VBUS_SW_ONLY)) + ri->rinfo->desc.ops = &tps80031_vbus_sw_ops; + break; + default: + break; + } + + /* Configure Active state to ON, SLEEP to OFF and OFF_state to OFF */ + ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->trans_reg, + TRANS_ACTIVE_ON | TRANS_SLEEP_OFF | TRANS_OFF_OFF, + TRANS_ACTIVE_MASK | TRANS_SLEEP_MASK | TRANS_OFF_MASK); + if (ret < 0) { + dev_err(ri->dev, "trans reg update failed, e %d\n", ret); + return ret; + } + + return ret; +} + +static int check_smps_mode_mult(struct device *parent, + struct tps80031_regulator *ri) +{ + int mult_offset; + int ret; + u8 smps_offset; + u8 smps_mult; + + ret = tps80031_read(parent, SLAVE_ID1, + TPS80031_SMPS_OFFSET, &smps_offset); + if (ret < 0) { + dev_err(parent, "Error in reading smps offset register\n"); + return ret; + } + + ret = tps80031_read(parent, SLAVE_ID1, + TPS80031_SMPS_MULT, &smps_mult); + if (ret < 0) { + dev_err(parent, "Error in reading smps mult register\n"); + return ret; + } + + switch (ri->rinfo->desc.id) { + case TPS80031_REGULATOR_VIO: + mult_offset = SMPS_MULTOFFSET_VIO; + break; + case TPS80031_REGULATOR_SMPS1: + mult_offset = SMPS_MULTOFFSET_SMPS1; + break; + case TPS80031_REGULATOR_SMPS2: + mult_offset = SMPS_MULTOFFSET_SMPS2; + break; + case TPS80031_REGULATOR_SMPS3: + mult_offset = SMPS_MULTOFFSET_SMPS3; + break; + case TPS80031_REGULATOR_SMPS4: + mult_offset = SMPS_MULTOFFSET_SMPS4; + break; + case TPS80031_REGULATOR_LDO2: + ri->device_flags = smps_mult & BIT(5) ? TRACK_MODE_ENABLE : 0; + /* TRACK mode the ldo2 varies from 600mV to 1300mV */ + if (ri->device_flags & TRACK_MODE_ENABLE) { + ri->rinfo->desc.min_uV = 600000; + ri->rinfo->desc.uV_step = 12500; + ri->rinfo->desc.n_voltages = 57; + ri->rinfo->desc.vsel_mask = LDO_TRACK_VSEL_MASK; + } + return 0; + default: + return 0; + } + + ri->device_flags = (smps_offset & mult_offset) ? DCDC_OFFSET_EN : 0; + ri->device_flags |= (smps_mult & mult_offset) ? DCDC_EXTENDED_EN : 0; + switch (ri->device_flags) { + case 0: + ri->rinfo->desc.min_uV = 607700; + ri->rinfo->desc.uV_step = 12660; + break; + case DCDC_OFFSET_EN: + ri->rinfo->desc.min_uV = 700000; + ri->rinfo->desc.uV_step = 12500; + break; + case DCDC_EXTENDED_EN: + ri->rinfo->desc.min_uV = 1852000; + ri->rinfo->desc.uV_step = 38600; + break; + case DCDC_OFFSET_EN | DCDC_EXTENDED_EN: + ri->rinfo->desc.min_uV = 2161000; + ri->rinfo->desc.uV_step = 38600; + break; + } + return 0; +} + +static int __devinit tps80031_regulator_probe(struct platform_device *pdev) +{ + struct tps80031_platform_data *pdata; + struct tps80031_regulator_platform_data *tps_pdata; + struct tps80031_regulator_info *rinfo; + struct tps80031_regulator *ri; + struct tps80031_regulator *pmic; + struct regulator_dev *rdev; + struct regulator_config config = { }; + int ret; + int num; + + pdata = dev_get_platdata(pdev->dev.parent); + + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + return -EINVAL; + } + + pmic = devm_kzalloc(&pdev->dev, + TPS80031_REGULATOR_MAX * sizeof(*pmic), GFP_KERNEL); + if (!pmic) { + dev_err(&pdev->dev, "mem alloc for pmic failed\n"); + return -ENOMEM; + } + + for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { + tps_pdata = pdata->regulator_pdata[num]; + rinfo = &tps80031_rinfo[num]; + ri = &pmic[num]; + ri->rinfo = rinfo; + ri->dev = &pdev->dev; + + check_smps_mode_mult(pdev->dev.parent, ri); + config.dev = &pdev->dev; + config.init_data = NULL; + config.driver_data = ri; + if (tps_pdata) { + config.init_data = tps_pdata->reg_init_data; + ri->config_flags = tps_pdata->config_flags; + ri->ext_ctrl_flag = tps_pdata->ext_ctrl_flag; + ret = tps80031_regulator_config(pdev->dev.parent, + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "regulator config failed, e %d\n", ret); + goto fail; + } + + ret = tps80031_power_req_config(pdev->dev.parent, + ri, tps_pdata); + if (ret < 0) { + dev_err(&pdev->dev, + "pwr_req config failed, err %d\n", ret); + goto fail; + } + } + rdev = regulator_register(&ri->rinfo->desc, &config); + if (IS_ERR_OR_NULL(rdev)) { + dev_err(&pdev->dev, + "register regulator failed %s\n", + ri->rinfo->desc.name); + ret = PTR_ERR(rdev); + goto fail; + } + ri->rdev = rdev; + } + + platform_set_drvdata(pdev, pmic); + return 0; +fail: + while (--num >= 0) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return ret; +} + +static int __devexit tps80031_regulator_remove(struct platform_device *pdev) +{ + struct tps80031_regulator *pmic = platform_get_drvdata(pdev); + struct tps80031_regulator *ri = NULL; + int num; + + for (num = 0; num < TPS80031_REGULATOR_MAX; ++num) { + ri = &pmic[num]; + regulator_unregister(ri->rdev); + } + return 0; +} + +static struct platform_driver tps80031_regulator_driver = { + .driver = { + .name = "tps80031-pmic", + .owner = THIS_MODULE, + }, + .probe = tps80031_regulator_probe, + .remove = __devexit_p(tps80031_regulator_remove), +}; + +static int __init tps80031_regulator_init(void) +{ + return platform_driver_register(&tps80031_regulator_driver); +} +subsys_initcall(tps80031_regulator_init); + +static void __exit tps80031_regulator_exit(void) +{ + platform_driver_unregister(&tps80031_regulator_driver); +} +module_exit(tps80031_regulator_exit); + +MODULE_ALIAS("platform:tps80031-regulator"); +MODULE_DESCRIPTION("Regulator Driver for TI TPS80031 PMIC"); +MODULE_AUTHOR("Laxman Dewangan "); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From f8a9f757cb425c4784b80b001c7a77c7810b499f Mon Sep 17 00:00:00 2001 From: Frank Li Date: Mon, 12 Nov 2012 17:59:52 +0800 Subject: regulator: gpio-regulator: fix can't find regulator node in dt Need initilize of_node in regulator config when register regulator, otherwise regulator driver think it is no-dt device. in regulator_dev_lookup list_for_each_entry(r, ®ulator_list, list) if (r->dev.parent && node == r->dev.of_node) return r r->dev.of_noe will be zero if miss config in cfg. Signed-off-by: Frank Li Signed-off-by: Mark Brown --- drivers/regulator/gpio-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index e467d0ac8705..faa2f712eb0d 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -303,6 +303,7 @@ static int __devinit gpio_regulator_probe(struct platform_device *pdev) cfg.dev = &pdev->dev; cfg.init_data = config->init_data; cfg.driver_data = drvdata; + cfg.of_node = np; if (config->enable_gpio >= 0) cfg.ena_gpio = config->enable_gpio; -- cgit v1.2.3 From 216f2b9c95ac6e4f00b08df807bc4454434a9afb Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Wed, 14 Nov 2012 11:51:36 +0000 Subject: regulator: gpio-regulator: Catch 'no states property' misuse A selection of voltage or current values (AKA states) should always be specified when using a GPIO regulator. If there are no switchable states then the fixed regulators should be used instead. Signed-off-by: Lee Jones Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/regulator/gpio-regulator.txt | 3 ++- drivers/regulator/gpio-regulator.c | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/regulator') diff --git a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt index f71f8083c2ae..63c659800c03 100644 --- a/Documentation/devicetree/bindings/regulator/gpio-regulator.txt +++ b/Documentation/devicetree/bindings/regulator/gpio-regulator.txt @@ -2,11 +2,12 @@ GPIO controlled regulators Required properties: - compatible : Must be "regulator-gpio". +- states : Selection of available voltages and GPIO configs. + if there are no states, then use a fixed regulator Optional properties: - enable-gpio : GPIO to use to enable/disable the regulator. - gpios : GPIO group used to control voltage. -- states : Selection of available voltages and GPIO configs. - startup-delay-us : Startup time in microseconds. - enable-active-high : Polarity of GPIO is active high (default is low). diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c index faa2f712eb0d..dc9260545b80 100644 --- a/drivers/regulator/gpio-regulator.c +++ b/drivers/regulator/gpio-regulator.c @@ -183,6 +183,11 @@ of_get_gpio_regulator_config(struct device *dev, struct device_node *np) /* Fetch states. */ prop = of_find_property(np, "states", NULL); + if (!prop) { + dev_err(dev, "No 'states' property found\n"); + return ERR_PTR(-EINVAL); + } + proplen = prop->length / sizeof(int); config->states = devm_kzalloc(dev, -- cgit v1.2.3 From b92f787d1479ffbf6350114a7353e2fd32cafc41 Mon Sep 17 00:00:00 2001 From: Laxman Dewangan Date: Wed, 14 Nov 2012 21:09:29 +0530 Subject: regulator: tps80031: add prefix TPS80031 on common defines. Pefix "TPS80031" is added on all defines of tps80031 header to avoid conflict with other header definitions. Update the regualtor driver of tps80031 to use the same name. Signed-off-by: Laxman Dewangan Signed-off-by: Mark Brown --- drivers/regulator/tps80031-regulator.c | 80 ++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 38 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps80031-regulator.c b/drivers/regulator/tps80031-regulator.c index 04844780b56c..a37ede80abbf 100644 --- a/drivers/regulator/tps80031-regulator.c +++ b/drivers/regulator/tps80031-regulator.c @@ -105,16 +105,17 @@ static int tps80031_reg_is_enabled(struct regulator_dev *rdev) u8 reg_val; int ret; - if (ri->ext_ctrl_flag & EXT_PWR_REQ) + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) return true; - ret = tps80031_read(parent, SLAVE_ID1, ri->rinfo->state_reg, ®_val); + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + ®_val); if (ret < 0) { dev_err(&rdev->dev, "Reg 0x%02x read failed, err = %d\n", ri->rinfo->state_reg, ret); return ret; } - return ((reg_val & STATE_MASK) == STATE_ON); + return ((reg_val & TPS80031_STATE_MASK) == TPS80031_STATE_ON); } static int tps80031_reg_enable(struct regulator_dev *rdev) @@ -123,11 +124,11 @@ static int tps80031_reg_enable(struct regulator_dev *rdev) struct device *parent = to_tps80031_dev(rdev); int ret; - if (ri->ext_ctrl_flag & EXT_PWR_REQ) + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) return 0; - ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->state_reg, - STATE_ON, STATE_MASK); + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + TPS80031_STATE_ON, TPS80031_STATE_MASK); if (ret < 0) { dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", ri->rinfo->state_reg, ret); @@ -142,11 +143,11 @@ static int tps80031_reg_disable(struct regulator_dev *rdev) struct device *parent = to_tps80031_dev(rdev); int ret; - if (ri->ext_ctrl_flag & EXT_PWR_REQ) + if (ri->ext_ctrl_flag & TPS80031_EXT_PWR_REQ) return 0; - ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->state_reg, - STATE_OFF, STATE_MASK); + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->state_reg, + TPS80031_STATE_OFF, TPS80031_STATE_MASK); if (ret < 0) dev_err(&rdev->dev, "Reg 0x%02x update failed, err = %d\n", ri->rinfo->state_reg, ret); @@ -299,14 +300,14 @@ static int tps80031_vbus_is_enabled(struct regulator_dev *rdev) uint8_t ctrl1 = 0; uint8_t ctrl3 = 0; - ret = tps80031_read(parent, SLAVE_ID2, + ret = tps80031_read(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL1, &ctrl1); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", TPS80031_CHARGERUSB_CTRL1, ret); return ret; } - ret = tps80031_read(parent, SLAVE_ID2, + ret = tps80031_read(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL3, &ctrl3); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", @@ -324,7 +325,7 @@ static int tps80031_vbus_enable(struct regulator_dev *rdev) struct device *parent = to_tps80031_dev(rdev); int ret; - ret = tps80031_set_bits(parent, SLAVE_ID2, + ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", @@ -332,7 +333,7 @@ static int tps80031_vbus_enable(struct regulator_dev *rdev) return ret; } - ret = tps80031_set_bits(parent, SLAVE_ID2, + ret = tps80031_set_bits(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x read failed, e = %d\n", @@ -348,8 +349,8 @@ static int tps80031_vbus_disable(struct regulator_dev *rdev) struct device *parent = to_tps80031_dev(rdev); int ret = 0; - if (ri->config_flags & VBUS_DISCHRG_EN_PDN) { - ret = tps80031_write(parent, SLAVE_ID2, + if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, TPS80031_SLAVE_ID2, USB_VBUS_CTRL_SET, VBUS_DISCHRG); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", @@ -358,7 +359,7 @@ static int tps80031_vbus_disable(struct regulator_dev *rdev) } } - ret = tps80031_clr_bits(parent, SLAVE_ID2, + ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL1, OPA_MODE_EN); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", @@ -366,7 +367,7 @@ static int tps80031_vbus_disable(struct regulator_dev *rdev) return ret; } - ret = tps80031_clr_bits(parent, SLAVE_ID2, + ret = tps80031_clr_bits(parent, TPS80031_SLAVE_ID2, TPS80031_CHARGERUSB_CTRL3, BOOST_HW_PWR_EN); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x clearbit failed, e = %d\n", @@ -375,8 +376,8 @@ static int tps80031_vbus_disable(struct regulator_dev *rdev) } mdelay(DIV_ROUND_UP(ri->rinfo->desc.enable_time, 1000)); - if (ri->config_flags & VBUS_DISCHRG_EN_PDN) { - ret = tps80031_write(parent, SLAVE_ID2, + if (ri->config_flags & TPS80031_VBUS_DISCHRG_EN_PDN) { + ret = tps80031_write(parent, TPS80031_SLAVE_ID2, USB_VBUS_CTRL_CLR, VBUS_DISCHRG); if (ret < 0) { dev_err(ri->dev, "reg 0x%02x write failed, e = %d\n", @@ -433,7 +434,7 @@ static struct regulator_ops tps80031_ext_reg_ops = { .state_reg = TPS80031_##_id##_CFG_STATE, \ .force_reg = TPS80031_##_id##_CFG_FORCE, \ .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ - .volt_id = SLAVE_##_volt_id, \ + .volt_id = TPS80031_SLAVE_##_volt_id, \ .preq_bit = _pbit, \ .desc = { \ .name = "tps80031_"#_id, \ @@ -451,7 +452,7 @@ static struct regulator_ops tps80031_ext_reg_ops = { .trans_reg = TPS80031_##_id##_CFG_TRANS, \ .state_reg = TPS80031_##_id##_CFG_STATE, \ .volt_reg = TPS80031_##_id##_CFG_VOLTAGE, \ - .volt_id = SLAVE_ID1, \ + .volt_id = TPS80031_SLAVE_ID1, \ .preq_bit = _preq_bit, \ .desc = { \ .owner = THIS_MODULE, \ @@ -471,7 +472,7 @@ static struct regulator_ops tps80031_ext_reg_ops = { { \ .trans_reg = TPS80031_##_id##_CFG_TRANS, \ .state_reg = TPS80031_##_id##_CFG_STATE, \ - .volt_id = SLAVE_ID1, \ + .volt_id = TPS80031_SLAVE_ID1, \ .preq_bit = _pbit, \ .desc = { \ .name = "tps80031_"#_id, \ @@ -524,9 +525,10 @@ static int tps80031_power_req_config(struct device *parent, } skip_pwr_req_config: - if (tps80031_pdata->ext_ctrl_flag & PWR_ON_ON_SLEEP) { - ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->trans_reg, - TRANS_SLEEP_ON, TRANS_SLEEP_MASK); + if (tps80031_pdata->ext_ctrl_flag & TPS80031_PWR_ON_ON_SLEEP) { + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, + ri->rinfo->trans_reg, TPS80031_TRANS_SLEEP_ON, + TPS80031_TRANS_SLEEP_MASK); if (ret < 0) { dev_err(ri->dev, "Reg 0x%02x update failed, e %d\n", ri->rinfo->trans_reg, ret); @@ -544,16 +546,17 @@ static int tps80031_regulator_config(struct device *parent, switch (ri->rinfo->desc.id) { case TPS80031_REGULATOR_LDOUSB: - if (ri->config_flags & - (USBLDO_INPUT_VSYS | USBLDO_INPUT_PMID)) { + if (ri->config_flags & (TPS80031_USBLDO_INPUT_VSYS | + TPS80031_USBLDO_INPUT_PMID)) { unsigned val = 0; - if (ri->config_flags & USBLDO_INPUT_VSYS) + if (ri->config_flags & TPS80031_USBLDO_INPUT_VSYS) val = MISC2_LDOUSB_IN_VSYS; else val = MISC2_LDOUSB_IN_PMID; - ret = tps80031_update(parent, SLAVE_ID1, - TPS80031_MISC2, val, MISC2_LDOUSB_IN_MASK); + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, + TPS80031_MISC2, val, + MISC2_LDOUSB_IN_MASK); if (ret < 0) { dev_err(ri->dev, "LDOUSB config failed, e= %d\n", ret); @@ -563,8 +566,8 @@ static int tps80031_regulator_config(struct device *parent, break; case TPS80031_REGULATOR_LDO3: - if (ri->config_flags & LDO3_OUTPUT_VIB) { - ret = tps80031_update(parent, SLAVE_ID1, + if (ri->config_flags & TPS80031_LDO3_OUTPUT_VIB) { + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, TPS80031_MISC2, MISC2_LDO3_SEL_VIB_VAL, MISC2_LDO3_SEL_VIB_MASK); if (ret < 0) { @@ -577,7 +580,7 @@ static int tps80031_regulator_config(struct device *parent, case TPS80031_REGULATOR_VBUS: /* Provide SW control Ops if VBUS is SW control */ - if (!(ri->config_flags & VBUS_SW_ONLY)) + if (!(ri->config_flags & TPS80031_VBUS_SW_ONLY)) ri->rinfo->desc.ops = &tps80031_vbus_sw_ops; break; default: @@ -585,9 +588,10 @@ static int tps80031_regulator_config(struct device *parent, } /* Configure Active state to ON, SLEEP to OFF and OFF_state to OFF */ - ret = tps80031_update(parent, SLAVE_ID1, ri->rinfo->trans_reg, - TRANS_ACTIVE_ON | TRANS_SLEEP_OFF | TRANS_OFF_OFF, - TRANS_ACTIVE_MASK | TRANS_SLEEP_MASK | TRANS_OFF_MASK); + ret = tps80031_update(parent, TPS80031_SLAVE_ID1, ri->rinfo->trans_reg, + TPS80031_TRANS_ACTIVE_ON | TPS80031_TRANS_SLEEP_OFF | + TPS80031_TRANS_OFF_OFF, TPS80031_TRANS_ACTIVE_MASK | + TPS80031_TRANS_SLEEP_MASK | TPS80031_TRANS_OFF_MASK); if (ret < 0) { dev_err(ri->dev, "trans reg update failed, e %d\n", ret); return ret; @@ -604,14 +608,14 @@ static int check_smps_mode_mult(struct device *parent, u8 smps_offset; u8 smps_mult; - ret = tps80031_read(parent, SLAVE_ID1, + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, TPS80031_SMPS_OFFSET, &smps_offset); if (ret < 0) { dev_err(parent, "Error in reading smps offset register\n"); return ret; } - ret = tps80031_read(parent, SLAVE_ID1, + ret = tps80031_read(parent, TPS80031_SLAVE_ID1, TPS80031_SMPS_MULT, &smps_mult); if (ret < 0) { dev_err(parent, "Error in reading smps mult register\n"); -- cgit v1.2.3