Skip to content

Commit a8ce7bd

Browse files
vwaxbroonie
authored andcommitted
regulator: core: Fix off_on_delay handling
The jiffies-based off_on_delay implementation has a couple of problems that cause it to sometimes not actually delay for the required time: (1) If, for example, the off_on_delay time is equivalent to one jiffy, and the ->last_off_jiffy is set just before a new jiffy starts, then _regulator_do_enable() does not wait at all since it checks using time_before(). (2) When jiffies overflows, the value of "remaining" becomes higher than "max_delay" and the code simply proceeds without waiting. Fix these problems by changing it to use ktime_t instead. [Note that since jiffies doesn't start at zero but at INITIAL_JIFFIES ("-5 minutes"), (2) above also led to the code not delaying if the first regulator_enable() is called when the ->last_off_jiffy is not initialised, such as for regulators with ->constraints->boot_on set. It's not clear to me if this was intended or not, but I've preserved this behaviour explicitly with the check for a non-zero ->last_off.] Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com> Link: https://lore.kernel.org/r/20210423114524.26414-1-vincent.whitchurch@axis.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent 41a36ff commit a8ce7bd

2 files changed

Lines changed: 9 additions & 26 deletions

File tree

drivers/regulator/core.c

Lines changed: 8 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1443,7 +1443,7 @@ static int set_machine_constraints(struct regulator_dev *rdev)
14431443
if (rdev->constraints->always_on)
14441444
rdev->use_count++;
14451445
} else if (rdev->desc->off_on_delay) {
1446-
rdev->last_off_jiffy = jiffies;
1446+
rdev->last_off = ktime_get();
14471447
}
14481448

14491449
print_constraints(rdev);
@@ -2488,29 +2488,15 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
24882488

24892489
trace_regulator_enable(rdev_get_name(rdev));
24902490

2491-
if (rdev->desc->off_on_delay) {
2491+
if (rdev->desc->off_on_delay && rdev->last_off) {
24922492
/* if needed, keep a distance of off_on_delay from last time
24932493
* this regulator was disabled.
24942494
*/
2495-
unsigned long start_jiffy = jiffies;
2496-
unsigned long intended, max_delay, remaining;
2497-
2498-
max_delay = usecs_to_jiffies(rdev->desc->off_on_delay);
2499-
intended = rdev->last_off_jiffy + max_delay;
2500-
2501-
if (time_before(start_jiffy, intended)) {
2502-
/* calc remaining jiffies to deal with one-time
2503-
* timer wrapping.
2504-
* in case of multiple timer wrapping, either it can be
2505-
* detected by out-of-range remaining, or it cannot be
2506-
* detected and we get a penalty of
2507-
* _regulator_enable_delay().
2508-
*/
2509-
remaining = intended - start_jiffy;
2510-
if (remaining <= max_delay)
2511-
_regulator_enable_delay(
2512-
jiffies_to_usecs(remaining));
2513-
}
2495+
ktime_t end = ktime_add_us(rdev->last_off, rdev->desc->off_on_delay);
2496+
s64 remaining = ktime_us_delta(end, ktime_get());
2497+
2498+
if (remaining > 0)
2499+
_regulator_enable_delay(remaining);
25142500
}
25152501

25162502
if (rdev->ena_pin) {
@@ -2740,11 +2726,8 @@ static int _regulator_do_disable(struct regulator_dev *rdev)
27402726
return ret;
27412727
}
27422728

2743-
/* cares about last_off_jiffy only if off_on_delay is required by
2744-
* device.
2745-
*/
27462729
if (rdev->desc->off_on_delay)
2747-
rdev->last_off_jiffy = jiffies;
2730+
rdev->last_off = ktime_get();
27482731

27492732
trace_regulator_disable_complete(rdev_get_name(rdev));
27502733

include/linux/regulator/driver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ struct regulator_dev {
476476
unsigned int is_switch:1;
477477

478478
/* time when this regulator was disabled last time */
479-
unsigned long last_off_jiffy;
479+
ktime_t last_off;
480480
};
481481

482482
struct regulator_dev *

0 commit comments

Comments
 (0)