regulator: core: add guard delay between calling regulator_disable and _enable
authorGuodong Xu <guodong.xu@linaro.org>
Wed, 13 Aug 2014 11:33:40 +0000 (19:33 +0800)
committerMark Brown <broonie@linaro.org>
Sat, 16 Aug 2014 21:55:42 +0000 (16:55 -0500)
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 <guodong.xu@linaro.org>
Signed-off-by: Mark Brown <broonie@linaro.org>
drivers/regulator/core.c
include/linux/regulator/driver.h

index dc0e9813b62dec4e5e160afa1840bb2b298807b9..1e976b6320a27978ba7a72fbfe1be060f92109ae 100644 (file)
@@ -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;
index 4b628139a9cbe9c90c5365343cad74be7a45ab00..efe058f8f746fc5ec3fd7ba193391ee529a574a6 100644 (file)
@@ -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 *