diff options
author | Laura Lawrence <Laura.Lawrence@freescale.com> | 2008-01-25 01:05:10 -0600 |
---|---|---|
committer | Daniel Schaeffer <daniel.schaeffer@timesys.com> | 2008-08-25 15:20:36 -0400 |
commit | 973c485f7f3e8849bce29589aa23e27fc035953c (patch) | |
tree | 7759bcb625f5253755258ec9cca5bb385f9964ec | |
parent | c1219fd957db3b7f789cb1a1ab6353cb34d31dc6 (diff) |
ENGR00063257 Add Regulator API
Description:
Generic interface to control voltage regulators at
http://opensource.wolfsonmicro.com/node/8 AudioPlus project
Signed-off-by: Rob Herring <ra7055@freescale.com>
-rw-r--r-- | arch/arm/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Kconfig | 2 | ||||
-rw-r--r-- | drivers/Makefile | 1 | ||||
-rw-r--r-- | drivers/regulator/Kconfig | 21 | ||||
-rw-r--r-- | drivers/regulator/Makefile | 11 | ||||
-rw-r--r-- | drivers/regulator/reg-core.c | 791 | ||||
-rw-r--r-- | include/linux/regulator/regulator-drv.h | 107 | ||||
-rw-r--r-- | include/linux/regulator/regulator-platform.h | 102 | ||||
-rw-r--r-- | include/linux/regulator/regulator.h | 332 |
9 files changed, 1369 insertions, 0 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 70f0b8600547..d7a0daca30e0 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1002,6 +1002,8 @@ if ALIGNMENT_TRAP || !CPU_CP15_MMU source "drivers/mtd/Kconfig" endif +source "drivers/regulator/Kconfig" + source "drivers/parport/Kconfig" source "drivers/pnp/Kconfig" diff --git a/drivers/Kconfig b/drivers/Kconfig index f12108b84c9c..5604d59c65d8 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -86,6 +86,8 @@ source "drivers/edac/Kconfig" source "drivers/rtc/Kconfig" +source "drivers/regulator/Kconfig" + source "drivers/dma/Kconfig" source "drivers/dca/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 48e670990615..343cf5230fe6 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -92,5 +92,6 @@ obj-$(CONFIG_DCA) += dca/ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ +obj-$(CONFIG_REGULATOR) += regulator/ obj-$(CONFIG_SSB) += ssb/ obj-$(CONFIG_VIRTIO) += virtio/ diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig new file mode 100644 index 000000000000..bec9a9421b40 --- /dev/null +++ b/drivers/regulator/Kconfig @@ -0,0 +1,21 @@ +menu "Voltage and Current regulators" + +config REGULATOR_API + bool + +config REGULATOR + tristate "Voltage and Current Regulator Support" + depends on EXPERIMENTAL + select REGULATOR_API + default n + help + Generic Voltage and Current Regulator support. + +config REGULATOR_DEBUG + bool "Regulator debug support" + depends on REGULATOR + help + Say yes here to enable debugging support. + + +endmenu diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile new file mode 100644 index 000000000000..9cb2def48ba0 --- /dev/null +++ b/drivers/regulator/Makefile @@ -0,0 +1,11 @@ +# +# Makefile for regulator drivers. +# + +obj-$(CONFIG_REGULATOR) += reg-core.o + +ifeq ($(CONFIG_REGULATOR_DEBUG),y) + EXTRA_CFLAGS += -DDEBUG +endif + + diff --git a/drivers/regulator/reg-core.c b/drivers/regulator/reg-core.c new file mode 100644 index 000000000000..1d68ad8fd88f --- /dev/null +++ b/drivers/regulator/reg-core.c @@ -0,0 +1,791 @@ +/* + * regulator.c -- Voltage/Current Regulator framework. + * + * Copyright 2007 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood <liam.girdwood@wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/regulator/regulator.h> +#include <linux/regulator/regulator-drv.h> +#include <linux/regulator/regulator-platform.h> + +static DEFINE_MUTEX(list_mutex); +static LIST_HEAD(regulators); + +struct regulator_load { + struct device *dev; + struct list_head list; + int uA_load; + struct device_attribute dev_attr; +}; + +#define to_regulator(cd) \ + container_of(cd, struct regulator, cdev) + +static struct regulator_load *get_regulator_load(struct device *dev) +{ + struct regulator_load *load = NULL; + struct regulator *regulator; + + list_for_each_entry(regulator, ®ulators, list) { + list_for_each_entry(load, ®ulator->user_list, list) { + if (load->dev == dev) + return load; + } + } + return NULL; +} + +#if 0 +static int constraint_check_state(struct regulator *regulator) +{ + if (!regulator->constraints) + return -ENODEV; + if (!regulator->constraints->valid_ops_mask & REGULATOR_CHANGE_STATUS) + return -EPERM; + return 0; +} +#endif + +static int constraint_check_voltage(struct regulator *regulator, int uV) +{ + if (!regulator->constraints) + return -ENODEV; + if (!regulator->constraints->valid_ops_mask & REGULATOR_CHANGE_VOLTAGE) + return -EPERM; + if (uV > regulator->constraints->max_uV || + uV < regulator->constraints->min_uV) + return -EINVAL; + return 0; +} + +static int constraint_check_current(struct regulator *regulator, int uA) +{ + if (!regulator->constraints) + return -ENODEV; + if (!regulator->constraints->valid_ops_mask & REGULATOR_CHANGE_CURRENT) + return -EPERM; + if (uA > regulator->constraints->max_uA || + uA < regulator->constraints->min_uA) + return -EINVAL; + return 0; +} + +static int constraint_check_mode(struct regulator *regulator, int mode) +{ + if (!regulator->constraints) + return -ENODEV; + if (!regulator->constraints->valid_ops_mask & REGULATOR_CHANGE_MODE) + return -EPERM; + if (!regulator->constraints->valid_modes_mask & mode) + return -EINVAL; + return 0; +} + +static int constraint_check_drms(struct regulator *regulator) +{ + if (!regulator->constraints) + return -ENODEV; + if (!regulator->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS) + return -EPERM; + return 0; +} + +static ssize_t dev_load_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct regulator_load *load; + + load = get_regulator_load(dev); + if (load == NULL) + return 0; + + return sprintf(buf, "%d\n", load->uA_load); +} + +static ssize_t regulator_uV_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + + return sprintf(buf, "%d\n", regulator_get_voltage(regulator)); +} + +static ssize_t regulator_uA_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + + return sprintf(buf, "%d\n", regulator_get_current(regulator)); +} + +static ssize_t regulator_mode_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + int mode = regulator_get_mode(regulator); + + 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 class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + int state = regulator_is_enabled(regulator); + + 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_constraint_uA_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + + if (!regulator->constraints) + return sprintf(buf, "no constraints\n"); + + return sprintf(buf, "%d %d\n", regulator->constraints->min_uA, + regulator->constraints->max_uA); +} + +static ssize_t +regulator_constraint_uV_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + + if (!regulator->constraints) + return sprintf(buf, "no constraints\n"); + + return sprintf(buf, "%d %d\n", regulator->constraints->min_uV, + regulator->constraints->max_uV); +} + +static ssize_t +regulator_constraint_modes_show(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + int count = 0; + + if (!regulator->constraints) + return sprintf(buf, "no constraints\n"); + + if (regulator->constraints->valid_modes_mask & REGULATOR_MODE_FAST) + count = sprintf(buf, "fast "); + if (regulator->constraints->valid_modes_mask & REGULATOR_MODE_NORMAL) + count += sprintf(buf + count, "normal "); + if (regulator->constraints->valid_modes_mask & REGULATOR_MODE_IDLE) + count += sprintf(buf + count, "idle "); + if (regulator->constraints->valid_modes_mask & REGULATOR_MODE_STANDBY) + count += sprintf(buf + count, "standby"); + count += sprintf(buf + count, "\n"); + return count; +} + +static ssize_t regulator_total_dev_load(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + struct regulator_load *load; + int uA = 0; + + list_for_each_entry(load, ®ulator->user_list, list) { + uA += load->uA_load; + } + return sprintf(buf, "%d\n", uA); +} + +static ssize_t regulator_enabled_use_count(struct class_device *cdev, char *buf) +{ + struct regulator *regulator = to_regulator(cdev); + return sprintf(buf, "%d\n", regulator->use_count); +} + + +static struct class_device_attribute regulator_dev_attrs[] = { + __ATTR(uV, 0444, regulator_uV_show, NULL), + __ATTR(uA, 0444, regulator_uA_show, NULL), + __ATTR(mode, 0444, regulator_mode_show, NULL), + __ATTR(state, 0444, regulator_state_show, NULL), + __ATTR(uA_limits, 0444, regulator_constraint_uA_show, NULL), + __ATTR(uV_limits, 0444, regulator_constraint_uV_show, NULL), + __ATTR(valid_modes, 0444, regulator_constraint_modes_show, NULL), + __ATTR(total_uA_load, 0444, regulator_total_dev_load, NULL), + __ATTR(enabled_count, 0444, regulator_enabled_use_count, NULL), + __ATTR_NULL, +}; + +static void regulator_dev_release(struct class_device *class_dev) {} + +struct class regulator_class = { + .name = "regulator", + .release = regulator_dev_release, + .class_dev_attrs = regulator_dev_attrs, +}; + +static struct regulator_load *create_load_dev(struct regulator *regulator, + struct device *dev) +{ + struct regulator_load *load; + char buf[32]; + int err; + + load = kzalloc(sizeof(*load), GFP_KERNEL); + if (load == NULL) + return NULL; + + sprintf(buf, "uA_load-%s", regulator->name); + list_add(&load->list, ®ulator->user_list); + load->dev = dev; + load->dev_attr.attr.name = kstrdup(buf, GFP_KERNEL); + load->dev_attr.attr.owner = THIS_MODULE; + load->dev_attr.attr.mode = 0444; + load->dev_attr.show = dev_load_show; + err = device_create_file(dev, &load->dev_attr); + if (err < 0) { + printk(KERN_WARNING "%s: could not add regulator load" + " sysfs\n", __func__); + goto err_out; + } + err = sysfs_create_link(®ulator->cdev.kobj, &dev->kobj, + dev->kobj.name); + if (err) { + printk + (KERN_WARNING "%s : could not add device link %s err %d\n", + __func__, dev->kobj.name, err); + goto err_out; + } + return load; +err_out: + kfree(load->dev_attr.attr.name); + kfree(load); + return NULL; +} + +struct regulator *regulator_get(struct device *dev, const char *id) +{ + struct regulator *r, *regulator = ERR_PTR(-ENOENT); + struct regulator_load *load = NULL; + + if (id == NULL) + return regulator; + + mutex_lock(&list_mutex); + + list_for_each_entry(r, ®ulators, list) { + if (strcmp(id, r->name) == 0 && + try_module_get(r->owner)) { + regulator = r; + goto found; + } + } + printk + (KERN_WARNING "regulator: Unable to get requested regulator: %s\n", + id); + mutex_unlock(&list_mutex); + return regulator; +found: + if (dev) { + load = create_load_dev(regulator, dev); + if (load == NULL) { + regulator = ERR_PTR(-ENOMEM); + module_put(regulator->owner); + } + } + + mutex_unlock(&list_mutex); + return regulator; +} +EXPORT_SYMBOL_GPL(regulator_get); + +void regulator_put(struct regulator *regulator, struct device *dev) +{ + struct regulator_load *load, *l; + + if (regulator == NULL || IS_ERR(regulator)) + return; + + if (!dev) + goto put; + + sysfs_remove_link(®ulator->cdev.kobj, dev->kobj.name); + list_for_each_entry_safe(load, l, ®ulator->user_list, list) { + device_remove_file(dev, &load->dev_attr); + list_del(&load->list); + kfree(load->dev_attr.attr.name); + kfree(load); + goto put; + } +put: + module_put(regulator->owner); + mutex_unlock(&list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_put); + +static int __regulator_disable(struct regulator *regulator); + +static int __regulator_enable(struct regulator *regulator) +{ + int ret = 0; + + if (regulator->use_count == 0) { + + if (regulator->parent) { + ret = __regulator_enable(regulator->parent); + + if (ret < 0) { + __regulator_disable(regulator->parent); + goto out; + } + } + + if (regulator->ops->enable) { + ret = regulator->ops->enable(regulator); + if (ret < 0) { + __regulator_disable(regulator); + goto out; + } + } + } + regulator->use_count++; +out: + return ret; +} + +int regulator_enable(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + ret = __regulator_enable(regulator); + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_enable); + +static int __regulator_disable(struct regulator *regulator) +{ + int ret = 0; + + if (regulator->use_count == 1) { + + if (regulator->ops->disable) { + ret = regulator->ops->disable(regulator); + if (ret < 0) + goto out; + } + if (regulator->parent) + __regulator_disable(regulator->parent); + } + regulator->use_count--; +out: + return ret; +} + +int regulator_disable(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + ret = __regulator_disable(regulator); + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_disable); + +int regulator_is_enabled(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->is_enabled) { + ret = -EINVAL; + goto out; + } + + ret = regulator->ops->is_enabled(regulator); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_is_enabled); + +int regulator_set_voltage(struct regulator *regulator, int uV) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->set_voltage) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = constraint_check_voltage(regulator, uV); + if (ret < 0) + goto out; + + ret = regulator->ops->set_voltage(regulator, uV); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_voltage); + +int regulator_get_voltage(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->get_voltage) { + ret = -EINVAL; + goto out; + } + + ret = regulator->ops->get_voltage(regulator); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_voltage); + +int regulator_set_current(struct regulator *regulator, int uA) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->set_current) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = constraint_check_current(regulator, uA); + if (ret < 0) + goto out; + + ret = regulator->ops->set_current(regulator, uA); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_current); + +int regulator_get_current(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->get_current) { + ret = -EINVAL; + goto out; + } + + ret = regulator->ops->get_current(regulator); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_current); + +int regulator_set_mode(struct regulator *regulator, unsigned int mode) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->set_mode) { + ret = -EINVAL; + goto out; + } + + /* constraints check */ + ret = constraint_check_mode(regulator, mode); + if (ret < 0) + goto out; + + ret = regulator->ops->set_mode(regulator, mode); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_set_mode); + +unsigned int regulator_get_mode(struct regulator *regulator) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->get_mode) { + ret = -EINVAL; + goto out; + } + + ret = regulator->ops->get_mode(regulator); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_mode); + +unsigned int regulator_get_optimum_mode(struct regulator *regulator, + int input_uV, int output_uV, int load_uA) +{ + int ret; + + mutex_lock(®ulator->mutex); + + /* sanity check */ + if (!regulator->ops->get_optimum_mode) { + ret = -EINVAL; + goto out; + } + + ret = regulator->ops->get_optimum_mode(regulator, + input_uV, output_uV, load_uA); +out: + mutex_unlock(®ulator->mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_get_optimum_mode); + +int regulator_register_client(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_register(®ulator->notifier, nb); +} +EXPORT_SYMBOL_GPL(regulator_register_client); + +int regulator_unregister_client(struct regulator *regulator, + struct notifier_block *nb) +{ + return blocking_notifier_chain_unregister(®ulator->notifier, nb); +} +EXPORT_SYMBOL_GPL(regulator_unregister_client); + +int regulator_notifier_call_chain(struct regulator *regulator, + unsigned long event, void *data) +{ + return blocking_notifier_call_chain(®ulator->notifier, event, data); +} +EXPORT_SYMBOL_GPL(regulator_notifier_call_chain); + +int regulator_register(struct regulator *regulator) +{ + static atomic_t regulator_no = ATOMIC_INIT(0); + int ret; + + if (regulator == NULL || IS_ERR(regulator)) + return -EINVAL; + + if (regulator->name == NULL || regulator->ops == NULL) + return -EINVAL; + + mutex_lock(&list_mutex); + + mutex_init(®ulator->mutex); + regulator->parent = NULL; + INIT_LIST_HEAD(®ulator->user_list); + BLOCKING_INIT_NOTIFIER_HEAD(®ulator->notifier); + + regulator->cdev.class = ®ulator_class; + class_device_initialize(®ulator->cdev); + snprintf(regulator->cdev.class_id, sizeof(regulator->cdev.class_id), + "regulator-%ld-%s", + (unsigned long) atomic_inc_return(®ulator_no) - 1, + regulator->name); + + ret = class_device_add(®ulator->cdev); + if (ret == 0) + list_add(®ulator->list, ®ulators); + mutex_unlock(&list_mutex); + return ret; +} +EXPORT_SYMBOL_GPL(regulator_register); + +void regulator_unregister(struct regulator *regulator) +{ + if (regulator == NULL || IS_ERR(regulator)) + return; + + mutex_lock(&list_mutex); + list_del(®ulator->list); + if (regulator->parent) + sysfs_remove_link(®ulator->cdev.kobj, "source"); + class_device_unregister(®ulator->cdev); + mutex_unlock(&list_mutex); +} +EXPORT_SYMBOL_GPL(regulator_unregister); + +int regulator_set_platform_source(struct regulator *regulator, + struct regulator *parent) +{ + int err; + + if (regulator == NULL || IS_ERR(regulator)) + return -EINVAL; + + if (parent == NULL || IS_ERR(parent)) + return -EINVAL; + + mutex_lock(&list_mutex); + regulator->parent = parent; + err = sysfs_create_link(®ulator->cdev.kobj, &parent->cdev.kobj, + "source"); + if (err) + printk(KERN_WARNING "%s : could not add device link %s err %d\n", + __func__, parent->cdev.kobj.name, err); + mutex_unlock(&list_mutex); + + return 0; +} +EXPORT_SYMBOL_GPL(regulator_set_platform_source); + +struct regulator *regulator_get_platform_source(struct regulator *regulator) +{ + if (regulator == NULL || IS_ERR(regulator)) + return ERR_PTR(-EINVAL); + return regulator->parent; +} +EXPORT_SYMBOL_GPL(regulator_get_platform_source); + +int regulator_set_platform_constraints(const char *regulator_name, + struct regulation_constraints *constraints) +{ + struct regulator *regulator; + + if (regulator_name == NULL) + return -EINVAL; + + mutex_lock(&list_mutex); + list_for_each_entry(regulator, ®ulators, list) { + if (!strcmp(regulator_name, regulator->name)) { + mutex_lock(®ulator->mutex); + regulator->constraints = constraints; + mutex_unlock(®ulator->mutex); + mutex_unlock(&list_mutex); + return 0; + } + } + mutex_unlock(&list_mutex); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(regulator_set_platform_constraints); + +static void load_change(struct regulator *regulator, + struct regulator_load *load, int uA) +{ + struct regulator_load *l; + int current_uA = 0, output_uV, input_uV, err; + unsigned int mode; + + load->uA_load = uA; + err = constraint_check_drms(regulator); + if (err < 0 || !regulator->ops->get_optimum_mode || + !regulator->ops->get_voltage || !regulator->ops->set_mode) + return; + + /* get output voltage */ + output_uV = regulator->ops->get_voltage(regulator); + + /* get input voltage */ + if (regulator->parent && regulator->parent->ops->get_voltage) + input_uV = + regulator->parent->ops->get_voltage(regulator->parent); + else + input_uV = regulator->constraints->input_uV; + + /* calc total requested load */ + list_for_each_entry(l, ®ulator->user_list, list) + current_uA += l->uA_load; + + mode = regulator->ops->get_optimum_mode(regulator, input_uV, + output_uV, current_uA); + err = constraint_check_mode(regulator, mode); + if (err < 0) + return; + + regulator->ops->set_mode(regulator, mode); +} + +void regulator_drms_notify_load(struct regulator *regulator, + struct device *dev, int uA) +{ + struct regulator_load *load; + + mutex_lock(®ulator->mutex); + list_for_each_entry(load, ®ulator->user_list, list) { + if (load->dev == dev) { + load_change(regulator, load, uA); + break; + } + } + mutex_unlock(®ulator->mutex); +} +EXPORT_SYMBOL_GPL(regulator_drms_notify_load); + +void *regulator_get_drvdata(struct regulator *regulator) +{ + return regulator->reg_data; +} +EXPORT_SYMBOL_GPL(regulator_get_drvdata); + +void regulator_set_drvdata(struct regulator *regulator, void *data) +{ + regulator->reg_data = data; +} +EXPORT_SYMBOL_GPL(regulator_set_drvdata); + +static int __init regulator_init(void) +{ + return class_register(®ulator_class); +} + +static void __exit regulator_exit(void) +{ + class_unregister(®ulator_class); +} + +subsys_initcall(regulator_init); +module_exit(regulator_exit); + +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, \ + www.wolfsonmicro.com "); +MODULE_DESCRIPTION("Regulator Interface"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/regulator/regulator-drv.h b/include/linux/regulator/regulator-drv.h new file mode 100644 index 000000000000..5235af9619fe --- /dev/null +++ b/include/linux/regulator/regulator-drv.h @@ -0,0 +1,107 @@ +/* + * regulator-drv.h -- SoC Regulator support. + * + * Copyright (C) 2007 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Regulator Driver Interface. + */ + + +#ifndef __LINUX_REGULATOR_DRV_H_ +#define __LINUX_REGULATOR_DRV_H_ + +#include <linux/device.h> +#include <linux/regulator/regulator.h> + +struct regulator_constraints; + +/** + * struct regulator_ops - regulator operations. + * + * This struct describes regulator operations. + */ +struct regulator_ops { + + /* get/set regulator voltage */ + int (*set_voltage)(struct regulator *, int uV); + int (*get_voltage)(struct regulator *); + + /* get/set regulator current */ + int (*set_current)(struct regulator *, int uA); + int (*get_current)(struct regulator *); + + /* enable/disable regulator */ + int (*enable)(struct regulator *); + int (*disable)(struct regulator *); + int (*is_enabled)(struct regulator *); + + /* get/set regulator mode (defined in regulator.h) */ + int (*set_mode)(struct regulator *, unsigned int mode); + unsigned int (*get_mode)(struct regulator *); + + /* get most efficient regulator mode for load */ + unsigned int (*get_optimum_mode)(struct regulator *, int input_uV, + int output_uV, int load_uA); +}; + +/** + * struct regulator + * + * Voltage / Current regulator. + */ +struct regulator { + const char *name; + int id; + struct regulator_ops *ops; + struct regulation_constraints *constraints; + int use_count; + + struct list_head list; + struct list_head user_list; + struct blocking_notifier_head notifier; + struct mutex mutex; + struct module *owner; + struct class_device cdev; + + struct regulator *parent; /* for tree */ + + void *reg_data; /* regulator data */ + void *vendor; /* regulator vendor extensions */ +}; + +/** + * regulator_register - register regulator + * @regulator: regulator source + * + * Called by regulator drivers to register a regulator. + * Returns 0 on success. + */ +int regulator_register(struct regulator *regulator); + +/** + * regulator_unregister - unregister regulator + * @regulator: regulator source + * + * Called by regulator drivers to unregister a regulator. + */ +void regulator_unregister(struct regulator *regulator); + +/** + * 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. + */ +int regulator_notifier_call_chain(struct regulator *regulator, + unsigned long event, void *data); + +#endif diff --git a/include/linux/regulator/regulator-platform.h b/include/linux/regulator/regulator-platform.h new file mode 100644 index 000000000000..7f57e035eb34 --- /dev/null +++ b/include/linux/regulator/regulator-platform.h @@ -0,0 +1,102 @@ +/* + * regulator-platform.h -- SoC Regulator support. + * + * Copyright (C) 2007 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Regulator Platform Interface. + */ + + +#ifndef __LINUX_REGULATOR_PLATFORM_H_ +#define __LINUX_REGULATOR_PLATFORM_H_ + +#include <linux/regulator/regulator.h> + +struct regulator; + +/* + * Regulator operations. + * + * @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 regulation_constraints - regulator operating constraints. + * + * This struct describes regulator and board/machine specific constraints. + */ +struct regulation_constraints { + + char *name; + + /* voltage output range - for voltage control */ + int min_uV; + int max_uV; + + /* current output range - 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; + + /* input voltage */ + int input_uV; +}; + +/** + * regulator_set_platform_source - set regulator source regulator + * @regulator: regulator source + * @parent: source or parent regulator + * + * Called by platform initialisation code to set the source supply or "parent" + * regulator for this regulator. + */ +int regulator_set_platform_source(struct regulator *reg, + struct regulator *parent); + +/** + * regulator_get_platform_source - get regulator source regulator + * @regulator: regulator source + * + * Returns the regulator supply regulator or NULL if no supply regulator + * exists (i.e the regulator is supplied directly from USB, Line, Battery, etc) + */ +struct regulator *regulator_get_platform_source(struct regulator *regulator); + + +/** + * regulator_set_platform_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, set_mode. + */ +int regulator_set_platform_constraints(const char *regulator_name, + struct regulation_constraints *constraints); + +#endif diff --git a/include/linux/regulator/regulator.h b/include/linux/regulator/regulator.h new file mode 100644 index 000000000000..404b2472b11a --- /dev/null +++ b/include/linux/regulator/regulator.h @@ -0,0 +1,332 @@ +/* + * regulator.h -- SoC Regulator support. + * + * Copyright (C) 2007 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood <lg@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Regulator Client 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. + */ + + +#ifndef __LINUX_REGULATOR_H_ +#define __LINUX_REGULATOR_H_ + +/* + * Regulator operating modes. + * + * Regulators can run in a variety of different operating modes depending + * output load. This allows further power saving though regulator efficiency. + * + * 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. usefull in CPU voltage & frequency scaling where + * load can quickly increase with freqency 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 most efficient mode for very + * light loads. Can be used by devices when they are + * in a sleep/standby state. This mode may be more noisy + * than NORMAL 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. + */ + +#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 undervoltage. + * @OVER_CURRENT: Regulator output current is too high. + * @POWER_ON: Regulator power ON event. + * @POWER_OFF: Regulator power OFF event. + * @REGULATION_OUT: Regulator output is out of regulation. + * @FAIL: Regulator output has failed. + * @OVER_TEMP: Regulator over temp. + */ + +#define REGULATOR_EVENT_UNDER_VOLTAGE 0x1 +#define REGULATOR_EVENT_OVER_CURRENT 0x2 +#define REGULATOR_EVENT_POWER_ON 0x4 +#define REGULATOR_EVENT_POWER_OFF 0x8 +#define REGULATOR_EVENT_REGULATION_OUT 0x10 +#define REGULATOR_EVENT_FAIL 0x20 +#define REGULATOR_EVENT_OVER_TEMP 0x40 + +/* + * Convenience conversion. + * Here atm, maybe there is somewhere better for this. + */ +#define mV_to_uV(mV) (mV * 1000) +#define uV_to_mV(uV) (uV / 1000) +#define V_to_uV(V) (mV_to_uV(V * 1000)) +#define uV_to_V(uV) (uV_to_mV(uV) / 1000) + +struct regulator; + +#if defined(CONFIG_REGULATOR_API) + +/** + * regulator_get - lookup and obtain a reference to a regulator. + * @dev: device for regulator "consumer" + * @id: regulator ID + * + * Returns a struct regulator corresponding to the regulator producer, or + * valid IS_ERR() condition containing errno. + * + * Drivers must assume that the clock source is not enabled. + */ +struct regulator *regulator_get(struct device *dev, const char *id); + +/** + * regulator_put - "free" the regulator source + * @regulator: regulator source + * @dev: device + * + * 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 device *dev); + +/** + * 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, bootloader or may be + * hardwired in the regulator. + */ +int regulator_enable(struct regulator *regulator); + +/** + * regulator_disable - disable regulator output + * @regulator: regulator source + * + * Disable the regulator output voltage or current. + */ +int regulator_disable(struct regulator *regulator); + +/** + * 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); + +/** + * regulator_set_voltage - set regulator output voltage + * @regulator: regulator source + * @uV: 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. + */ +int regulator_set_voltage(struct regulator *regulator, int uV); + +/** + * regulator_get_voltage - get regulator output voltage + * @regulator: regulator source + * + * This returns the 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); + +/** + * regulator_set_current - set regulator output current + * @regulator: regulator source + * @uV: voltage in uA + * + * Sets current regulator 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. + */ +int regulator_set_current(struct regulator *regulator, int uA); + +/** + * regulator_get_current - get regulator output current + * @regulator: regulator source + * + * This returns the regulator current 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(struct regulator *regulator); + +/** + * regulator_set_mode - set regulator operating mode + * @regulator: regulator source + * @mode: operating mode + * + * Set regulator operating mode to increase regulator efficiency or improve + * regulation performance. + */ +int regulator_set_mode(struct regulator *regulator, unsigned int mode); + +/** + * regulator_get_mode - set regulator operating mode + * @regulator: regulator source + * + * Get the current regulator operating mode. + */ +unsigned int regulator_get_mode(struct regulator *regulator); + +/** + * regulator_get_optimum_mode - get regulator optimum operating mode + * @regulator: regulator source + * @input_uV: input voltage + * @output_uV: output voltage + * @load_uV: load current + * + * Get the most efficient regulator operating mode for the given input + * and output voltages at a specific load.. + */ +unsigned int regulator_get_optimum_mode(struct regulator *regulator, + int input_uV, int output_uV, int load_uA); + +/** + * regulator_register_client - register regulator event notifier + * @regulator: regulator source + * @notifier_block: notifier block + * + * Register notifier block to receive regulator events. + */ +int regulator_register_client(struct regulator *regulator, + struct notifier_block *nb); + +/** + * regulator_unregister_client - unregister regulator event notifier + * @regulator: regulator source + * @notifier_block: notifier block + * + * Unregister regulator event notifier block. + */ +int regulator_unregister_client(struct regulator *regulator, + struct notifier_block *nb); + +/** + * regulator_notify_load - notify regulator of device max load + * @regulator: regulator + * @dev: device + * @uA: load + * + * Notifies the regulator of new max device load. Can be used by DRMS to select + * the most efficient regulator operating mode. + * + * Client devices notify their supply regulator of the maximum power + * they will require 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 IO is about to begin or has just finished. + * o Device is idling in between work. + * + * The power tables in device datasheets would be used by the driver for + * the power consumption in each operational state. This information is + * also exported via sysfs to userspace. + * + * DRMS would then sum the total requested load on the regulator and change + * to the most efficient operating mode if platform constraints allow. + */ +void regulator_drms_notify_load(struct regulator *regulator, + struct device *dev, int uA); + +/** + * regulator_get_drvdata - get regulator driver data + * @regulator: regulator + */ +void *regulator_get_drvdata(struct regulator *regulator); + +/** + * regulator_set_drvdata - set regulator driver data + * @regulator: regulator + * @void: data + */ +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. + */ +#define regulator_get(dev, id) ({ (void)(dev); (void)(id); NULL; }) +#define regulator_put(regulator, dev) \ + do { (void)(regulator); (void)(dev); } while (0) +#define regulator_enable(regulator) ({ (void)(regulator); 0; }) +#define regulator_disable(regulator) ({ (void)(regulator); 0; }) +#define regulator_is_enabled(regulator) ({ (void)(regulator); 1; }) +#define regulator_set_voltage(regulator, uV) \ + ({ (void)(regulator); (void)(uV); 0; }) +#define regulator_get_voltage(regulator) ({ (void)(regulator); 0; }) +#define regulator_set_current(regulator, uA) \ + ({ (void)(regulator); (void)(uA); 0; }) +#define regulator_get_current(regulator) ({ (void)(regulator); 0; }) +#define regulator_set_mode(regulator, mode) \ + ({ (void)(regulator); (void)(mode); 0; }) +#define regulator_get_mode(regulator) ({ (void)(regulator); 0; }) +#define regulator_get_optimum_mode(regulator, input_uV, output_uV, load_uA) \ + ({ (void)(regulator); (void)(input_uV); \ + (void)(output_uV); (void)(load_uA); 0; }) +#define regulator_register_client(regulator, nb) \ + ({ (void)(regulator); (void)(nb); 0; }) +#define regulator_unregister_client(regulator, nb) \ + do { (void)(regulator); (void)(nb); } while (0) +#define regulator_notify_load(regulator, dev, uA) \ + do { (void)(regulator); (void)(dev); (void)(uA); } while (0) +#define regulator_get_drvdata(regulator) ({ (void)(regulator); NULL; }) +#define regulator_set_drvdata(regulator, data) \ + do { (void)(regulator); (void)(data); } while (0) + +#endif + +#endif |