diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2008-08-01 10:56:40 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-08-01 10:56:40 -0700 |
commit | 561b35b341b1aeeab486affe1ede0ee6640ce33b (patch) | |
tree | a7570737884bef3c15db98cae629a82ce3586b3c /drivers | |
parent | a7c2a10dab4e5122cbcfa3d5e9d589a52ccc2287 (diff) | |
parent | 0eb5d5ab3ec99bfd22ff16797d95835369ffb25b (diff) |
Merge branch 'reg-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6
* 'reg-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6:
regulator: TI bq24022 Li-Ion Charger driver
regulator: maintainers - add maintainers for regulator framework.
regulator: documentation - ABI
regulator: documentation - machine
regulator: documentation - regulator driver
regulator: documentation - consumer interface
regulator: documentation - overview
regulator: core kbuild files
regulator: regulator test harness
regulator: add support for fixed regulators.
regulator: regulator framework core
regulator: fixed regulator interface
regulator: machine driver interface
regulator: regulator driver interface
regulator: consumer device interface
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 59 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 12 | ||||
-rw-r--r-- | drivers/regulator/bq24022.c | 167 | ||||
-rw-r--r-- | drivers/regulator/core.c | 1903 | ||||
-rw-r--r-- | drivers/regulator/fixed.c | 129 | ||||
-rw-r--r-- | drivers/regulator/virtual.c | 345 |
7 files changed, 2616 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile index 54ec5e718c0..a280ab3d083 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -97,3 +97,4 @@ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_VIRTIO) += virtio/ +obj-$(CONFIG_REGULATOR) += regulator/ diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig new file mode 100644 index 00000000000..a656128f1fd --- /dev/null +++ b/drivers/regulator/Kconfig @@ -0,0 +1,59 @@ +menu "Voltage and Current regulators" + +config REGULATOR + bool "Voltage and Current Regulator Support" + default n + help + Generic Voltage and Current Regulator support. + + This framework is designed to provide a generic interface to voltage + and current regulators within the Linux kernel. It's intended to + provide voltage and current control to client or consumer drivers and + also provide status information to user space applications through a + sysfs interface. + + The intention is to allow systems to dynamically control regulator + output in order to save power and prolong battery life. This applies + to both voltage regulators (where voltage output is controllable) and + current sinks (where current output is controllable). + + This framework safely compiles out if not selected so that client + drivers can still be used in systems with no software controllable + regulators. + + If unsure, say no. + +config REGULATOR_DEBUG + bool "Regulator debug support" + depends on REGULATOR + help + Say yes here to enable debugging support. + +config REGULATOR_FIXED_VOLTAGE + tristate + default n + select REGULATOR + +config REGULATOR_VIRTUAL_CONSUMER + tristate "Virtual regulator consumer support" + default n + select REGULATOR + help + This driver provides a virtual consumer for the voltage and + current regulator API which provides sysfs controls for + configuring the supplies requested. This is mainly useful + for test purposes. + + If unsure, say no. + +config REGULATOR_BQ24022 + tristate "TI bq24022 Dual Input 1-Cell Li-Ion Charger IC" + default n + select REGULATOR + help + This driver controls a TI bq24022 Charger attached via + GPIOs. The provided current regulator can enable/disable + charging select between 100 mA and 500 mA charging current + limit. + +endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile new file mode 100644 index 00000000000..ac2c64efe65 --- /dev/null +++ b/drivers/regulator/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for regulator drivers. +# + + +obj-$(CONFIG_REGULATOR) += core.o +obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o +obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o + +obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o + +ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG diff --git a/drivers/regulator/bq24022.c b/drivers/regulator/bq24022.c new file mode 100644 index 00000000000..263699d6152 --- /dev/null +++ b/drivers/regulator/bq24022.c @@ -0,0 +1,167 @@ +/* + * Support for TI bq24022 (bqTINY-II) Dual Input (USB/AC Adpater) + * 1-Cell Li-Ion Charger connected via GPIOs. + * + * Copyright (c) 2008 Philipp Zabel + * + * 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/init.h> +#include <linux/platform_device.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/regulator/bq24022.h> +#include <linux/regulator/driver.h> + +static int bq24022_set_current_limit(struct regulator_dev *rdev, + int min_uA, int max_uA) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "setting current limit to %s mA\n", + max_uA >= 500000 ? "500" : "100"); + + /* REVISIT: maybe return error if min_uA != 0 ? */ + gpio_set_value(pdata->gpio_iset2, max_uA >= 500000); + return 0; +} + +static int bq24022_get_current_limit(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + return gpio_get_value(pdata->gpio_iset2) ? 500000 : 100000; +} + +static int bq24022_enable(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "enabling charger\n"); + + gpio_set_value(pdata->gpio_nce, 0); + return 0; +} + +static int bq24022_disable(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + dev_dbg(&pdev->dev, "disabling charger\n"); + + gpio_set_value(pdata->gpio_nce, 1); + return 0; +} + +static int bq24022_is_enabled(struct regulator_dev *rdev) +{ + struct platform_device *pdev = rdev_get_drvdata(rdev); + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + + return !gpio_get_value(pdata->gpio_nce); +} + +static struct regulator_ops bq24022_ops = { + .set_current_limit = bq24022_set_current_limit, + .get_current_limit = bq24022_get_current_limit, + .enable = bq24022_enable, + .disable = bq24022_disable, + .is_enabled = bq24022_is_enabled, +}; + +static struct regulator_desc bq24022_desc = { + .name = "bq24022", + .ops = &bq24022_ops, + .type = REGULATOR_CURRENT, +}; + +static int __init bq24022_probe(struct platform_device *pdev) +{ + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct regulator_dev *bq24022; + int ret; + + if (!pdata || !pdata->gpio_nce || !pdata->gpio_iset2) + return -EINVAL; + + ret = gpio_request(pdata->gpio_nce, "ncharge_en"); + if (ret) { + dev_dbg(&pdev->dev, "couldn't request nCE GPIO: %d\n", + pdata->gpio_nce); + goto err_ce; + } + ret = gpio_request(pdata->gpio_iset2, "charge_mode"); + if (ret) { + dev_dbg(&pdev->dev, "couldn't request ISET2 GPIO: %d\n", + pdata->gpio_iset2); + goto err_iset2; + } + ret = gpio_direction_output(pdata->gpio_iset2, 0); + ret = gpio_direction_output(pdata->gpio_nce, 1); + + bq24022 = regulator_register(&bq24022_desc, pdev); + if (IS_ERR(bq24022)) { + dev_dbg(&pdev->dev, "couldn't register regulator\n"); + ret = PTR_ERR(bq24022); + goto err_reg; + } + platform_set_drvdata(pdev, bq24022); + dev_dbg(&pdev->dev, "registered regulator\n"); + + return 0; +err_reg: + gpio_free(pdata->gpio_iset2); +err_iset2: + gpio_free(pdata->gpio_nce); +err_ce: + return ret; +} + +static int __devexit bq24022_remove(struct platform_device *pdev) +{ + struct bq24022_mach_info *pdata = pdev->dev.platform_data; + struct regulator_dev *bq24022 = platform_get_drvdata(pdev); + + regulator_unregister(bq24022); + gpio_free(pdata->gpio_iset2); + gpio_free(pdata->gpio_nce); + + return 0; +} + +static struct platform_driver bq24022_driver = { + .driver = { + .name = "bq24022", + }, + .remove = __devexit_p(bq24022_remove), +}; + +static int __init bq24022_init(void) +{ + return platform_driver_probe(&bq24022_driver, bq24022_probe); +} + +static void __exit bq24022_exit(void) +{ + platform_driver_unregister(&bq24022_driver); +} + +/* + * make sure this is probed before gpio_vbus and pda_power, + * but after asic3 or other GPIO expander drivers. + */ +subsys_initcall(bq24022_init); +module_exit(bq24022_exit); + +MODULE_AUTHOR("Philipp Zabel"); +MODULE_DESCRIPTION("TI bq24022 Li-Ion Charger driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c new file mode 100644 index 00000000000..9c798626156 --- /dev/null +++ b/drivers/regulator/core.c @@ -0,0 +1,1903 @@ +/* + * core.c -- Voltage/Current Regulator framework. + * + * Copyright 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood <liam.girdwood@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/kernel.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/suspend.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> + +#define REGULATOR_VERSION "0.5" + +static DEFINE_MUTEX(regulator_list_mutex); +static LIST_HEAD(regulator_list); +static LIST_HEAD(regulator_map_list); + +/** + * struct regulator_dev + * + * Voltage / Current regulator class device. One for each regulator. + */ +struct regulator_dev { + struct regulator_desc *desc; + int use_count; + + /* lists we belong to */ + struct list_head list; /* list of all regulators */ + struct list_head slist; /* list of supplied regulators */ + + /* lists we own */ + struct list_head consumer_list; /* consumers we supply */ + struct list_head supply_list; /* regulators we supply */ + + struct blocking_notifier_head notifier; + struct mutex mutex; /* consumer lock */ + struct module *owner; + struct device dev; + struct regulation_constraints *constraints; + struct regulator_dev *supply; /* for tree */ + + void *reg_data; /* regulator_dev data */ +}; + +/** + * struct regulator_map + * + * Used to provide symbolic supply names to devices. + */ +struct regulator_map { + struct list_head list; + struct device *dev; + const char *supply; + const char *regulator; +}; + +static inline struct regulator_dev *to_rdev(struct device *d) +{ + return container_of(d, struct regulator_dev, dev); +} + +/* + * struct regulator + * + * One for each consumer device. + */ +struct regulator { + struct device *dev; + struct list_head list; + int uA_load; + int min_uV; + int max_uV; + int enabled; /* client has called enabled */ + char *supply_name; + struct device_attribute dev_attr; + struct regulator_dev *rdev; +}; + +static int _regulator_is_enabled(struct regulator_dev *rdev); +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); +static void _notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data); + +/* gets the regulator for a given consumer device */ +static struct regulator *get_device_regulator(struct device *dev) +{ + struct regulator *regulator = NULL; + struct regulator_dev *rdev; + + mutex_lock(®ulator_list_mutex); + list_for_each_entry(rdev, ®ulator_list, list) { + mutex_lock(&rdev->mutex); + list_for_each_entry(regulator, &rdev->consumer_list, list) { + if (regulator->dev == dev) { + mutex_unlock(&rdev->mutex); + mutex_unlock(®ulator_list_mutex); + return regulator; + } + } + mutex_unlock(&rdev->mutex); + } + mutex_unlock(®ulator_list_mutex); + return NULL; +} + +/* Platform voltage constraint check */ +static int regulator_check_voltage(struct regulator_dev *rdev, + int *min_uV, int *max_uV) +{ + BUG_ON(*min_uV > *max_uV); + + if (!rdev->constraints) { + printk(KERN_ERR "%s: no constraints for %s\n", __func__, + rdev->desc->name); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE)) { + printk(KERN_ERR "%s: operation not allowed for %s\n", + __func__, rdev->desc->name); + return -EPERM; + } + + if (*max_uV > rdev->constraints->max_uV) + *max_uV = rdev->constraints->max_uV; + if (*min_uV < rdev->constraints->min_uV) + *min_uV = rdev->constraints->min_uV; + + if (*min_uV > *max_uV) + return -EINVAL; + + return 0; +} + +/* current constraint check */ +static int regulator_check_current_limit(struct regulator_dev *rdev, + int *min_uA, int *max_uA) +{ + BUG_ON(*min_uA > *max_uA); + + if (!rdev->constraints) { + printk(KERN_ERR "%s: no constraints for %s\n", __func__, + rdev->desc->name); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT)) { + printk(KERN_ERR "%s: operation not allowed for %s\n", + __func__, rdev->desc->name); + return -EPERM; + } + + if (*max_uA > rdev->constraints->max_uA) + *max_uA = rdev->constraints->max_uA; + if (*min_uA < rdev->constraints->min_uA) + *min_uA = rdev->constraints->min_uA; + + if (*min_uA > *max_uA) + return -EINVAL; + + return 0; +} + +/* operating mode constraint check */ +static int regulator_check_mode(struct regulator_dev *rdev, int mode) +{ + if (!rdev->constraints) { + printk(KERN_ERR "%s: no constraints for %s\n", __func__, + rdev->desc->name); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE)) { + printk(KERN_ERR "%s: operation not allowed for %s\n", + __func__, rdev->desc->name); + return -EPERM; + } + if (!(rdev->constraints->valid_modes_mask & mode)) { + printk(KERN_ERR "%s: invalid mode %x for %s\n", + __func__, mode, rdev->desc->name); + return -EINVAL; + } + return 0; +} + +/* dynamic regulator mode switching constraint check */ +static int regulator_check_drms(struct regulator_dev *rdev) +{ + if (!rdev->constraints) { + printk(KERN_ERR "%s: no constraints for %s\n", __func__, + rdev->desc->name); + return -ENODEV; + } + if (!(rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS)) { + printk(KERN_ERR "%s: operation not allowed for %s\n", + __func__, rdev->desc->name); + return -EPERM; + } + return 0; +} + +static ssize_t device_requested_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator *regulator; + + regulator = get_device_regulator(dev); + if (regulator == NULL) + return 0; + + return sprintf(buf, "%d\n", regulator->uA_load); +} + +static ssize_t regulator_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + ssize_t ret; + + mutex_lock(&rdev->mutex); + ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev)); + mutex_unlock(&rdev->mutex); + + return ret; +} + +static ssize_t regulator_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + return sprintf(buf, "%d\n", _regulator_get_current_limit(rdev)); +} + +static ssize_t regulator_opmode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + int mode = _regulator_get_mode(rdev); + + switch (mode) { + case REGULATOR_MODE_FAST: + return sprintf(buf, "fast\n"); + case REGULATOR_MODE_NORMAL: + return sprintf(buf, "normal\n"); + case REGULATOR_MODE_IDLE: + return sprintf(buf, "idle\n"); + case REGULATOR_MODE_STANDBY: + return sprintf(buf, "standby\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + int state = _regulator_is_enabled(rdev); + + if (state > 0) + return sprintf(buf, "enabled\n"); + else if (state == 0) + return sprintf(buf, "disabled\n"); + else + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_min_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->min_uA); +} + +static ssize_t regulator_max_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->max_uA); +} + +static ssize_t regulator_min_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->min_uV); +} + +static ssize_t regulator_max_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "constraint not defined\n"); + + return sprintf(buf, "%d\n", rdev->constraints->max_uV); +} + +static ssize_t regulator_total_uA_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + struct regulator *regulator; + int uA = 0; + + mutex_lock(&rdev->mutex); + list_for_each_entry(regulator, &rdev->consumer_list, list) + uA += regulator->uA_load; + mutex_unlock(&rdev->mutex); + return sprintf(buf, "%d\n", uA); +} + +static ssize_t regulator_num_users_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + return sprintf(buf, "%d\n", rdev->use_count); +} + +static ssize_t regulator_type_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + switch (rdev->desc->type) { + case REGULATOR_VOLTAGE: + return sprintf(buf, "voltage\n"); + case REGULATOR_CURRENT: + return sprintf(buf, "current\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_suspend_mem_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return sprintf(buf, "%d\n", rdev->constraints->state_mem.uV); +} + +static ssize_t regulator_suspend_disk_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return sprintf(buf, "%d\n", rdev->constraints->state_disk.uV); +} + +static ssize_t regulator_suspend_standby_uV_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return sprintf(buf, "%d\n", rdev->constraints->state_standby.uV); +} + +static ssize_t suspend_opmode_show(struct regulator_dev *rdev, + unsigned int mode, char *buf) +{ + switch (mode) { + case REGULATOR_MODE_FAST: + return sprintf(buf, "fast\n"); + case REGULATOR_MODE_NORMAL: + return sprintf(buf, "normal\n"); + case REGULATOR_MODE_IDLE: + return sprintf(buf, "idle\n"); + case REGULATOR_MODE_STANDBY: + return sprintf(buf, "standby\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t regulator_suspend_mem_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return suspend_opmode_show(rdev, + rdev->constraints->state_mem.mode, buf); +} + +static ssize_t regulator_suspend_disk_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return suspend_opmode_show(rdev, + rdev->constraints->state_disk.mode, buf); +} + +static ssize_t regulator_suspend_standby_mode_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + return suspend_opmode_show(rdev, + rdev->constraints->state_standby.mode, buf); +} + +static ssize_t regulator_suspend_mem_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + + if (rdev->constraints->state_mem.enabled) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static ssize_t regulator_suspend_disk_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + + if (rdev->constraints->state_disk.enabled) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} + +static ssize_t regulator_suspend_standby_state_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_dev *rdev = to_rdev(dev); + + if (!rdev->constraints) + return sprintf(buf, "not defined\n"); + + if (rdev->constraints->state_standby.enabled) + return sprintf(buf, "enabled\n"); + else + return sprintf(buf, "disabled\n"); +} +static struct device_attribute regulator_dev_attrs[] = { + __ATTR(microvolts, 0444, regulator_uV_show, NULL), + __ATTR(microamps, 0444, regulator_uA_show, NULL), + __ATTR(opmode, 0444, regulator_opmode_show, NULL), + __ATTR(state, 0444, regulator_state_show, NULL), + __ATTR(min_microvolts, 0444, regulator_min_uV_show, NULL), + __ATTR(min_microamps, 0444, regulator_min_uA_show, NULL), + __ATTR(max_microvolts, 0444, regulator_max_uV_show, NULL), + __ATTR(max_microamps, 0444, regulator_max_uA_show, NULL), + __ATTR(requested_microamps, 0444, regulator_total_uA_show, NULL), + __ATTR(num_users, 0444, regulator_num_users_show, NULL), + __ATTR(type, 0444, regulator_type_show, NULL), + __ATTR(suspend_mem_microvolts, 0444, + regulator_suspend_mem_uV_show, NULL), + __ATTR(suspend_disk_microvolts, 0444, + regulator_suspend_disk_uV_show, NULL), + __ATTR(suspend_standby_microvolts, 0444, + regulator_suspend_standby_uV_show, NULL), + __ATTR(suspend_mem_mode, 0444, + regulator_suspend_mem_mode_show, NULL), + __ATTR(suspend_disk_mode, 0444, + regulator_suspend_disk_mode_show, NULL), + __ATTR(suspend_standby_mode, 0444, + regulator_suspend_standby_mode_show, NULL), + __ATTR(suspend_mem_state, 0444, + regulator_suspend_mem_state_show, NULL), + __ATTR(suspend_disk_state, 0444, + regulator_suspend_disk_state_show, NULL), + __ATTR(suspend_standby_state, 0444, + regulator_suspend_standby_state_show, NULL), + __ATTR_NULL, +}; + +static void regulator_dev_release(struct device *dev) +{ + struct regulator_dev *rdev = to_rdev(dev); + kfree(rdev); +} + +static struct class regulator_class = { + .name = "regulator", + .dev_release = regulator_dev_release, + .dev_attrs = regulator_dev_attrs, +}; + +/* Calculate the new optimum regulator operating mode based on the new total + * consumer load. All locks held by caller */ +static void drms_uA_update(struct regulator_dev *rdev) +{ + struct regulator *sibling; + int current_uA = 0, output_uV, input_uV, err; + unsigned int mode; + + err = regulator_check_drms(rdev); + if (err < 0 || !rdev->desc->ops->get_optimum_mode || + !rdev->desc->ops->get_voltage || !rdev->desc->ops->set_mode); + return; + + /* get output voltage */ + output_uV = rdev->desc->ops->get_voltage(rdev); + if (output_uV <= 0) + return; + + /* get input voltage */ + if (rdev->supply && rdev->supply->desc->ops->get_voltage) + input_uV = rdev->supply->desc->ops->get_voltage(rdev->supply); + else + input_uV = rdev->constraints->input_uV; + if (input_uV <= 0) + return; + + /* calc total requested load */ + list_for_each_entry(sibling, &rdev->consumer_list, list) + current_uA += sibling->uA_load; + + /* now get the optimum mode for our new total regulator load */ + mode = rdev->desc->ops->get_optimum_mode(rdev, input_uV, + output_uV, current_uA); + + /* check the new mode is allowed */ + err = regulator_check_mode(rdev, mode); + if (err == 0) + rdev->desc->ops->set_mode(rdev, mode); +} + +static int suspend_set_state(struct regulator_dev *rdev, + struct regulator_state *rstate) +{ + int ret = 0; + + /* enable & disable are mandatory for suspend control */ + if (!rdev->desc->ops->set_suspend_enable || + !rdev->desc->ops->set_suspend_disable) + return -EINVAL; + + if (rstate->enabled) + ret = rdev->desc->ops->set_suspend_enable(rdev); + else + ret = rdev->desc->ops->set_suspend_disable(rdev); + if (ret < 0) { + printk(KERN_ERR "%s: failed to enabled/disable\n", __func__); + return ret; + } + + if (rdev->desc->ops->set_suspend_voltage && rstate->uV > 0) { + ret = rdev->desc->ops->set_suspend_voltage(rdev, rstate->uV); + if (ret < 0) { + printk(KERN_ERR "%s: failed to set voltage\n", + __func__); + return ret; + } + } + + if (rdev->desc->ops->set_suspend_mode && rstate->mode > 0) { + ret = rdev->desc->ops->set_suspend_mode(rdev, rstate->mode); + if (ret < 0) { + printk(KERN_ERR "%s: failed to set mode\n", __func__); + return ret; + } + } + return ret; +} + +/* locks held by caller */ +static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state) +{ + if (!rdev->constraints) + return -EINVAL; + + switch (state) { + case PM_SUSPEND_STANDBY: + return suspend_set_state(rdev, + &rdev->constraints->state_standby); + case PM_SUSPEND_MEM: + return suspend_set_state(rdev, + &rdev->constraints->state_mem); + case PM_SUSPEND_MAX: + return suspend_set_state(rdev, + &rdev->constraints->state_disk); + default: + return -EINVAL; + } +} + +static void print_constraints(struct regulator_dev *rdev) +{ + struct regulation_constraints *constraints = rdev->constraints; + char buf[80]; + int count; + + if (rdev->desc->type == REGULATOR_VOLTAGE) { + if (constraints->min_uV == constraints->max_uV) + count = sprintf(buf, "%d mV ", + constraints->min_uV / 1000); + else + count = sprintf(buf, "%d <--> %d mV ", + constraints->min_uV / 1000, + constraints->max_uV / 1000); + } else { + if (constraints->min_uA == constraints->max_uA) + count = sprintf(buf, "%d mA ", + constraints->min_uA / 1000); + else + count = sprintf(buf, "%d <--> %d mA ", + constraints->min_uA / 1000, + constraints->max_uA / 1000); + } + if (constraints->valid_modes_mask & REGULATOR_MODE_FAST) + count += sprintf(buf + count, "fast "); + if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL) + count += sprintf(buf + count, "normal "); + if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE) + count += sprintf(buf + count, "idle "); + if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) + count += sprintf(buf + count, "standby"); + + printk(KERN_INFO "regulator: %s: %s\n", rdev->desc->name, buf); +} + +#define REG_STR_SIZE 32 + +static struct regulator *create_regulator(struct regulator_dev *rdev, + struct device *dev, + const char *supply_name) +{ + struct regulator *regulator; + char buf[REG_STR_SIZE]; + int err, size; + + regulator = kzalloc(sizeof(*regulator), GFP_KERNEL); + if (regulator == NULL) + return NULL; + + mutex_lock(&rdev->mutex); + regulator->rdev = rdev; + list_add(®ulator->list, &rdev->consumer_list); + + if (dev) { + /* create a 'requested_microamps_name' sysfs entry */ + size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s", + supply_name); + if (size >= REG_STR_SIZE) + goto overflow_err; + + regulator->dev = dev; + regulator->dev_attr.attr.name = kstrdup(buf, GFP_KERNEL); + if (regulator->dev_attr.attr.name == NULL) + goto attr_name_err; + + regulator->dev_attr.attr.owner = THIS_MODULE; + regulator->dev_attr.attr.mode = 0444; + regulator->dev_attr.show = device_requested_uA_show; + err = device_create_file(dev, ®ulator->dev_attr); + if (err < 0) { + printk(KERN_WARNING "%s: could not add regulator_dev" + " load sysfs\n", __func__); + goto attr_name_err; + } + + /* also add a link to the device sysfs entry */ + size = scnprintf(buf, REG_STR_SIZE, "%s-%s", + dev->kobj.name, supply_name); + if (size >= REG_STR_SIZE) + goto attr_err; + + regulator->supply_name = kstrdup(buf, GFP_KERNEL); + if (regulator->supply_name == NULL) + goto attr_err; + + err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj, + buf); + if (err) { + printk(KERN_WARNING + "%s: could not add device link %s err %d\n", + __func__, dev->kobj.name, err); + device_remove_file(dev, ®ulator->dev_attr); + goto link_name_err; + } + } + mutex_unlock(&rdev->mutex); + return regulator; +link_name_err: + kfree(regulator->supply_name); +attr_err: + device_remove_file(regulator->dev, ®ulator->dev_attr); +attr_name_err: + kfree(regulator->dev_attr.attr.name); +overflow_err: + list_del(®ulator->list); + kfree(regulator); + mutex_unlock(&rdev->mutex); + return NULL; +} + +/** + * regulator_get - lookup and obtain a reference to a regulator. + * @dev: device for regulator "consumer" + * @id: Supply name or regulator ID. + * + * Returns a struct regulator corresponding to the regulator producer, + * or IS_ERR() condition containing errno. Use of supply names + * configured via regulator_set_device_supply() is strongly + * encouraged. + */ +struct regulator *regulator_get(struct device *dev, const char *id) +{ + struct regulator_dev *rdev; + struct regulator_map *map; + struct regulator *regulator = ERR_PTR(-ENODEV); + const char *supply = id; + + if (id == NULL) { + printk(KERN_ERR "regulator: get() with no identifier\n"); + return regulator; + } + + mutex_lock(®ulator_list_mutex); + + list_for_each_entry(map, ®ulator_map_list, list) { + if (dev == map->dev && + strcmp(map->supply, id) == 0) { + supply = map->regulator; + break; + } + } + + list_for_each_entry(rdev, ®ulator_list, list) { + if (strcmp(supply, rdev->desc->name) == 0 && + try_module_get(rdev->owner)) + goto found; + } + printk(KERN_ERR "regulator: Unable to get requested regulator: %s\n", + id); + mutex_unlock(®ulator_list_mutex); + return regulator; + +found: + regulator = create_regulator(rdev, dev, id); + if (regulator == NULL) { + regulator = ERR_PTR(-ENOMEM); + module_put(rdev->owner); + } + + mutex_unlock(®ulator_list_mutex); + return regulator; +} +EXPORT_SYMBOL_GPL(regulator_get); + +/** + * regulator_put - "free" the regulator source + * @regulator: regulator source + * + * Note: drivers must ensure that all regulator_enable calls made on this + * regulator source are balanced by regulator_disable calls prior to calling + * this function. + */ +void regulator_put(struct regulator *regulator) +{ + struct regulator_dev *rdev; + + if (regulator == NULL || IS_ERR(regulator)) + return; + + if (regulator->enabled) { |