diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-01 15:06:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-11-01 15:06:20 -0700 |
commit | f3c3f0670501fee130f22193369249deea8cc630 (patch) | |
tree | 27ae58e4f338d571be93a7557ae7de48de10a947 | |
parent | c18ae42aa5b3473c00f51b6937d0c00bdb6ce2d2 (diff) | |
parent | 0151546fb34e92494acd65ed84a603c2a4a90168 (diff) |
Merge branch 'for-linus' of git://opensource.wolfsonmicro.com/regulator
* 'for-linus' of git://opensource.wolfsonmicro.com/regulator: (22 commits)
regulator: Constify constraints name
regulator: Fix possible nullpointer dereference in regulator_enable()
regulator: gpio-regulator add dependency on GENERIC_GPIO
regulator: Add module.h include to gpio-regulator
regulator: Add driver for gpio-controlled regulators
regulator: remove duplicate REG_CTRL2 defines in tps65023
regulator: Clarify documentation for regulator-regulator supplies
regulator: Fix some bitrot in the machine driver documentation
regulator: tps65023: Added support for the similiar TPS65020 chip
regulator: tps65023: Setting correct core regulator for tps65021
regulator: tps65023: Set missing bit for update core-voltage
regulator: tps65023: Fixes i2c configuration issues
regulator: Add debugfs file showing the supply map table
regulator: tps6586x: add SMx slew rate setting
regulator: tps65023: Fixes i2c configuration issues
regulator: tps6507x: Remove num_voltages array
regulator: max8952: removed unused mutex.
regulator: fix regulator/consumer.h kernel-doc warning
regulator: Ensure enough enable time for max8649
regulator: 88pm8607: Fix off-by-one value range checking in the case of no id is matched
...
-rw-r--r-- | Documentation/power/regulator/machine.txt | 19 | ||||
-rw-r--r-- | drivers/regulator/88pm8607.c | 2 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 10 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/aat2870-regulator.c | 1 | ||||
-rw-r--r-- | drivers/regulator/core.c | 43 | ||||
-rw-r--r-- | drivers/regulator/gpio-regulator.c | 358 | ||||
-rw-r--r-- | drivers/regulator/max8649.c | 2 | ||||
-rw-r--r-- | drivers/regulator/max8952.c | 3 | ||||
-rw-r--r-- | drivers/regulator/tps65023-regulator.c | 183 | ||||
-rw-r--r-- | drivers/regulator/tps6507x-regulator.c | 8 | ||||
-rw-r--r-- | drivers/regulator/tps6586x-regulator.c | 32 | ||||
-rw-r--r-- | drivers/regulator/tps65912-regulator.c | 2 | ||||
-rw-r--r-- | include/linux/mfd/tps6586x.h | 16 | ||||
-rw-r--r-- | include/linux/regulator/gpio-regulator.h | 87 | ||||
-rw-r--r-- | include/linux/regulator/machine.h | 2 |
16 files changed, 721 insertions, 48 deletions
diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt index b42419b52e4..ce63af0a8e3 100644 --- a/Documentation/power/regulator/machine.txt +++ b/Documentation/power/regulator/machine.txt @@ -16,7 +16,7 @@ initialisation code by creating a struct regulator_consumer_supply for each regulator. struct regulator_consumer_supply { - struct device *dev; /* consumer */ + const char *dev_name; /* consumer dev_name() */ const char *supply; /* consumer supply - e.g. "vcc" */ }; @@ -24,13 +24,13 @@ e.g. for the machine above static struct regulator_consumer_supply regulator1_consumers[] = { { - .dev = &platform_consumerB_device.dev, - .supply = "Vcc", + .dev_name = "dev_name(consumer B)", + .supply = "Vcc", },}; static struct regulator_consumer_supply regulator2_consumers[] = { { - .dev = &platform_consumerA_device.dev, + .dev = "dev_name(consumer A"), .supply = "Vcc", },}; @@ -43,6 +43,7 @@ to their supply regulator :- static struct regulator_init_data regulator1_data = { .constraints = { + .name = "Regulator-1", .min_uV = 3300000, .max_uV = 3300000, .valid_modes_mask = REGULATOR_MODE_NORMAL, @@ -51,13 +52,19 @@ static struct regulator_init_data regulator1_data = { .consumer_supplies = regulator1_consumers, }; +The name field should be set to something that is usefully descriptive +for the board for configuration of supplies for other regulators and +for use in logging and other diagnostic output. Normally the name +used for the supply rail in the schematic is a good choice. If no +name is provided then the subsystem will choose one. + Regulator-1 supplies power to Regulator-2. This relationship must be registered with the core so that Regulator-1 is also enabled when Consumer A enables its supply (Regulator-2). The supply regulator is set by the supply_regulator -field below:- +field below and co:- static struct regulator_init_data regulator2_data = { - .supply_regulator = "regulator_name", + .supply_regulator = "Regulator-1", .constraints = { .min_uV = 1800000, .max_uV = 2000000, diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index d63fddb0fbb..e821b2159b4 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -412,7 +412,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) if (info->desc.id == res->start) break; } - if ((i < 0) || (i > PM8607_ID_RG_MAX)) { + if (i == ARRAY_SIZE(pm8607_regulator_info)) { dev_err(&pdev->dev, "Failed to find regulator %llu\n", (unsigned long long)res->start); return -EINVAL; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index c7fd2c0e3f2..9713b1b860c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -64,6 +64,16 @@ config REGULATOR_USERSPACE_CONSUMER If unsure, say no. +config REGULATOR_GPIO + tristate "GPIO regulator support" + depends on GENERIC_GPIO + help + This driver provides support for regulators that can be + controlled via gpios. + It is capable of supporting current and voltage regulators + and the platform has to provide a mapping of GPIO-states + to target volts/amps. + config REGULATOR_BQ24022 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 040d5aa6353..93a6318f532 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o +obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o obj-$(CONFIG_REGULATOR_AD5398) += ad5398.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o diff --git a/drivers/regulator/aat2870-regulator.c b/drivers/regulator/aat2870-regulator.c index cd4104542f0..5abeb3ac3e8 100644 --- a/drivers/regulator/aat2870-regulator.c +++ b/drivers/regulator/aat2870-regulator.c @@ -22,6 +22,7 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/module.h> #include <linux/slab.h> #include <linux/delay.h> #include <linux/platform_device.h> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9e4c123c402..67fa2a3297a 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1425,7 +1425,7 @@ int regulator_enable(struct regulator *regulator) ret = _regulator_enable(rdev); mutex_unlock(&rdev->mutex); - if (ret != 0) + if (ret != 0 && rdev->supply) regulator_disable(rdev->supply); return ret; @@ -2971,6 +2971,43 @@ void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data) } EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); +#ifdef CONFIG_DEBUG_FS +static ssize_t supply_map_read_file(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + ssize_t len, ret = 0; + struct regulator_map *map; + + if (!buf) + return -ENOMEM; + + list_for_each_entry(map, ®ulator_map_list, list) { + len = snprintf(buf + ret, PAGE_SIZE - ret, + "%s -> %s.%s\n", + rdev_get_name(map->regulator), map->dev_name, + map->supply); + if (len >= 0) + ret += len; + if (ret > PAGE_SIZE) { + ret = PAGE_SIZE; + break; + } + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, ret); + + kfree(buf); + + return ret; +} + +static const struct file_operations supply_map_fops = { + .read = supply_map_read_file, + .llseek = default_llseek, +}; +#endif + static int __init regulator_init(void) { int ret; @@ -2983,6 +3020,10 @@ static int __init regulator_init(void) pr_warn("regulator: Failed to create debugfs directory\n"); debugfs_root = NULL; } + + if (IS_ERR(debugfs_create_file("supply_map", 0444, debugfs_root, + NULL, &supply_map_fops))) + pr_warn("regulator: Failed to create supplies debugfs\n"); #endif regulator_dummy_init(); diff --git a/drivers/regulator/gpio-regulator.c b/drivers/regulator/gpio-regulator.c new file mode 100644 index 00000000000..f0acf52498b --- /dev/null +++ b/drivers/regulator/gpio-regulator.c @@ -0,0 +1,358 @@ +/* + * gpio-regulator.c + * + * Copyright 2011 Heiko Stuebner <heiko@sntech.de> + * + * based on fixed.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros <ext-roger.quadros@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/gpio-regulator.h> +#include <linux/gpio.h> +#include <linux/delay.h> +#include <linux/slab.h> + +struct gpio_regulator_data { + struct regulator_desc desc; + struct regulator_dev *dev; + + int enable_gpio; + bool enable_high; + bool is_enabled; + unsigned startup_delay; + + struct gpio *gpios; + int nr_gpios; + + struct gpio_regulator_state *states; + int nr_states; + + int state; +}; + +static int gpio_regulator_is_enabled(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + return data->is_enabled; +} + +static int gpio_regulator_enable(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->enable_gpio)) { + gpio_set_value_cansleep(data->enable_gpio, data->enable_high); + data->is_enabled = true; + } + + return 0; +} + +static int gpio_regulator_disable(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (gpio_is_valid(data->enable_gpio)) { + gpio_set_value_cansleep(data->enable_gpio, !data->enable_high); + data->is_enabled = false; + } + + return 0; +} + +static int gpio_regulator_enable_time(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + return data->startup_delay; +} + +static int gpio_regulator_get_value(struct regulator_dev *dev) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr; + + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].gpios == data->state) + return data->states[ptr].value; + + return -EINVAL; +} + +static int gpio_regulator_set_value(struct regulator_dev *dev, + int min, int max) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + int ptr, target, state; + + target = -1; + for (ptr = 0; ptr < data->nr_states; ptr++) + if (data->states[ptr].value >= min && + data->states[ptr].value <= max) + target = data->states[ptr].gpios; + + if (target < 0) + return -EINVAL; + + for (ptr = 0; ptr < data->nr_gpios; ptr++) { + state = (target & (1 << ptr)) >> ptr; + gpio_set_value(data->gpios[ptr].gpio, state); + } + data->state = target; + + return 0; +} + +static int gpio_regulator_set_voltage(struct regulator_dev *dev, + int min_uV, int max_uV, + unsigned *selector) +{ + return gpio_regulator_set_value(dev, min_uV, max_uV); +} + +static int gpio_regulator_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct gpio_regulator_data *data = rdev_get_drvdata(dev); + + if (selector >= data->nr_states) + return -EINVAL; + + return data->states[selector].value; +} + +static int gpio_regulator_set_current_limit(struct regulator_dev *dev, + int min_uA, int max_uA) +{ + return gpio_regulator_set_value(dev, min_uA, max_uA); +} + +static struct regulator_ops gpio_regulator_voltage_ops = { + .is_enabled = gpio_regulator_is_enabled, + .enable = gpio_regulator_enable, + .disable = gpio_regulator_disable, + .enable_time = gpio_regulator_enable_time, + .get_voltage = gpio_regulator_get_value, + .set_voltage = gpio_regulator_set_voltage, + .list_voltage = gpio_regulator_list_voltage, +}; + +static struct regulator_ops gpio_regulator_current_ops = { + .is_enabled = gpio_regulator_is_enabled, + .enable = gpio_regulator_enable, + .disable = gpio_regulator_disable, + .enable_time = gpio_regulator_enable_time, + .get_current_limit = gpio_regulator_get_value, + .set_current_limit = gpio_regulator_set_current_limit, +}; + +static int __devinit gpio_regulator_probe(struct platform_device *pdev) +{ + struct gpio_regulator_config *config = pdev->dev.platform_data; + struct gpio_regulator_data *drvdata; + int ptr, ret, state; + + drvdata = kzalloc(sizeof(struct gpio_regulator_data), GFP_KERNEL); + if (drvdata == NULL) { + dev_err(&pdev->dev, "Failed to allocate device data\n"); + return -ENOMEM; + } + + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (drvdata->desc.name == NULL) { + dev_err(&pdev->dev, "Failed to allocate supply name\n"); + ret = -ENOMEM; + goto err; + } + + drvdata->gpios = kmemdup(config->gpios, + config->nr_gpios * sizeof(struct gpio), + GFP_KERNEL); + if (drvdata->gpios == NULL) { + dev_err(&pdev->dev, "Failed to allocate gpio data\n"); + ret = -ENOMEM; + goto err_name; + } + + drvdata->states = kmemdup(config->states, + config->nr_states * + sizeof(struct gpio_regulator_state), + GFP_KERNEL); + if (drvdata->states == NULL) { + dev_err(&pdev->dev, "Failed to allocate state data\n"); + ret = -ENOMEM; + goto err_memgpio; + } + drvdata->nr_states = config->nr_states; + + drvdata->desc.owner = THIS_MODULE; + + /* handle regulator type*/ + switch (config->type) { + case REGULATOR_VOLTAGE: + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.ops = &gpio_regulator_voltage_ops; + drvdata->desc.n_voltages = config->nr_states; + break; + case REGULATOR_CURRENT: + drvdata->desc.type = REGULATOR_CURRENT; + drvdata->desc.ops = &gpio_regulator_current_ops; + break; + default: + dev_err(&pdev->dev, "No regulator type set\n"); + ret = -EINVAL; + goto err_memgpio; + break; + } + + drvdata->enable_gpio = config->enable_gpio; + drvdata->startup_delay = config->startup_delay; + + if (gpio_is_valid(config->enable_gpio)) { + drvdata->enable_high = config->enable_high; + + ret = gpio_request(config->enable_gpio, config->supply_name); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator enable GPIO %d: %d\n", + config->enable_gpio, ret); + goto err_memstate; + } + + /* set output direction without changing state + * to prevent glitch + */ + if (config->enabled_at_boot) { + drvdata->is_enabled = true; + ret = gpio_direction_output(config->enable_gpio, + config->enable_high); + } else { + drvdata->is_enabled = false; + ret = gpio_direction_output(config->enable_gpio, + !config->enable_high); + } + + if (ret) { + dev_err(&pdev->dev, + "Could not configure regulator enable GPIO %d direction: %d\n", + config->enable_gpio, ret); + goto err_enablegpio; + } + } else { + /* Regulator without GPIO control is considered + * always enabled + */ + drvdata->is_enabled = true; + } + + drvdata->nr_gpios = config->nr_gpios; + ret = gpio_request_array(drvdata->gpios, drvdata->nr_gpios); + if (ret) { + dev_err(&pdev->dev, + "Could not obtain regulator setting GPIOs: %d\n", ret); + goto err_enablegpio; + } + + /* build initial state from gpio init data. */ + state = 0; + for (ptr = 0; ptr < drvdata->nr_gpios; ptr++) { + if (config->gpios[ptr].flags & GPIOF_OUT_INIT_HIGH) + state |= (1 << ptr); + } + drvdata->state = state; + + drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, + config->init_data, drvdata); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + dev_err(&pdev->dev, "Failed to register regulator: %d\n", ret); + goto err_stategpio; + } + + platform_set_drvdata(pdev, drvdata); + + return 0; + +err_stategpio: + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); +err_enablegpio: + if (gpio_is_valid(config->enable_gpio)) + gpio_free(config->enable_gpio); +err_memstate: + kfree(drvdata->states); +err_memgpio: + kfree(drvdata->gpios); +err_name: + kfree(drvdata->desc.name); +err: + kfree(drvdata); + return ret; +} + +static int __devexit gpio_regulator_remove(struct platform_device *pdev) +{ + struct gpio_regulator_data *drvdata = platform_get_drvdata(pdev); + + regulator_unregister(drvdata->dev); + + gpio_free_array(drvdata->gpios, drvdata->nr_gpios); + + kfree(drvdata->states); + kfree(drvdata->gpios); + + if (gpio_is_valid(drvdata->enable_gpio)) + gpio_free(drvdata->enable_gpio); + + kfree(drvdata->desc.name); + kfree(drvdata); + + return 0; +} + +static struct platform_driver gpio_regulator_driver = { + .probe = gpio_regulator_probe, + .remove = __devexit_p(gpio_regulator_remove), + .driver = { + .name = "gpio-regulator", + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_regulator_init(void) +{ + return platform_driver_register(&gpio_regulator_driver); +} +subsys_initcall(gpio_regulator_init); + +static void __exit gpio_regulator_exit(void) +{ + platform_driver_unregister(&gpio_regulator_driver); +} +module_exit(gpio_regulator_exit); + +MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); +MODULE_DESCRIPTION("gpio voltage regulator"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:gpio-regulator"); diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c index 30eb9e54f7e..1062cf9f02d 100644 --- a/drivers/regulator/max8649.c +++ b/drivers/regulator/max8649.c @@ -221,7 +221,7 @@ static int max8649_enable_time(struct regulator_dev *rdev) ret = (ret & MAX8649_RAMP_MASK) >> 5; rate = (32 * 1000) >> ret; /* uV/uS */ - return (voltage / rate); + return DIV_ROUND_UP(voltage, rate); } static int max8649_set_mode(struct regulator_dev *rdev, unsigned int mode) diff --git a/drivers/regulator/max8952.c b/drivers/regulator/max8952.c index 486ed8141fc..3883d85c5b8 100644 --- a/drivers/regulator/max8952.c +++ b/drivers/regulator/max8952.c @@ -26,7 +26,6 @@ #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/max8952.h> -#include <linux/mutex.h> #include <linux/gpio.h> #include <linux/io.h> #include <linux/slab.h> @@ -47,7 +46,6 @@ enum { struct max8952_data { struct i2c_client *client; struct device *dev; - struct mutex mutex; struct max8952_platform_data *pdata; struct regulator_dev *rdev; @@ -208,7 +206,6 @@ static int __devinit max8952_pmic_probe(struct i2c_client *client, max8952->client = client; max8952->dev = &client->dev; max8952->pdata = pdata; - mutex_init(&max8952->mutex); max8952->rdev = regulator_register(®ulator, max8952->dev, &pdata->reg_data, max8952); diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index 701a5900f83..9fb4c7b8175 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -63,6 +63,13 @@ #define TPS65023_REG_CTRL_LDO2_EN BIT(2) #define TPS65023_REG_CTRL_LDO1_EN BIT(1) +/* REG_CTRL2 bitfields */ +#define TPS65023_REG_CTRL2_GO BIT(7) +#define TPS65023_REG_CTRL2_CORE_ADJ BIT(6) +#define TPS65023_REG_CTRL2_DCDC2 BIT(2) +#define TPS65023_REG_CTRL2_DCDC1 BIT(1) +#define TPS65023_REG_CTRL2_DCDC3 BIT(0) + /* LDO_CTRL bitfields */ #define TPS65023_LDO_CTRL_LDOx_SHIFT(ldo_id) ((ldo_id)*4) #define TPS65023_LDO_CTRL_LDOx_MASK(ldo_id) (0xF0 >> ((ldo_id)*4)) @@ -85,7 +92,7 @@ #define TPS65023_MAX_REG_ID TPS65023_LDO_2 /* Supported voltage values for regulators */ -static const u16 VDCDC1_VSEL_table[] = { +static const u16 VCORE_VSEL_table[] = { 800, 825, 850, 875, 900, 925, 950, 975, 1000, 1025, 1050, 1075, @@ -96,20 +103,29 @@ static const u16 VDCDC1_VSEL_table[] = { 1500, 1525, 1550, 1600, }; -static const u16 LDO1_VSEL_table[] = { +/* Supported voltage values for LDO regulators for tps65020 */ +static const u16 TPS65020_LDO1_VSEL_table[] = { + 1000, 1050, 1100, 1300, + 1800, 2500, 3000, 3300, +}; + +static const u16 TPS65020_LDO2_VSEL_table[] = { + 1000, 1050, 1100, 1300, + 1800, 2500, 3000, 3300, +}; + +/* Supported voltage values for LDO regulators + * for tps65021 and tps65023 */ +static const u16 TPS65023_LDO1_VSEL_table[] = { 1000, 1100, 1300, 1800, 2200, 2600, 2800, 3150, }; -static const u16 LDO2_VSEL_table[] = { +static const u16 TPS65023_LDO2_VSEL_table[] = { 1050, 1200, 1300, 1800, 2500, 2800, 3000, 3300, }; -static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDC1_VSEL_table), - 0, 0, ARRAY_SIZE(LDO1_VSEL_table), - ARRAY_SIZE(LDO2_VSEL_table)}; - /* Regulator specific details */ struct tps_info { const char *name; @@ -127,6 +143,13 @@ struct tps_pmic { struct regulator_dev *rdev[TPS65023_NUM_REGULATOR]; const struct tps_info *info[TPS65023_NUM_REGULATOR]; struct regmap *regmap; + u8 core_regulator; +}; + +/* Struct passed as driver data */ +struct tps_driver_data { + const struct tps_info *info; + u8 core_regulator; }; static int tps_65023_set_bits(struct tps_pmic *tps, u8 reg, u8 mask) @@ -253,7 +276,7 @@ static int tps65023_dcdc_get_voltage(struct regulator_dev *dev) if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) return -EINVAL; - if (dcdc == TPS65023_DCDC_1) { + if (dcdc == tps->core_regulator) { data = tps_65023_reg_read(tps, TPS65023_REG_DEF_CORE); if (data < 0) return data; @@ -270,10 +293,10 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, struct tps_pmic *tps = rdev_get_drvdata(dev); int dcdc = rdev_get_id(dev); int vsel; + int ret; - if (dcdc != TPS65023_DCDC_1) + if (dcdc != tps->core_regulator) return -EINVAL; - if (min_uV < tps->info[dcdc]->min_uV || min_uV > tps->info[dcdc]->max_uV) return -EINVAL; @@ -292,11 +315,21 @@ static int tps65023_dcdc_set_voltage(struct regulator_dev *dev, *selector = vsel; - /* write to the register in case we found a match */ if (vsel == tps->info[dcdc]->table_len) - return -EINVAL; - else - return tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + goto failed; + + ret = tps_65023_reg_write(tps, TPS65023_REG_DEF_CORE, vsel); + + /* Tell the chip that we have changed the value in DEFCORE + * and its time to update the core voltage + */ + tps_65023_set_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_GO); + + return ret; + +failed: + return -EINVAL; } static int tps65023_ldo_get_voltage(struct regulator_dev *dev) @@ -362,7 +395,7 @@ static int tps65023_dcdc_list_voltage(struct regulator_dev *dev, if (dcdc < TPS65023_DCDC_1 || dcdc > TPS65023_DCDC_3) return -EINVAL; - if (dcdc == TPS65023_DCDC_1) { + if (dcdc == tps->core_regulator) { if (selector >= tps->info[dcdc]->table_len) return -EINVAL; else @@ -414,7 +447,8 @@ static struct regmap_config tps65023_regmap_config = { static int __devinit tps_65023_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const struct tps_info *info = (void *)id->driver_data; + const struct tps_driver_data *drv_data = (void *)id->driver_data; + const struct tps_info *info = drv_data->info; struct regulator_init_data *init_data; struct regulator_dev *rdev; struct tps_pmic *tps; @@ -446,6 +480,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, /* common for all regulators */ tps->client = client; + tps->core_regulator = drv_data->core_regulator; for (i = 0; i < TPS65023_NUM_REGULATOR; i++, info++, init_data++) { /* Store regulator specific information */ @@ -453,7 +488,7 @@ static int __devinit tps_65023_probe(struct i2c_client *client, tps->desc[i].name = info->name; tps->desc[i].id = i; - tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].n_voltages = info->table_len; tps->desc[i].ops = (i > TPS65023_DCDC_3 ? &tps65023_ldo_ops : &tps65023_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; @@ -475,6 +510,14 @@ static int __devinit tps_65023_probe(struct i2c_client *client, i2c_set_clientdata(client, tps); + /* Enable setting output voltage by I2C */ + tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_CORE_ADJ); + + /* Enable setting output voltage by I2C */ + tps_65023_clear_bits(tps, TPS65023_REG_CON_CTRL2, + TPS65023_REG_CTRL2_CORE_ADJ); + return 0; fail: @@ -507,13 +550,86 @@ static int __devexit tps_65023_remove(struct i2c_client *client) return 0; } +static const struct tps_info tps65020_regs[] = { + { + .name = "VDCDC1", + .min_uV = 3300000, + .max_uV = 3300000, + .fixed = 1, + }, + { + .name = "VDCDC2", + .min_uV = 1800000, + .max_uV = 1800000, + .fixed = 1, + }, + { + .name = "VDCDC3", + .min_uV = 800000, + .max_uV = 1600000, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3150000, + .table_len = ARRAY_SIZE(TPS65020_LDO1_VSEL_table), + .table = TPS65020_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 1050000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(TPS65020_LDO2_VSEL_table), + .table = TPS65020_LDO2_VSEL_table, + }, +}; + +static const struct tps_info tps65021_regs[] = { + { + .name = "VDCDC1", + .min_uV = 3300000, + .max_uV = 3300000, + .fixed = 1, + }, + { + .name = "VDCDC2", + .min_uV = 1800000, + .max_uV = 1800000, + .fixed = 1, + }, + { + .name = "VDCDC3", + .min_uV = 800000, + .max_uV = 1600000, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, + }, + { + .name = "LDO1", + .min_uV = 1000000, + .max_uV = 3150000, + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, + }, + { + .name = "LDO2", + .min_uV = 1050000, + .max_uV = 3300000, + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, + }, +}; + static const struct tps_info tps65023_regs[] = { { .name = "VDCDC1", .min_uV = 800000, .max_uV = 1600000, - .table_len = ARRAY_SIZE(VDCDC1_VSEL_table), - .table = VDCDC1_VSEL_table, + .table_len = ARRAY_SIZE(VCORE_VSEL_table), + .table = VCORE_VSEL_table, }, { .name = "VDCDC2", @@ -531,23 +647,40 @@ static const struct tps_info tps65023_regs[] = { .name = "LDO1", .min_uV = 1000000, .max_uV = 3150000, - .table_len = ARRAY_SIZE(LDO1_VSEL_table), - .table = LDO1_VSEL_table, + .table_len = ARRAY_SIZE(TPS65023_LDO1_VSEL_table), + .table = TPS65023_LDO1_VSEL_table, }, { .name = "LDO2", .min_uV = 1050000, .max_uV = 3300000, - .table_len = ARRAY_SIZE(LDO2_VSEL_table), - .table = LDO2_VSEL_table, + .table_len = ARRAY_SIZE(TPS65023_LDO2_VSEL_table), + .table = TPS65023_LDO2_VSEL_table, }, }; +static struct tps_driver_data tps65020_drv_data = { + .info = tps65020_regs, + .core_regulator = TPS65023_DCDC_3, +}; + +static struct tps_driver_data tps65021_drv_data = { + .info = tps65021_regs, + .core_regulator = TPS65023_DCDC_3, +}; + +static struct tps_driver_data tps65023_drv_data = { + .info = tps65023_regs, + .core_regulator = TPS65023_DCDC_1, +}; + static const struct i2c_device_id tps_65023_id[] = { {.name = "tps65023", - .driver_data = (unsigned long) tps65023_regs,}, + .driver_data = (unsigned long) &tps65023_drv_data}, {.name = "tps65021", - .driver_data = (unsigned long) tps65023_regs,}, + .driver_data = (unsigned long) &tps65021_drv_data,}, + {.name = "tps65020", + .driver_data = (unsigned long) &tps65020_drv_data}, { }, }; diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index bfffabc21ed..bdef70365f5 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -90,12 +90,6 @@ static const u16 LDO2_VSEL_table[] = { 3000, 3100, 3200, 3300, }; -static unsigned int num_voltages[] = {ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(VDCDCx_VSEL_table), - ARRAY_SIZE(LDO1_VSEL_table), - ARRAY_SIZE(LDO2_VSEL_table)}; - struct tps_info { const char *name; unsigned min_uV; @@ -598,7 +592,7 @@ int tps6507x_pmic_probe(struct platform_device *pdev) tps->desc[i].name = info->name; tps->desc[i].id = i; - tps->desc[i].n_voltages = num_voltages[i]; + tps->desc[i].n_voltages = info->table_len; tps->desc[i].ops = (i > TPS6507X_DCDC_3 ? &tps6507x_pmic_ldo_ops : &tps6507x_pmic_dcdc_ops); tps->desc[i].type = REGULATOR_VOLTAGE; diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index bb04a75a4c9..dbcf09d5080 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -332,6 +332,36 @@ static inline int tps6586x_regulator_preinit(struct device *parent, 1 << ri->enable_bit[1]); } +static int tps6586x_regulator_set_slew_rate(struct platform_device *pdev) +{ + struct device *parent = pdev->dev.parent; + struct regulator_init_data *p = pdev->dev.platform_data; + struct tps6586x_settings *setting = p->driver_data; + uint8_t reg; + + if (setting == NULL) + return 0; + + if (!(setting->slew_rate & TPS6586X_SLEW_RATE_SET)) + return 0; + + /* only SM0 and SM1 can have the slew rate settings */ + switch (pdev->id) { + case TPS6586X_ID_SM_0: + reg = TPS6586X_SM0SL; + break; + case TPS6586X_ID_SM_1: + reg = TPS6586X_SM1SL; + break; + default: + dev_warn(&pdev->dev, "Only SM0/SM1 can set slew rate\n"); + return -EINVAL; + } + + return tps6586x_write(parent, reg, + setting->slew_rate & TPS6586X_SLEW_RATE_MASK); +} + static inline struct tps6586x_regulator *find_regulator_info(int id) { struct tps6586x_regulator *ri; @@ -374,7 +404,7 @@ static int __devinit tps6586x_regulator_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rdev); - return 0; + return tps6586x_regulator_set_slew_rate(pdev); } static int __devexit tps6586x_regulator_remove(struct platform_device *pdev) diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index 3a9313e00fa..39d4a1749e7 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -43,8 +43,6 @@ #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 diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index b6bab1b04e2..b19176eab44 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -1,6 +1,18 @@ #ifndef __LINUX_MFD_TPS6586X_H #define __LINUX_MFD_TPS6586X_H +#define TPS6586X_SLEW_RATE_INSTANTLY 0x00 +#define TPS6586X_SLEW_RATE_110UV 0x01 +#define TPS6586X_SLEW_RATE_220UV 0x02 +#define TPS6586X_SLEW_RATE_440UV 0x03 +#define TPS6586X_SLEW_RATE_880UV 0x04 +#define TPS6586X_SLEW_RATE_1760UV 0x05 +#define TPS6586X_SLEW_RATE_3520UV 0x06 +#define TPS6586X_SLEW_RATE_7040UV 0x07 + +#define TPS6586X_SLEW_RATE_SET 0x08 +#define TPS6586X_SLEW_RATE_MASK 0x07 + enum { TPS6586X_ID_SM_0, TPS6586X_ID_SM_1, @@ -48,6 +60,10 @@ enum { TPS6586X_INT_RTC_ALM2, }; +struct tps6586x_settings { + int slew_rate; +}; + struct tps6586x_subdev_info { int id; const char *name; diff --git a/include/linux/regulator/gpio-regulator.h b/include/linux/regulator/gpio-regulator.h new file mode 100644 index 00000000000..19fbd267406 --- /dev/null +++ b/include/linux/regulator/gpio-regulator.h @@ -0,0 +1,87 @@ +/* + * gpio-regulator.h + * + * Copyright 2011 Heiko Stuebner <heiko@sntech.de> + * + * based on fixed.h + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * Copyright (c) 2009 Nokia Corporation + * Roger Quadros <ext-roger.quadros@nokia.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + */ + +#ifndef __REGULATOR_GPIO_H +#define __REGULATOR_GPIO_H + +struct regulator_init_data; + +enum regulator_type; + +/** + * struct gpio_regulator_state - state description + * @value: microvolts or microamps + * @gpios: bitfield of gpio target-states for the value + * + * This structure describes a supported setting of the regulator + * and the necessary gpio-state to achieve it. + * + * The n-th bit in the bitfield describes the state of the n-th GPIO + * from the gpios-array defined in gpio_regulator_config below. + */ +struct gpio_regulator_state { + int value; + int gpios; +}; + +/** + * struct gpio_regulator_config - config structure + * @supply_name: Name of the regulator supply + * @enable_gpio: GPIO to use for enable control + * set to -EINVAL if not used + * @enable_high: Polarity of enable GPIO + * 1 = Active high, 0 = Active low + * @enabled_at_boot: Whether regulator has been enabled at + * boot or not. 1 = Yes, 0 = No + * This is used to keep the regulator at + * the default state + * @startup_delay: Start-up time in microseconds + * @gpios: Array containing the gpios needed to control + * the setting of the regulator + * @nr_gpios: Number of gpios + * @states: Array of gpio_regulator_state entries describing + * the gpio state for specific voltages + * @nr_states: Number of states available + * @regulator_type: either REGULATOR_CURRENT or REGULATOR_VOLTAGE + * @init_data: regulator_init_data + * + * This structure contains gpio-voltage regulator configuration + * information that must be passed by platform code to the + * gpio-voltage regulator driver. + */ +struct gpio_regulator_config { + const char *supply_name; + + int enable_gpio; + unsigned enable_high:1; + unsigned enabled_at_boot:1; + unsigned startup_delay; + + struct gpio *gpios; + int nr_gpios; + + struct gpio_regulator_state *states; + int nr_states; + + enum regulator_type type; + struct regulator_init_data *init_data; +}; + +#endif diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index ce3127a75c8..f3f13fd5868 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -95,7 +95,7 @@ struct regulator_state { */ struct regulation_constraints { - char *name; + const char *name; /* voltage output range (inclusive) - for voltage control */ int min_uV; |