diff options
author | Bill Richardson <wfrichar@chromium.org> | 2016-03-30 17:45:12 -0700 |
---|---|---|
committer | chrome-bot <chrome-bot@chromium.org> | 2016-04-03 10:27:39 -0700 |
commit | 9406cf384aba9554dd49eef17a3d4a4eb2947972 (patch) | |
tree | c6d209038d23287e03d96ef59cf81fd7c17b9296 | |
parent | e79b32eb3d2f61a23c2c9ac75003315c964f91c9 (diff) | |
download | chrome-ec-9406cf384aba9554dd49eef17a3d4a4eb2947972.tar.gz |
Cr50: Sleep only when SPS has been quiet for a while
This adds the SPI slave bus and TPM task to the things that
can prevent deep sleep. Even when things are quiet, we wait at
least a second
With this CL, it will wait at least one second after the last SPS
transaction before sleeping. Since most TPM-protocol commands are
built up of a number of back-to-back SPS messages, if we don't
wait we'll keep trying to sleep in the middle of active commands.
Even if everything is quiet, we wait 0.2 seconds anyway to give
the UART buffers time to drain.
BUG=chrome-os-partner:49955, chrome-os-partner:50721
BRANCH=none
TEST=make buildall; extensive tests on Cr50
Testing is a pain.
In addition to ALL the steps listed in commit
d917d3f1867e96369ff25bf6906043a5f488a6fb, loading the firmware
with the spiflash tool leaves SPS_CS_L low, so you have to drive
it high manually. The easiest way is to build and run
test/tpm_test/tpmtest.py for a few seconds then interrupt it with
Ctrl-C.
Note that because the system wakes from deep sleep when it sees
SPS_CS_L go low but it can't get ready fast enough to capture the
incoming bits, that first SPI transaction will be garbled or
lost. You'll have to either retry it, or wake the system another
way first.
Change-Id: Iae2fe5ef33869c48e98a3afecd6b98991a51a488
Signed-off-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/336690
Reviewed-by: Randall Spangler <rspangler@chromium.org>
-rw-r--r-- | chip/g/idle.c | 61 | ||||
-rw-r--r-- | chip/g/sps_tpm.c | 11 | ||||
-rw-r--r-- | include/system.h | 7 |
3 files changed, 73 insertions, 6 deletions
diff --git a/chip/g/idle.c b/chip/g/idle.c index 7319c6edb8..a141b80630 100644 --- a/chip/g/idle.c +++ b/chip/g/idle.c @@ -67,9 +67,29 @@ static void prepare_to_deep_sleep(void) /* Latch the pinmux values */ GREG32(PINMUX, HOLD) = 1; - /* Wake only from USB for now */ + /* + * Specify the PINMUX pads that can wake us. + * A1 is UART RX. Idle is high, so wake on low level + * A12 is SPS_CS_L. Also wake on low. + * HEY: Use something in gpio.inc to identify these! + */ + GREG32(PINMUX, EXITEN0) = + GC_PINMUX_EXITEN0_DIOA1_MASK | + GC_PINMUX_EXITEN0_DIOA12_MASK; + + GREG32(PINMUX, EXITEDGE0) = 0; /* level sensitive */ + + GREG32(PINMUX, EXITINV0) = /* low or falling */ + GC_PINMUX_EXITINV0_DIOA1_MASK | + GC_PINMUX_EXITINV0_DIOA12_MASK; + + /* Enable all possible internal wake sources */ GR_PMU_EXITPD_MASK = - GC_PMU_EXITPD_MASK_UTMI_SUSPEND_N_MASK; + GC_PMU_EXITPD_MASK_PIN_PD_EXIT_MASK | + GC_PMU_EXITPD_MASK_UTMI_SUSPEND_N_MASK | + GC_PMU_EXITPD_MASK_RDD0_PD_EXIT_TIMER_MASK | + GC_PMU_EXITPD_MASK_TIMELS0_PD_EXIT_TIMER0_MASK | + GC_PMU_EXITPD_MASK_TIMELS0_PD_EXIT_TIMER1_MASK; /* Clamp the USB pins and shut the PHY down. We have to do this in * three separate steps, or Bad Things happen. */ @@ -88,10 +108,23 @@ static void prepare_to_deep_sleep(void) GC_PMU_LOW_POWER_DIS_JTR_RC_MASK; } +/* The time in the future at which sleeping will be allowed. */ +static timestamp_t next_sleep_time; + +/* Update the future sleep time. */ +void delay_sleep_by(uint32_t us) +{ + timestamp_t tmp = get_time(); + + tmp.val += us; + if (tmp.val > next_sleep_time.val) + next_sleep_time = tmp; +} + /* Custom idle task, executed when no tasks are ready to be scheduled. */ void __idle(void) { - int sleep_ok; + int sleep_ok, sleep_delay_passed, prev_ok = 0; while (1) { @@ -102,8 +135,26 @@ void __idle(void) /* Anyone still busy? */ sleep_ok = DEEP_SLEEP_ALLOWED; - /* We're allowed to sleep now, so set it up. */ - if (sleep_ok) + /* + * We'll always wait a little bit before sleeping no matter + * what. This is more likely to let any console output finish + * than calling clock_refresh_console_in_use(), because that + * function is called BEFORE waking the console task, not after + * it runs. We can't call cflush() here because that wakes a + * task to do it and so we're not idle any more. + */ + if (sleep_ok && !prev_ok) + delay_sleep_by(200 * MSEC); + + prev_ok = sleep_ok; + sleep_delay_passed = timestamp_expired(next_sleep_time, 0); + + /* If it hasn't yet been long enough, check again when it is */ + if (!sleep_delay_passed) + timer_arm(next_sleep_time, TASK_ID_IDLE); + + /* We're allowed to deep sleep, so set it up. */ + if (sleep_ok && sleep_delay_passed) if (idle_action == IDLE_DEEP_SLEEP) prepare_to_deep_sleep(); /* Normal sleep is not yet implemented */ diff --git a/chip/g/sps_tpm.c b/chip/g/sps_tpm.c index 9d266056e6..8af3765642 100644 --- a/chip/g/sps_tpm.c +++ b/chip/g/sps_tpm.c @@ -7,6 +7,7 @@ #include "console.h" #include "hooks.h" #include "sps.h" +#include "system.h" #include "tpm_registers.h" #include "util.h" @@ -123,6 +124,9 @@ static void init_new_cycle(void) sps_tpm_state = SPS_TPM_STATE_RECEIVING_HEADER; rx_fifo_base = sps_rx_fifo_wrptr(); sps_tx_status(TPM_STALL_ASSERT); + /* We're just waiting for a new command, so we could sleep. */ + delay_sleep_by(1 * SECOND); + enable_sleep(SLEEP_MASK_SPI); } /* Extract R/W bit, register addresss, and data count from 4-byte header */ @@ -139,7 +143,9 @@ static int header_says_to_read(uint8_t *data, uint32_t *reg, uint32_t *count) /* actual RX FIFO handler (runs in interrupt context) */ static void process_rx_data(uint8_t *data, size_t data_size) { - /* We're collecting incoming bytes ... */ + /* We're receiving some bytes, so don't sleep */ + disable_sleep(SLEEP_MASK_SPI); + if ((rxbuf_count + data_size) > RXBUF_MAX) { CPRINTS("TPM SPI input overflow: %d + %d > %d in state %d", rxbuf_count, data_size, RXBUF_MAX, sps_tpm_state); @@ -266,6 +272,9 @@ static void sps_tpm_disable(void) { sps_tpm_state = SPS_TPM_STATE_PONDERING; sps_unregister_rx_handler(); + /* We don't care anymore, so we can sleep whenever */ + delay_sleep_by(0); + enable_sleep(SLEEP_MASK_SPI); } static int command_sps_tpm(int argc, char **argv) diff --git a/include/system.h b/include/system.h index 67a867f39e..220e0790e7 100644 --- a/include/system.h +++ b/include/system.h @@ -369,6 +369,13 @@ static inline void disable_sleep(uint32_t mask) } /** + * Postpone sleeping for at least this long, regardless of sleep_mask. + * + * @param Amount of time to postpone sleeping + */ +void delay_sleep_by(uint32_t us); + +/** * Use hibernate module to set up an RTC interrupt at a given * time from now * |