diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-05-04 14:17:27 +1000 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2009-05-04 14:17:27 +1000 |
commit | 230b5cff36a7aa24b496b1319df1c7ecfaca9df8 (patch) | |
tree | a118da10602717468a8ef2e31bab0a1ff3f32168 /drivers | |
parent | 74ba112a4e36455c92ea88dfb0edd2ce55338f94 (diff) | |
parent | 1a0528366d3a61a0fd9182225fd6462987bb64d7 (diff) |
Merge commit 'voltage/for-next'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/regulator/Kconfig | 19 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 2 | ||||
-rw-r--r-- | drivers/regulator/da903x.c | 2 | ||||
-rw-r--r-- | drivers/regulator/fixed.c | 18 | ||||
-rw-r--r-- | drivers/regulator/max1586.c | 249 | ||||
-rw-r--r-- | drivers/regulator/pcf50633-regulator.c | 2 | ||||
-rw-r--r-- | drivers/regulator/userspace-consumer.c | 200 | ||||
-rw-r--r-- | drivers/regulator/virtual.c | 1 | ||||
-rw-r--r-- | drivers/regulator/wm8350-regulator.c | 1 | ||||
-rw-r--r-- | drivers/regulator/wm8400-regulator.c | 2 |
10 files changed, 491 insertions, 5 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index e58c0ce65aa6..5bec17cf1d5c 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -47,6 +47,16 @@ config REGULATOR_VIRTUAL_CONSUMER If unsure, say no. +config REGULATOR_USERSPACE_CONSUMER + tristate "Userspace regulator consumer support" + default n + help + There are some classes of devices that are controlled entirely + from user space. Usersapce consumer driver provides ability to + control power supplies for such devices. + + If unsure, say no. + config REGULATOR_BQ24022 tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" default n @@ -56,6 +66,15 @@ config REGULATOR_BQ24022 charging select between 100 mA and 500 mA charging current limit. +config REGULATOR_MAX1586 + tristate "Maxim 1586/1587 voltage regulator" + depends on I2C + default n + help + This driver controls a Maxim 1586 or 1587 voltage output + regulator via I2C bus. The provided regulator is suitable + for PXA27x chips to control VCC_CORE and VCC_USIM voltages. + config REGULATOR_TWL4030 bool "TI TWL4030/TWL5030/TPS695x0 PMIC" depends on TWL4030_CORE diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bac133afc061..faf7bcc1af98 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -6,8 +6,10 @@ obj-$(CONFIG_REGULATOR) += core.o 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_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_TWL4030) += twl4030-regulator.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o diff --git a/drivers/regulator/da903x.c b/drivers/regulator/da903x.c index 72b15495183c..2a491bf15865 100644 --- a/drivers/regulator/da903x.c +++ b/drivers/regulator/da903x.c @@ -504,7 +504,7 @@ static int __init da903x_regulator_init(void) { return platform_driver_register(&da903x_regulator_driver); } -module_init(da903x_regulator_init); +subsys_initcall(da903x_regulator_init); static void __exit da903x_regulator_exit(void) { diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index 23d554628a76..cdc674fb46c3 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -44,10 +44,22 @@ static int fixed_voltage_get_voltage(struct regulator_dev *dev) return data->microvolts; } +static int fixed_voltage_list_voltage(struct regulator_dev *dev, + unsigned selector) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + if (selector != 0) + return -EINVAL; + + return data->microvolts; +} + static struct regulator_ops fixed_voltage_ops = { .is_enabled = fixed_voltage_is_enabled, .enable = fixed_voltage_enable, .get_voltage = fixed_voltage_get_voltage, + .list_voltage = fixed_voltage_list_voltage, }; static int regulator_fixed_voltage_probe(struct platform_device *pdev) @@ -69,7 +81,8 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) } drvdata->desc.type = REGULATOR_VOLTAGE; drvdata->desc.owner = THIS_MODULE; - drvdata->desc.ops = &fixed_voltage_ops, + drvdata->desc.ops = &fixed_voltage_ops; + drvdata->desc.n_voltages = 1; drvdata->microvolts = config->microvolts; @@ -117,7 +130,7 @@ static int __init regulator_fixed_voltage_init(void) { return platform_driver_register(®ulator_fixed_voltage_driver); } -module_init(regulator_fixed_voltage_init); +subsys_initcall(regulator_fixed_voltage_init); static void __exit regulator_fixed_voltage_exit(void) { @@ -128,3 +141,4 @@ module_exit(regulator_fixed_voltage_exit); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("Fixed voltage regulator"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:reg-fixed-voltage"); diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c new file mode 100644 index 000000000000..bbbb55fcfe8c --- /dev/null +++ b/drivers/regulator/max1586.c @@ -0,0 +1,249 @@ +/* + * max1586.c -- Voltage and current regulation for the Maxim 1586 + * + * Copyright (C) 2008 Robert Jarzmik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include <linux/module.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/max1586.h> + +#define MAX1586_V3_MAX_VSEL 31 +#define MAX1586_V6_MAX_VSEL 3 + +#define MAX1586_V3_MIN_UV 700000 +#define MAX1586_V3_MAX_UV 1475000 +#define MAX1586_V3_STEP_UV 25000 + +#define MAX1586_V6_MIN_UV 0 +#define MAX1586_V6_MAX_UV 3000000 + +#define I2C_V3_SELECT (0 << 5) +#define I2C_V6_SELECT (1 << 5) + +/* + * V3 voltage + * On I2C bus, sending a "x" byte to the max1586 means : + * set V3 to 0.700V + (x & 0x1f) * 0.025V + */ +static int max1586_v3_calc_voltage(unsigned selector) +{ + return MAX1586_V3_MIN_UV + (MAX1586_V3_STEP_UV * selector); +} + +static int max1586_v3_set(struct regulator_dev *rdev, int min_uV, int max_uV) +{ + struct i2c_client *client = rdev_get_drvdata(rdev); + unsigned selector; + u8 v3_prog; + + if (min_uV < MAX1586_V3_MIN_UV || min_uV > MAX1586_V3_MAX_UV) + return -EINVAL; + if (max_uV < MAX1586_V3_MIN_UV || max_uV > MAX1586_V3_MAX_UV) + return -EINVAL; + + selector = (min_uV - MAX1586_V3_MIN_UV) / MAX1586_V3_STEP_UV; + if (max1586_v3_calc_voltage(selector) > max_uV) + return -EINVAL; + + dev_dbg(&client->dev, "changing voltage v3 to %dmv\n", + max1586_v3_calc_voltage(selector) / 1000); + + v3_prog = I2C_V3_SELECT | (u8) selector; + return i2c_smbus_write_byte(client, v3_prog); +} + +static int max1586_v3_list(struct regulator_dev *rdev, unsigned selector) +{ + if (selector > MAX1586_V3_MAX_VSEL) + return -EINVAL; + return max1586_v3_calc_voltage(selector); +} + +/* + * V6 voltage + * On I2C bus, sending a "x" byte to the max1586 means : + * set V6 to either 0V, 1.8V, 2.5V, 3V depending on (x & 0x3) + * As regulator framework doesn't accept voltages to be 0V, we use 1uV. + */ +static int max1586_v6_calc_voltage(unsigned selector) +{ + static int voltages_uv[] = { 1, 1800000, 2500000, 3000000 }; + + return voltages_uv[selector]; +} + +static int max1586_v6_set(struct regulator_dev *rdev, int min_uV, int max_uV) +{ + struct i2c_client *client = rdev_get_drvdata(rdev); + unsigned selector; + u8 v6_prog; + + if (min_uV < MAX1586_V6_MIN_UV || min_uV > MAX1586_V6_MAX_UV) + return -EINVAL; + if (max_uV < MAX1586_V6_MIN_UV || max_uV > MAX1586_V6_MAX_UV) + return -EINVAL; + + if (min_uV >= 3000000) + selector = 3; + if (min_uV < 3000000) + selector = 2; + if (min_uV < 2500000) + selector = 1; + if (min_uV < 1800000) + selector = 0; + + if (max1586_v6_calc_voltage(selector) > max_uV) + return -EINVAL; + + dev_dbg(&client->dev, "changing voltage v6 to %dmv\n", + max1586_v6_calc_voltage(selector) / 1000); + + v6_prog = I2C_V6_SELECT | (u8) selector; + return i2c_smbus_write_byte(client, v6_prog); +} + +static int max1586_v6_list(struct regulator_dev *rdev, unsigned selector) +{ + if (selector > MAX1586_V6_MAX_VSEL) + return -EINVAL; + return max1586_v6_calc_voltage(selector); +} + +/* + * The Maxim 1586 controls V3 and V6 voltages, but offers no way of reading back + * the set up value. + */ +static struct regulator_ops max1586_v3_ops = { + .set_voltage = max1586_v3_set, + .list_voltage = max1586_v3_list, +}; + +static struct regulator_ops max1586_v6_ops = { + .set_voltage = max1586_v6_set, + .list_voltage = max1586_v6_list, +}; + +static struct regulator_desc max1586_reg[] = { + { + .name = "Output_V3", + .id = MAX1586_V3, + .ops = &max1586_v3_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX1586_V3_MAX_VSEL + 1, + .owner = THIS_MODULE, + }, + { + .name = "Output_V6", + .id = MAX1586_V6, + .ops = &max1586_v6_ops, + .type = REGULATOR_VOLTAGE, + .n_voltages = MAX1586_V6_MAX_VSEL + 1, + .owner = THIS_MODULE, + }, +}; + +static int max1586_pmic_probe(struct i2c_client *client, + const struct i2c_device_id *i2c_id) +{ + struct regulator_dev **rdev; + struct max1586_platform_data *pdata = client->dev.platform_data; + int i, id, ret = 0; + + rdev = kzalloc(sizeof(struct regulator_dev *) * (MAX1586_V6 + 1), + GFP_KERNEL); + if (!rdev) + return -ENOMEM; + + ret = -EINVAL; + for (i = 0; i < pdata->num_subdevs && i <= MAX1586_V6; i++) { + id = pdata->subdevs[i].id; + if (!pdata->subdevs[i].platform_data) + continue; + if (id < MAX1586_V3 || id > MAX1586_V6) { + dev_err(&client->dev, "invalid regulator id %d\n", id); + goto err; + } + rdev[i] = regulator_register(&max1586_reg[id], &client->dev, + pdata->subdevs[i].platform_data, + client); + if (IS_ERR(rdev[i])) { + ret = PTR_ERR(rdev[i]); + dev_err(&client->dev, "failed to register %s\n", + max1586_reg[id].name); + goto err; + } + } + + i2c_set_clientdata(client, rdev); + dev_info(&client->dev, "Maxim 1586 regulator driver loaded\n"); + return 0; + +err: + while (--i >= 0) + regulator_unregister(rdev[i]); + kfree(rdev); + return ret; +} + +static int max1586_pmic_remove(struct i2c_client *client) +{ + struct regulator_dev **rdev = i2c_get_clientdata(client); + int i; + + for (i = 0; i <= MAX1586_V6; i++) + if (rdev[i]) + regulator_unregister(rdev[i]); + kfree(rdev); + i2c_set_clientdata(client, NULL); + + return 0; +} + +static const struct i2c_device_id max1586_id[] = { + { "max1586", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1586_id); + +static struct i2c_driver max1586_pmic_driver = { + .probe = max1586_pmic_probe, + .remove = max1586_pmic_remove, + .driver = { + .name = "max1586", + }, + .id_table = max1586_id, +}; + +static int __init max1586_pmic_init(void) +{ + return i2c_add_driver(&max1586_pmic_driver); +} +subsys_initcall(max1586_pmic_init); + +static void __exit max1586_pmic_exit(void) +{ + i2c_del_driver(&max1586_pmic_driver); +} +module_exit(max1586_pmic_exit); + +/* Module information */ +MODULE_DESCRIPTION("MAXIM 1586 voltage regulator driver"); +MODULE_AUTHOR("Robert Jarzmik"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/pcf50633-regulator.c b/drivers/regulator/pcf50633-regulator.c index cd761d85c8fd..8e14900eb686 100644 --- a/drivers/regulator/pcf50633-regulator.c +++ b/drivers/regulator/pcf50633-regulator.c @@ -316,7 +316,7 @@ static int __init pcf50633_regulator_init(void) { return platform_driver_register(&pcf50633_regulator_driver); } -module_init(pcf50633_regulator_init); +subsys_initcall(pcf50633_regulator_init); static void __exit pcf50633_regulator_exit(void) { diff --git a/drivers/regulator/userspace-consumer.c b/drivers/regulator/userspace-consumer.c new file mode 100644 index 000000000000..06d2fa96a8b4 --- /dev/null +++ b/drivers/regulator/userspace-consumer.c @@ -0,0 +1,200 @@ +/* + * userspace-consumer.c + * + * Copyright 2009 CompuLab, Ltd. + * + * Author: Mike Rapoport <mike@compulab.co.il> + * + * Based of virtual consumer driver: + * Copyright 2008 Wolfson Microelectronics PLC. + * Author: Mark Brown <broonie@opensource.wolfsonmicro.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. + * + */ + +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/userspace-consumer.h> + +struct userspace_consumer_data { + const char *name; + + struct mutex lock; + bool enabled; + + int num_supplies; + struct regulator_bulk_data *supplies; +}; + +static ssize_t reg_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static ssize_t reg_show_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + + if (data->enabled) + return sprintf(buf, "enabled\n"); + + return sprintf(buf, "disabled\n"); +} + +static ssize_t reg_set_state(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct userspace_consumer_data *data = dev_get_drvdata(dev); + bool enabled; + int ret; + + /* + * sysfs_streq() doesn't need the \n's, but we add them so the strings + * will be shared with show_state(), above. + */ + if (sysfs_streq(buf, "enabled\n") || sysfs_streq(buf, "1")) + enabled = true; + else if (sysfs_streq(buf, "disabled\n") || sysfs_streq(buf, "0")) + enabled = false; + else { + dev_err(dev, "Configuring invalid mode\n"); + return count; + } + + mutex_lock(&data->lock); + if (enabled != data->enabled) { + if (enabled) + ret = regulator_bulk_enable(data->num_supplies, + data->supplies); + else + ret = regulator_bulk_disable(data->num_supplies, + data->supplies); + + if (ret == 0) + data->enabled = enabled; + else + dev_err(dev, "Failed to configure state: %d\n", ret); + } + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(name, 0444, reg_show_name, NULL); +static DEVICE_ATTR(state, 0644, reg_show_state, reg_set_state); + +static struct device_attribute *attributes[] = { + &dev_attr_name, + &dev_attr_state, +}; + +static int regulator_userspace_consumer_probe(struct platform_device *pdev) +{ + struct regulator_userspace_consumer_data *pdata; + struct userspace_consumer_data *drvdata; + int ret, i; + + pdata = pdev->dev.platform_data; + if (!pdata) + return -EINVAL; + + drvdata = kzalloc(sizeof(struct userspace_consumer_data), GFP_KERNEL); + if (drvdata == NULL) + return -ENOMEM; + + drvdata->name = pdata->name; + drvdata->num_supplies = pdata->num_supplies; + drvdata->supplies = pdata->supplies; + + mutex_init(&drvdata->lock); + + ret = regulator_bulk_get(&pdev->dev, drvdata->num_supplies, + drvdata->supplies); + if (ret) { + dev_err(&pdev->dev, "Failed to get supplies: %d\n", ret); + goto err_alloc_supplies; + } + + for (i = 0; i < ARRAY_SIZE(attributes); i++) { + ret = device_create_file(&pdev->dev, attributes[i]); + if (ret != 0) + goto err_create_attrs; + } + + if (pdata->init_on) + ret = regulator_bulk_enable(drvdata->num_supplies, + drvdata->supplies); + + drvdata->enabled = pdata->init_on; + + if (ret) { + dev_err(&pdev->dev, "Failed to set initial state: %d\n", ret); + goto err_create_attrs; + } + + platform_set_drvdata(pdev, drvdata); + + return 0; + +err_create_attrs: + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(&pdev->dev, attributes[i]); + + regulator_bulk_free(drvdata->num_supplies, drvdata->supplies); + +err_alloc_supplies: + kfree(drvdata); + return ret; +} + +static int regulator_userspace_consumer_remove(struct platform_device *pdev) +{ + struct userspace_consumer_data *data = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(&pdev->dev, attributes[i]); + + if (data->enabled) + regulator_bulk_disable(data->num_supplies, data->supplies); + + regulator_bulk_free(data->num_supplies, data->supplies); + kfree(data); + + return 0; +} + +static struct platform_driver regulator_userspace_consumer_driver = { + .probe = regulator_userspace_consumer_probe, + .remove = regulator_userspace_consumer_remove, + .driver = { + .name = "reg-userspace-consumer", + }, +}; + + +static int __init regulator_userspace_consumer_init(void) +{ + return platform_driver_register(®ulator_userspace_consumer_driver); +} +module_init(regulator_userspace_consumer_init); + +static void __exit regulator_userspace_consumer_exit(void) +{ + platform_driver_unregister(®ulator_userspace_consumer_driver); +} +module_exit(regulator_userspace_consumer_exit); + +MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); +MODULE_DESCRIPTION("Userspace consumer for voltage and current regulators"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c index 71403fa3ffa1..e7db5664722e 100644 --- a/drivers/regulator/virtual.c +++ b/drivers/regulator/virtual.c @@ -347,3 +347,4 @@ module_exit(regulator_virtual_consumer_exit); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_DESCRIPTION("Virtual regulator consumer"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:reg-virt-consumer"); diff --git a/drivers/regulator/wm8350-regulator.c b/drivers/regulator/wm8350-regulator.c index 771eca1066b5..17a00b0fafd1 100644 --- a/drivers/regulator/wm8350-regulator.c +++ b/drivers/regulator/wm8350-regulator.c @@ -1570,3 +1570,4 @@ module_exit(wm8350_regulator_exit); MODULE_AUTHOR("Liam Girdwood"); MODULE_DESCRIPTION("WM8350 voltage and current regulator driver"); MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-regulator"); diff --git a/drivers/regulator/wm8400-regulator.c b/drivers/regulator/wm8400-regulator.c index 157426029071..01a6c952b7c9 100644 --- a/drivers/regulator/wm8400-regulator.c +++ b/drivers/regulator/wm8400-regulator.c @@ -380,7 +380,7 @@ static int __init wm8400_regulator_init(void) { return platform_driver_register(&wm8400_regulator_driver); } -module_init(wm8400_regulator_init); +subsys_initcall(wm8400_regulator_init); static void __exit wm8400_regulator_exit(void) { |