summaryrefslogtreecommitdiff
path: root/chip/g/idle.c
diff options
context:
space:
mode:
Diffstat (limited to 'chip/g/idle.c')
-rw-r--r--chip/g/idle.c61
1 files changed, 56 insertions, 5 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 */