diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-04 07:52:06 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-03-04 07:52:06 -0800 |
commit | c8bf9fec478bb8d3c66067be2aaa4082b83da46c (patch) | |
tree | 0fd9761ba12f5f58a3435e9b3bc5a285d552f0c4 /drivers | |
parent | a2e28fc1164e56d37a56e3c53bd3e5a64462fd02 (diff) | |
parent | 1ad02bbce64e5226b0582af85df4e481e2f6b7b9 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6: (27 commits)
Regulators: wm8400 - cleanup platform driver data handling
Regulators: wm8994 - clean up driver data after removal
Regulators: wm831x-xxx - clean up driver data after removal
Regulators: pcap-regulator - clean up driver data after removal
Regulators: max8660 - annotate probe and remove methods
Regulators: max1586 - annotate probe and remove methods
Regulators: lp3971 - fail if platform data was not supplied
Regulators: tps6507x-regulator - mark probe method as __devinit
Regulators: tps65023-regulator - mark probe method as __devinit
Regulators: twl-regulator - mark probe function as __devinit
Regulators: fixed - annotate probe and remove methods
Regulators: ab3100 - fix probe and remove annotations
Regulators: virtual - use sysfs attribute groups
twl6030: regulator: Configure STATE register instead of REMAP
regulator: Provide optional dummy regulator for consumers
regulator: Assume regulators are enabled if they don't report anything
regulator: Convert fixed voltage regulator to use enable_time()
regulator: Add WM8994 regulator support
regulator: enable max8649 regulator driver
regulator: trivial: fix typos in user-visible Kconfig text
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/regulator/Kconfig | 29 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 3 | ||||
-rw-r--r-- | drivers/regulator/ab3100.c | 6 | ||||
-rw-r--r-- | drivers/regulator/core.c | 79 | ||||
-rw-r--r-- | drivers/regulator/dummy.c | 66 | ||||
-rw-r--r-- | drivers/regulator/dummy.h | 31 | ||||
-rw-r--r-- | drivers/regulator/fixed.c | 30 | ||||
-rw-r--r-- | drivers/regulator/lp3971.c | 68 | ||||
-rw-r--r-- | drivers/regulator/max1586.c | 9 | ||||
-rw-r--r-- | drivers/regulator/max8649.c | 408 | ||||
-rw-r--r-- | drivers/regulator/max8660.c | 11 | ||||
-rw-r--r-- | drivers/regulator/mc13783-regulator.c | 465 | ||||
-rw-r--r-- | drivers/regulator/pcap-regulator.c | 8 | ||||
-rw-r--r-- | drivers/regulator/tps65023-regulator.c | 35 | ||||
-rw-r--r-- | drivers/regulator/tps6507x-regulator.c | 34 | ||||
-rw-r--r-- | drivers/regulator/twl-regulator.c | 22 | ||||
-rw-r--r-- | drivers/regulator/virtual.c | 64 | ||||
-rw-r--r-- | drivers/regulator/wm831x-dcdc.c | 12 | ||||
-rw-r--r-- | drivers/regulator/wm831x-isink.c | 3 | ||||
-rw-r--r-- | drivers/regulator/wm831x-ldo.c | 5 | ||||
-rw-r--r-- | drivers/regulator/wm8350-regulator.c | 46 | ||||
-rw-r--r-- | drivers/regulator/wm8400-regulator.c | 7 | ||||
-rw-r--r-- | drivers/regulator/wm8994-regulator.c | 307 |
23 files changed, 1565 insertions, 183 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 262f62eec83..834b4844182 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -27,6 +27,17 @@ config REGULATOR_DEBUG help Say yes here to enable debugging support. +config REGULATOR_DUMMY + bool "Provide a dummy regulator if regulator lookups fail" + help + If this option is enabled then when a regulator lookup fails + and the board has not specified that it has provided full + constraints then the regulator core will provide an always + enabled dummy regulator will be provided, allowing consumer + drivers to continue. + + A warning will be generated when this substitution is done. + config REGULATOR_FIXED_VOLTAGE tristate "Fixed voltage regulator support" help @@ -69,6 +80,13 @@ config REGULATOR_MAX1586 regulator via I2C bus. The provided regulator is suitable for PXA27x chips to control VCC_CORE and VCC_USIM voltages. +config REGULATOR_MAX8649 + tristate "Maxim 8649 voltage regulator" + depends on I2C + help + This driver controls a Maxim 8649 voltage output regulator via + I2C bus. + config REGULATOR_MAX8660 tristate "Maxim 8660/8661 voltage regulator" depends on I2C @@ -91,19 +109,26 @@ config REGULATOR_WM831X of PMIC devices. config REGULATOR_WM8350 - tristate "Wolfson Microelectroncis WM8350 AudioPlus PMIC" + tristate "Wolfson Microelectronics WM8350 AudioPlus PMIC" depends on MFD_WM8350 help This driver provides support for the voltage and current regulators of the WM8350 AudioPlus PMIC. config REGULATOR_WM8400 - tristate "Wolfson Microelectroncis WM8400 AudioPlus PMIC" + tristate "Wolfson Microelectronics WM8400 AudioPlus PMIC" depends on MFD_WM8400 help This driver provides support for the voltage regulators of the WM8400 AudioPlus PMIC. +config REGULATOR_WM8994 + tristate "Wolfson Microelectronics WM8994 CODEC" + depends on MFD_WM8994 + help + This driver provides support for the voltage regulators on the + WM8994 CODEC. + config REGULATOR_DA903X tristate "Support regulators on Dialog Semiconductor DA9030/DA9034 PMIC" depends on PMIC_DA903X diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index b3c806c7941..e845b66ad59 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -9,15 +9,18 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_MAX8649) += max8649.o obj-$(CONFIG_REGULATOR_MAX8660) += max8660.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o +obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_PCF50633) += pcf50633-regulator.o obj-$(CONFIG_REGULATOR_PCAP) += pcap-regulator.o diff --git a/drivers/regulator/ab3100.c b/drivers/regulator/ab3100.c index b349db4504b..7de950959ed 100644 --- a/drivers/regulator/ab3100.c +++ b/drivers/regulator/ab3100.c @@ -561,7 +561,7 @@ ab3100_regulator_desc[AB3100_NUM_REGULATORS] = { * for all the different regulators. */ -static int __init ab3100_regulators_probe(struct platform_device *pdev) +static int __devinit ab3100_regulators_probe(struct platform_device *pdev) { struct ab3100_platform_data *plfdata = pdev->dev.platform_data; struct ab3100 *ab3100 = platform_get_drvdata(pdev); @@ -641,7 +641,7 @@ static int __init ab3100_regulators_probe(struct platform_device *pdev) return 0; } -static int __exit ab3100_regulators_remove(struct platform_device *pdev) +static int __devexit ab3100_regulators_remove(struct platform_device *pdev) { int i; @@ -659,7 +659,7 @@ static struct platform_driver ab3100_regulators_driver = { .owner = THIS_MODULE, }, .probe = ab3100_regulators_probe, - .remove = __exit_p(ab3100_regulators_remove), + .remove = __devexit_p(ab3100_regulators_remove), }; static __init int ab3100_regulators_init(void) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b60a4c9f8f1..c7bbe30010f 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -19,10 +19,13 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/suspend.h> +#include <linux/delay.h> #include <linux/regulator/consumer.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> +#include "dummy.h" + #define REGULATOR_VERSION "0.5" static DEFINE_MUTEX(regulator_list_mutex); @@ -1084,6 +1087,13 @@ overflow_err: return NULL; } +static int _regulator_get_enable_time(struct regulator_dev *rdev) +{ + if (!rdev->desc->ops->enable_time) + return 0; + return rdev->desc->ops->enable_time(rdev); +} + /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, int exclusive) @@ -1115,6 +1125,22 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, goto found; } } + +#ifdef CONFIG_REGULATOR_DUMMY + if (!devname) + devname = "deviceless"; + + /* If the board didn't flag that it was fully constrained then + * substitute in a dummy regulator so consumers can continue. + */ + if (!has_full_constraints) { + pr_warning("%s supply %s not found, using dummy regulator\n", + devname, id); + rdev = dummy_regulator_rdev; + goto found; + } +#endif + mutex_unlock(®ulator_list_mutex); return regulator; @@ -1251,7 +1277,7 @@ static int _regulator_can_change_status(struct regulator_dev *rdev) /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret; + int ret, delay; /* do we need to enable the supply regulator first */ if (rdev->supply) { @@ -1275,13 +1301,34 @@ static int _regulator_enable(struct regulator_dev *rdev) if (!_regulator_can_change_status(rdev)) return -EPERM; - if (rdev->desc->ops->enable) { - ret = rdev->desc->ops->enable(rdev); - if (ret < 0) - return ret; - } else { + if (!rdev->desc->ops->enable) return -EINVAL; + + /* Query before enabling in case configuration + * dependant. */ + ret = _regulator_get_enable_time(rdev); + if (ret >= 0) { + delay = ret; + } else { + printk(KERN_WARNING + "%s: enable_time() failed for %s: %d\n", + __func__, rdev_get_name(rdev), + ret); + delay = 0; } + + /* Allow the regulator to ramp; it would be useful + * to extend this for bulk operations so that the + * regulators can ramp together. */ + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + + if (delay >= 1000) + mdelay(delay / 1000); + else if (delay) + udelay(delay); + } else if (ret < 0) { printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", __func__, rdev_get_name(rdev), ret); @@ -1341,6 +1388,9 @@ static int _regulator_disable(struct regulator_dev *rdev) __func__, rdev_get_name(rdev)); return ret; } + + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); } /* decrease our supplies ref count and disable if required */ @@ -1399,8 +1449,8 @@ static int _regulator_force_disable(struct regulator_dev *rdev) return ret; } /* notify other consumers that power has been forced off */ - _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE, - NULL); + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_DISABLE, NULL); } /* decrease our supplies ref count and disable if required */ @@ -1434,9 +1484,9 @@ EXPORT_SYMBOL_GPL(regulator_force_disable); static int _regulator_is_enabled(struct regulator_dev *rdev) { - /* sanity check */ + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) - return -EINVAL; + return 1; return rdev->desc->ops->is_enabled(rdev); } @@ -2451,8 +2501,15 @@ EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); static int __init regulator_init(void) { + int ret; + printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); - return class_register(®ulator_class); + + ret = class_register(®ulator_class); + + regulator_dummy_init(); + + return ret; } /* init early to allow our consumers to complete system booting */ diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c new file mode 100644 index 00000000000..c7410bde7b5 --- /dev/null +++ b/drivers/regulator/dummy.c @@ -0,0 +1,66 @@ +/* + * dummy.c + * + * Copyright 2010 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. + * + * 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/platform_device.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +#include "dummy.h" + +struct regulator_dev *dummy_regulator_rdev; + +static struct regulator_init_data dummy_initdata; + +static struct regulator_ops dummy_ops; + +static struct regulator_desc dummy_desc = { + .name = "dummy", + .id = -1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &dummy_ops, +}; + +static struct platform_device *dummy_pdev; + +void __init regulator_dummy_init(void) +{ + int ret; + + dummy_pdev = platform_device_alloc("reg-dummy", -1); + if (!dummy_pdev) { + pr_err("Failed to allocate dummy regulator device\n"); + return; + } + + ret = platform_device_add(dummy_pdev); + if (ret != 0) { + pr_err("Failed to register dummy regulator device: %d\n", ret); + platform_device_put(dummy_pdev); + 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); + platform_device_unregister(dummy_pdev); + return; + } +} diff --git a/drivers/regulator/dummy.h b/drivers/regulator/dummy.h new file mode 100644 index 00000000000..3921c0e2424 --- /dev/null +++ b/drivers/regulator/dummy.h @@ -0,0 +1,31 @@ +/* + * dummy.h + * + * Copyright 2010 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. + * + * This is useful for systems with mixed controllable and + * non-controllable regulators, as well as for allowing testing on + * systems with no controllable regulators. + */ + +#ifndef _DUMMY_H +#define _DUMMY_H + +struct regulator_dev; + +extern struct regulator_dev *dummy_regulator_rdev; + +#ifdef CONFIG_REGULATOR_DUMMY +void __init regulator_dummy_init(void); +#else +static inline void regulator_dummy_init(void) { } +#endif + +#endif diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c index f9f516a3028..d11f7622430 100644 --- a/drivers/regulator/fixed.c +++ b/drivers/regulator/fixed.c @@ -24,14 +24,16 @@ #include <linux/regulator/driver.h> #include <linux/regulator/fixed.h> #include <linux/gpio.h> +#include <linux/delay.h> struct fixed_voltage_data { struct regulator_desc desc; struct regulator_dev *dev; int microvolts; int gpio; - unsigned enable_high:1; - unsigned is_enabled:1; + unsigned startup_delay; + bool enable_high; + bool is_enabled; }; static int fixed_voltage_is_enabled(struct regulator_dev *dev) @@ -47,7 +49,7 @@ static int fixed_voltage_enable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, data->enable_high); - data->is_enabled = 1; + data->is_enabled = true; } return 0; @@ -59,12 +61,19 @@ static int fixed_voltage_disable(struct regulator_dev *dev) if (gpio_is_valid(data->gpio)) { gpio_set_value_cansleep(data->gpio, !data->enable_high); - data->is_enabled = 0; + data->is_enabled = false; } return 0; } +static int fixed_voltage_enable_time(struct regulator_dev *dev) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + return data->startup_delay; +} + static int fixed_voltage_get_voltage(struct regulator_dev *dev) { struct fixed_voltage_data *data = rdev_get_drvdata(dev); @@ -87,11 +96,12 @@ static struct regulator_ops fixed_voltage_ops = { .is_enabled = fixed_voltage_is_enabled, .enable = fixed_voltage_enable, .disable = fixed_voltage_disable, + .enable_time = fixed_voltage_enable_time, .get_voltage = fixed_voltage_get_voltage, .list_voltage = fixed_voltage_list_voltage, }; -static int regulator_fixed_voltage_probe(struct platform_device *pdev) +static int __devinit reg_fixed_voltage_probe(struct platform_device *pdev) { struct fixed_voltage_config *config = pdev->dev.platform_data; struct fixed_voltage_data *drvdata; @@ -117,6 +127,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) drvdata->microvolts = config->microvolts; drvdata->gpio = config->gpio; + drvdata->startup_delay = config->startup_delay; if (gpio_is_valid(config->gpio)) { drvdata->enable_high = config->enable_high; @@ -163,7 +174,7 @@ static int regulator_fixed_voltage_probe(struct platform_device *pdev) /* Regulator without GPIO control is considered * always enabled */ - drvdata->is_enabled = 1; + drvdata->is_enabled = true; } drvdata->dev = regulator_register(&drvdata->desc, &pdev->dev, @@ -191,7 +202,7 @@ err: return ret; } -static int regulator_fixed_voltage_remove(struct platform_device *pdev) +static int __devexit reg_fixed_voltage_remove(struct platform_device *pdev) { struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); @@ -205,10 +216,11 @@ static int regulator_fixed_voltage_remove(struct platform_device *pdev) } static struct platform_driver regulator_fixed_voltage_driver = { - .probe = regulator_fixed_voltage_probe, - .remove = regulator_fixed_voltage_remove, + .probe = reg_fixed_voltage_probe, + .remove = __devexit_p(reg_fixed_voltage_remove), .driver = { .name = "reg-fixed-voltage", + .owner = THIS_MODULE, }, }; diff --git a/drivers/regulator/lp3971.c b/drivers/regulator/lp3971.c index 4f33a0f4a17..f5532ed7927 100644 --- a/drivers/regulator/lp3971.c +++ b/drivers/regulator/lp3971.c @@ -54,7 +54,7 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val); #define LP3971_BUCK2_BASE 0x29 #define LP3971_BUCK3_BASE 0x32 -const static int buck_base_addr[] = { +static const int buck_base_addr[] = { LP3971_BUCK1_BASE, LP3971_BUCK2_BASE, LP3971_BUCK3_BASE, @@ -63,7 +63,7 @@ const static int buck_base_addr[] = { #define LP3971_BUCK_TARGET_VOL1_REG(x) (buck_base_addr[x]) #define LP3971_BUCK_TARGET_VOL2_REG(x) (buck_base_addr[x]+1) -const static int buck_voltage_map[] = { +static const int buck_voltage_map[] = { 0, 800, 850, 900, 950, 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550, 1600, 1650, 1700, 1800, 1900, 2500, 2800, @@ -96,17 +96,17 @@ const static int buck_voltage_map[] = { #define LDO_VOL_CONTR_SHIFT(x) ((x & 1) << 2) #define LDO_VOL_CONTR_MASK 0x0f -const static int ldo45_voltage_map[] = { +static const int ldo45_voltage_map[] = { 1000, 1050, 1100, 1150, 1200, 1250, 1300, 1350, 1400, 1500, 1800, 1900, 2500, 2800, 3000, 3300, }; -const static int ldo123_voltage_map[] = { +static const int ldo123_voltage_map[] = { 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100, 3200, 3300, }; -const static int *ldo_voltage_map[] = { +static const int *ldo_voltage_map[] = { ldo123_voltage_map, /* LDO1 */ ldo123_voltage_map, /* LDO2 */ ldo123_voltage_map, /* LDO3 */ @@ -431,20 +431,20 @@ static int lp3971_set_bits(struct lp3971 *lp3971, u8 reg, u16 mask, u16 val) return ret; } -static int setup_regulators(struct lp3971 *lp3971, - struct lp3971_platform_data *pdata) +static int __devinit setup_regulators(struct lp3971 *lp3971, + struct lp3971_platform_data *pdata) { int i, err; - int num_regulators = pdata->num_regulators; - lp3971->num_regulators = num_regulators; - lp3971->rdev = kzalloc(sizeof(struct regulator_dev *) * num_regulators, - GFP_KERNEL); + + lp3971->num_regulators = pdata->num_regulators; + lp3971->rdev = kcalloc(pdata->num_regulators, + sizeof(struct regulator_dev *), GFP_KERNEL); /* Instantiate the regulators */ - for (i = 0; i < num_regulators; i++) { - int id = pdata->regulators[i].id; - lp3971->rdev[i] = regulator_register(®ulators[id], - lp3971->dev, pdata->regulators[i].initdata, lp3971); + for (i = 0; i < pdata->num_regulators; i++) { + struct lp3971_regulator_subdev *reg = &pdata->regulators[i]; + lp3971->rdev[i] = regulator_register(®ulators[reg->id], + lp3971->dev, reg->initdata, lp3971); if (IS_ERR(lp3971->rdev[i])) { err = PTR_ERR(lp3971->rdev[i]); @@ -455,10 +455,10 @@ static int setup_regulators(struct lp3971 *lp3971, } return 0; + error: - for (i = 0; i < num_regulators; i++) - if (lp3971->rdev[i]) - regulator_unregister(lp3971->rdev[i]); + while (--i >= 0) + regulator_unregister(lp3971->rdev[i]); kfree(lp3971->rdev); lp3971->rdev = NULL; return err; @@ -472,15 +472,17 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c, int ret; u16 val; - lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); - if (lp3971 == NULL) { - ret = -ENOMEM; - goto err; + if (!pdata) { + dev_dbg(&i2c->dev, "No platform init data supplied\n"); + return -ENODEV; } + lp3971 = kzalloc(sizeof(struct lp3971), GFP_KERNEL); + if (lp3971 == NULL) + return -ENOMEM; + lp3971->i2c = i2c; lp3971->dev = &i2c->dev; - i2c_set_clientdata(i2c, lp3971); mutex_init(&lp3971->io_lock); @@ -493,19 +495,15 @@ static int __devinit lp3971_i2c_probe(struct i2c_client *i2c, goto err_detect; } - if (pdata) { - ret = setup_regulators(lp3971, pdata); - if (ret < 0) - goto err_detect; - } else - dev_warn(lp3971->dev, "No platform init data supplied\n"); + ret = setup_regulators(lp3971, pdata); + if (ret < 0) + goto err_detect; + i2c_set_clientdata(i2c, lp3971); return 0; err_detect: - i2c_set_clientdata(i2c, NULL); kfree(lp3971); -err: return ret; } @@ -513,11 +511,13 @@ static int __devexit lp3971_i2c_remove(struct i2c_client *i2c) { struct lp3971 *lp3971 = i2c_get_clientdata(i2c); int i; + + i2c_set_clientdata(i2c, NULL); + for (i = 0; i < lp3971->num_regulators; i++) - if (lp3971->rdev[i]) - regulator_unregister(lp3971->rdev[i]); + regulator_unregister(lp3971->rdev[i]); + kfree(lp3971->rdev); - i2c_set_clientdata(i2c, NULL); kfree(lp3971); return 0; diff --git a/drivers/regulator/max1586.c b/drivers/regulator/max1586.c index 2c082d3ef48..a49fc952c9a 100644 --- a/drivers/regulator/max1586.c +++ b/drivers/regulator/max1586.c @@ -179,8 +179,8 @@ static struct regulator_desc max1586_reg[] = { }, }; -static int max1586_pmic_probe(struct i2c_client *client, - const struct i2c_device_id *i2c_id) +static int __devinit 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; @@ -235,7 +235,7 @@ out: return ret; } -static int max1586_pmic_remove(struct i2c_client *client) +static int __devexit max1586_pmic_remove(struct i2c_client *client) { struct regulator_dev **rdev = i2c_get_clientdata(client); int i; @@ -257,9 +257,10 @@ MODULE_DEVICE_TABLE(i2c, max1586_id); static struct i2c_driver max1586_pmic_driver = { .probe = max1586_pmic_probe, - .remove = max1586_pmic_remove, + .remove = __devexit_p(max1586_pmic_remove), .driver = { .name = "max1586", + .owner = THIS_MODULE, }, .id_table = max1586_id, }; diff --git a/drivers/regulator/max8649.c b/drivers/regulator/max8649.c new file mode 100644 index 00000000000..3ebdf698c64 --- /dev/null +++ b/drivers/regulator/max8649.c @@ -0,0 +1,408 @@ +/* + * Regulators driver for Maxim max8649 + * + * Copyright (C) 2009-2010 Marvell International Ltd. + * Haojian Zhuang <haojian.zhuang@marvell.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. + */ +#include <linux/kernel.h> +#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/max8649.h> + +#define MAX8649_DCDC_VMIN 750000 /* uV */ +#define MAX8649_DCDC_VMAX 1380000 /* uV */ +#define MAX8649_DCDC_STEP 10000 /* uV */ +#define MAX8649_VOL_MASK 0x3f + +/* Registers */ +#define MAX8649_MODE0 0x00 +#define MAX8649_MODE1 0x01 +#define MAX8649_MODE2 0x02 +#define MAX8649_MODE3 0x03 +#define MAX8649_CONTROL 0x04 +#define MAX8649_SYNC 0x05 +#define MAX8649_RAMP 0x06 +#define MAX8649_CHIP_ID1 0x08 +#define MAX8649_CHIP_ID2 0x09 + +/* Bits */ +#define MAX8649_EN_PD (1 << 7) +#define MAX8649_VID0_PD (1 << 6) +#define MAX8649_VID1_PD (1 << 5) +#define MAX8649_VID_MASK (3 << 5) + +#define MAX8649_FORCE_PWM (1 << 7) +#define MAX8649_SYNC_EXTCLK (1 << 6) + +#define MAX8649_EXT_MASK (3 << 6) + +#define MAX8649_RAMP_MASK (7 << 5) +#define MAX8649_RAMP_DOWN (1 << 1) + +struct max8649_regulator_info { + struct regulator_dev *regulator; + struct i2c_client *i2c; + struct device *dev; + struct mutex io_lock; + + int vol_reg; + unsigned mode:2; /* bit[1:0] = VID1, VID0 */ + unsigned extclk_freq:2; + unsigned extclk:1; + unsigned ramp_timing:3; + unsigned ramp_down:1; +}; + +/* I2C operations */ + +static inline int max8649_read_device(struct i2c_client *i2c, + int reg, int bytes, void *dest) +{ + unsigned char data; + int ret; + + data = (unsigned char)reg; + ret = i2c_master_send(i2c, &data, 1); + if (ret < 0) + return ret; + ret = i2c_master_recv(i2c, dest, bytes); + if (ret < 0) + return ret; + return 0; +} + +static inline int max8649_write_device(struct i2c_client *i2c, + int reg, int bytes, void *src) +{ + unsigned char buf[bytes + 1]; + int ret; + + buf[0] = (unsigned char)reg; + memcpy(&buf[1], src, bytes); + + ret = i2c_master_send(i2c, buf, bytes + 1); + if (ret < 0) + return ret; + return 0; +} + +static int max8649_reg_read(struct i2c_client *i2c, int reg) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(i2c); + unsigned char data; + int ret; + + mutex_lock(&info->io_lock); + ret = max8649_read_device(i2c, reg, 1, &data); + mutex_unlock(&info->io_lock); + + if (ret < 0) + return ret; + return (int)data; +} + +static int max8649_set_bits(struct i2c_client *i2c, int reg, + unsigned char mask, unsigned char data) +{ + struct max8649_regulator_info *info = i2c_get_clientdata(i2c); + unsigned char value; + int ret; + + mutex_lock(&info->io_lock); + ret = max8649_read_device(i2c, reg, 1, &value); + if (ret < 0) + goto out; + value &= ~mask; + value |= data; + ret = max8649_write_device(i2c, reg, 1, &value); +out: + mutex_unlock(&info->io_lock); + return ret; +} + +static inline int check_range(int min_uV, int max_uV) +{ + if ((min_uV < MAX8649_DCDC_VMIN) || (max_uV > MAX8649_DCDC_VMAX) + || (min_uV > max_uV)) + return -EINVAL; + return 0; +} + +static int max8649_list_voltage(struct regulator_dev *rdev, unsigned index) +{ + return (MAX8649_DCDC_VMIN + index * MAX8649_DCDC_STEP); +} + +static int max8649_get_voltage(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data; + int ret; + + ret = max8649_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + data = (unsigned char)ret & MAX8649_VOL_MASK; + return max8649_list_voltage(rdev, data); +} + +static int max8649_set_voltage(struct regulator_dev *rdev, + int min_uV, int max_uV) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + unsigned char data, mask; + + if (check_range(min_uV, max_uV)) { + dev_err(info->dev, "invalid voltage range (%d, %d) uV\n", + min_uV, max_uV); + return -EINVAL; + } + data = (min_uV - MAX8649_DCDC_VMIN + MAX8649_DCDC_STEP - 1) + / MAX8649_DCDC_STEP; + mask = MAX8649_VOL_MASK; + + return max8649_set_bits(info->i2c, info->vol_reg, mask, data); +} + +/* EN_PD means pulldown on EN input */ +static int max8649_enable(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD, 0); +} + +/* + * Applied internal pulldown resistor on EN input pin. + * If pulldown EN pin outside, it would be better. + */ +static int max8649_disable(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + return max8649_set_bits(info->i2c, MAX8649_CONTROL, MAX8649_EN_PD, + MAX8649_EN_PD); +} + +static int max8649_is_enabled(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int ret; + + ret = max8649_reg_read(info->i2c, MAX8649_CONTROL); + if (ret < 0) + return ret; + return !((unsigned char)ret & MAX8649_EN_PD); +} + +static int max8649_enable_time(struct regulator_dev *rdev) +{ + struct max8649_regulator_info *info = rdev_get_drvdata(rdev); + int voltage, rate, ret; + + /* get voltage */ + ret = max8649_reg_read(info->i2c, info->vol_reg); + if (ret < 0) + return ret; + ret &= MAX8649_VOL_MASK; + voltage = max8649_list_voltage(rdev, (unsigned char)ret); /* uV */ + + /* get rate */ + ret = max8649_reg_read(info-&g |