summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2016-03-30 17:45:12 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-04-03 10:27:39 -0700
commit9406cf384aba9554dd49eef17a3d4a4eb2947972 (patch)
treec6d209038d23287e03d96ef59cf81fd7c17b9296
parente79b32eb3d2f61a23c2c9ac75003315c964f91c9 (diff)
downloadchrome-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.c61
-rw-r--r--chip/g/sps_tpm.c11
-rw-r--r--include/system.h7
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
*