summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/puff/board.c36
-rw-r--r--board/puff/gpio.inc2
-rw-r--r--power/cometlake-discrete.c28
-rw-r--r--power/cometlake-discrete.h13
4 files changed, 66 insertions, 13 deletions
diff --git a/board/puff/board.c b/board/puff/board.c
index 93cdc0f374..b3215f7855 100644
--- a/board/puff/board.c
+++ b/board/puff/board.c
@@ -395,23 +395,37 @@ const struct ina3221_t ina3221[] = {
};
const unsigned int ina3221_count = ARRAY_SIZE(ina3221);
+static void override_interrupt_priority(int irq, int priority)
+{
+ const uint32_t prio_shift = irq % 4 * 8 + 5;
+
+ CPU_NVIC_PRI(irq / 4) =
+ (CPU_NVIC_PRI(irq / 4) &
+ ~(0x7 << prio_shift)) |
+ (priority << prio_shift);
+}
+
static void board_init(void)
{
uint8_t *memmap_batt_flags;
- /* Increase priority of C10 gate interrupts to minimize latency.
+ /* Override some GPIO interrupt priorities.
*
- * We assume that GPIO_CPU_C10_GATE_L is on GPIO6.7, which is on
- * the WKINTH_1 IRQ.
+ * These interrupts are timing-critical for AP power sequencing, so we
+ * increase their NVIC priority from the default of 3. This affects
+ * whole MIWU groups of 8 GPIOs since they share an IRQ.
+ *
+ * Latency at the default priority level can be hundreds of
+ * microseconds while other equal-priority IRQs are serviced, so GPIOs
+ * requiring faster response must be higher priority.
*/
- const int c10_gpio_irq = NPCX_IRQ_WKINTH_1;
- const int c10_gpio_prio = 2;
- const uint32_t prio_shift = c10_gpio_irq % 4 * 8 + 5;
-
- CPU_NVIC_PRI(c10_gpio_irq / 4) =
- (CPU_NVIC_PRI(c10_gpio_irq / 4) &
- ~(0x7 << prio_shift)) |
- (c10_gpio_prio << prio_shift);
+ /* CPU_C10_GATE_L on GPIO6.7: must be ~instant for ~60us response. */
+ override_interrupt_priority(NPCX_IRQ_WKINTH_1, 1);
+ /*
+ * slp_s3_interrupt (GPIOA.5 on WKINTC_0) must respond within 200us
+ * (tPLT18); less critical than the C10 gate.
+ */
+ override_interrupt_priority(NPCX_IRQ_WKINTC_0, 2);
update_port_limits();
gpio_enable_interrupt(GPIO_BJ_ADP_PRESENT_L);
diff --git a/board/puff/gpio.inc b/board/puff/gpio.inc
index a0d3f044e8..9296805be5 100644
--- a/board/puff/gpio.inc
+++ b/board/puff/gpio.inc
@@ -31,7 +31,7 @@ GPIO_INT(SLP_S4_L, PIN(D, 4), GPIO_INT_BOTH, power_signal_interrupt)
GPIO_INT(PG_PP2500_DRAM_U_OD, PIN(2, 0), GPIO_INT_BOTH, power_signal_interrupt)
GPIO_INT(PG_PP1200_U_OD, PIN(2, 1), GPIO_INT_BOTH, power_signal_interrupt)
#ifndef CONFIG_HOSTCMD_ESPI_VW_SLP_S3
-GPIO_INT(SLP_S3_L, PIN(A, 5), GPIO_INT_BOTH, power_signal_interrupt)
+GPIO_INT(SLP_S3_L, PIN(A, 5), GPIO_INT_BOTH, slp_s3_interrupt)
#endif
GPIO_INT(PG_PP950_VCCIO_OD, PIN(1, 7), GPIO_INT_BOTH, power_signal_interrupt)
GPIO_INT(SLP_S0_L, PIN(D, 5), GPIO_INT_BOTH, power_signal_interrupt)
diff --git a/power/cometlake-discrete.c b/power/cometlake-discrete.c
index 5465920334..56465ef61f 100644
--- a/power/cometlake-discrete.c
+++ b/power/cometlake-discrete.c
@@ -159,6 +159,9 @@ BUILD_ASSERT(ARRAY_SIZE(power_signal_list) == POWER_SIGNAL_COUNT);
*
* This is a separate function so it can be reused when forcing shutdown due to
* power failure or other reasons.
+ *
+ * This function may be called from an ISR (slp_s3_interrupt) so must not
+ * assume that it's running in a regular task.
*/
static void shutdown_s0_rails(void)
{
@@ -172,7 +175,15 @@ static void shutdown_s0_rails(void)
gpio_set_level(GPIO_EC_PCH_SYS_PWROK, 0);
gpio_set_level(GPIO_EN_IMVP8_VR, 0);
gpio_set_level(GPIO_EN_S0_RAILS, 0);
- usleep(1); /* tPCH10: PCH_PWROK to VCCIO off >400 ns */
+ /*
+ * * tPCH10: PCH_PWROK to VCCIO off >400ns (but only on unexpected
+ * power-down)
+ * * tPLT18: SLP_S3_L to VCCIO disable <200us
+ *
+ * tPCH10 is only 7 CPU cycles at 16 MHz so we should satisfy that
+ * minimum time with no extra code, and sleeping is likely to cause
+ * a delay that exceeds tPLT18.
+ */
gpio_set_level(GPIO_EN_PP950_VCCIO, 0);
}
@@ -331,6 +342,10 @@ enum power_state power_handle_state(enum power_state state)
break;
case POWER_S0S3:
+ /*
+ * Handled in the slp_s3_interrupt fast path, but also run
+ * here in case we miss the interrupt somehow.
+ */
shutdown_s0_rails();
break;
@@ -388,3 +403,14 @@ void c10_gate_interrupt(enum gpio_signal signal)
return power_signal_interrupt(signal);
}
+
+void slp_s3_interrupt(enum gpio_signal signal)
+{
+ if (!gpio_get_level(GPIO_SLP_S3_L)
+ && chipset_in_state(CHIPSET_STATE_ON)) {
+ /* Falling edge on SLP_S3_L means dropping to S3 from S0 */
+ shutdown_s0_rails();
+ }
+
+ return power_signal_interrupt(signal);
+}
diff --git a/power/cometlake-discrete.h b/power/cometlake-discrete.h
index e69deb195c..6f5370beee 100644
--- a/power/cometlake-discrete.h
+++ b/power/cometlake-discrete.h
@@ -101,6 +101,9 @@ enum power_signal {
* Board-specific enable for any additional rails in S0.
*
* Input 0 to turn off, 1 to turn on.
+ *
+ * This function may be called from interrupts so must not assume it's running
+ * in a task.
*/
void board_enable_s0_rails(int enable);
@@ -134,4 +137,14 @@ int board_is_c10_gate_enabled(void);
*/
void c10_gate_interrupt(enum gpio_signal signal);
+/*
+ * Special interrupt for SLP_S3_L handling.
+ *
+ * The time window in which to turn off some rails when dropping to S3 is
+ * ~200us, and using the regular power state machine path tends to have latency
+ * >1ms. This ISR short-circuits the relevant signals in a fast path before
+ * scheduling a state machine update to ensure sufficiently low latency.
+ */
+void slp_s3_interrupt(enum gpio_signal signal);
+
#endif /* __CROS_EC_COMETLAKE_DISCRETE_H */