From 871f565055ed232e5751da18a331b73e8254adaf Mon Sep 17 00:00:00 2001 From: Guodong Xu Date: Wed, 13 Aug 2014 19:33:40 +0800 Subject: [PATCH] regulator: core: add guard delay between calling regulator_disable and _enable Some regulator require a minimum delay between its disable and next enable. This is to avoid damages when out-of-range frequent disable/enable of a single regulator can bring to the regulator chip. Add @off_on_delay to struct regulator_desc. Device drivers' can use this field to set this guard time. Add @last_off_jiffy to struct regulator_dev. When @off_on_delay is set by driver, regulator core can store its last off (disable) time into this field. Signed-off-by: Guodong Xu Signed-off-by: Mark Brown --- drivers/regulator/core.c | 31 +++++++++++++++++++++++++++++++ include/linux/regulator/driver.h | 6 ++++++ 2 files changed, 37 insertions(+) diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index dc0e9813b62d..1e976b6320a2 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -1813,6 +1813,31 @@ static int _regulator_do_enable(struct regulator_dev *rdev) trace_regulator_enable(rdev_get_name(rdev)); + if (rdev->desc->off_on_delay) { + /* if needed, keep a distance of off_on_delay from last time + * this regulator was disabled. + */ + unsigned long start_jiffy = jiffies; + unsigned long intended, max_delay, remaining; + + max_delay = usecs_to_jiffies(rdev->desc->off_on_delay); + intended = rdev->last_off_jiffy + max_delay; + + if (time_before(start_jiffy, intended)) { + /* calc remaining jiffies to deal with one-time + * timer wrapping. + * in case of multiple timer wrapping, either it can be + * detected by out-of-range remaining, or it cannot be + * detected and we gets a panelty of + * _regulator_enable_delay(). + */ + remaining = intended - start_jiffy; + if (remaining <= max_delay) + _regulator_enable_delay( + jiffies_to_usecs(remaining)); + } + } + if (rdev->ena_pin) { ret = regulator_ena_gpio_ctrl(rdev, true); if (ret < 0) @@ -1925,6 +1950,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev) return ret; } + /* cares about last_off_jiffy only if off_on_delay is required by + * device. + */ + if (rdev->desc->off_on_delay) + rdev->last_off_jiffy = jiffies; + trace_regulator_disable_complete(rdev_get_name(rdev)); return 0; diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h index 4b628139a9cb..efe058f8f746 100644 --- a/include/linux/regulator/driver.h +++ b/include/linux/regulator/driver.h @@ -238,6 +238,7 @@ enum regulator_type { * @bypass_val_off: Disabling value for control when using regmap set_bypass * * @enable_time: Time taken for initial enable of regulator (in uS). + * @off_on_delay: guard time (in uS), before re-enabling a regulator */ struct regulator_desc { const char *name; @@ -276,6 +277,8 @@ struct regulator_desc { unsigned int bypass_val_off; unsigned int enable_time; + + unsigned int off_on_delay; }; /** @@ -348,6 +351,9 @@ struct regulator_dev { struct regulator_enable_gpio *ena_pin; unsigned int ena_gpio_state:1; + + /* time when this regulator was disabled last time */ + unsigned long last_off_jiffy; }; struct regulator_dev * -- 2.34.1