From e3336f4c8d4fb59137d35f87f4a42d22848aabcd Mon Sep 17 00:00:00 2001 From: Nicolas Boichat Date: Tue, 20 Jun 2017 09:07:52 +0800 Subject: [PATCH] chip/stm32/pwm: Prevent sleeping while PWM output is active STM32F0 cannot keep PWM output active when chip is in deep sleep. The only other board that uses both CONFIG_LOW_POWER_IDLE and CONFIG_PWM on stm32 is jerry, and this logic should also apply to it. Also, switch using_pwm from array to bitmask to simplify handling. BRANCH=none BUG=b:36173380 TEST=On AP, tell it to autosuspend hammer: echo auto > /sys/bus/usb/devices/1-2/power/control Then see, using idlestats, that hammer does to deep sleep. In hammer console: pwm 0 50, see that PWM output is stable, idlestats shows EC does not sleep. In hammer console: pwm 0 -1, idlestats shows EC sleeps again. Change-Id: Ic74c1905364fe4335239da95a99193d0e3e979f7 Reviewed-on: https://chromium-review.googlesource.com/541115 Commit-Ready: Nicolas Boichat Tested-by: Nicolas Boichat Reviewed-by: Vincent Palatin --- chip/stm32/pwm.c | 28 ++++++++++++++++++++-------- include/system.h | 1 + 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/chip/stm32/pwm.c b/chip/stm32/pwm.c index aa5149b074..ce2f8636f6 100644 --- a/chip/stm32/pwm.c +++ b/chip/stm32/pwm.c @@ -12,9 +12,11 @@ #include "pwm.h" #include "pwm_chip.h" #include "registers.h" +#include "system.h" #include "util.h" -static int using_pwm[PWM_CH_COUNT]; +/* Bitmap of currently active PWM channels. 1 bit per channel. */ +static uint32_t using_pwm; void pwm_set_duty(enum pwm_channel ch, int percent) { @@ -41,7 +43,7 @@ static void pwm_configure(enum pwm_channel ch) int frequency = pwm->frequency ? pwm->frequency : 100; uint16_t ccer; - if (using_pwm[ch]) + if (using_pwm & (1 << ch)) return; /* Enable timer */ @@ -96,7 +98,10 @@ static void pwm_configure(enum pwm_channel ch) /* Enable auto-reload preload, start counting */ tim->cr1 |= (1 << 7) | (1 << 0); - using_pwm[ch] = 1; + atomic_or(&using_pwm, 1 << ch); + + /* Prevent sleep */ + disable_sleep(SLEEP_MASK_PWM); } static void pwm_disable(enum pwm_channel ch) @@ -104,7 +109,7 @@ static void pwm_disable(enum pwm_channel ch) const struct pwm_t *pwm = pwm_channels + ch; timer_ctlr_t *tim = (timer_ctlr_t *)(pwm->tim.base); - if (using_pwm[ch] == 0) + if ((using_pwm & (1 << ch)) == 0) return; /* Main output disable */ @@ -116,7 +121,14 @@ static void pwm_disable(enum pwm_channel ch) /* Disable timer clock */ __hw_timer_enable_clock(pwm->tim.id, 0); - using_pwm[ch] = 0; + /* Allow sleep */ + enable_sleep(SLEEP_MASK_PWM); + + atomic_clear(&using_pwm, 1 << ch); + + /* Unless another PWM is active... Then prevent sleep */ + if (using_pwm) + disable_sleep(SLEEP_MASK_PWM); } void pwm_enable(enum pwm_channel ch, int enabled) @@ -129,12 +141,12 @@ void pwm_enable(enum pwm_channel ch, int enabled) int pwm_get_enabled(enum pwm_channel ch) { - return using_pwm[ch]; + return using_pwm & (1 << ch); } static void pwm_reconfigure(enum pwm_channel ch) { - using_pwm[ch] = 0; + atomic_clear(&using_pwm, 1 << ch); pwm_configure(ch); } @@ -145,7 +157,7 @@ static void pwm_freq_change(void) { int i; for (i = 0; i < PWM_CH_COUNT; ++i) - if (using_pwm[i]) + if (pwm_get_enabled(i)) pwm_reconfigure(i); } DECLARE_HOOK(HOOK_FREQ_CHANGE, pwm_freq_change, HOOK_PRIO_DEFAULT); diff --git a/include/system.h b/include/system.h index 8bc63332c2..cd7f42817e 100644 --- a/include/system.h +++ b/include/system.h @@ -388,6 +388,7 @@ enum { SLEEP_MASK_I2C_SLAVE = (1 << 7), /* I2C slave communication ongoing */ SLEEP_MASK_FAN = (1 << 8), /* Fan control loop ongoing */ SLEEP_MASK_USB_DEVICE = (1 << 9), /* Generic USB device in use */ + SLEEP_MASK_PWM = (1 << 10), /* PWM output is enabled */ SLEEP_MASK_FORCE_NO_DSLEEP = (1 << 15), /* Force disable. */