diff options
author | Tinghan Shen <tinghan.shen@mediatek.com> | 2022-01-24 17:06:27 +0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2022-01-26 09:43:26 +0000 |
commit | e894b1be353b521e5c1e8fc0d8e81f71720ad116 (patch) | |
tree | 3153d59b7c4f7f7c881da99ec5c1eda81875b642 /chip | |
parent | 982408e4d09c163370d68f7e8107a861a187c85d (diff) | |
download | chrome-ec-e894b1be353b521e5c1e8fc0d8e81f71720ad116.tar.gz |
chip/mt_scp: rv32i: improve mt8195 handling suspend/resume
The original suspend/resume flow of SCP doesn't know when will the 26M
clock source be suspended by SPM. SCP has to disable watchdog once receiving
host suspend command and enable watchdog after receiving host resume command.
The drawback is that SCP has to run without the protection of
watchdog in the period between host suspend command and host resume
command.
In order to solve this, we can star a timer with 26M clock source, so that
SCP can detect the timing by snooping the 26M timer counter. The
improved flow allows SCP to disable watchdog and irq after detecting 26M
is off and re-enable immediately when 26M available again.
BRANCH=None
BUG=b:189356151
BUG=b:202754468
TEST=suspend_stress_test on 5 DUTs overnight
Signed-off-by: Tinghan Shen <tinghan.shen@mediatek.com>
Change-Id: I85d29d68af4d2108bf31f7cc2fd0f6c0bc4f9a5d
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3411688
Reviewed-by: Rong Chang <rongchang@chromium.org>
Commit-Queue: Rong Chang <rongchang@chromium.org>
Diffstat (limited to 'chip')
-rw-r--r-- | chip/mt_scp/mt8195/clock.c | 72 | ||||
-rw-r--r-- | chip/mt_scp/rv32i_common/hrtimer.c | 14 | ||||
-rw-r--r-- | chip/mt_scp/rv32i_common/scp_timer.h | 16 |
3 files changed, 94 insertions, 8 deletions
diff --git a/chip/mt_scp/mt8195/clock.c b/chip/mt_scp/mt8195/clock.c index eac6436a46..f3b32ab52e 100644 --- a/chip/mt_scp/mt8195/clock.c +++ b/chip/mt_scp/mt8195/clock.c @@ -15,12 +15,24 @@ #include "ec_commands.h" #include "power.h" #include "registers.h" +#include "scp_timer.h" #include "scp_watchdog.h" +#include "task.h" #include "timer.h" #define CPRINTF(format, args...) cprintf(CC_CLOCK, format, ##args) #define CPRINTS(format, args...) cprints(CC_CLOCK, format, ##args) +#define TASK_EVENT_SUSPEND TASK_EVENT_CUSTOM_BIT(4) +#define TASK_EVENT_RESUME TASK_EVENT_CUSTOM_BIT(5) +#define CHECK_26M_PERIOD_US 50000 + +enum scp_sr_state { + SR_S0, + SR_S02S3, + SR_S3, +}; + enum scp_clock_source { SCP_CLK_SYSTEM, SCP_CLK_32K, @@ -377,13 +389,61 @@ power_chipset_handle_host_sleep_event(enum host_sleep_event state, struct host_sleep_event_context *ctx) { if (state == HOST_SLEEP_EVENT_S3_SUSPEND) { - CPRINTS("AP suspend"); - watchdog_disable(); - clock_select_clock(SCP_CLK_SYSTEM); + task_set_event(TASK_ID_SR, TASK_EVENT_SUSPEND); } else if (state == HOST_SLEEP_EVENT_S3_RESUME) { - clock_select_clock(SCP_CLK_ULPOSC2_HIGH_SPEED); - watchdog_enable(); - CPRINTS("AP resume"); + task_set_event(TASK_ID_SR, TASK_EVENT_RESUME); + } +} + +void sr_task(void *u) +{ + enum scp_sr_state state = SR_S0; + uint32_t event; + uint32_t prev, now; + + while(1) { + switch (state) { + case SR_S0: + event = task_wait_event(-1); + if (event & TASK_EVENT_SUSPEND) { + timer_enable(TIMER_SR); + prev = timer_read_raw_sr(); + state = SR_S02S3; + } + break; + case SR_S02S3: + event = task_wait_event(CHECK_26M_PERIOD_US); + if (event & TASK_EVENT_RESUME) { + /* suspend is aborted */ + timer_disable(TIMER_SR); + state = SR_S0; + } else if (event & TASK_EVENT_TIMER) { + now = timer_read_raw_sr(); + if (now != prev) { + /* 26M is still on */ + prev = now; + } else { + /* 26M is off */ + state = SR_S3; + } + } + break; + case SR_S3: + interrupt_disable(); + watchdog_disable(); + + /* change to 26M to stop core at here */ + clock_select_clock(SCP_CLK_SYSTEM); + + /* 26M is back */ + clock_select_clock(SCP_CLK_ULPOSC2_HIGH_SPEED); + + watchdog_enable(); + interrupt_enable(); + timer_disable(TIMER_SR); + state = SR_S0; + break; + } } } diff --git a/chip/mt_scp/rv32i_common/hrtimer.c b/chip/mt_scp/rv32i_common/hrtimer.c index 89ffaa2fca..a844527494 100644 --- a/chip/mt_scp/rv32i_common/hrtimer.c +++ b/chip/mt_scp/rv32i_common/hrtimer.c @@ -15,6 +15,7 @@ #include "common.h" #include "hwtimer.h" #include "registers.h" +#include "scp_timer.h" #include "task.h" #define TIMER_SYSTEM 5 @@ -27,20 +28,25 @@ static uint8_t sys_high; /* High 32-bit for event timer. */ static uint8_t event_high; -static void timer_enable(int n) +void timer_enable(int n) { /* cannot be changed when timer is enabled */ SCP_CORE0_TIMER_IRQ_CTRL(n) |= TIMER_IRQ_EN; SCP_CORE0_TIMER_EN(n) |= TIMER_EN; } -static void timer_disable(int n) +void timer_disable(int n) { SCP_CORE0_TIMER_EN(n) &= ~TIMER_EN; /* cannot be changed when timer is enabled */ SCP_CORE0_TIMER_IRQ_CTRL(n) &= ~TIMER_IRQ_EN; } +uint32_t timer_read_raw_sr(void) +{ + return SCP_CORE0_TIMER_CUR_VAL(TIMER_SR); +} + static int timer_is_irq(int n) { return SCP_CORE0_TIMER_IRQ_CTRL(n) & TIMER_IRQ_STATUS; @@ -139,6 +145,10 @@ int __hw_clock_source_init(uint32_t start_t) timer_set_clock(TIMER_EVENT, TIMER_CLK_SRC_BCLK); task_enable_irq(SCP_IRQ_TIMER(TIMER_EVENT)); + /* SR timer */ + timer_set_clock(TIMER_SR, TIMER_CLK_SRC_26M); + task_disable_irq(SCP_IRQ_TIMER(TIMER_SR)); + return SCP_IRQ_TIMER(TIMER_SYSTEM); } diff --git a/chip/mt_scp/rv32i_common/scp_timer.h b/chip/mt_scp/rv32i_common/scp_timer.h new file mode 100644 index 0000000000..5c0650f913 --- /dev/null +++ b/chip/mt_scp/rv32i_common/scp_timer.h @@ -0,0 +1,16 @@ +/* Copyright 2022 The Chromium OS Authors. All rights reserved. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef __SCP_TIMER_H +#define __SCP_TIMER_H + +/* detect existance of 26m clock in S3 stage */ +#define TIMER_SR 4 + +void timer_enable(int n); +void timer_disable(int n); +uint32_t timer_read_raw_sr(void); + +#endif /* __SCP_TIMER_H */ |