From 84b6826306119dc3c41ef9d7ed6c408112f63301 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 1 Dec 2009 21:12:27 +0000 Subject: regulator: Add notifier event on regulator disable The intended use case is for drivers which disable regulators to save power but need to do some work to restore the hardware state when restarting. If the supplies are not actually disabled due to board limits or sharing with other active devices this notifier allows the driver to avoid unneeded reinitialisation, particularly when used with runtime PM. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/regulator/core.c') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index b60a4c9f8f16..6d2ce8a05331 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1341,6 +1341,9 @@ static int _regulator_disable(struct regulator_dev *rdev) __func__, rdev_get_name(rdev)); return ret; } + + _notifier_call_chain(rdev, REGULATOR_EVENT_DISABLE, + NULL); } /* decrease our supplies ref count and disable if required */ @@ -1399,8 +1402,8 @@ static int _regulator_force_disable(struct regulator_dev *rdev) return ret; } /* notify other consumers that power has been forced off */ - _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE, - NULL); + _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE | + REGULATOR_EVENT_DISABLE, NULL); } /* decrease our supplies ref count and disable if required */ -- cgit v1.2.3 From 31aae2beeb3d601d556b6a8c39085940ad1e9f42 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 21 Dec 2009 12:21:52 +0000 Subject: regulator: Allow regulators to specify the time taken to ramp on enable Regulators may sometimes take longer to enable than the control operation used to do so, either because the regulator has ramp rate control used to limit inrush current or because the control operation is very fast (GPIO being the most common example of this). In order to ensure that consumers do not rely on the regulator before it is enabled provide an enable_time() operation and have the core delay for that time before returning to the caller. This is implemented as a function since the ramp rate may be specified in voltage per unit time and therefore the time depend on the configuration. In future it would be desirable to allow the bulk operations to run the delays for multiple enables in parallel but this is not currently supported. Signed-off-by: Mark Brown --- drivers/regulator/core.c | 41 ++++++++++++++++++++++++++++++++++------ include/linux/regulator/driver.h | 6 ++++++ 2 files changed, 41 insertions(+), 6 deletions(-) (limited to 'drivers/regulator/core.c') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 6d2ce8a05331..ca8e1642538b 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -1084,6 +1085,13 @@ overflow_err: return NULL; } +static int _regulator_get_enable_time(struct regulator_dev *rdev) +{ + if (!rdev->desc->ops->enable_time) + return 0; + return rdev->desc->ops->enable_time(rdev); +} + /* Internal regulator request function */ static struct regulator *_regulator_get(struct device *dev, const char *id, int exclusive) @@ -1251,7 +1259,7 @@ static int _regulator_can_change_status(struct regulator_dev *rdev) /* locks held by regulator_enable() */ static int _regulator_enable(struct regulator_dev *rdev) { - int ret; + int ret, delay; /* do we need to enable the supply regulator first */ if (rdev->supply) { @@ -1275,13 +1283,34 @@ static int _regulator_enable(struct regulator_dev *rdev) if (!_regulator_can_change_status(rdev)) return -EPERM; - if (rdev->desc->ops->enable) { - ret = rdev->desc->ops->enable(rdev); - if (ret < 0) - return ret; - } else { + if (!rdev->desc->ops->enable) return -EINVAL; + + /* Query before enabling in case configuration + * dependant. */ + ret = _regulator_get_enable_time(rdev); + if (ret >= 0) { + delay = ret; + } else { + printk(KERN_WARNING + "%s: enable_time() failed for %s: %d\n", + __func__, rdev_get_name(rdev), + ret); + delay = 0; } + + /* Allow the regulator to ramp; it would be useful + * to extend this for bulk operations so that the + * regulators can ramp together. */ + ret = rdev->desc->ops->enable(rdev); + if (ret < 0) + return ret; + + if (delay >= 1000) + mdelay(delay / 1000); + else if (delay) + udelay(delay); + } else if (ret < 0) { printk(KERN_ERR "%s: is_enabled() failed for %s: %d\n", __func__, rdev_get_name(rdev), ret); diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 31f2055eae28..592cd7c642c2 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -58,6 +58,9 @@ enum regulator_status { * @get_optimum_mode: Get the most efficient operating mode for the regulator * when running with the specified parameters. * + * @enable_time: Time taken for the regulator voltage output voltage to + * stabalise after being enabled, in microseconds. + * * @set_suspend_voltage: Set the voltage for the regulator when the system * is suspended. * @set_suspend_enable: Mark the regulator as enabled when the system is @@ -93,6 +96,9 @@ struct regulator_ops { int (*set_mode) (struct regulator_dev *, unsigned int mode); unsigned int (*get_mode) (struct regulator_dev *); + /* Time taken to enable the regulator */ + int (*enable_time) (struct regulator_dev *); + /* report regulator status ... most other accessors report * control inputs, this reports results of combining inputs * from Linux (and other sources) with the actual load. -- cgit v1.2.3 From 9a7f6a4c6edc84748c6477c9df56691a0e61b8fd Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 11 Feb 2010 17:22:45 +0000 Subject: regulator: Assume regulators are enabled if they don't report anything If a regulator driver does not provide a way to query if the driver is enabled then assume that it is enabled. This is very likely to reflect the actual state is more useful for callers than reporting an error. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/regulator/core.c') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca8e1642538b..75a26f780918 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1466,9 +1466,9 @@ EXPORT_SYMBOL_GPL(regulator_force_disable); static int _regulator_is_enabled(struct regulator_dev *rdev) { - /* sanity check */ + /* If we don't know then assume that the regulator is always on */ if (!rdev->desc->ops->is_enabled) - return -EINVAL; + return 1; return rdev->desc->ops->is_enabled(rdev); } -- cgit v1.2.3 From 34abbd68efe09765465b81dfedeee9994f13302f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 12 Feb 2010 10:18:08 +0000 Subject: regulator: Provide optional dummy regulator for consumers In order to ease transitions with drivers are boards start using regulators provide an option to cause all regulator_get() calls to succeed, with a dummy always on regulator being supplied where one has not been configured. A warning is printed whenever the dummy regulator is used to aid system development. This regulator does not implement any regulator operations but will allow simple consumers which only do enable() and disable() calls to run. It is kept separate from the fixed voltage regulator to avoid Kconfig confusion on the part of users when it is extended to allow boards to explicitly use the dummy regulator to simplify cases where the majority of supplies are from fixed regulators without software control. This option is currently only effective for systems which do not specify full constriants. If required an override could also be provided to allow these systems to use the dummy regulator, though it is likely that unconfigured supplies on such systems will lead to error due to regulators being powered down more aggressively when not in use. Signed-off-by: Mark Brown Signed-off-by: Liam Girdwood --- drivers/regulator/Kconfig | 11 ++++++++ drivers/regulator/Makefile | 1 + drivers/regulator/core.c | 27 ++++++++++++++++++- drivers/regulator/dummy.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/regulator/dummy.h | 31 ++++++++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 drivers/regulator/dummy.c create mode 100644 drivers/regulator/dummy.h (limited to 'drivers/regulator/core.c') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 3c07169498cf..834b48441829 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -27,6 +27,17 @@ config REGULATOR_DEBUG help Say yes here to enable debugging support. +config REGULATOR_DUMMY + bool "Provide a dummy regulator if regulator lookups fail" + help + If this option is enabled then when a regulator lookup fails + and the board has not specified that it has provided full + constraints then the regulator core will provide an always + enabled dummy regulator will be provided, allowing consumer + drivers to continue. + + A warning will be generated when this substitution is done. + config REGULATOR_FIXED_VOLTAGE tristate "Fixed voltage regulator support" help diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 7c59bcb10613..e845b66ad59c 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_REGULATOR_VIRTUAL_CONSUMER) += virtual.o obj-$(CONFIG_REGULATOR_USERSPACE_CONSUMER) += userspace-consumer.o obj-$(CONFIG_REGULATOR_BQ24022) += bq24022.o +obj-$(CONFIG_REGULATOR_DUMMY) += dummy.o obj-$(CONFIG_REGULATOR_LP3971) += lp3971.o obj-$(CONFIG_REGULATOR_MAX1586) += max1586.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 75a26f780918..c7bbe30010f7 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -24,6 +24,8 @@ #include #include +#include "dummy.h" + #define REGULATOR_VERSION "0.5" static DEFINE_MUTEX(regulator_list_mutex); @@ -1123,6 +1125,22 @@ static struct regulator *_regulator_get(struct device *dev, const char *id, goto found; } } + +#ifdef CONFIG_REGULATOR_DUMMY + if (!devname) + devname = "deviceless"; + + /* If the board didn't flag that it was fully constrained then + * substitute in a dummy regulator so consumers can continue. + */ + if (!has_full_constraints) { + pr_warning("%s supply %s not found, using dummy regulator\n", + devname, id); + rdev = dummy_regulator_rdev; + goto found; + } +#endif + mutex_unlock(®ulator_list_mutex); return regulator; @@ -2483,8 +2501,15 @@ EXPORT_SYMBOL_GPL(regulator_get_init_drvdata); static int __init regulator_init(void) { + int ret; + printk(KERN_INFO "regulator: core version %s\n", REGULATOR_VERSION); - return class_register(®ulator_class); + + ret = class_register(®ulator_class); + + regulator_dummy_init(); + + return ret; } /* init early to allow our consumers to complete system booting */ diff --git a/drivers/regulator/dummy.c b/drivers/regulator/dummy.c new file mode 100644 index 000000000000..c7410bde7b5d --- /dev/null +++ b/drivers/regulator/dummy.c @@ -0,0 +1,66 @@ +/* + * dummy.c + * + * Copyright 2010 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 "dummy.h" + +struct regulator_dev *dummy_regulator_rdev; + +static struct regulator_init_data dummy_initdata; + +static struct regulator_ops dummy_ops; + +static struct regulator_desc dummy_desc = { + .name = "dummy", + .id = -1, + .type = REGULATOR_VOLTAGE, + .owner = THIS_MODULE, + .ops = &dummy_ops, +}; + +static struct platform_device *dummy_pdev; + +void __init regulator_dummy_init(void) +{ + int ret; + + dummy_pdev = platform_device_alloc("reg-dummy", -1); + if (!dummy_pdev) { + pr_err("Failed to allocate dummy regulator device\n"); + return; + } + + ret = platform_device_add(dummy_pdev); + if (ret != 0) { + pr_err("Failed to register dummy regulator device: %d\n", ret); + platform_device_put(dummy_pdev); + return; + } + + dummy_regulator_rdev = regulator_register(&dummy_desc, NULL, + &dummy_initdata, NULL); + if (IS_ERR(dummy_regulator_rdev)) { + ret = PTR_ERR(dummy_regulator_rdev); + pr_err("Failed to register regulator: %d\n", ret); + platform_device_unregister(dummy_pdev); + return; + } +} diff --git a/drivers/regulator/dummy.h b/drivers/regulator/dummy.h new file mode 100644 index 000000000000..3921c0e24249 --- /dev/null +++ b/drivers/regulator/dummy.h @@ -0,0 +1,31 @@ +/* + * dummy.h + * + * Copyright 2010 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. + */ + +#ifndef _DUMMY_H +#define _DUMMY_H + +struct regulator_dev; + +extern struct regulator_dev *dummy_regulator_rdev; + +#ifdef CONFIG_REGULATOR_DUMMY +void __init regulator_dummy_init(void); +#else +static inline void regulator_dummy_init(void) { } +#endif + +#endif -- cgit v1.2.3