From e2ce4eaa76214f65a3f328ec5b45c30248115768 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:10:07 +0100 Subject: [PATCH 01/15] regulator: consumer device interface Add support to allow consumer device drivers to control their regulator power supply. This uses a similar API to the kernel clock interface in that consumer drivers can get and put a regulator (like they can with clocks atm) and get/set voltage, current limit, mode, enable and disable. This should allow consumers complete control over their supply voltage and current limit. This also compiles out if not in use so drivers can be reused in systems with no regulator based power control. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/linux/regulator/consumer.h | 284 +++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 include/linux/regulator/consumer.h diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h new file mode 100644 index 00000000000..afdc4558bb9 --- /dev/null +++ b/include/linux/regulator/consumer.h @@ -0,0 +1,284 @@ +/* + * consumer.h -- SoC Regulator consumer support. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Consumer Interface. + * + * A Power Management Regulator framework for SoC based devices. + * Features:- + * o Voltage and current level control. + * o Operating mode control. + * o Regulator status. + * o sysfs entries for showing client devices and status + * + * EXPERIMENTAL FEATURES: + * Dynamic Regulator operating Mode Switching (DRMS) - allows regulators + * to use most efficient operating mode depending upon voltage and load and + * is transparent to client drivers. + * + * e.g. Devices x,y,z share regulator r. Device x and y draw 20mA each during + * IO and 1mA at idle. Device z draws 100mA when under load and 5mA when + * idling. Regulator r has > 90% efficiency in NORMAL mode at loads > 100mA + * but this drops rapidly to 60% when below 100mA. Regulator r has > 90% + * efficiency in IDLE mode at loads < 10mA. Thus regulator r will operate + * in normal mode for loads > 10mA and in IDLE mode for load <= 10mA. + * + */ + +#ifndef __LINUX_REGULATOR_CONSUMER_H_ +#define __LINUX_REGULATOR_CONSUMER_H_ + +/* + * Regulator operating modes. + * + * Regulators can run in a variety of different operating modes depending on + * output load. This allows further system power savings by selecting the + * best (and most efficient) regulator mode for a desired load. + * + * Most drivers will only care about NORMAL. The modes below are generic and + * will probably not match the naming convention of your regulator data sheet + * but should match the use cases in the datasheet. + * + * In order of power efficiency (least efficient at top). + * + * Mode Description + * FAST Regulator can handle fast changes in it's load. + * e.g. useful in CPU voltage & frequency scaling where + * load can quickly increase with CPU frequency increases. + * + * NORMAL Normal regulator power supply mode. Most drivers will + * use this mode. + * + * IDLE Regulator runs in a more efficient mode for light + * loads. Can be used for devices that have a low power + * requirement during periods of inactivity. This mode + * may be more noisy than NORMAL and may not be able + * to handle fast load switching. + * + * STANDBY Regulator runs in the most efficient mode for very + * light loads. Can be used by devices when they are + * in a sleep/standby state. This mode is likely to be + * the most noisy and may not be able to handle fast load + * switching. + * + * NOTE: Most regulators will only support a subset of these modes. Some + * will only just support NORMAL. + * + * These modes can be OR'ed together to make up a mask of valid register modes. + */ + +#define REGULATOR_MODE_FAST 0x1 +#define REGULATOR_MODE_NORMAL 0x2 +#define REGULATOR_MODE_IDLE 0x4 +#define REGULATOR_MODE_STANDBY 0x8 + +/* + * Regulator notifier events. + * + * UNDER_VOLTAGE Regulator output is under voltage. + * OVER_CURRENT Regulator output current is too high. + * REGULATION_OUT Regulator output is out of regulation. + * FAIL Regulator output has failed. + * OVER_TEMP Regulator over temp. + * FORCE_DISABLE Regulator shut down by software. + * + * NOTE: These events can be OR'ed together when passed into handler. + */ + +#define REGULATOR_EVENT_UNDER_VOLTAGE 0x01 +#define REGULATOR_EVENT_OVER_CURRENT 0x02 +#define REGULATOR_EVENT_REGULATION_OUT 0x04 +#define REGULATOR_EVENT_FAIL 0x08 +#define REGULATOR_EVENT_OVER_TEMP 0x10 +#define REGULATOR_EVENT_FORCE_DISABLE 0x20 + +struct regulator; + +/** + * struct regulator_bulk_data - Data used for bulk regulator operations. + * + * @supply The name of the supply. Initialised by the user before + * using the bulk regulator APIs. + * @consumer The regulator consumer for the supply. This will be managed + * by the bulk API. + * + * The regulator APIs provide a series of regulator_bulk_() API calls as + * a convenience to consumers which require multiple supplies. This + * structure is used to manage data for these calls. + */ +struct regulator_bulk_data { + const char *supply; + struct regulator *consumer; +}; + +#if defined(CONFIG_REGULATOR) + +/* regulator get and put */ +struct regulator *__must_check regulator_get(struct device *dev, + const char *id); +void regulator_put(struct regulator *regulator); + +/* regulator output control and status */ +int regulator_enable(struct regulator *regulator); +int regulator_disable(struct regulator *regulator); +int regulator_force_disable(struct regulator *regulator); +int regulator_is_enabled(struct regulator *regulator); + +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); +int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers); +int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers); +void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers); + +int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV); +int regulator_get_voltage(struct regulator *regulator); +int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA); +int regulator_get_current_limit(struct regulator *regulator); + +int regulator_set_mode(struct regulator *regulator, unsigned int mode); +unsigned int regulator_get_mode(struct regulator *regulator); +int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); + +/* regulator notifier block */ +int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb); +int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb); + +/* driver data - core doesn't touch */ +void *regulator_get_drvdata(struct regulator *regulator); +void regulator_set_drvdata(struct regulator *regulator, void *data); + +#else + +/* + * Make sure client drivers will still build on systems with no software + * controllable voltage or current regulators. + */ +static inline struct regulator *__must_check regulator_get(struct device *dev, + const char *id) +{ + /* Nothing except the stubbed out regulator API should be + * looking at the value except to check if it is an error + * value so the actual return value doesn't matter. + */ + return (struct regulator *)id; +} +static inline void regulator_put(struct regulator *regulator) +{ +} + +static inline int regulator_enable(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_disable(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_is_enabled(struct regulator *regulator) +{ + return 1; +} + +static inline int regulator_bulk_get(struct device *dev, + int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + return 0; +} + +static inline void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers) +{ +} + +static inline int regulator_set_voltage(struct regulator *regulator, + int min_uV, int max_uV) +{ + return 0; +} + +static inline int regulator_get_voltage(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA) +{ + return 0; +} + +static inline int regulator_get_current_limit(struct regulator *regulator) +{ + return 0; +} + +static inline int regulator_set_mode(struct regulator *regulator, + unsigned int mode) +{ + return 0; +} + +static inline unsigned int regulator_get_mode(struct regulator *regulator) +{ + return REGULATOR_MODE_NORMAL; +} + +static inline int regulator_set_optimum_mode(struct regulator *regulator, + int load_uA) +{ + return REGULATOR_MODE_NORMAL; +} + +static inline int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return 0; +} + +static inline int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return 0; +} + +static inline void *regulator_get_drvdata(struct regulator *regulator) +{ + return NULL; +} + +static inline void regulator_set_drvdata(struct regulator *regulator, + void *data) +{ +} + +#endif + +#endif From 571a354b1542a274d88617e1f6703f3fe7a517f1 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:42:28 +0100 Subject: [PATCH 02/15] regulator: regulator driver interface This allows regulator drivers to register their regulators and provide operations to the core. It also has a notifier call chain for propagating regulator events to clients. Signed-off-by: Liam Girdwood Signed-off-by: Mark Brown --- include/linux/regulator/driver.h | 99 ++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 include/linux/regulator/driver.h diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h new file mode 100644 index 00000000000..1d712c7172a --- /dev/null +++ b/include/linux/regulator/driver.h @@ -0,0 +1,99 @@ +/* + * driver.h -- SoC Regulator driver support. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Driver Interface. + */ + +#ifndef __LINUX_REGULATOR_DRIVER_H_ +#define __LINUX_REGULATOR_DRIVER_H_ + +#include +#include + +struct regulator_constraints; +struct regulator_dev; + +/** + * struct regulator_ops - regulator operations. + * + * This struct describes regulator operations. + */ +struct regulator_ops { + + /* get/set regulator voltage */ + int (*set_voltage) (struct regulator_dev *, int min_uV, int max_uV); + int (*get_voltage) (struct regulator_dev *); + + /* get/set regulator current */ + int (*set_current_limit) (struct regulator_dev *, + int min_uA, int max_uA); + int (*get_current_limit) (struct regulator_dev *); + + /* enable/disable regulator */ + int (*enable) (struct regulator_dev *); + int (*disable) (struct regulator_dev *); + int (*is_enabled) (struct regulator_dev *); + + /* get/set regulator operating mode (defined in regulator.h) */ + int (*set_mode) (struct regulator_dev *, unsigned int mode); + unsigned int (*get_mode) (struct regulator_dev *); + + /* get most efficient regulator operating mode for load */ + unsigned int (*get_optimum_mode) (struct regulator_dev *, int input_uV, + int output_uV, int load_uA); + + /* the operations below are for configuration of regulator state when + * it's parent PMIC enters a global STANBY/HIBERNATE state */ + + /* set regulator suspend voltage */ + int (*set_suspend_voltage) (struct regulator_dev *, int uV); + + /* enable/disable regulator in suspend state */ + int (*set_suspend_enable) (struct regulator_dev *); + int (*set_suspend_disable) (struct regulator_dev *); + + /* set regulator suspend operating mode (defined in regulator.h) */ + int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode); +}; + +/* + * Regulators can either control voltage or current. + */ +enum regulator_type { + REGULATOR_VOLTAGE, + REGULATOR_CURRENT, +}; + +/** + * struct regulator_desc - Regulator descriptor + * + */ +struct regulator_desc { + const char *name; + int id; + struct regulator_ops *ops; + int irq; + enum regulator_type type; + struct module *owner; +}; + + +struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, + void *reg_data); +void regulator_unregister(struct regulator_dev *rdev); + +int regulator_notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data); + +void *rdev_get_drvdata(struct regulator_dev *rdev); +int rdev_get_id(struct regulator_dev *rdev); + +#endif From 4c1184e85cb381121a5273ea20ad31ca3faa0a4f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:46:09 +0100 Subject: [PATCH 03/15] regulator: machine driver interface This interface is for machine specific code and allows the creation of voltage/current domains (with constraints) for each regulator. It can provide regulator constraints that will prevent device damage through overvoltage or over current caused by buggy client drivers. It also allows the creation of a regulator tree whereby some regulators are supplied by others (similar to a clock tree). Signed-off-by: Liam Girdwood Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown --- include/linux/regulator/machine.h | 104 ++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 include/linux/regulator/machine.h diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h new file mode 100644 index 00000000000..11e737dbfcf --- /dev/null +++ b/include/linux/regulator/machine.h @@ -0,0 +1,104 @@ +/* + * machine.h -- SoC Regulator support, machine/board driver API. + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * + * 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. + * + * Regulator Machine/Board Interface. + */ + +#ifndef __LINUX_REGULATOR_MACHINE_H_ +#define __LINUX_REGULATOR_MACHINE_H_ + +#include +#include + +struct regulator; + +/* + * Regulator operation constraint flags. These flags are used to enable + * certain regulator operations and can be OR'ed together. + * + * VOLTAGE: Regulator output voltage can be changed by software on this + * board/machine. + * CURRENT: Regulator output current can be changed by software on this + * board/machine. + * MODE: Regulator operating mode can be changed by software on this + * board/machine. + * STATUS: Regulator can be enabled and disabled. + * DRMS: Dynamic Regulator Mode Switching is enabled for this regulator. + */ + +#define REGULATOR_CHANGE_VOLTAGE 0x1 +#define REGULATOR_CHANGE_CURRENT 0x2 +#define REGULATOR_CHANGE_MODE 0x4 +#define REGULATOR_CHANGE_STATUS 0x8 +#define REGULATOR_CHANGE_DRMS 0x10 + +/** + * struct regulator_state - regulator state during low power syatem states + * + * This describes a regulators state during a system wide low power state. + */ +struct regulator_state { + int uV; /* suspend voltage */ + unsigned int mode; /* suspend regulator operating mode */ + int enabled; /* is regulator enabled in this suspend state */ +}; + +/** + * struct regulation_constraints - regulator operating constraints. + * + * This struct describes regulator and board/machine specific constraints. + */ +struct regulation_constraints { + + char *name; + + /* voltage output range (inclusive) - for voltage control */ + int min_uV; + int max_uV; + + /* current output range (inclusive) - for current control */ + int min_uA; + int max_uA; + + /* valid regulator operating modes for this machine */ + unsigned int valid_modes_mask; + + /* valid operations for regulator on this machine */ + unsigned int valid_ops_mask; + + /* regulator input voltage - only if supply is another regulator */ + int input_uV; + + /* regulator suspend states for global PMIC STANDBY/HIBERNATE */ + struct regulator_state state_disk; + struct regulator_state state_mem; + struct regulator_state state_standby; + suspend_state_t initial_state; /* suspend state to set at init */ + + /* constriant flags */ + unsigned always_on:1; /* regulator never off when system is on */ + unsigned boot_on:1; /* bootloader/firmware enabled regulator */ + unsigned apply_uV:1; /* apply uV constraint iff min == max */ +}; + +int regulator_set_supply(const char *regulator, const char *regulator_supply); + +const char *regulator_get_supply(const char *regulator); + +int regulator_set_machine_constraints(const char *regulator, + struct regulation_constraints *constraints); + +int regulator_set_device_supply(const char *regulator, struct device *dev, + const char *supply); + +int regulator_suspend_prepare(suspend_state_t state); + +#endif From 48d335ba3164ce99cb8847513d0e3b6ee604eb20 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Apr 2008 15:50:21 +0100 Subject: [PATCH 04/15] regulator: fixed regulator interface This patch adds support for fixed regulators. This class of regulator is not software controllable but can coexist on machines with software controlable regulators. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- include/linux/regulator/fixed.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 include/linux/regulator/fixed.h diff --git a/include/linux/regulator/fixed.h b/include/linux/regulator/fixed.h new file mode 100644 index 00000000000..1387a5d2190 --- /dev/null +++ b/include/linux/regulator/fixed.h @@ -0,0 +1,22 @@ +/* + * fixed.h + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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_FIXED_H +#define __REGULATOR_FIXED_H + +struct fixed_voltage_config { + const char *supply_name; + int microvolts; +}; + +#endif From 414c70cb91c445ec813b61e16fe4882807e40240 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 15:59:04 +0100 Subject: [PATCH 05/15] regulator: regulator framework core This adds the regulator framework core. 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. Signed-off-by: Liam Girdwood Signed-off-by: Greg Kroah-Hartman Signed-off-by: Mark Brown --- drivers/regulator/core.c | 1903 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 1903 insertions(+) create mode 100644 drivers/regulator/core.c 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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) { + printk(KERN_WARNING "Releasing supply %s while enabled\n", + regulator->supply_name); + WARN_ON(regulator->enabled); + regulator_disable(regulator); + } + + mutex_lock(®ulator_list_mutex); + rdev = regulator->rdev; + + /* remove any sysfs entries */ + if (regulator->dev) { + sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name); + kfree(regulator->supply_name); + device_remove_file(regulator->dev, ®ulator->dev_attr); + kfree(regulator->dev_attr.attr.name); + } + list_del(®ulator->list); + kfree(regulator); + + module_put(rdev->owner); + mutex_unlock(®ulator_list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_put); + +/* locks held by regulator_enable() */ +static int _regulator_enable(struct regulator_dev *rdev) +{ + int ret = -EINVAL; + + if (!rdev->constraints) { + printk(KERN_ERR "%s: %s has no constraints\n", + __func__, rdev->desc->name); + return ret; + } + + /* do we need to enable the supply regulator first */ + if (rdev->supply) { + ret = _regulator_enable(rdev->supply); + if (ret < 0) { + printk(KERN_ERR "%s: failed to enable %s: %d\n", + __func__, rdev->desc->name, ret); + return ret; + } + } + + /* check voltage and requested load before enabling */ + if (rdev->desc->ops->enable) { + + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & + REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) { + printk(KERN_ERR "%s: failed to enable %s: %d\n", + __func__, rdev->desc->name, ret); + return ret; + } + rdev->use_count++; + return ret; + } + + return ret; +} + +/** + * regulator_enable - enable regulator output + * @regulator: regulator source + * + * Enable the regulator output at the predefined voltage or current value. + * NOTE: the output value can be set by other drivers, boot loader or may be + * hardwired in the regulator. + * NOTE: calls to regulator_enable() must be balanced with calls to + * regulator_disable(). + */ +int regulator_enable(struct regulator *regulator) +{ + int ret; + + if (regulator->enabled) { + printk(KERN_CRIT "Regulator %s already enabled\n", + regulator->supply_name); + WARN_ON(regulator->enabled); + return 0; + } + + mutex_lock(®ulator->rdev->mutex); + regulator->enabled = 1; + ret = _regulator_enable(regulator->rdev); + if (ret != 0) + regulator->enabled = 0; + mutex_unlock(®ulator->rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_enable); + +/* locks held by regulator_disable() */ +static int _regulator_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + /* are we the last user and permitted to disable ? */ + if (rdev->use_count == 1 && !rdev->constraints->always_on) { + + /* we are last user */ + if (rdev->desc->ops->disable) { + ret = rdev->desc->ops->disable(rdev); + if (ret < 0) { + printk(KERN_ERR "%s: failed to disable %s\n", + __func__, rdev->desc->name); + return ret; + } + } + + /* decrease our supplies ref count and disable if required */ + if (rdev->supply) + _regulator_disable(rdev->supply); + + rdev->use_count = 0; + } else if (rdev->use_count > 1) { + + if (rdev->constraints && + (rdev->constraints->valid_ops_mask & + REGULATOR_CHANGE_DRMS)) + drms_uA_update(rdev); + + rdev->use_count--; + } + return ret; +} + +/** + * regulator_disable - disable regulator output + * @regulator: regulator source + * + * Disable the regulator output voltage or current. + * NOTE: this will only disable the regulator output if no other consumer + * devices have it enabled. + * NOTE: calls to regulator_enable() must be balanced with calls to + * regulator_disable(). + */ +int regulator_disable(struct regulator *regulator) +{ + int ret; + + if (!regulator->enabled) { + printk(KERN_ERR "%s: not in use by this consumer\n", + __func__); + return 0; + } + + mutex_lock(®ulator->rdev->mutex); + regulator->enabled = 0; + regulator->uA_load = 0; + ret = _regulator_disable(regulator->rdev); + mutex_unlock(®ulator->rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_disable); + +/* locks held by regulator_force_disable() */ +static int _regulator_force_disable(struct regulator_dev *rdev) +{ + int ret = 0; + + /* force disable */ + if (rdev->desc->ops->disable) { + /* ah well, who wants to live forever... */ + ret = rdev->desc->ops->disable(rdev); + if (ret < 0) { + printk(KERN_ERR "%s: failed to force disable %s\n", + __func__, rdev->desc->name); + return ret; + } + /* notify other consumers that power has been forced off */ + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE, + NULL); + } + + /* decrease our supplies ref count and disable if required */ + if (rdev->supply) + _regulator_disable(rdev->supply); + + rdev->use_count = 0; + return ret; +} + +/** + * regulator_force_disable - force disable regulator output + * @regulator: regulator source + * + * Forcibly disable the regulator output voltage or current. + * NOTE: this *will* disable the regulator output even if other consumer + * devices have it enabled. This should be used for situations when device + * damage will likely occur if the regulator is not disabled (e.g. over temp). + */ +int regulator_force_disable(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->rdev->mutex); + regulator->enabled = 0; + regulator->uA_load = 0; + ret = _regulator_force_disable(regulator->rdev); + mutex_unlock(®ulator->rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_force_disable); + +static int _regulator_is_enabled(struct regulator_dev *rdev) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->is_enabled) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->is_enabled(rdev); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_is_enabled - is the regulator output enabled + * @regulator: regulator source + * + * Returns zero for disabled otherwise return number of enable requests. + */ +int regulator_is_enabled(struct regulator *regulator) +{ + return _regulator_is_enabled(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_is_enabled); + +/** + * regulator_set_voltage - set regulator output voltage + * @regulator: regulator source + * @min_uV: Minimum required voltage in uV + * @max_uV: Maximum acceptable voltage in uV + * + * Sets a voltage regulator to the desired output voltage. This can be set + * during any regulator state. IOW, regulator can be disabled or enabled. + * + * If the regulator is enabled then the voltage will change to the new value + * immediately otherwise if the regulator is disabled the regulator will + * output at the new voltage when enabled. + * + * NOTE: If the regulator is shared between several devices then the lowest + * request voltage that meets the system constraints will be used. + * NOTE: Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->set_voltage) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = regulator_check_voltage(rdev, &min_uV, &max_uV); + if (ret < 0) + goto out; + regulator->min_uV = min_uV; + regulator->max_uV = max_uV; + ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV); + +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage); + +static int _regulator_get_voltage(struct regulator_dev *rdev) +{ + /* sanity check */ + if (rdev->desc->ops->get_voltage) + return rdev->desc->ops->get_voltage(rdev); + else + return -EINVAL; +} + +/** + * regulator_get_voltage - get regulator output voltage + * @regulator: regulator source + * + * This returns the current regulator voltage in uV. + * + * NOTE: If the regulator is disabled it will return the voltage value. This + * function should not be used to determine regulator state. + */ +int regulator_get_voltage(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->rdev->mutex); + + ret = _regulator_get_voltage(regulator->rdev); + + mutex_unlock(®ulator->rdev->mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage); + +/** + * regulator_set_current_limit - set regulator output current limit + * @regulator: regulator source + * @min_uA: Minimuum supported current in uA + * @max_uA: Maximum supported current in uA + * + * Sets current sink to the desired output current. This can be set during + * any regulator state. IOW, regulator can be disabled or enabled. + * + * If the regulator is enabled then the current will change to the new value + * immediately otherwise if the regulator is disabled the regulator will + * output at the new current when enabled. + * + * NOTE: Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_current_limit(struct regulator *regulator, + int min_uA, int max_uA) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->set_current_limit) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = regulator_check_current_limit(rdev, &min_uA, &max_uA); + if (ret < 0) + goto out; + + ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_current_limit); + +static int _regulator_get_current_limit(struct regulator_dev *rdev) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->get_current_limit) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->get_current_limit(rdev); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_get_current_limit - get regulator output current + * @regulator: regulator source + * + * This returns the current supplied by the specified current sink in uA. + * + * NOTE: If the regulator is disabled it will return the current value. This + * function should not be used to determine regulator state. + */ +int regulator_get_current_limit(struct regulator *regulator) +{ + return _regulator_get_current_limit(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_get_current_limit); + +/** + * regulator_set_mode - set regulator operating mode + * @regulator: regulator source + * @mode: operating mode - one of the REGULATOR_MODE constants + * + * Set regulator operating mode to increase regulator efficiency or improve + * regulation performance. + * + * NOTE: Regulator system constraints must be set for this regulator before + * calling this function otherwise this call will fail. + */ +int regulator_set_mode(struct regulator *regulator, unsigned int mode) +{ + struct regulator_dev *rdev = regulator->rdev; + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->set_mode) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = regulator_check_mode(rdev, mode); + if (ret < 0) + goto out; + + ret = rdev->desc->ops->set_mode(rdev, mode); +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_mode); + +static unsigned int _regulator_get_mode(struct regulator_dev *rdev) +{ + int ret; + + mutex_lock(&rdev->mutex); + + /* sanity check */ + if (!rdev->desc->ops->get_mode) { + ret = -EINVAL; + goto out; + } + + ret = rdev->desc->ops->get_mode(rdev); +out: + mutex_unlock(&rdev->mutex); + return ret; +} + +/** + * regulator_get_mode - get regulator operating mode + * @regulator: regulator source + * + * Get the current regulator operating mode. + */ +unsigned int regulator_get_mode(struct regulator *regulator) +{ + return _regulator_get_mode(regulator->rdev); +} +EXPORT_SYMBOL_GPL(regulator_get_mode); + +/** + * regulator_set_optimum_mode - set regulator optimum operating mode + * @regulator: regulator source + * @uA_load: load current + * + * Notifies the regulator core of a new device load. This is then used by + * DRMS (if enabled by constraints) to set the most efficient regulator + * operating mode for the new regulator loading. + * + * Consumer devices notify their supply regulator of the maximum power + * they will require (can be taken from device datasheet in the power + * consumption tables) when they change operational status and hence power + * state. Examples of operational state changes that can affect power + * consumption are :- + * + * o Device is opened / closed. + * o Device I/O is about to begin or has just finished. + * o Device is idling in between work. + * + * This information is also exported via sysfs to userspace. + * + * DRMS will sum the total requested load on the regulator and change + * to the most efficient operating mode if platform constraints allow. + * + * Returns the new regulator mode or error. + */ +int regulator_set_optimum_mode(struct regulator *regulator, int uA_load) +{ + struct regulator_dev *rdev = regulator->rdev; + struct regulator *consumer; + int ret, output_uV, input_uV, total_uA_load = 0; + unsigned int mode; + + mutex_lock(&rdev->mutex); + + regulator->uA_load = uA_load; + ret = regulator_check_drms(rdev); + if (ret < 0) + goto out; + ret = -EINVAL; + + /* sanity check */ + if (!rdev->desc->ops->get_optimum_mode) + goto out; + + /* get output voltage */ + output_uV = rdev->desc->ops->get_voltage(rdev); + if (output_uV <= 0) { + printk(KERN_ERR "%s: invalid output voltage found for %s\n", + __func__, rdev->desc->name); + goto out; + } + + /* 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) { + printk(KERN_ERR "%s: invalid input voltage found for %s\n", + __func__, rdev->desc->name); + goto out; + } + + /* calc total requested load for this regulator */ + list_for_each_entry(consumer, &rdev->consumer_list, list) + total_uA_load += consumer->uA_load; + + mode = rdev->desc->ops->get_optimum_mode(rdev, + input_uV, output_uV, + total_uA_load); + if (ret <= 0) { + printk(KERN_ERR "%s: failed to get optimum mode for %s @" + " %d uA %d -> %d uV\n", __func__, rdev->desc->name, + total_uA_load, input_uV, output_uV); + goto out; + } + + ret = rdev->desc->ops->set_mode(rdev, mode); + if (ret <= 0) { + printk(KERN_ERR "%s: failed to set optimum mode %x for %s\n", + __func__, mode, rdev->desc->name); + goto out; + } + ret = mode; +out: + mutex_unlock(&rdev->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_optimum_mode); + +/** + * regulator_register_notifier - register regulator event notifier + * @regulator: regulator source + * @notifier_block: notifier block + * + * Register notifier block to receive regulator events. + */ +int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(®ulator->rdev->notifier, + nb); +} +EXPORT_SYMBOL_GPL(regulator_register_notifier); + +/** + * regulator_unregister_notifier - unregister regulator event notifier + * @regulator: regulator source + * @notifier_block: notifier block + * + * Unregister regulator event notifier block. + */ +int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(®ulator->rdev->notifier, + nb); +} +EXPORT_SYMBOL_GPL(regulator_unregister_notifier); + +/* notify regulator consumers and downstream regulator consumers */ +static void _notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data) +{ + struct regulator_dev *_rdev; + + /* call rdev chain first */ + mutex_lock(&rdev->mutex); + blocking_notifier_call_chain(&rdev->notifier, event, NULL); + mutex_unlock(&rdev->mutex); + + /* now notify regulator we supply */ + list_for_each_entry(_rdev, &rdev->supply_list, slist) + _notifier_call_chain(_rdev, event, data); +} + +/** + * regulator_bulk_get - get multiple regulator consumers + * + * @dev: Device to supply + * @num_consumers: Number of consumers to register + * @consumers: Configuration of consumers; clients are stored here. + * + * @return 0 on success, an errno on failure. + * + * This helper function allows drivers to get several regulator + * consumers in one operation. If any of the regulators cannot be + * acquired then any regulators that were allocated will be freed + * before returning to the caller. + */ +int regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) + consumers[i].consumer = NULL; + + for (i = 0; i < num_consumers; i++) { + consumers[i].consumer = regulator_get(dev, + consumers[i].supply); + if (IS_ERR(consumers[i].consumer)) { + dev_err(dev, "Failed to get supply '%s'\n", + consumers[i].supply); + ret = PTR_ERR(consumers[i].consumer); + consumers[i].consumer = NULL; + goto err; + } + } + + return 0; + +err: + for (i = 0; i < num_consumers && consumers[i].consumer; i++) + regulator_put(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_get); + +/** + * regulator_bulk_enable - enable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to enable multiple regulator + * clients in a single API call. If any consumers cannot be enabled + * then any others that were enabled will be disabled again prior to + * return. + */ +int regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) { + ret = regulator_enable(consumers[i].consumer); + if (ret != 0) + goto err; + } + + return 0; + +err: + printk(KERN_ERR "Failed to enable %s\n", consumers[i].supply); + for (i = 0; i < num_consumers; i++) + regulator_disable(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_enable); + +/** + * regulator_bulk_disable - disable multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * @return 0 on success, an errno on failure + * + * This convenience API allows consumers to disable multiple regulator + * clients in a single API call. If any consumers cannot be enabled + * then any others that were disabled will be disabled again prior to + * return. + */ +int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + int ret; + + for (i = 0; i < num_consumers; i++) { + ret = regulator_disable(consumers[i].consumer); + if (ret != 0) + goto err; + } + + return 0; + +err: + printk(KERN_ERR "Failed to disable %s\n", consumers[i].supply); + for (i = 0; i < num_consumers; i++) + regulator_enable(consumers[i].consumer); + + return ret; +} +EXPORT_SYMBOL_GPL(regulator_bulk_disable); + +/** + * regulator_bulk_free - free multiple regulator consumers + * + * @num_consumers: Number of consumers + * @consumers: Consumer data; clients are stored here. + * + * This convenience API allows consumers to free multiple regulator + * clients in a single API call. + */ +void regulator_bulk_free(int num_consumers, + struct regulator_bulk_data *consumers) +{ + int i; + + for (i = 0; i < num_consumers; i++) { + regulator_put(consumers[i].consumer); + consumers[i].consumer = NULL; + } +} +EXPORT_SYMBOL_GPL(regulator_bulk_free); + +/** + * regulator_notifier_call_chain - call regulator event notifier + * @regulator: regulator source + * @event: notifier block + * @data: + * + * Called by regulator drivers to notify clients a regulator event has + * occurred. We also notify regulator clients downstream. + */ +int regulator_notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data) +{ + _notifier_call_chain(rdev, event, data); + return NOTIFY_DONE; + +} +EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); + +/** + * regulator_register - register regulator + * @regulator: regulator source + * @reg_data: private regulator data + * + * Called by regulator drivers to register a regulator. + * Returns 0 on success. + */ +struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, + void *reg_data) +{ + static atomic_t regulator_no = ATOMIC_INIT(0); + struct regulator_dev *rdev; + int ret; + + if (regulator_desc == NULL) + return ERR_PTR(-EINVAL); + + if (regulator_desc->name == NULL || regulator_desc->ops == NULL) + return ERR_PTR(-EINVAL); + + if (!regulator_desc->type == REGULATOR_VOLTAGE && + !regulator_desc->type == REGULATOR_CURRENT) + return ERR_PTR(-EINVAL); + + rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL); + if (rdev == NULL) + return ERR_PTR(-ENOMEM); + + mutex_lock(®ulator_list_mutex); + + mutex_init(&rdev->mutex); + rdev->reg_data = reg_data; + rdev->owner = regulator_desc->owner; + rdev->desc = regulator_desc; + INIT_LIST_HEAD(&rdev->consumer_list); + INIT_LIST_HEAD(&rdev->supply_list); + INIT_LIST_HEAD(&rdev->list); + INIT_LIST_HEAD(&rdev->slist); + BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier); + + rdev->dev.class = ®ulator_class; + device_initialize(&rdev->dev); + snprintf(rdev->dev.bus_id, sizeof(rdev->dev.bus_id), + "regulator_%ld_%s", + (unsigned long)atomic_inc_return(®ulator_no) - 1, + regulator_desc->name); + + ret = device_add(&rdev->dev); + if (ret == 0) + list_add(&rdev->list, ®ulator_list); + else { + kfree(rdev); + rdev = ERR_PTR(ret); + } + mutex_unlock(®ulator_list_mutex); + return rdev; +} +EXPORT_SYMBOL_GPL(regulator_register); + +/** + * regulator_unregister - unregister regulator + * @regulator: regulator source + * + * Called by regulator drivers to unregister a regulator. + */ +void regulator_unregister(struct regulator_dev *rdev) +{ + if (rdev == NULL) + return; + + mutex_lock(®ulator_list_mutex); + list_del(&rdev->list); + if (rdev->supply) + sysfs_remove_link(&rdev->dev.kobj, "supply"); + device_unregister(&rdev->dev); + mutex_unlock(®ulator_list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_unregister); + +/** + * regulator_set_supply - set regulator supply regulator + * @regulator: regulator name + * @supply: supply regulator name + * + * Called by platform initialisation code to set the supply regulator for this + * regulator. This ensures that a regulators supply will also be enabled by the + * core if it's child is enabled. + */ +int regulator_set_supply(const char *regulator, const char *supply) +{ + struct regulator_dev *rdev, *supply_rdev; + int err; + + if (regulator == NULL || supply == NULL) + return -EINVAL; + + mutex_lock(®ulator_list_mutex); + + list_for_each_entry(rdev, ®ulator_list, list) { + if (!strcmp(rdev->desc->name, regulator)) + goto found_regulator; + } + mutex_unlock(®ulator_list_mutex); + return -ENODEV; + +found_regulator: + list_for_each_entry(supply_rdev, ®ulator_list, list) { + if (!strcmp(supply_rdev->desc->name, supply)) + goto found_supply; + } + mutex_unlock(®ulator_list_mutex); + return -ENODEV; + +found_supply: + err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj, + "supply"); + if (err) { + printk(KERN_ERR + "%s: could not add device link %s err %d\n", + __func__, supply_rdev->dev.kobj.name, err); + goto out; + } + rdev->supply = supply_rdev; + list_add(&rdev->slist, &supply_rdev->supply_list); +out: + mutex_unlock(®ulator_list_mutex); + return err; +} +EXPORT_SYMBOL_GPL(regulator_set_supply); + +/** + * regulator_get_supply - get regulator supply regulator + * @regulator: regulator name + * + * Returns the supply supply regulator name or NULL if no supply regulator + * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc) + */ +const char *regulator_get_supply(const char *regulator) +{ + struct regulator_dev *rdev; + + if (regulator == NULL) + return NULL; + + mutex_lock(®ulator_list_mutex); + list_for_each_entry(rdev, ®ulator_list, list) { + if (!strcmp(rdev->desc->name, regulator)) + goto found; + } + mutex_unlock(®ulator_list_mutex); + return NULL; + +found: + mutex_unlock(®ulator_list_mutex); + if (rdev->supply) + return rdev->supply->desc->name; + else + return NULL; +} +EXPORT_SYMBOL_GPL(regulator_get_supply); + +/** + * regulator_set_machine_constraints - sets regulator constraints + * @regulator: regulator source + * + * Allows platform initialisation code to define and constrain + * regulator circuits e.g. valid voltage/current ranges, etc. NOTE: + * Constraints *must* be set by platform code in order for some + * regulator operations to proceed i.e. set_voltage, set_current_limit, + * set_mode. + */ +int regulator_set_machine_constraints(const char *regulator_name, + struct regulation_constraints *constraints) +{ + struct regulator_dev *rdev; + int ret = 0; + + if (regulator_name == NULL) + return -EINVAL; + + mutex_lock(®ulator_list_mutex); + + list_for_each_entry(rdev, ®ulator_list, list) { + if (!strcmp(regulator_name, rdev->desc->name)) + goto found; + } + ret = -ENODEV; + goto out; + +found: + mutex_lock(&rdev->mutex); + rdev->constraints = constraints; + + /* do we need to apply the constraint voltage */ + if (rdev->constraints->apply_uV && + rdev->constraints->min_uV == rdev->constraints->max_uV && + rdev->desc->ops->set_voltage) { + ret = rdev->desc->ops->set_voltage(rdev, + rdev->constraints->min_uV, rdev->constraints->max_uV); + if (ret < 0) { + printk(KERN_ERR "%s: failed to apply %duV" + " constraint\n", __func__, + rdev->constraints->min_uV); + rdev->constraints = NULL; + goto out; + } + } + + /* are we enabled at boot time by firmware / bootloader */ + if (rdev->constraints->boot_on) + rdev->use_count = 1; + + /* do we need to setup our suspend state */ + if (constraints->initial_state) + ret = suspend_prepare(rdev, constraints->initial_state); + + print_constraints(rdev); + mutex_unlock(&rdev->mutex); + +out: + mutex_unlock(®ulator_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_machine_constraints); + + +/** + * regulator_set_device_supply: Bind a regulator to a symbolic supply + * @regulator: regulator source + * @dev: device the supply applies to + * @supply: symbolic name for supply + * + * Allows platform initialisation code to map physical regulator + * sources to symbolic names for supplies for use by devices. Devices + * should use these symbolic names to request regulators, avoiding the + * need to provide board-specific regulator names as platform data. + */ +int regulator_set_device_supply(const char *regulator, struct device *dev, + const char *supply) +{ + struct regulator_map *node; + + if (regulator == NULL || supply == NULL) + return -EINVAL; + + node = kmalloc(sizeof(struct regulator_map), GFP_KERNEL); + if (node == NULL) + return -ENOMEM; + + node->regulator = regulator; + node->dev = dev; + node->supply = supply; + + mutex_lock(®ulator_list_mutex); + list_add(&node->list, ®ulator_map_list); + mutex_unlock(®ulator_list_mutex); + return 0; +} +EXPORT_SYMBOL_GPL(regulator_set_device_supply); + +/** + * regulator_suspend_prepare: prepare regulators for system wide suspend + * @state: system suspend state + * + * Configure each regulator with it's suspend operating parameters for state. + * This will usually be called by machine suspend code prior to supending. + */ +int regulator_suspend_prepare(suspend_state_t state) +{ + struct regulator_dev *rdev; + int ret = 0; + + /* ON is handled by regulator active state */ + if (state == PM_SUSPEND_ON) + return -EINVAL; + + mutex_lock(®ulator_list_mutex); + list_for_each_entry(rdev, ®ulator_list, list) { + + mutex_lock(&rdev->mutex); + ret = suspend_prepare(rdev, state); + mutex_unlock(&rdev->mutex); + + if (ret < 0) { + printk(KERN_ERR "%s: failed to prepare %s\n", + __func__, rdev->desc->name); + goto out; + } + } +out: + mutex_unlock(®ulator_list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_suspend_prepare); + +/** + * rdev_get_drvdata - get rdev regulator driver data + * @regulator: regulator + * + * Get rdev regulator driver private data. This call can be used in the + * regulator driver context. + */ +void *rdev_get_drvdata(struct regulator_dev *rdev) +{ + return rdev->reg_data; +} +EXPORT_SYMBOL_GPL(rdev_get_drvdata); + +/** + * regulator_get_drvdata - get regulator driver data + * @regulator: regulator + * + * Get regulator driver private data. This call can be used in the consumer + * driver context when non API regulator specific functions need to be called. + */ +void *regulator_get_drvdata(struct regulator *regulator) +{ + return regulator->rdev->reg_data; +} +EXPORT_SYMBOL_GPL(regulator_get_drvdata); + +/** + * regulator_set_drvdata - set regulator driver data + * @regulator: regulator + * @data: data + */ +void regulator_set_drvdata(struct regulator *regulator, void *data) +{ + regulator->rdev->reg_data = data; +} +EXPORT_SYMBOL_GPL(regulator_set_drvdata); + +/** + * regulator_get_id - get regulator ID + * @regulator: regulator + */ +int rdev_get_id(struct regulator_dev *rdev) +{ + return rdev->desc->id; +} +EXPORT_SYMBOL_GPL(rdev_get_id); + +static int __init regulator_init(void) +{ + printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); + return class_register(®ulator_class); +} + +/* init early to allow our consumers to complete system booting */ +core_initcall(regulator_init); From 4b74ff6512492dedea353f89d9b56cb715df0d7f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Apr 2008 16:27:12 +0100 Subject: [PATCH 06/15] regulator: add support for fixed regulators. This adds supports for regulator that are not software controlable. It allows them to coexist in systems with mixed supplies. Signed-off-by: Mark Brown Signed-off-by: Mike Rapoport Signed-off-by: Liam Girdwood --- drivers/regulator/fixed.c | 129 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 drivers/regulator/fixed.c diff --git a/drivers/regulator/fixed.c b/drivers/regulator/fixed.c new file mode 100644 index 00000000000..d31db3e1491 --- /dev/null +++ b/drivers/regulator/fixed.c @@ -0,0 +1,129 @@ +/* + * fixed.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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 +#include +#include +#include +#include + +struct fixed_voltage_data { + struct regulator_desc desc; + struct regulator_dev *dev; + int microvolts; +}; + +static int fixed_voltage_is_enabled(struct regulator_dev *dev) +{ + return 1; +} + +static int fixed_voltage_enable(struct regulator_dev *dev) +{ + return 0; +} + +static int fixed_voltage_get_voltage(struct regulator_dev *dev) +{ + struct fixed_voltage_data *data = rdev_get_drvdata(dev); + + 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, +}; + +static int regulator_fixed_voltage_probe(struct platform_device *pdev) +{ + struct fixed_voltage_config *config = pdev->dev.platform_data; + struct fixed_voltage_data *drvdata; + int ret; + + drvdata = kzalloc(sizeof(struct fixed_voltage_data), GFP_KERNEL); + if (drvdata == NULL) { + ret = -ENOMEM; + goto err; + } + + drvdata->desc.name = kstrdup(config->supply_name, GFP_KERNEL); + if (drvdata->desc.name == NULL) { + ret = -ENOMEM; + goto err; + } + drvdata->desc.type = REGULATOR_VOLTAGE; + drvdata->desc.owner = THIS_MODULE; + drvdata->desc.ops = &fixed_voltage_ops, + + drvdata->microvolts = config->microvolts; + + drvdata->dev = regulator_register(&drvdata->desc, drvdata); + if (IS_ERR(drvdata->dev)) { + ret = PTR_ERR(drvdata->dev); + goto err_name; + } + + platform_set_drvdata(pdev, drvdata); + + dev_dbg(&pdev->dev, "%s supplying %duV\n", drvdata->desc.name, + drvdata->microvolts); + + return 0; + +err_name: + kfree(drvdata->desc.name); +err: + kfree(drvdata); + return ret; +} + +static int regulator_fixed_voltage_remove(struct platform_device *pdev) +{ + struct fixed_voltage_data *drvdata = platform_get_drvdata(pdev); + + regulator_unregister(drvdata->dev); + kfree(drvdata->desc.name); + kfree(drvdata); + + return 0; +} + +static struct platform_driver regulator_fixed_voltage_driver = { + .probe = regulator_fixed_voltage_probe, + .remove = regulator_fixed_voltage_remove, + .driver = { + .name = "reg-fixed-voltage", + }, +}; + +static int __init regulator_fixed_voltage_init(void) +{ + return platform_driver_register(®ulator_fixed_voltage_driver); +} +module_init(regulator_fixed_voltage_init); + +static void __exit regulator_fixed_voltage_exit(void) +{ + platform_driver_unregister(®ulator_fixed_voltage_driver); +} +module_exit(regulator_fixed_voltage_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Fixed voltage regulator"); +MODULE_LICENSE("GPL"); From c080909eef2b3e7fba70f57cde3264fba95bdf09 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 30 Apr 2008 17:05:33 +0100 Subject: [PATCH 07/15] regulator: regulator test harness This provides a virtual regulator test harness which exposes a sysfs interface for setting power requirements, intended for test purposes only. Signed-off-by: Mark Brown Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood --- drivers/regulator/virtual.c | 345 ++++++++++++++++++++++++++++++++++++ 1 file changed, 345 insertions(+) create mode 100644 drivers/regulator/virtual.c diff --git a/drivers/regulator/virtual.c b/drivers/regulator/virtual.c new file mode 100644 index 00000000000..5ddb464b1c3 --- /dev/null +++ b/drivers/regulator/virtual.c @@ -0,0 +1,345 @@ +/* + * reg-virtual-consumer.c + * + * Copyright 2008 Wolfson Microelectronics PLC. + * + * Author: Mark Brown + * + * 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 +#include +#include +#include + +struct virtual_consumer_data { + struct mutex lock; + struct regulator *regulator; + int enabled; + int min_uV; + int max_uV; + int min_uA; + int max_uA; + unsigned int mode; +}; + +static void update_voltage_constraints(struct virtual_consumer_data *data) +{ + int ret; + + if (data->min_uV && data->max_uV + && data->min_uV <= data->max_uV) { + ret = regulator_set_voltage(data->regulator, + data->min_uV, data->max_uV); + if (ret != 0) { + printk(KERN_ERR "regulator_set_voltage() failed: %d\n", + ret); + return; + } + } + + if (data->min_uV && data->max_uV && !data->enabled) { + ret = regulator_enable(data->regulator); + if (ret == 0) + data->enabled = 1; + else + printk(KERN_ERR "regulator_enable() failed: %d\n", + ret); + } + + if (!(data->min_uV && data->max_uV) && data->enabled) { + ret = regulator_disable(data->regulator); + if (ret == 0) + data->enabled = 0; + else + printk(KERN_ERR "regulator_disable() failed: %d\n", + ret); + } +} + +static void update_current_limit_constraints(struct virtual_consumer_data + *data) +{ + int ret; + + if (data->max_uA + && data->min_uA <= data->max_uA) { + ret = regulator_set_current_limit(data->regulator, + data->min_uA, data->max_uA); + if (ret != 0) { + pr_err("regulator_set_current_limit() failed: %d\n", + ret); + return; + } + } + + if (data->max_uA && !data->enabled) { + ret = regulator_enable(data->regulator); + if (ret == 0) + data->enabled = 1; + else + printk(KERN_ERR "regulator_enable() failed: %d\n", + ret); + } + + if (!(data->min_uA && data->max_uA) && data->enabled) { + ret = regulator_disable(data->regulator); + if (ret == 0) + data->enabled = 0; + else + printk(KERN_ERR "regulator_disable() failed: %d\n", + ret); + } +} + +static ssize_t show_min_uV(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->min_uV); +} + +static ssize_t set_min_uV(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->min_uV = val; + update_voltage_constraints(data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_max_uV(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->max_uV); +} + +static ssize_t set_max_uV(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->max_uV = val; + update_voltage_constraints(data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_min_uA(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->min_uA); +} + +static ssize_t set_min_uA(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->min_uA = val; + update_current_limit_constraints(data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_max_uA(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", data->max_uA); +} + +static ssize_t set_max_uA(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val) != 0) + return count; + + mutex_lock(&data->lock); + + data->max_uA = val; + update_current_limit_constraints(data); + + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_mode(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + + switch (data->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"); + default: + return sprintf(buf, "unknown\n"); + } +} + +static ssize_t set_mode(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct virtual_consumer_data *data = dev_get_drvdata(dev); + unsigned int mode; + int ret; + + if (strncmp(buf, "fast", strlen("fast")) == 0) + mode = REGULATOR_MODE_FAST; + else if (strncmp(buf, "normal", strlen("normal")) == 0) + mode = REGULATOR_MODE_NORMAL; + else if (strncmp(buf, "idle", strlen("idle")) == 0) + mode = REGULATOR_MODE_IDLE; + else if (strncmp(buf, "standby", strlen("standby")) == 0) + mode = REGULATOR_MODE_STANDBY; + else { + dev_err(dev, "Configuring invalid mode\n"); + return count; + } + + mutex_lock(&data->lock); + ret = regulator_set_mode(data->regulator, mode); + if (ret == 0) + data->mode = mode; + else + dev_err(dev, "Failed to configure mode: %d\n", ret); + mutex_unlock(&data->lock); + + return count; +} + +static DEVICE_ATTR(min_microvolts, 0666, show_min_uV, set_min_uV); +static DEVICE_ATTR(max_microvolts, 0666, show_max_uV, set_max_uV); +static DEVICE_ATTR(min_microamps, 0666, show_min_uA, set_min_uA); +static DEVICE_ATTR(max_microamps, 0666, show_max_uA, set_max_uA); +static DEVICE_ATTR(mode, 0666, show_mode, set_mode); + +struct device_attribute *attributes[] = { + &dev_attr_min_microvolts, + &dev_attr_max_microvolts, + &dev_attr_min_microamps, + &dev_attr_max_microamps, + &dev_attr_mode, +}; + +static int regulator_virtual_consumer_probe(struct platform_device *pdev) +{ + char *reg_id = pdev->dev.platform_data; + struct virtual_consumer_data *drvdata; + int ret, i; + + drvdata = kzalloc(sizeof(struct virtual_consumer_data), GFP_KERNEL); + if (drvdata == NULL) { + ret = -ENOMEM; + goto err; + } + + mutex_init(&drvdata->lock); + + drvdata->regulator = regulator_get(&pdev->dev, reg_id); + if (IS_ERR(drvdata->regulator)) { + ret = PTR_ERR(drvdata->regulator); + goto err; + } + + for (i = 0; i < ARRAY_SIZE(attributes); i++) { + ret = device_create_file(&pdev->dev, attributes[i]); + if (ret != 0) + goto err; + } + + drvdata->mode = regulator_get_mode(drvdata->regulator); + + platform_set_drvdata(pdev, drvdata); + + return 0; + +err: + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(&pdev->dev, attributes[i]); + kfree(drvdata); + return ret; +} + +static int regulator_virtual_consumer_remove(struct platform_device *pdev) +{ + struct virtual_consumer_data *drvdata = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < ARRAY_SIZE(attributes); i++) + device_remove_file(&pdev->dev, attributes[i]); + if (drvdata->enabled) + regulator_disable(drvdata->regulator); + regulator_put(drvdata->regulator); + + kfree(drvdata); + + return 0; +} + +static struct platform_driver regulator_virtual_consumer_driver = { + .probe = regulator_virtual_consumer_probe, + .remove = regulator_virtual_consumer_remove, + .driver = { + .name = "reg-virt-consumer", + }, +}; + + +static int __init regulator_virtual_consumer_init(void) +{ + return platform_driver_register(®ulator_virtual_consumer_driver); +} +module_init(regulator_virtual_consumer_init); + +static void __exit regulator_virtual_consumer_exit(void) +{ + platform_driver_unregister(®ulator_virtual_consumer_driver); +} +module_exit(regulator_virtual_consumer_exit); + +MODULE_AUTHOR("Mark Brown "); +MODULE_DESCRIPTION("Virtual regulator consumer"); +MODULE_LICENSE("GPL"); From ba7e4763437561763b6cca14a41f1d2a7def23e2 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:13:42 +0100 Subject: [PATCH 08/15] regulator: core kbuild files This patch adds kernel build support for the regulator core. Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood --- arch/arm/Kconfig | 2 ++ drivers/Makefile | 1 + drivers/regulator/Kconfig | 49 ++++++++++++++++++++++++++++++++++++++ drivers/regulator/Makefile | 10 ++++++++ 4 files changed, 62 insertions(+) create mode 100644 drivers/regulator/Kconfig create mode 100644 drivers/regulator/Makefile diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 257033c691f..4b8acd2851f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1225,6 +1225,8 @@ source "drivers/dma/Kconfig" source "drivers/dca/Kconfig" +source "drivers/regulator/Kconfig" + source "drivers/uio/Kconfig" endmenu 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..84f89ecce69 --- /dev/null +++ b/drivers/regulator/Kconfig @@ -0,0 +1,49 @@ +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. + +endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile new file mode 100644 index 00000000000..29528b78c8d --- /dev/null +++ b/drivers/regulator/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for regulator drivers. +# + + +obj-$(CONFIG_REGULATOR) += core.o +obj-$(CONFIG_REGULATOR_FIXED_VOLTAGE) += fixed.o +obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o + +ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG From 8e6f0848be83c5c406ed73a6d7b4bfbf87880eec Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:16:51 +0100 Subject: [PATCH 09/15] regulator: documentation - overview This adds overview documentation describing the regulator framework and nomenclature used in the interface specific documentation and code. Signed-off-by: Liam Girdwood --- Documentation/power/regulator/overview.txt | 171 +++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 Documentation/power/regulator/overview.txt diff --git a/Documentation/power/regulator/overview.txt b/Documentation/power/regulator/overview.txt new file mode 100644 index 00000000000..bdcb332bd7f --- /dev/null +++ b/Documentation/power/regulator/overview.txt @@ -0,0 +1,171 @@ +Linux voltage and current regulator framework +============================================= + +About +===== + +This framework is designed to provide a standard kernel interface to control +voltage and current regulators. + +The intention is to allow systems to dynamically control regulator power 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 limit is controllable). + +(C) 2008 Wolfson Microelectronics PLC. +Author: Liam Girdwood + + +Nomenclature +============ + +Some terms used in this document:- + + o Regulator - Electronic device that supplies power to other devices. + Most regulators can enable and disable their output whilst + some can control their output voltage and or current. + + Input Voltage -> Regulator -> Output Voltage + + + o PMIC - Power Management IC. An IC that contains numerous regulators + and often contains other susbsystems. + + + o Consumer - Electronic device that is supplied power by a regulator. + Consumers can be classified into two types:- + + Static: consumer does not change it's supply voltage or + current limit. It only needs to enable or disable it's + power supply. It's supply voltage is set by the hardware, + bootloader, firmware or kernel board initialisation code. + + Dynamic: consumer needs to change it's supply voltage or + current limit to meet operation demands. + + + o Power Domain - Electronic circuit that is supplied it's input power by the + output power of a regulator, switch or by another power + domain. + + The supply regulator may be behind a switch(s). i.e. + + Regulator -+-> Switch-1 -+-> Switch-2 --> [Consumer A] + | | + | +-> [Consumer B], [Consumer C] + | + +-> [Consumer D], [Consumer E] + + That is one regulator and three power domains: + + Domain 1: Switch-1, Consumers D & E. + Domain 2: Switch-2, Consumers B & C. + Domain 3: Consumer A. + + and this represents a "supplies" relationship: + + Domain-1 --> Domain-2 --> Domain-3. + + A power domain may have regulators that are supplied power + by other regulators. i.e. + + Regulator-1 -+-> Regulator-2 -+-> [Consumer A] + | + +-> [Consumer B] + + This gives us two regulators and two power domains: + + Domain 1: Regulator-2, Consumer B. + Domain 2: Consumer A. + + and a "supplies" relationship: + + Domain-1 --> Domain-2 + + + o Constraints - Constraints are used to define power levels for performance + and hardware protection. Constraints exist at three levels: + + Regulator Level: This is defined by the regulator hardware + operating parameters and is specified in the regulator + datasheet. i.e. + + - voltage output is in the range 800mV -> 3500mV. + - regulator current output limit is 20mA @ 5V but is + 10mA @ 10V. + + Power Domain Level: This is defined in software by kernel + level board initialisation code. It is used to constrain a + power domain to a particular power range. i.e. + + - Domain-1 voltage is 3300mV + - Domain-2 voltage is 1400mV -> 1600mV + - Domain-3 current limit is 0mA -> 20mA. + + Consumer Level: This is defined by consumer drivers + dynamically setting voltage or current limit levels. + + e.g. a consumer backlight driver asks for a current increase + from 5mA to 10mA to increase LCD illumination. This passes + to through the levels as follows :- + + Consumer: need to increase LCD brightness. Lookup and + request next current mA value in brightness table (the + consumer driver could be used on several different + personalities based upon the same reference device). + + Power Domain: is the new current limit within the domain + operating limits for this domain and system state (e.g. + battery power, USB power) + + Regulator Domains: is the new current limit within the + regulator operating parameters for input/ouput voltage. + + If the regulator request passes all the constraint tests + then the new regulator value is applied. + + +Design +====== + +The framework is designed and targeted at SoC based devices but may also be +relevant to non SoC devices and is split into the following four interfaces:- + + + 1. Consumer driver interface. + + This uses a similar API to the kernel clock interface in that consumer + drivers can get and put a regulator (like they can with clocks atm) and + get/set voltage, current limit, mode, enable and disable. This should + allow consumers complete control over their supply voltage and current + limit. This also compiles out if not in use so drivers can be reused in + systems with no regulator based power control. + + See Documentation/power/regulator/consumer.txt + + 2. Regulator driver interface. + + This allows regulator drivers to register their regulators and provide + operations to the core. It also has a notifier call chain for propagating + regulator events to clients. + + See Documentation/power/regulator/regulator.txt + + 3. Machine interface. + + This interface is for machine specific code and allows the creation of + voltage/current domains (with constraints) for each regulator. It can + provide regulator constraints that will prevent device damage through + overvoltage or over current caused by buggy client drivers. It also + allows the creation of a regulator tree whereby some regulators are + supplied by others (similar to a clock tree). + + See Documentation/power/regulator/machine.txt + + 4. Userspace ABI. + + The framework also exports a lot of useful voltage/current/opmode data to + userspace via sysfs. This could be used to help monitor device power + consumption and status. + + See Documentation/ABI/testing/regulator-sysfs.txt From 6392776d262fcd290616ff5e4246ee95b22c13f0 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:19:02 +0100 Subject: [PATCH 10/15] regulator: documentation - consumer interface This adds documentation describing the consumer device interface. Signed-off-by: Liam Girdwood --- Documentation/power/regulator/consumer.txt | 182 +++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 Documentation/power/regulator/consumer.txt diff --git a/Documentation/power/regulator/consumer.txt b/Documentation/power/regulator/consumer.txt new file mode 100644 index 00000000000..82b7a43aadb --- /dev/null +++ b/Documentation/power/regulator/consumer.txt @@ -0,0 +1,182 @@ +Regulator Consumer Driver Interface +=================================== + +This text describes the regulator interface for consumer device drivers. +Please see overview.txt for a description of the terms used in this text. + + +1. Consumer Regulator Access (static & dynamic drivers) +======================================================= + +A consumer driver can get access to it's supply regulator by calling :- + +regulator = regulator_get(dev, "Vcc"); + +The consumer passes in it's struct device pointer and power supply ID. The core +then finds the correct regulator by consulting a machine specific lookup table. +If the lookup is successful then this call will return a pointer to the struct +regulator that supplies this consumer. + +To release the regulator the consumer driver should call :- + +regulator_put(regulator); + +Consumers can be supplied by more than one regulator e.g. codec consumer with +analog and digital supplies :- + +digital = regulator_get(dev, "Vcc"); /* digital core */ +analog = regulator_get(dev, "Avdd"); /* analog */ + +The regulator access functions regulator_get() and regulator_put() will +usually be called in your device drivers probe() and remove() respectively. + + +2. Regulator Output Enable & Disable (static & dynamic drivers) +==================================================================== + +A consumer can enable it's power supply by calling:- + +int regulator_enable(regulator); + +NOTE: The supply may already be enabled before regulator_enabled() is called. +This may happen if the consumer shares the regulator or the regulator has been +previously enabled by bootloader or kernel board initialization code. + +A consumer can determine if a regulator is enabled by calling :- + +int regulator_is_enabled(regulator); + +This will return > zero when the regulator is enabled. + + +A consumer can disable it's supply when no longer needed by calling :- + +int regulator_disable(regulator); + +NOTE: This may not disable the supply if it's shared with other consumers. The +regulator will only be disabled when the enabled reference count is zero. + +Finally, a regulator can be forcefully disabled in the case of an emergency :- + +int regulator_force_disable(regulator); + +NOTE: this will immediately and forcefully shutdown the regulator output. All +consumers will be powered off. + + +3. Regulator Voltage Control & Status (dynamic drivers) +====================================================== + +Some consumer drivers need to be able to dynamically change their supply +voltage to match system operating points. e.g. CPUfreq drivers can scale +voltage along with frequency to save power, SD drivers may need to select the +correct card voltage, etc. + +Consumers can control their supply voltage by calling :- + +int regulator_set_voltage(regulator, min_uV, max_uV); + +Where min_uV and max_uV are the minimum and maximum acceptable voltages in +microvolts. + +NOTE: this can be called when the regulator is enabled or disabled. If called +when enabled, then the voltage changes instantly, otherwise the voltage +configuration changes and the voltage is physically set when the regulator is +next enabled. + +The regulators configured voltage output can be found by calling :- + +int regulator_get_voltage(regulator); + +NOTE: get_voltage() will return the configured output voltage whether the +regulator is enabled or disabled and should NOT be used to determine regulator +output state. However this can be used in conjunction with is_enabled() to +determine the regulator physical output voltage. + + +4. Regulator Current Limit Control & Status (dynamic drivers) +=========================================================== + +Some consumer drivers need to be able to dynamically change their supply +current limit to match system operating points. e.g. LCD backlight driver can +change the current limit to vary the backlight brightness, USB drivers may want +to set the limit to 500mA when supplying power. + +Consumers can control their supply current limit by calling :- + +int regulator_set_current_limit(regulator, min_uV, max_uV); + +Where min_uA and max_uA are the minimum and maximum acceptable current limit in +microamps. + +NOTE: this can be called when the regulator is enabled or disabled. If called +when enabled, then the current limit changes instantly, otherwise the current +limit configuration changes and the current limit is physically set when the +regulator is next enabled. + +A regulators current limit can be found by calling :- + +int regulator_get_current_limit(regulator); + +NOTE: get_current_limit() will return the current limit whether the regulator +is enabled or disabled and should not be used to determine regulator current +load. + + +5. Regulator Operating Mode Control & Status (dynamic drivers) +============================================================= + +Some consumers can further save system power by changing the operating mode of +their supply regulator to be more efficient when the consumers operating state +changes. e.g. consumer driver is idle and subsequently draws less current + +Regulator operating mode can be changed indirectly or directly. + +Indirect operating mode control. +-------------------------------- +Consumer drivers can request a change in their supply regulator operating mode +by calling :- + +int regulator_set_optimum_mode(struct regulator *regulator, int load_uA); + +This will cause the core to recalculate the total load on the regulator (based +on all it's consumers) and change operating mode (if necessary and permitted) +to best match the current operating load. + +The load_uA value can be determined from the consumers datasheet. e.g.most +datasheets have tables showing the max current consumed in certain situations. + +Most consumers will use indirect operating mode control since they have no +knowledge of the regulator or whether the regulator is shared with other +consumers. + +Direct operating mode control. +------------------------------ +Bespoke or tightly coupled drivers may want to directly control regulator +operating mode depending on their operating point. This can be achieved by +calling :- + +int regulator_set_mode(struct regulator *regulator, unsigned int mode); +unsigned int regulator_get_mode(struct regulator *regulator); + +Direct mode will only be used by consumers that *know* about the regulator and +are not sharing the regulator with other consumers. + + +6. Regulator Events +=================== +Regulators can notify consumers of external events. Events could be received by +consumers under regulator stress or failure conditions. + +Consumers can register interest in regulator events by calling :- + +int regulator_register_notifier(struct regulator *regulator, + struct notifier_block *nb); + +Consumers can uregister interest by calling :- + +int regulator_unregister_notifier(struct regulator *regulator, + struct notifier_block *nb); + +Regulators use the kernel notifier framework to send event to thier interested +consumers. From e8695ebe5568921c41c269f4434e17590735865c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:20:11 +0100 Subject: [PATCH 11/15] regulator: documentation - regulator driver This adds documentation describing the regulator driver interface. Signed-off-by: Liam Girdwood --- Documentation/power/regulator/regulator.txt | 30 +++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/power/regulator/regulator.txt diff --git a/Documentation/power/regulator/regulator.txt b/Documentation/power/regulator/regulator.txt new file mode 100644 index 00000000000..a6905014359 --- /dev/null +++ b/Documentation/power/regulator/regulator.txt @@ -0,0 +1,30 @@ +Regulator Driver Interface +========================== + +The regulator driver interface is relatively simple and designed to allow +regulator drivers to register their services with the core framework. + + +Registration +============ + +Drivers can register a regulator by calling :- + +struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc, + void *reg_data); + +This will register the regulators capabilities and operations the regulator +core. The core does not touch reg_data (private to regulator driver). + +Regulators can be unregistered by calling :- + +void regulator_unregister(struct regulator_dev *rdev); + + +Regulator Events +================ +Regulators can send events (e.g. over temp, under voltage, etc) to consumer +drivers by calling :- + +int regulator_notifier_call_chain(struct regulator_dev *rdev, + unsigned long event, void *data); From e7d0fe340557b202dc00135ab3cc877db794a01f Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:22:50 +0100 Subject: [PATCH 12/15] regulator: documentation - machine This adds documenation describing the regulator machine interface. Signed-off-by: Liam Girdwood --- Documentation/power/regulator/machine.txt | 101 ++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 Documentation/power/regulator/machine.txt diff --git a/Documentation/power/regulator/machine.txt b/Documentation/power/regulator/machine.txt new file mode 100644 index 00000000000..c9a35665cf7 --- /dev/null +++ b/Documentation/power/regulator/machine.txt @@ -0,0 +1,101 @@ +Regulator Machine Driver Interface +=================================== + +The regulator machine driver interface is intended for board/machine specific +initialisation code to configure the regulator subsystem. Typical things that +machine drivers would do are :- + + 1. Regulator -> Device mapping. + 2. Regulator supply configuration. + 3. Power Domain constraint setting. + + + +1. Regulator -> device mapping +============================== +Consider the following machine :- + + Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] + | + +-> [Consumer B @ 3.3V] + +The drivers for consumers A & B must be mapped to the correct regulator in +order to control their power supply. This mapping can be achieved in machine +initialisation code by calling :- + +int regulator_set_device_supply(const char *regulator, struct device *dev, + const char *supply); + +and is shown with the following code :- + +regulator_set_device_supply("Regulator-1", devB, "Vcc"); +regulator_set_device_supply("Regulator-2", devA, "Vcc"); + +This maps Regulator-1 to the 'Vcc' supply for Consumer B and maps Regulator-2 +to the 'Vcc' supply for Consumer A. + + +2. Regulator supply configuration. +================================== +Consider the following machine (again) :- + + Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] + | + +-> [Consumer B @ 3.3V] + +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 it's +supply (Regulator-2). + +This relationship can be register with the core via :- + +int regulator_set_supply(const char *regulator, const char *regulator_supply); + +In this example we would use the following code :- + +regulator_set_supply("Regulator-2", "Regulator-1"); + +Relationships can be queried by calling :- + +const char *regulator_get_supply(const char *regulator); + + +3. Power Domain constraint setting. +=================================== +Each power domain within a system has physical constraints on voltage and +current. This must be defined in software so that the power domain is always +operated within specifications. + +Consider the following machine (again) :- + + Regulator-1 -+-> Regulator-2 --> [Consumer A @ 1.8 - 2.0V] + | + +-> [Consumer B @ 3.3V] + +This gives us two regulators and two power domains: + + Domain 1: Regulator-2, Consumer B. + Domain 2: Consumer A. + +Constraints can be registered by calling :- + +int regulator_set_platform_constraints(const char *regulator, + struct regulation_constraints *constraints); + +The example is defined as follows :- + +struct regulation_constraints domain_1 = { + .min_uV = 3300000, + .max_uV = 3300000, + .valid_modes_mask = REGULATOR_MODE_NORMAL, +}; + +struct regulation_constraints domain_2 = { + .min_uV = 1800000, + .max_uV = 2000000, + .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE, + .valid_modes_mask = REGULATOR_MODE_NORMAL, +}; + +regulator_set_platform_constraints("Regulator-1", &domain_1); +regulator_set_platform_constraints("Regulator-2", &domain_2); From e941d0ce532daf8d8610b2495c06f787fd587b85 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Wed, 30 Apr 2008 17:23:59 +0100 Subject: [PATCH 13/15] regulator: documentation - ABI This adds documentation describing the sysfs ABI used by the regulator framework. Signed-off-by: Liam Girdwood --- .../ABI/testing/sysfs-class-regulator | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-regulator diff --git a/Documentation/ABI/testing/sysfs-class-regulator b/Documentation/ABI/testing/sysfs-class-regulator new file mode 100644 index 00000000000..79a4a75b2d2 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-regulator @@ -0,0 +1,315 @@ +What: /sys/class/regulator/.../state +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + state. This holds the regulator output state. + + This will be one of the following strings: + + 'enabled' + 'disabled' + 'unknown' + + 'enabled' means the regulator output is ON and is supplying + power to the system. + + 'disabled' means the regulator output is OFF and is not + supplying power to the system.. + + 'unknown' means software cannot determine the state. + + NOTE: this field can be used in conjunction with microvolts + and microamps to determine regulator output levels. + + +What: /sys/class/regulator/.../type +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + type. This holds the regulator type. + + This will be one of the following strings: + + 'voltage' + 'current' + 'unknown' + + 'voltage' means the regulator output voltage can be controlled + by software. + + 'current' means the regulator output current limit can be + controlled by software. + + 'unknown' means software cannot control either voltage or + current limit. + + +What: /sys/class/regulator/.../microvolts +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + microvolts. This holds the regulator output voltage setting + measured in microvolts (i.e. E-6 Volts). + + NOTE: This value should not be used to determine the regulator + output voltage level as this value is the same regardless of + whether the regulator is enabled or disabled. + + +What: /sys/class/regulator/.../microamps +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + microamps. This holds the regulator output current limit + setting measured in microamps (i.e. E-6 Amps). + + NOTE: This value should not be used to determine the regulator + output current level as this value is the same regardless of + whether the regulator is enabled or disabled. + + +What: /sys/class/regulator/.../opmode +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + opmode. This holds the regulator operating mode setting. + + The opmode value can be one of the following strings: + + 'fast' + 'normal' + 'idle' + 'standby' + 'unknown' + + The modes are described in include/linux/regulator/regulator.h + + NOTE: This value should not be used to determine the regulator + output operating mode as this value is the same regardless of + whether the regulator is enabled or disabled. + + +What: /sys/class/regulator/.../min_microvolts +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + min_microvolts. This holds the minimum safe working regulator + output voltage setting for this domain measured in microvolts. + + NOTE: this will return the string 'constraint not defined' if + the power domain has no min microvolts constraint defined by + platform code. + + +What: /sys/class/regulator/.../max_microvolts +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + max_microvolts. This holds the maximum safe working regulator + output voltage setting for this domain measured in microvolts. + + NOTE: this will return the string 'constraint not defined' if + the power domain has no max microvolts constraint defined by + platform code. + + +What: /sys/class/regulator/.../min_microamps +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + min_microamps. This holds the minimum safe working regulator + output current limit setting for this domain measured in + microamps. + + NOTE: this will return the string 'constraint not defined' if + the power domain has no min microamps constraint defined by + platform code. + + +What: /sys/class/regulator/.../max_microamps +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + max_microamps. This holds the maximum safe working regulator + output current limit setting for this domain measured in + microamps. + + NOTE: this will return the string 'constraint not defined' if + the power domain has no max microamps constraint defined by + platform code. + + +What: /sys/class/regulator/.../num_users +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + num_users. This holds the number of consumer devices that + have called regulator_enable() on this regulator. + + +What: /sys/class/regulator/.../requested_microamps +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + requested_microamps. This holds the total requested load + current in microamps for this regulator from all its consumer + devices. + + +What: /sys/class/regulator/.../parent +Date: April 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Some regulator directories will contain a link called parent. + This points to the parent or supply regulator if one exists. + +What: /sys/class/regulator/.../suspend_mem_microvolts +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_mem_microvolts. This holds the regulator output + voltage setting for this domain measured in microvolts when + the system is suspended to memory. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to memory voltage defined by + platform code. + +What: /sys/class/regulator/.../suspend_disk_microvolts +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_disk_microvolts. This holds the regulator output + voltage setting for this domain measured in microvolts when + the system is suspended to disk. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to disk voltage defined by + platform code. + +What: /sys/class/regulator/.../suspend_standby_microvolts +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_standby_microvolts. This holds the regulator output + voltage setting for this domain measured in microvolts when + the system is suspended to standby. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to standby voltage defined by + platform code. + +What: /sys/class/regulator/.../suspend_mem_mode +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_mem_mode. This holds the regulator operating mode + setting for this domain when the system is suspended to + memory. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to memory mode defined by + platform code. + +What: /sys/class/regulator/.../suspend_disk_mode +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_disk_mode. This holds the regulator operating mode + setting for this domain when the system is suspended to disk. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to disk mode defined by + platform code. + +What: /sys/class/regulator/.../suspend_standby_mode +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_standby_mode. This holds the regulator operating mode + setting for this domain when the system is suspended to + standby. + + NOTE: this will return the string 'not defined' if + the power domain has no suspend to standby mode defined by + platform code. + +What: /sys/class/regulator/.../suspend_mem_state +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_mem_state. This holds the regulator operating state + when suspended to memory. + + This will be one of the following strings: + + 'enabled' + 'disabled' + 'not defined' + +What: /sys/class/regulator/.../suspend_disk_state +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_disk_state. This holds the regulator operating state + when suspended to disk. + + This will be one of the following strings: + + 'enabled' + 'disabled' + 'not defined' + +What: /sys/class/regulator/.../suspend_standby_state +Date: May 2008 +KernelVersion: 2.6.26 +Contact: Liam Girdwood +Description: + Each regulator directory will contain a field called + suspend_standby_state. This holds the regulator operating + state when suspended to standby. + + This will be one of the following strings: + + 'enabled' + 'disabled' + 'not defined' From e53e86c7ae123b11c269b0835c04e1b42ca4baed Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Thu, 10 Jul 2008 15:48:00 +0100 Subject: [PATCH 14/15] regulator: maintainers - add maintainers for regulator framework. Adds Liam Girdwood and Mark Brown as regulator framework maintainers. Signed-off-by: Liam Girdwood --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index deedc0d827b..42e7ed920fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4504,6 +4504,15 @@ M: kaber@trash.net L: netdev@vger.kernel.org S: Maintained +VOLTAGE AND CURRENT REGULATOR FRAMEWORK +P: Liam Girdwood +M: lg@opensource.wolfsonmicro.com +P: Mark Brown +M: broonie@opensource.wolfsonmicro.com +W: http://opensource.wolfsonmicro.com/node/15 +T: git kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6.git +S: Supported + VT1211 HARDWARE MONITOR DRIVER P: Juerg Haefliger M: juergh@gmail.com From 0eb5d5ab3ec99bfd22ff16797d95835369ffb25b Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Fri, 11 Jul 2008 17:28:06 +0200 Subject: [PATCH 15/15] regulator: TI bq24022 Li-Ion Charger driver This adds a regulator driver for the TI bq24022 Single-Chip Li-Ion Charger with its nCE and ISET2 pins connected to GPIOs. Signed-off-by: Philipp Zabel Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 10 ++ drivers/regulator/Makefile | 2 + drivers/regulator/bq24022.c | 167 ++++++++++++++++++++++++++++++ include/linux/regulator/bq24022.h | 21 ++++ 4 files changed, 200 insertions(+) create mode 100644 drivers/regulator/bq24022.c create mode 100644 include/linux/regulator/bq24022.h diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 84f89ecce69..a656128f1fd 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -46,4 +46,14 @@ config REGULATOR_VIRTUAL_CONSUMER 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 index 29528b78c8d..ac2c64efe65 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -7,4 +7,6 @@ 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 +#include +#include +#include +#include +#include +#include + +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/include/linux/regulator/bq24022.h b/include/linux/regulator/bq24022.h new file mode 100644 index 00000000000..e84b0a9feda --- /dev/null +++ b/include/linux/regulator/bq24022.h @@ -0,0 +1,21 @@ +/* + * 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. + * + */ + +/** + * bq24022_mach_info - platform data for bq24022 + * @gpio_nce: GPIO line connected to the nCE pin, used to enable / disable charging + * @gpio_iset2: GPIO line connected to the ISET2 pin, used to limit charging current to 100 mA / 500 mA + */ +struct bq24022_mach_info { + int gpio_nce; + int gpio_iset2; +};