regulator: Implement deferred disable support

It is a reasonably common pattern for hardware to require some delay after
being quiesced before the disable has finalised, especially in mixed signal
devices. For example, an active discharge may be required to ensure that
the circuit starts up again in a known state. Avoid having to implement
such delays in the regulator API by providing regulator_deferred_disable()
which will do a regulator_disable() a specified number of milliseconds
after it is called.

Due to the reference counting done on regulators a deferred disable can
be cancelled by doing another regulator_enable().

Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Acked-by: Liam Girdwood <lrg@ti.com>
This commit is contained in:
Mark Brown 2011-09-11 09:53:50 +01:00
parent 30ab1e7886
commit da07ecd93b
3 changed files with 69 additions and 0 deletions

View File

@ -1552,6 +1552,63 @@ int regulator_force_disable(struct regulator *regulator)
}
EXPORT_SYMBOL_GPL(regulator_force_disable);
static void regulator_disable_work(struct work_struct *work)
{
struct regulator_dev *rdev = container_of(work, struct regulator_dev,
disable_work.work);
int count, i, ret;
mutex_lock(&rdev->mutex);
BUG_ON(!rdev->deferred_disables);
count = rdev->deferred_disables;
rdev->deferred_disables = 0;
for (i = 0; i < count; i++) {
ret = _regulator_disable(rdev);
if (ret != 0)
rdev_err(rdev, "Deferred disable failed: %d\n", ret);
}
mutex_unlock(&rdev->mutex);
if (rdev->supply) {
for (i = 0; i < count; i++) {
ret = regulator_disable(rdev->supply);
if (ret != 0) {
rdev_err(rdev,
"Supply disable failed: %d\n", ret);
}
}
}
}
/**
* regulator_disable_deferred - disable regulator output with delay
* @regulator: regulator source
* @ms: miliseconds until the regulator is disabled
*
* Execute regulator_disable() on the regulator after a delay. This
* is intended for use with devices that require some time to quiesce.
*
* NOTE: this will only disable the regulator output if no other consumer
* devices have it enabled, the regulator device supports disabling and
* machine constraints permit this operation.
*/
int regulator_disable_deferred(struct regulator *regulator, int ms)
{
struct regulator_dev *rdev = regulator->rdev;
mutex_lock(&rdev->mutex);
rdev->deferred_disables++;
mutex_unlock(&rdev->mutex);
return schedule_delayed_work(&rdev->disable_work,
msecs_to_jiffies(ms));
}
EXPORT_SYMBOL_GPL(regulator_disable_deferred);
static int _regulator_is_enabled(struct regulator_dev *rdev)
{
/* If we don't know then assume that the regulator is always on */
@ -2622,6 +2679,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
INIT_LIST_HEAD(&rdev->consumer_list);
INIT_LIST_HEAD(&rdev->list);
BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
INIT_DELAYED_WORK(&rdev->disable_work, regulator_disable_work);
/* preform any regulator specific init */
if (init_data->regulator_init) {
@ -2729,6 +2787,7 @@ void regulator_unregister(struct regulator_dev *rdev)
#ifdef CONFIG_DEBUG_FS
debugfs_remove_recursive(rdev->debugfs);
#endif
flush_work_sync(&rdev->disable_work.work);
WARN_ON(rdev->open_count);
unset_regulator_supplies(rdev);
list_del(&rdev->list);

View File

@ -141,6 +141,7 @@ 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_disable_deferred(struct regulator *regulator, int ms);
int regulator_bulk_get(struct device *dev, int num_consumers,
struct regulator_bulk_data *consumers);
@ -211,6 +212,12 @@ static inline int regulator_disable(struct regulator *regulator)
return 0;
}
static inline int regulator_disable_deferred(struct regulator *regulator,
int ms)
{
return 0;
}
static inline int regulator_is_enabled(struct regulator *regulator)
{
return 1;

View File

@ -199,6 +199,9 @@ struct regulator_dev {
struct regulation_constraints *constraints;
struct regulator *supply; /* for tree */
struct delayed_work disable_work;
int deferred_disables;
void *reg_data; /* regulator_dev data */
#ifdef CONFIG_DEBUG_FS