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 <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
This commit is contained in:
Nicolas Boichat
2017-06-20 09:07:52 +08:00
committed by chrome-bot
parent d68b1ca803
commit e3336f4c8d
2 changed files with 21 additions and 8 deletions

View File

@@ -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);

View File

@@ -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. */