diff options
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/Kconfig | 13 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/aat2870-regulator.c | 232 | ||||
-rw-r--r-- | drivers/regulator/core.c | 190 | ||||
-rw-r--r-- | drivers/regulator/dummy.c | 32 | ||||
-rw-r--r-- | drivers/regulator/tps65910-regulator.c | 63 | ||||
-rw-r--r-- | drivers/regulator/tps65912-regulator.c | 800 | ||||
-rw-r--r-- | drivers/regulator/twl-regulator.c | 66 | ||||
-rw-r--r-- | drivers/regulator/wm831x-dcdc.c | 126 | ||||
-rw-r--r-- | drivers/regulator/wm831x-ldo.c | 25 | ||||
-rw-r--r-- | drivers/regulator/wm8994-regulator.c | 4 |
11 files changed, 1354 insertions, 199 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 118eb213eb3..c7fd2c0e3f2 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -249,6 +249,12 @@ config REGULATOR_TPS6507X three step-down converters and two general-purpose LDO voltage regulators. It supports TI's software based Class-2 SmartReflex implementation. +config REGULATOR_TPS65912 + tristate "TI TPS65912 Power regulator" + depends on (MFD_TPS65912_I2C || MFD_TPS65912_SPI) + help + This driver supports TPS65912 voltage regulator chip. + config REGULATOR_88PM8607 bool "Marvell 88PM8607 Power regulators" depends on MFD_88PM860X=y @@ -304,5 +310,12 @@ config REGULATOR_TPS65910 help This driver supports TPS65910 voltage regulator chips. +config REGULATOR_AAT2870 + tristate "AnalogicTech AAT2870 Regulators" + depends on MFD_AAT2870_CORE + help + If you have a AnalogicTech AAT2870 say Y to enable the + regulator driver. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 3932d2ec38f..040d5aa6353 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -38,10 +38,12 @@ obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o +obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o +obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c new file mode 100644 index 00000000000..cd4104542f0 --- /dev/null +++ b/drivers/regulator/aat2870-regulator.c @@ -0,0 +1,232 @@ +/* + * linux/drivers/regulator/aat2870-regulator.c + * + * Copyright (c) 2011, NVIDIA Corporation. + * Author: Jin Park <jinyoungp@nvidia.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 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. + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/mfd/aat2870.h> + +struct aat2870_regulator { + struct platform_device *pdev; + struct regulator_desc desc; + + const int *voltages; /* uV */ + + int min_uV; + int max_uV; + + u8 enable_addr; + u8 enable_shift; + u8 enable_mask; + + u8 voltage_addr; + u8 voltage_shift; + u8 voltage_mask; +}; + +static int aat2870_ldo_list_voltage(struct regulator_dev *rdev, + unsigned selector) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + + return ri->voltages[selector]; +} + +static int aat2870_ldo_set_voltage_sel(struct regulator_dev *rdev, + unsigned selector) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + + return aat2870->update(aat2870, ri->voltage_addr, ri->voltage_mask, + (selector << ri->voltage_shift) & ri->voltage_mask); +} + +static int aat2870_ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + u8 val; + int ret; + + ret = aat2870->read(aat2870, ri->voltage_addr, &val); + if (ret) + return ret; + + return (val & ri->voltage_mask) >> ri->voltage_shift; +} + +static int aat2870_ldo_enable(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + + return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, + ri->enable_mask); +} + +static int aat2870_ldo_disable(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + + return aat2870->update(aat2870, ri->enable_addr, ri->enable_mask, 0); +} + +static int aat2870_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct aat2870_regulator *ri = rdev_get_drvdata(rdev); + struct aat2870_data *aat2870 = dev_get_drvdata(ri->pdev->dev.parent); + u8 val; + int ret; + + ret = aat2870->read(aat2870, ri->enable_addr, &val); + if (ret) + return ret; + + return val & ri->enable_mask ? 1 : 0; +} + +static struct regulator_ops aat2870_ldo_ops = { + .list_voltage = aat2870_ldo_list_voltage, + .set_voltage_sel = aat2870_ldo_set_voltage_sel, + .get_voltage_sel = aat2870_ldo_get_voltage_sel, + .enable = aat2870_ldo_enable, + .disable = aat2870_ldo_disable, + .is_enabled = aat2870_ldo_is_enabled, +}; + +static const int aat2870_ldo_voltages[] = { + 1200000, 1300000, 1500000, 1600000, + 1800000, 2000000, 2200000, 2500000, + 2600000, 2700000, 2800000, 2900000, + 3000000, 3100000, 3200000, 3300000, +}; + +#define AAT2870_LDO(ids) \ + { \ + .desc = { \ + .name = #ids, \ + .id = AAT2870_ID_##ids, \ + .n_voltages = ARRAY_SIZE(aat2870_ldo_voltages), \ + .ops = &aat2870_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ + .voltages = aat2870_ldo_voltages, \ + .min_uV = 1200000, \ + .max_uV = 3300000, \ + } + +static struct aat2870_regulator aat2870_regulators[] = { + AAT2870_LDO(LDOA), + AAT2870_LDO(LDOB), + AAT2870_LDO(LDOC), + AAT2870_LDO(LDOD), +}; + +static struct aat2870_regulator *aat2870_get_regulator(int id) +{ + struct aat2870_regulator *ri = NULL; + int i; + + for (i = 0; i < ARRAY_SIZE(aat2870_regulators); i++) { + ri = &aat2870_regulators[i]; + if (ri->desc.id == id) + break; + } + + if (!ri) + return NULL; + + ri->enable_addr = AAT2870_LDO_EN; + ri->enable_shift = id - AAT2870_ID_LDOA; + ri->enable_mask = 0x1 << ri->enable_shift; + + ri->voltage_addr = (id - AAT2870_ID_LDOA) / 2 ? + AAT2870_LDO_CD : AAT2870_LDO_AB; + ri->voltage_shift = (id - AAT2870_ID_LDOA) % 2 ? 0 : 4; + ri->voltage_mask = 0xF << ri->voltage_shift; + + return ri; +} + +static int aat2870_regulator_probe(struct platform_device *pdev) +{ + struct aat2870_regulator *ri; + struct regulator_dev *rdev; + + ri = aat2870_get_regulator(pdev->id); + if (!ri) { + dev_err(&pdev->dev, "Invalid device ID, %d\n", pdev->id); + return -EINVAL; + } + ri->pdev = pdev; + + rdev = regulator_register(&ri->desc, &pdev->dev, + pdev->dev.platform_data, ri); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register regulator %s\n", + ri->desc.name); + return PTR_ERR(rdev); + } + platform_set_drvdata(pdev, rdev); + + return 0; +} + +static int __devexit aat2870_regulator_remove(struct platform_device *pdev) +{ + struct regulator_dev *rdev = platform_get_drvdata(pdev); + + regulator_unregister(rdev); + return 0; +} + +static struct platform_driver aat2870_regulator_driver = { + .driver = { + .name = "aat2870-regulator", + .owner = THIS_MODULE, + }, + .probe = aat2870_regulator_probe, + .remove = __devexit_p(aat2870_regulator_remove), +}; + +static int __init aat2870_regulator_init(void) +{ + return platform_driver_register(&aat2870_regulator_driver); +} +subsys_initcall(aat2870_regulator_init); + +static void __exit aat2870_regulator_exit(void) +{ + platform_driver_unregister(&aat2870_regulator_driver); +} +module_exit(aat2870_regulator_exit); + +MODULE_DESCRIPTION("AnalogicTech AAT2870 Regulator"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jin Park <jinyoungp@nvidia.com>"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index d3e38790906..d8e6a429e8b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -20,6 +20,7 @@ #include <linux/debugfs.h> #include <linux/device.h> #include <linux/slab.h> +#include <linux/async.h> #include <linux/err.h> #include <linux/mutex.h> #include <linux/suspend.h> @@ -33,6 +34,8 @@ #include "dummy.h" +#define rdev_crit(rdev, fmt, ...) \ + pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) #define rdev_err(rdev, fmt, ...) \ pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__) #define rdev_warn(rdev, fmt, ...) \ @@ -78,11 +81,13 @@ struct regulator { char *supply_name; struct device_attribute dev_attr; struct regulator_dev *rdev; +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs; +#endif }; static int _regulator_is_enabled(struct regulator_dev *rdev); -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr); +static int _regulator_disable(struct regulator_dev *rdev); static int _regulator_get_voltage(struct regulator_dev *rdev); static int _regulator_get_current_limit(struct regulator_dev *rdev); static unsigned int _regulator_get_mode(struct regulator_dev *rdev); @@ -90,6 +95,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data); static int _regulator_do_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV); +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name); static const char *rdev_get_name(struct regulator_dev *rdev) { @@ -143,8 +151,11 @@ static int regulator_check_voltage(struct regulator_dev *rdev, if (*min_uV < rdev->constraints->min_uV) *min_uV = rdev->constraints->min_uV; - if (*min_uV > *max_uV) + if (*min_uV > *max_uV) { + rdev_err(rdev, "unsupportable voltage range: %d-%duV\n", + *min_uV, *max_uV); return -EINVAL; + } return 0; } @@ -197,8 +208,11 @@ static int regulator_check_current_limit(struct regulator_dev *rdev, if (*min_uA < rdev->constraints->min_uA) *min_uA = rdev->constraints->min_uA; - if (*min_uA > *max_uA) + if (*min_uA > *max_uA) { + rdev_err(rdev, "unsupportable current range: %d-%duA\n", + *min_uA, *max_uA); return -EINVAL; + } return 0; } @@ -213,6 +227,7 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode) case REGULATOR_MODE_STANDBY: break; default: + rdev_err(rdev, "invalid mode %x specified\n", *mode); return -EINVAL; } @@ -779,7 +794,6 @@ static int machine_constraints_voltage(struct regulator_dev *rdev, if (ret < 0) { rdev_err(rdev, "failed to apply %duV constraint\n", rdev->constraints->min_uV); - rdev->constraints = NULL; return ret; } } @@ -882,7 +896,6 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = suspend_prepare(rdev, rdev->constraints->initial_state); if (ret < 0) { rdev_err(rdev, "failed to set suspend state\n"); - rdev->constraints = NULL; goto out; } } @@ -909,13 +922,15 @@ static int set_machine_constraints(struct regulator_dev *rdev, ret = ops->enable(rdev); if (ret < 0) { rdev_err(rdev, "failed to enable\n"); - rdev->constraints = NULL; goto out; } } print_constraints(rdev); + return 0; out: + kfree(rdev->constraints); + rdev->constraints = NULL; return ret; } @@ -929,21 +944,20 @@ out: * core if it's child is enabled. */ static int set_supply(struct regulator_dev *rdev, - struct regulator_dev *supply_rdev) + struct regulator_dev *supply_rdev) { int err; - err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, - "supply"); - if (err) { - rdev_err(rdev, "could not add device link %s err %d\n", - supply_rdev->dev.kobj.name, err); - goto out; + rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev)); + + rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY"); + if (IS_ERR(rdev->supply)) { + err = PTR_ERR(rdev->supply); + rdev->supply = NULL; + return err; } - rdev->supply = supply_rdev; - list_add(&rdev->slist, &supply_rdev->supply_list); -out: - return err; + + return 0; } /** @@ -1032,7 +1046,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev) } } -#define REG_STR_SIZE 32 +#define REG_STR_SIZE 64 static struct regulator *create_regulator(struct regulator_dev *rdev, struct device *dev, @@ -1052,8 +1066,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, if (dev) { /* create a 'requested_microamps_name' sysfs entry */ - size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s", - supply_name); + size = scnprintf(buf, REG_STR_SIZE, + "microamps_requested_%s-%s", + dev_name(dev), supply_name); if (size >= REG_STR_SIZE) goto overflow_err; @@ -1088,7 +1103,28 @@ static struct regulator *create_regulator(struct regulator_dev *rdev, dev->kobj.name, err); goto link_name_err; } + } else { + regulator->supply_name = kstrdup(supply_name, GFP_KERNEL); + if (regulator->supply_name == NULL) + goto attr_err; + } + +#ifdef CONFIG_DEBUG_FS + regulator->debugfs = debugfs_create_dir(regulator->supply_name, + rdev->debugfs); + if (IS_ERR_OR_NULL(regulator->debugfs)) { + rdev_warn(rdev, "Failed to create debugfs directory\n"); + regulator->debugfs = NULL; + } else { + debugfs_create_u32("uA_load", 0444, regulator->debugfs, + ®ulator->uA_load); + debugfs_create_u32("min_uV", 0444, regulator->debugfs, + ®ulator->min_uV); + debugfs_create_u32("max_uV", 0444, regulator->debugfs, + ®ulator->max_uV); } +#endif + mutex_unlock(&rdev->mutex); return regulator; link_name_err: @@ -1267,13 +1303,17 @@ void regulator_put(struct regulator *regulator) mutex_lock(®ulator_list_mutex); rdev = regulator->rdev; +#ifdef CONFIG_DEBUG_FS + debugfs_remove_recursive(regulator->debugfs); +#endif + /* remove any sysfs entries */ if (regulator->dev) { sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); - kfree(regulator->supply_name); device_remove_file(regulator->dev, ®ulator->dev_attr); kfree(regulator->dev_attr.attr.name); } + kfree(regulator->supply_name); list_del(®ulator->list); kfree(regulator); @@ -1301,19 +1341,6 @@ static int _regulator_enable(struct regulator_dev *rdev) { int ret, delay; - if (rdev->use_count == 0) { - /* do we need to enable the supply regulator first */ - if (rdev->supply) { - mutex_lock(&rdev->supply->mutex); - ret = _regulator_enable(rdev->supply); - mutex_unlock(&rdev->supply->mutex); - if (ret < 0) { - rdev_err(rdev, "failed to enable: %d\n", ret); - return ret; - } - } - } - /* check voltage and requested load before enabling */ if (rdev->constraints && (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) @@ -1388,19 +1415,27 @@ int regulator_enable(struct regulator *regulator) struct regulator_dev *rdev = regulator->rdev; int ret = 0; + if (rdev->supply) { + ret = regulator_enable(rdev->supply); + if (ret != 0) + return ret; + } + mutex_lock(&rdev->mutex); ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); + + if (ret != 0) + regulator_disable(rdev->supply); + return ret; } EXPORT_SYMBOL_GPL(regulator_enable); /* locks held by regulator_disable() */ -static int _regulator_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_disable(struct regulator_dev *rdev) { int ret = 0; - *supply_rdev_ptr = NULL; if (WARN(rdev->use_count <= 0, "unbalanced disables for %s\n", rdev_get_name(rdev))) @@ -1427,9 +1462,6 @@ static int _regulator_disable(struct regulator_dev *rdev, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - rdev->use_count = 0; } else if (rdev->use_count > 1) { @@ -1440,6 +1472,7 @@ static int _regulator_disable(struct regulator_dev *rdev, rdev->use_count--; } + return ret; } @@ -1458,29 +1491,21 @@ static int _regulator_disable(struct regulator_dev *rdev, int regulator_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret = 0; mutex_lock(&rdev->mutex); - ret = _regulator_disable(rdev, &supply_rdev); + ret = _regulator_disable(rdev); mutex_unlock(&rdev->mutex); - /* decrease our supplies ref count and disable if required */ - while (supply_rdev != NULL) { - rdev = supply_rdev; - - mutex_lock(&rdev->mutex); - _regulator_disable(rdev, &supply_rdev); - mutex_unlock(&rdev->mutex); - } + if (ret == 0 && rdev->supply) + regulator_disable(rdev->supply); return ret; } EXPORT_SYMBOL_GPL(regulator_disable); /* locks held by regulator_force_disable() */ -static int _regulator_force_disable(struct regulator_dev *rdev, - struct regulator_dev **supply_rdev_ptr) +static int _regulator_force_disable(struct regulator_dev *rdev) { int ret = 0; @@ -1497,10 +1522,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev, REGULATOR_EVENT_DISABLE, NULL); } - /* decrease our supplies ref count and disable if required */ - *supply_rdev_ptr = rdev->supply; - - rdev->use_count = 0; return ret; } @@ -1516,16 +1537,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev, int regulator_force_disable(struct regulator *regulator) { struct regulator_dev *rdev = regulator->rdev; - struct regulator_dev *supply_rdev = NULL; int ret; mutex_lock(&rdev->mutex); regulator->uA_load = 0; - ret = _regulator_force_disable(rdev, &supply_rdev); + ret = _regulator_force_disable(regulator->rdev); mutex_unlock(&rdev->mutex); - if (supply_rdev) - regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev))); + if (rdev->supply) + while (rdev->open_count--) + regulator_disable(rdev->supply); return ret; } @@ -2136,7 +2157,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) /* get input voltage */ input_uV = 0; if (rdev->supply) - input_uV = _regulator_get_voltage(rdev->supply); + input_uV = regulator_get_voltage(rdev->supply); if (input_uV <= 0) input_uV = rdev->constraints->input_uV; if (input_uV <= 0) { @@ -2206,17 +2227,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier); static void _notifier_call_chain(struct regulator_dev *rdev, unsigned long event, void *data) { - struct regulator_dev *_rdev; - /* call rdev chain first */ blocking_notifier_call_chain(&rdev->notifier, event, NULL); - - /* now notify regulator we supply */ - list_for_each_entry(_rdev, &rdev->supply_list, slist) { - mutex_lock(&_rdev->mutex); - _notifier_call_chain(_rdev, event, data); - mutex_unlock(&_rdev->mutex); - } } /** @@ -2264,6 +2276,13 @@ err: } EXPORT_SYMBOL_GPL(regulator_bulk_get); +static void regulator_bulk_enable_async(void *data, async_cookie_t cookie) +{ + struct regulator_bulk_data *bulk = data; + + bulk->ret = regulator_enable(bulk->consumer); +} + /** * regulator_bulk_enable - enable multiple regulator consumers * @@ -2279,21 +2298,33 @@ EXPORT_SYMBOL_GPL(regulator_bulk_get); int regulator_bulk_enable(int num_consumers, struct regulator_bulk_data *consumers) { + LIST_HEAD(async_domain); int i; - int ret; + int ret = 0; + + for (i = 0; i < num_consumers; i++) + async_schedule_domain(regulator_bulk_enable_async, + &consumers[i], &async_domain); + + async_synchronize_full_domain(&async_domain); + /* If any consumer failed we need to unwind any that succeeded */ for (i = 0; i < num_consumers; i++) { - ret = regulator_enable(consumers[i].consumer); - if (ret != 0) + if (consumers[i].ret != 0) { + ret = consumers[i].ret; goto err; + } } return 0; err: - pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret); - for (--i; i >= 0; --i) - regulator_disable(consumers[i].consumer); + for (i = 0; i < num_consumers; i++) + if (consumers[i].ret == 0) + regulator_disable(consumers[i].consumer); + else + pr_err("Failed to enable %s: %d\n", + consumers[i].supply, consumers[i].ret); return ret; } @@ -2589,9 +2620,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, rdev->owner = regulator_desc->owner; rdev->desc = regulator_desc; INIT_LIST_HEAD(&rdev->consumer_list); - INIT_LIST_HEAD(&rdev->supply_list); INIT_LIST_HEAD(&rdev->list); - INIT_LIST_HEAD(&rdev->slist); BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); /* preform any regulator specific init */ @@ -2672,6 +2701,7 @@ unset_supplies: unset_regulator_supplies(rdev); scrub: + kfree(rdev->constraints); device_unregister(&rdev->dev); /* device core frees rdev */ rdev = ERR_PTR(ret); @@ -2703,7 +2733,7 @@ void regulator_unregister(struct regulator_dev *rdev) unset_regulator_supplies(rdev); list_del(&rdev->list); if (rdev->supply) - sysfs_remove_link(&rdev->dev.kobj, "supply"); + regulator_put(rdev->supply); device_unregister(&rdev->dev); kfree(rdev->constraints); mutex_unlock(®ulator_list_mutex); diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c index c7410bde7b5..f6ef6694ab9 100644 --- a/drivers/regulator/dummy.c +++ b/drivers/regulator/dummy.c @@ -36,6 +36,29 @@ static struct regulator_desc dummy_desc = { .ops = &dummy_ops, }; +static int __devinit dummy_regulator_probe(struct platform_device *pdev) +{ + int ret; + + dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, + &dummy_initdata, NULL); + if (IS_ERR(dummy_regulator_rdev)) { + ret = PTR_ERR(dummy_regulator_rdev); + pr_err("Failed to register regulator: %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver dummy_regulator_driver = { + .probe = dummy_regulator_probe, + .driver = { + .name = "reg-dummy", + .owner = THIS_MODULE, + }, +}; + static struct platform_device *dummy_pdev; void __init regulator_dummy_init(void) @@ -55,12 +78,9 @@ void __init regulator_dummy_init(void) return; } - dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, - &dummy_initdata, NULL); - if (IS_ERR(dummy_regulator_rdev)) { - ret = PTR_ERR(dummy_regulator_rdev); - pr_err("Failed to register regulator: %d\n", ret); + ret = platform_driver_register(&dummy_regulator_driver); + if (ret != 0) { + pr_err("Failed to register dummy regulator driver: %d\n", ret); platform_device_unregister(dummy_pdev); - return; } } diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c index 55dd4e6650d..66d2d60b436 100644 --- a/drivers/regulator/tps65910-regulator.c +++ b/drivers/regulator/tps65910-regulator.c @@ -49,7 +49,6 @@ #define TPS65911_REG_LDO7 11 #define TPS65911_REG_LDO8 12 -#define TPS65910_NUM_REGULATOR 13 #define TPS65910_SUPPLY_STATE_ENABLED 0x1 /* supported VIO voltages in milivolts */ @@ -264,11 +263,12 @@ static struct tps_info tps65911_regs[] = { }; struct tps65910_reg { - struct regulator_desc desc[TPS65910_NUM_REGULATOR]; + struct regulator_desc *desc; struct tps65910 *mfd; - struct regulator_dev *rdev[TPS65910_NUM_REGULATOR]; - struct tps_info *info[TPS65910_NUM_REGULATOR]; + struct regulator_dev **rdev; + struct tps_info **info; struct mutex mutex; + int num_regulators; int mode; int (*get_ctrl_reg)(int); }; @@ -759,8 +759,13 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev, mult = (selector / VDD1_2_NUM_VOLTS) + 1; volt = VDD1_2_MIN_VOLT + (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET; + break; case TPS65911_REG_VDDCTRL: volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET); + break; + default: + BUG(); + return -EINVAL; } return volt * 100 * mult; @@ -897,16 +902,42 @@ static __devinit int tps65910_probe(struct platform_device *pdev) switch(tps65910_chip_id(tps65910)) { case TPS65910: pmic->get_ctrl_reg = &tps65910_get_ctrl_register; + pmic->num_regulators = ARRAY_SIZE(tps65910_regs); info = tps65910_regs; + break; case TPS65911: pmic->get_ctrl_reg = &tps65911_get_ctrl_register; + pmic->num_regulators = ARRAY_SIZE(tps65911_regs); info = tps65911_regs; + break; default: pr_err("Invalid tps chip version\n"); + kfree(pmic); return -ENODEV; } - for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) { + pmic->desc = kcalloc(pmic->num_regulators, + sizeof(struct regulator_desc), GFP_KERNEL); + if (!pmic->desc) { + err = -ENOMEM; + goto err_free_pmic; + } + + pmic->info = kcalloc(pmic->num_regulators, + sizeof(struct tps_info *), GFP_KERNEL); + if (!pmic->info) { + err = -ENOMEM; + goto err_free_desc; + } + + pmic->rdev = kcalloc(pmic->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); + if (!pmic->rdev) { + err = -ENOMEM; + goto err_free_info; + } + + for (i = 0; i < pmic->num_regulators; i++, info++, reg_data++) { /* Register the regulators */ pmic->info[i] = info; @@ -938,7 +969,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev) "failed to register %s regulator\n", pdev->name); err = PTR_ERR(rdev); - goto err; + goto err_unregister_regulator; } /* Save regulator for cleanup */ @@ -946,23 +977,31 @@ static __devinit int tps65910_probe(struct platform_device *pdev) } return 0; -err: +err_unregister_regulator: while (--i >= 0) regulator_unregister(pmic->rdev[i]); - + kfree(pmic->rdev); +err_free_info: + kfree(pmic->info); +err_free_desc: + kfree(pmic->desc); +err_free_pmic: kfree(pmic); return err; } static int __devexit tps65910_remove(struct platform_device *pdev) { - struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev); + struct tps65910_reg *pmic = platform_get_drvdata(pdev); int i; - for (i = 0; i < TPS65910_NUM_REGULATOR; i++) - regulator_unregister(tps65910_reg->rdev[i]); + for (i = 0; i < pmic->num_regulators; i++) + regulator_unregister(pmic->rdev[i]); - kfree(tps65910_reg); + kfree(pmic->rdev); + kfree(pmic->info); + kfree(pmic->desc); + kfree(pmic); return 0; } diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c new file mode 100644 index 00000000000..3a9313e00fa --- /dev/null +++ b/drivers/regulator/tps65912-regulator.c @@ -0,0 +1,800 @@ +/* + * tps65912.c -- TI tps65912 + * + * Copyright 2011 Texas Instruments Inc. + * + * Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk> + * + * 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 driver is based on wm8350 implementation. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/gpio.h> +#include <linux/mfd/tps65912.h> + +/* DCDC's */ +#define TPS65912_REG_DCDC1 0 +#define TPS65912_REG_DCDC2 1 +#define TPS65912_REG_DCDC3 2 +#define TPS65912_REG_DCDC4 3 + +/* LDOs */ +#define TPS65912_REG_LDO1 4 +#define TPS65912_REG_LDO2 5 +#define TPS65912_REG_LDO3 6 +#define TPS65912_REG_LDO4 7 +#define TPS65912_REG_LDO5 8 +#define TPS65912_REG_LDO6 9 +#define TPS65912_REG_LDO7 10 +#define TPS65912_REG_LDO8 11 +#define TPS65912_REG_LDO9 12 +#define TPS65912_REG_LDO10 13 + +#define TPS65912_MAX_REG_ID TPS65912_REG_LDO_10 + +/* Number of step-down converters available */ +#define TPS65912_NUM_DCDC 4 + +/* Number of LDO voltage regulators available */ +#define TPS65912_NUM_LDO 10 + +/* Number of total regulators available */ +#define TPS65912_NUM_REGULATOR (TPS65912_NUM_DCDC + TPS65912_NUM_LDO) + +#define TPS65912_REG_ENABLED 0x80 +#define OP_SELREG_MASK 0x40 +#define OP_SELREG_SHIFT 6 + +struct tps_info { + const char *name; +}; + +static struct tps_info tps65912_regs[] = { + { + .name = "DCDC1", + }, + { + .name = "DCDC2", + }, + { + .name = "DCDC3", + }, + { + .name = "DCDC4", + }, + { + .name = "LDO1", + }, + { + .name = "LDO2", + }, + { + .name = "LDO3", + }, + { + .name = "LDO4", + }, + { + .name = "LDO5", + }, + { + .name = "LDO6", + }, + { + .name = "LDO7", + }, + { + .name = "LDO8", + }, + { + .name = "LDO9", + }, + { + .name = "LDO10", + }, +}; + +struct tps65912_reg { + struct regulator_desc desc[TPS65912_NUM_REGULATOR]; + struct tps65912 *mfd; + struct regulator_dev *rdev[TPS65912_NUM_REGULATOR]; + struct tps_info *info[TPS65912_NUM_REGULATOR]; + /* for read/write access */ + struct mutex io_lock; + int mode; + int (*get_ctrl_reg)(int); + int dcdc1_range; + int dcdc2_range; + int dcdc3_range; + int dcdc4_range; + int pwm_mode_reg; + int eco_reg; +}; + +static int tps65912_get_range(struct tps65912_reg *pmic, int id) +{ + struct tps65912 *mfd = pmic->mfd; + + if (id > TPS65912_REG_DCDC4) + return 0; + + switch (id) { + case TPS65912_REG_DCDC1: + pmic->dcdc1_range = tps65912_reg_read(mfd, + TPS65912_DCDC1_LIMIT); + if (pmic->dcdc1_range < 0) + return pmic->dcdc1_range; + pmic->dcdc1_range = (pmic->dcdc1_range & + DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; + return pmic->dcdc1_range; + case TPS65912_REG_DCDC2: + pmic->dcdc2_range = tps65912_reg_read(mfd, + TPS65912_DCDC2_LIMIT); + if (pmic->dcdc2_range < 0) + return pmic->dcdc2_range; + pmic->dcdc2_range = (pmic->dcdc2_range & + DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; + return pmic->dcdc2_range; + case TPS65912_REG_DCDC3: + pmic->dcdc3_range = tps65912_reg_read(mfd, + TPS65912_DCDC3_LIMIT); + if (pmic->dcdc3_range < 0) + return pmic->dcdc3_range; + pmic->dcdc3_range = (pmic->dcdc3_range & + DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; + return pmic->dcdc3_range; + case TPS65912_REG_DCDC4: + pmic->dcdc4_range = tps65912_reg_read(mfd, + TPS65912_DCDC4_LIMIT); + if (pmic->dcdc4_range < 0) + return pmic->dcdc4_range; + pmic->dcdc4_range = (pmic->dcdc4_range & + DCDC_LIMIT_RANGE_MASK) >> DCDC_LIMIT_RANGE_SHIFT; + return pmic->dcdc4_range; + default: + return 0; + } +} + +static unsigned long tps65912_vsel_to_uv_range0(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 12500) + 500000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range1(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 12500) + 700000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range2(u8 vsel) +{ + unsigned long uv; + + uv = ((vsel * 25000) + 500000); + return uv; +} + +static unsigned long tps65912_vsel_to_uv_range3(u8 vsel) +{ + unsigned long uv; + + if (vsel == 0x3f) + uv = 3800000; + else + uv = ((vsel * 50000) + 500000); + + return uv; +} + +static unsigned long tps65912_vsel_to_uv_ldo(u8 vsel) +{ + unsigned long uv = 0; + + if (vsel <= 32) + uv = ((vsel * 25000) + 800000); + else if (vsel > 32 && vsel <= 60) + uv = (((vsel - 32) * 50000) + 1600000); + else if (vsel > 60) + uv = (((vsel - 60) * 100000) + 3000000); + + return uv; +} + +static int tps65912_get_ctrl_register(int id) +{ + switch (id) { + case TPS65912_REG_DCDC1: + return TPS65912_DCDC1_AVS; + case TPS65912_REG_DCDC2: + return TPS65912_DCDC2_AVS; + case TPS65912_REG_DCDC3: + return TPS65912_DCDC3_AVS; + case TPS65912_REG_DCDC4: + return TPS65912_DCDC4_AVS; + case TPS65912_REG_LDO1: + return TPS65912_LDO1_AVS; + case TPS65912_REG_LDO2: + return TPS65912_LDO2_AVS; + case TPS65912_REG_LDO3: + return TPS65912_LDO3_AVS; + case TPS65912_REG_LDO4: + return TPS65912_LDO4_AVS; + case TPS65912_REG_LDO5: + return TPS65912_LDO5; + case TPS65912_REG_LDO6: + return TPS65912_LDO6; + case TPS65912_REG_LDO7: + return TPS65912_LDO7; + case TPS65912_REG_LDO8: + return TPS65912_LDO8; + case TPS65912_REG_LDO9: + return TPS65912_LDO9; + case TPS65912_REG_LDO10: + return TPS65912_LDO10; + default: + return -EINVAL; + } +} + +static int tps65912_get_dcdc_sel_register(struct tps65912_reg *pmic, int id) +{ + struct tps65912 *mfd = pmic->mfd; + int opvsel = 0, sr = 0; + u8 reg = 0; + + if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_DCDC4) + return -EINVAL; + + switch (id) { + case TPS65912_REG_DCDC1: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); + sr = ((opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT); + if (sr) + reg = TPS65912_DCDC1_AVS; + else + reg = TPS65912_DCDC1_OP; + break; + case TPS65912_REG_DCDC2: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_DCDC2_AVS; + else + reg = TPS65912_DCDC2_OP; + break; + case TPS65912_REG_DCDC3: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_DCDC3_AVS; + else + reg = TPS65912_DCDC3_OP; + break; + case TPS65912_REG_DCDC4: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_DCDC4_AVS; + else + reg = TPS65912_DCDC4_OP; + break; + } + return reg; +} + +static int tps65912_get_ldo_sel_register(struct tps65912_reg *pmic, int id) +{ + struct tps65912 *mfd = pmic->mfd; + int opvsel = 0, sr = 0; + u8 reg = 0; + + if (id < TPS65912_REG_LDO1 || id > TPS65912_REG_LDO10) + return -EINVAL; + + switch (id) { + case TPS65912_REG_LDO1: + opvsel = tps65912_reg_read(mfd, TPS65912_LDO1_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_LDO1_AVS; + else + reg = TPS65912_LDO1_OP; + break; + case TPS65912_REG_LDO2: + opvsel = tps65912_reg_read(mfd, TPS65912_LDO2_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_LDO2_AVS; + else + reg = TPS65912_LDO2_OP; + break; + case TPS65912_REG_LDO3: + opvsel = tps65912_reg_read(mfd, TPS65912_LDO3_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_LDO3_AVS; + else + reg = TPS65912_LDO3_OP; + break; + case TPS65912_REG_LDO4: + opvsel = tps65912_reg_read(mfd, TPS65912_LDO4_OP); + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + reg = TPS65912_LDO4_AVS; + else + reg = TPS65912_LDO4_OP; + break; + case TPS65912_REG_LDO5: + reg = TPS65912_LDO5; + break; + case TPS65912_REG_LDO6: + reg = TPS65912_LDO6; + break; + case TPS65912_REG_LDO7: + reg = TPS65912_LDO7; + break; + case TPS65912_REG_LDO8: + reg = TPS65912_LDO8; + break; + case TPS65912_REG_LDO9: + reg = TPS65912_LDO9; + break; + case TPS65912_REG_LDO10: + reg = TPS65912_LDO10; + break; + } + + return reg; +} + +static int tps65912_get_mode_regiters(struct tps65912_reg *pmic, int id) +{ + switch (id) { + case TPS65912_REG_DCDC1: + pmic->pwm_mode_reg = TPS65912_DCDC1_CTRL; + pmic->eco_reg = TPS65912_DCDC1_AVS; + break; + case TPS65912_REG_DCDC2: + pmic->pwm_mode_reg = TPS65912_DCDC2_CTRL; + pmic->eco_reg = TPS65912_DCDC2_AVS; + break; + case TPS65912_REG_DCDC3: + pmic->pwm_mode_reg = TPS65912_DCDC3_CTRL; + pmic->eco_reg = TPS65912_DCDC3_AVS; + break; + case TPS65912_REG_DCDC4: + pmic->pwm_mode_reg = TPS65912_DCDC4_CTRL; + pmic->eco_reg = TPS65912_DCDC4_AVS; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tps65912_reg_is_enabled(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int reg, value, id = rdev_get_id(dev); + + if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) + return -EINVAL; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + value = tps65912_reg_read(mfd, reg); + if (value < 0) + return value; + + return value & TPS65912_REG_ENABLED; +} + +static int tps65912_reg_enable(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int reg; + + if (id < TPS65912_REG_DCDC1 || id > TPS65912_REG_LDO10) + return -EINVAL; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65912_set_bits(mfd, reg, TPS65912_REG_ENABLED); +} + +static int tps65912_reg_disable(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev), reg; + + reg = pmic->get_ctrl_reg(id); + if (reg < 0) + return reg; + + return tps65912_clear_bits(mfd, reg, TPS65912_REG_ENABLED); +} + +static int tps65912_set_mode(struct regulator_dev *dev, unsigned int mode) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int pwm_mode, eco, id = rdev_get_id(dev); + + tps65912_get_mode_regiters(pmic, id); + + pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); + eco = tps65912_reg_read(mfd, pmic->eco_reg); + + pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; + eco &= DCDC_AVS_ECO_MASK; + + switch (mode) { + case REGULATOR_MODE_FAST: + /* Verify if mode alredy set */ + if (pwm_mode && !eco) + break; + tps65912_set_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + case REGULATOR_MODE_NORMAL: + case REGULATOR_MODE_IDLE: + if (!pwm_mode && !eco) + break; + tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_clear_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + case REGULATOR_MODE_STANDBY: + if (!pwm_mode && eco) + break; + tps65912_clear_bits(mfd, pmic->pwm_mode_reg, DCDCCTRL_DCDC_MODE_MASK); + tps65912_set_bits(mfd, pmic->eco_reg, DCDC_AVS_ECO_MASK); + break; + default: + return -EINVAL; + } + + return 0; +} + +static unsigned int tps65912_get_mode(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int pwm_mode, eco, mode = 0, id = rdev_get_id(dev); + + tps65912_get_mode_regiters(pmic, id); + + pwm_mode = tps65912_reg_read(mfd, pmic->pwm_mode_reg); + eco = tps65912_reg_read(mfd, pmic->eco_reg); + + pwm_mode &= DCDCCTRL_DCDC_MODE_MASK; + eco &= DCDC_AVS_ECO_MASK; + + if (pwm_mode && !eco) + mode = REGULATOR_MODE_FAST; + else if (!pwm_mode && !eco) + mode = REGULATOR_MODE_NORMAL; + else if (!pwm_mode && eco) + mode = REGULATOR_MODE_STANDBY; + + return mode; +} + +static int tps65912_get_voltage_dcdc(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev), voltage = 0, range; + int opvsel = 0, avsel = 0, sr, vsel; + + switch (id) { + case TPS65912_REG_DCDC1: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC1_OP); + avsel = tps65912_reg_read(mfd, TPS65912_DCDC1_AVS); + range = pmic->dcdc1_range; + break; + case TPS65912_REG_DCDC2: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC2_OP); + avsel = tps65912_reg_read(mfd, TPS65912_DCDC2_AVS); + range = pmic->dcdc2_range; + break; + case TPS65912_REG_DCDC3: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC3_OP); + avsel = tps65912_reg_read(mfd, TPS65912_DCDC3_AVS); + range = pmic->dcdc3_range; + break; + case TPS65912_REG_DCDC4: + opvsel = tps65912_reg_read(mfd, TPS65912_DCDC4_OP); + avsel = tps65912_reg_read(mfd, TPS65912_DCDC4_AVS); + range = pmic->dcdc4_range; + break; + default: + return -EINVAL; + } + + sr = (opvsel & OP_SELREG_MASK) >> OP_SELREG_SHIFT; + if (sr) + vsel = avsel; + else + vsel = opvsel; + vsel &= 0x3F; + + switch (range) { + case 0: + /* 0.5 - 1.2875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range0(vsel); + break; + case 1: + /* 0.7 - 1.4875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range1(vsel); + break; + case 2: + /* 0.5 - 2.075V in 25mV steps */ + voltage = tps65912_vsel_to_uv_range2(vsel); + break; + case 3: + /* 0.5 - 3.8V in 50mV steps */ + voltage = tps65912_vsel_to_uv_range3(vsel); + break; + } + return voltage; +} + +static int tps65912_set_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int value; + u8 reg; + + reg = tps65912_get_dcdc_sel_register(pmic, id); + value = tps65912_reg_read(mfd, reg); + value &= 0xC0; + return tps65912_reg_write(mfd, reg, selector | value); +} + +static int tps65912_get_voltage_ldo(struct regulator_dev *dev) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev); + int vsel = 0; + u8 reg; + + reg = tps65912_get_ldo_sel_register(pmic, id); + vsel = tps65912_reg_read(mfd, reg); + vsel &= 0x3F; + + return tps65912_vsel_to_uv_ldo(vsel); +} + +static int tps65912_set_voltage_ldo(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + struct tps65912 *mfd = pmic->mfd; + int id = rdev_get_id(dev), reg, value; + + reg = tps65912_get_ldo_sel_register(pmic, id); + value = tps65912_reg_read(mfd, reg); + value &= 0xC0; + return tps65912_reg_write(mfd, reg, selector | value); +} + +static int tps65912_list_voltage_dcdc(struct regulator_dev *dev, + unsigned selector) +{ + struct tps65912_reg *pmic = rdev_get_drvdata(dev); + int range, voltage = 0, id = rdev_get_id(dev); + + switch (id) { + case TPS65912_REG_DCDC1: + range = pmic->dcdc1_range; + break; + case TPS65912_REG_DCDC2: + range = pmic->dcdc2_range; + break; + case TPS65912_REG_DCDC3: + range = pmic->dcdc3_range; + break; + case TPS65912_REG_DCDC4: + range = pmic->dcdc4_range; + break; + default: + return -EINVAL; + } + + switch (range) { + case 0: + /* 0.5 - 1.2875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range0(selector); + break; + case 1: + /* 0.7 - 1.4875V in 12.5mV steps */ + voltage = tps65912_vsel_to_uv_range1(selector); + break; + case 2: + /* 0.5 - 2.075V in 25mV steps */ + voltage = tps65912_vsel_to_uv_range2(selector); + break; + case 3: + /* 0.5 - 3.8V in 50mV steps */ + voltage = tps65912_vsel_to_uv_range3(selector); + break; + } + return voltage; +} + +static int tps65912_list_voltage_ldo(struct regulator_dev *dev, + unsigned selector) +{ + int ldo = rdev_get_id(dev); + + if (ldo < TPS65912_REG_LDO1 || ldo > TPS65912_REG_LDO10) + return -EINVAL; + + return tps65912_vsel_to_uv_ldo(selector); +} + +/* Operations permitted on DCDCx */ +static struct regulator_ops tps65912_ops_dcdc = { + .is_enabled = tps65912_reg_is_enabled, + .enable = tps65912_reg_enable, + .disable = tps65912_reg_disable, + .set_mode = tps65912_set_mode, + .get_mode = tps65912_get_mode, + .get_voltage = tps65912_get_voltage_dcdc, + .set_voltage_sel = tps65912_set_voltage_dcdc, + .list_voltage = tps65912_list_voltage_dcdc, +}; + +/* Operations permitted on LDOx */ +static struct regulator_ops tps65912_ops_ldo = { + .is_enabled = tps65912_reg_is_enabled, + .enable = tps65912_reg_enable, + .disable = tps65912_reg_disable, + .get_voltage = tps65912_get_voltage_ldo, + .set_voltage_sel = tps65912_set_voltage_ldo, + .list_voltage = tps65912_list_voltage_ldo, +}; + +static __devinit int tps65912_probe(struct platform_device *pdev) +{ + struct tps65912 *tps65912 = dev_get_drvdata(pdev->dev.parent); + struct tps_info *info; + struct regulator_init_data *reg_data; + struct regulator_dev *rdev; + struct tps65912_reg *pmic; + struct tps65912_board *pmic_plat_data; + int i, err; + + pmic_plat_data = dev_get_platdata(tps65912->dev); + if (!pmic_plat_data) + return -EINVAL; + + reg_data = pmic_plat_data->tps65912_pmic_init_data; + + pmic = kzalloc(sizeof(*pmic), GFP_KERNEL); + if (!pmic) + return -ENOMEM; + + mutex_init(&pmic->io_lock); + pmic->mfd = tps65912; + platform_set_drvdata(pdev, pmic); + + pmic->get_ctrl_reg = &tps65912_get_ctrl_register; + info = tps65912_regs; + + for (i = 0; i < TPS65912_NUM_REGULATOR; i++, info++, reg_data++) { + int range = 0; + /* Register the regulators */ + pmic->info[i] = info; + + pmic->desc[i].name = info->name; + pmic->desc[i].id = i; + pmic->desc[i].n_voltages = 64; + pmic->desc[i].ops = (i > TPS65912_REG_DCDC4 ? + &tps65912_ops_ldo : &tps65912_ops_dcdc); + pmic->desc[i].type = REGULATOR_VOLTAGE; + pmic->desc[i].owner = THIS_MODULE; + range = tps65912_get_range(pmic, i); + rdev = regulator_register(&pmic->desc[i], + tps65912->dev, reg_data, pmic); + if (IS_ERR(rdev)) { + dev_err(tps65912->dev, + "failed to register %s regulator\n", + pdev->name); + err = PTR_ERR(rdev); + goto err; + } + + /* Save regulator for cleanup */ + pmic->rdev[i] = rdev; + } + return 0; + +err: + while (--i >= 0) + regulator_unregister(pmic->rdev[i]); + + kfree(pmic); + return err; +} + +static int __devexit tps65912_remove(struct platform_device *pdev) +{ + struct tps65912_reg *tps65912_reg = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < TPS65912_NUM_REGULATOR; i++) + regulator_unregister(tps65912_reg->rdev[i]); + + kfree(tps65912_reg); + return 0; +} + +static struct platform_driver tps65912_driver = { + .driver = { + .name = "tps65912-pmic", + .owner = THIS_MODULE, + }, + .probe = tps65912_probe, + .remove = __devexit_p(tps65912_remove), +}; + +/** + * tps65912_init + * + * Module init function + */ +static int __init tps65912_init(void) +{ + return platform_driver_register(&tps65912_driver); +} +subsys_initcall(tps65912_init); + +/** + * tps65912_cleanup + * + * Module exit function + */ +static void __exit tps65912_cleanup(void) +{ + platform_driver_unregister(&tps65912_driver); +} +module_exit(tps65912_cleanup); + +MODULE_AUTHOR("Margarita Olaya Cabrera <magi@slimlogic.co.uk>"); +MODULE_DESCRIPTION("TPS65912 voltage regulator driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:tps65912-pmic"); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index 87fe0f75a56..ee8747f4fa0 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -835,8 +835,8 @@ static struct regulator_ops twlsmps_ops = { remap_conf) \ TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ remap_conf, TWL4030, twl4030fixed_ops) -#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \ - TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \ +#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \ + TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \ 0x0, TWL6030, twl6030fixed_ops) #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \ @@ -856,24 +856,22 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ +#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ .base = offset, \ - .id = num, \ .min_mV = min_mVolts, \ .max_mV = max_mVolts, \ .desc = { \ .name = #label, \ .id = TWL6030_REG_##label, \ - .n_voltages = (max_mVolts - min_mVolts)/100, \ + .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \ .ops = &twl6030ldo_ops, \ .type = REGULATOR_VOLTAGE, \ .owner = THIS_MODULE, \ }, \ } -#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \ +#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \ .base = offset, \ - .id = num, \ .min_mV = min_mVolts, \ .max_mV = max_mVolts, \ .desc = { \ @@ -903,9 +901,8 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \ +#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \ .base = offset, \ - .id = num, \ .delay = turnon_delay, \ .desc = { \ .name = #label, \ @@ -916,9 +913,8 @@ static struct regulator_ops twlsmps_ops = { }, \ } -#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \ +#define TWL6025_ADJUSTABLE_SMPS(label, offset) { \ .base = offset, \ - .id = num, \ .min_mV = 600, \ .max_mV = 2100, \ .desc = { \ @@ -961,32 +957,32 @@ static struct twlreg_info twl_regs[] = { /* 6030 REG with base as PMC Slave Misc : 0x0030 */ /* Turnon-delay and remap configuration values for 6030 are not verified since the specification is not public */ - TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1), - TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2), - TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3), - TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4), - TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5), - TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7), - TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0), - TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0), - TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0), - TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0), - TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0), + TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300), + TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300), + TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0), + TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0), + TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0), + TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0), + TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0), /* 6025 are renamed compared to 6030 versions */ - TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1), - TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2), - TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3), - TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4), - TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5), - TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7), - TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16), - TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17), - TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18), - - TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1), - TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2), - TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3), + TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300), + TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300), + + TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34), + TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10), + TWL6025_ADJUSTABLE_SMPS(VIO, 0x16), }; static u8 twl_get_smps_offset(void) diff --git a/drivers/regulator/wm831x-dcdc.c b/drivers/regulator/wm831x-dcdc.c index a0982e80985..bd3531d8b2a 100644 --- a/drivers/regulator/wm831x-dcdc.c +++ b/drivers/regulator/wm831x-dcdc.c @@ -267,23 +267,6 @@ static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev, return vsel; } -static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev, - int min_uV, int max_uV) -{ - u16 vsel; - - if (max_uV < 600000 || max_uV > 1800000) - return -EINVAL; - - vsel = ((max_uV - 600000) / 12500) + 8; - - if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV || - wm831x_buckv_list_voltage(rdev, vsel) < max_uV) - return -EINVAL; - - return vsel; -} - static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state) { struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev); @@ -338,28 +321,23 @@ static int wm831x_buckv_set_voltage(struct regulator_dev *rdev, if (ret < 0) return ret; - /* Set the high voltage as the DVS voltage. This is optimised - * for CPUfreq usage, most processors will keep the maximum - * voltage constant and lower the minimum with the frequency. */ - vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV); - if (vsel < 0) { - /* This should never happen - at worst the same vsel - * should be chosen */ - WARN_ON(vsel < 0); - return 0; + /* + * If this VSEL is higher than the last one we've seen then + * remember it as the DVS VSEL. This is optimised for CPUfreq + * usage where we want to get to the highest voltage very + * quickly. + */ + if (vsel > dcdc->dvs_vsel) { + ret = wm831x_set_bits(wm831x, dvs_reg, + WM831X_DC1_DVS_VSEL_MASK, + dcdc->dvs_vsel); + if (ret == 0) + dcdc->dvs_vsel = vsel; + else + dev_warn(wm831x->dev, + "Failed to set DCDC DVS VSEL: %d\n", ret); } - /* Don't bother if it's the same VSEL we're already using */ - if (vsel == dcdc->on_vsel) - return 0; - - ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel); - if (ret == 0) - dcdc->dvs_vsel = vsel; - else - dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n", - ret); - return 0; } @@ -456,27 +434,6 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, if (!pdata || !pdata->dvs_gpio) return; - switch (pdata->dvs_control_src) { - case 1: - ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; - break; - case 2: - ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT; - break; - default: - dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n", - pdata->dvs_control_src, dcdc->name); - return; - } - - ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL, - WM831X_DC1_DVS_SRC_MASK, ctrl); - if (ret < 0) { - dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n", - dcdc->name, ret); - return; - } - ret = gpio_request(pdata->dvs_gpio, "DCDC DVS"); if (ret < 0) { dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n", @@ -498,17 +455,57 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc, } dcdc->dvs_gpio = pdata->dvs_gpio; + + switch (pdata->dvs_control_src) { + case 1: + ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT; + break; + case 2: + ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT; + break; + default: + dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n", + pdata->dvs_control_src, dcdc->name); + return; + } + + /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL + * to make bootstrapping a bit smoother. + */ + if (!dcdc->dvs_vsel) { + ret = wm831x_set_bits(wm831x, + dcdc->base + WM831X_DCDC_DVS_CONTROL, + WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel); + if (ret == 0) + dcdc->dvs_vsel = dcdc->on_vsel; + else + dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n", + ret); + } + + ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL, + WM831X_DC1_DVS_SRC_MASK, ctrl); + if (ret < 0) { + dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n", + dcdc->name, ret); + } } static __devinit int wm831x_buckv_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int id = pdev->id % ARRAY_SIZE(pdata->dcdc); + int id; struct wm831x_dcdc *dcdc; struct resource *res; int ret, irq; + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); if (pdata == NULL || pdata->dcdc[id] == NULL) @@ -545,7 +542,7 @@ static __devinit int wm831x_buckv_probe(struct platform_device *pdev) } dcdc->on_vsel = ret & WM831X_DC1_ON_VSEL_MASK; - ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_ON_CONFIG); + ret = wm831x_reg_read(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL); if (ret < 0) { dev_err(wm831x->dev, "Failed to read DVS VSEL: %d\n", ret); goto err; @@ -709,11 +706,17 @@ static __devinit int wm831x_buckp_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int id = pdev->id % ARRAY_SIZE(pdata->dcdc); + int id; struct wm831x_dcdc *dcdc; struct resource *res; int ret, irq; + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + dev_dbg(&pdev->dev, "Probing DCDC%d\n", id + 1); if (pdata == NULL || pdata->dcdc[id] == NULL) @@ -1046,3 +1049,4 @@ MODULE_DESCRIPTION("WM831x DC-DC convertor driver"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:wm831x-buckv"); MODULE_ALIAS("platform:wm831x-buckp"); +MODULE_ALIAS("platform:wm831x-epe"); diff --git a/drivers/regulator/wm831x-ldo.c b/drivers/regulator/wm831x-ldo.c index 2220cf8defb..6709710a059 100644 --- a/drivers/regulator/wm831x-ldo.c +++ b/drivers/regulator/wm831x-ldo.c @@ -310,11 +310,17 @@ static __devinit int wm831x_gp_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int id = pdev->id % ARRAY_SIZE(pdata->ldo); + int id; struct wm831x_ldo *ldo; struct resource *res; int ret, irq; + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) @@ -574,11 +580,17 @@ static __devinit int wm831x_aldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int id = pdev->id % ARRAY_SIZE(pdata->ldo); + int id; struct wm831x_ldo *ldo; struct resource *res; int ret, irq; + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) @@ -764,11 +776,18 @@ static __devinit int wm831x_alive_ldo_probe(struct platform_device *pdev) { struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); struct wm831x_pdata *pdata = wm831x->dev->platform_data; - int id = pdev->id % ARRAY_SIZE(pdata->ldo); + int id; struct wm831x_ldo *ldo; struct resource *res; int ret; + if (pdata && pdata->wm831x_num) + id = (pdata->wm831x_num * 10) + 1; + else + id = 0; + id = pdev->id - id; + + dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1); if (pdata == NULL || pdata->ldo[id] == NULL) diff --git a/drivers/regulator/wm8994-regulator.c b/drivers/regulator/wm8994-regulator.c index 35b2958d510..1a6a690f24d 100644 --- a/drivers/regulator/wm8994-regulator.c +++ b/drivers/regulator/wm8994-regulator.c @@ -43,7 +43,7 @@ static int wm8994_ldo_enable(struct regulator_dev *rdev) if (!ldo->enable) return 0; - gpio_set_value(ldo->enable, 1); + gpio_set_value_cansleep(ldo->enable, 1); ldo->is_enabled = true; return 0; @@ -57,7 +57,7 @@ static int wm8994_ldo_disable(struct regulator_dev *rdev) if (!ldo->enable) return -EINVAL; - gpio_set_value(ldo->enable, 0); + gpio_set_value_cansleep(ldo->enable, 0); ldo->is_enabled = false; return 0; |