summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2017-08-23 14:40:29 -0700
committerchrome-bot <chrome-bot@chromium.org>2017-08-30 16:13:16 -0700
commit17150b05ff20069198f009a7d48282013dcd02cf (patch)
tree19bf3444d1c2b3e258ae6e7b6993e71bf57a6279
parent8202ddaa95b853ae7a4b6893e06c85df7b2c947f (diff)
downloadchrome-ec-17150b05ff20069198f009a7d48282013dcd02cf.tar.gz
cr50: Split AP state machine into its own file
The device state machines aren't quite similar enough to use common code. Split the AP state machine out, the way we split out the EC state machine in the previous CL. BUG=b:35587387 BRANCH=cr50 TEST=manual, with Cr50 strapped (or hard-coded) not to use platform reset and not to use TPM reset to detect the AP: Pull CCD_MODE_L low, so Cr50 detects/enables CCD Pull AP_DETECT high. Pull INT_AP_L low (with resistor). Pull AP_DETECT low --> See 'AP off' message gpioget --> INT_AP_L=0 ccd --> AP UART disabled Pull AP_DETECT high --> See 'AP on' message gpioget --> INT_AP_L=1 ccd --> AP UART RX+TX Pull AP_DETECT low for <1 sec then back high (don't see AP off/on message) gpioget --> INT_AP_L=1 ccd --> AP UART RX+TX Reboot with AP_DETECT still low -> AP off at 1 second Reboot with AP_DETECT still low and then assert AP_DETECT within a second -> AP on immediately Repeat with Cr50 strapped/hard coded to use platform reset, but using TPM_RST_L instead of AP_DETECT. Note that this will also show TPM reset debugging output when TPM_RST_L is asserted. Change-Id: Ief9e4e5f2585ff925de1595cc8fbd5306c94a806 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/634248 Reviewed-by: Mary Ruthven <mruthven@chromium.org>
-rw-r--r--board/cr50/ap_state.c211
-rw-r--r--board/cr50/board.c164
-rw-r--r--board/cr50/board.h7
-rw-r--r--board/cr50/build.mk2
-rw-r--r--board/cr50/gpio.inc2
-rw-r--r--board/cr50/rdd.c1
6 files changed, 237 insertions, 150 deletions
diff --git a/board/cr50/ap_state.c b/board/cr50/ap_state.c
new file mode 100644
index 0000000000..4e95d1c1c4
--- /dev/null
+++ b/board/cr50/ap_state.c
@@ -0,0 +1,211 @@
+/* Copyright 2017 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.
+ *
+ * AP state machine
+ */
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "system.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+static enum device_state state = DEVICE_STATE_INIT;
+
+void print_ap_state(void)
+{
+ ccprintf("AP: %s\n", device_state_name(state));
+}
+
+int ap_is_on(void)
+{
+ /* Debouncing and on are both still on */
+ return (state == DEVICE_STATE_DEBOUNCING || state == DEVICE_STATE_ON);
+}
+
+/**
+ * Set the AP state.
+ *
+ * Done as a function to make it easier to debug state transitions. Note that
+ * this ONLY sets the state (and possibly prints debug info), and doesn't do
+ * all the additional transition work that set_ap_on(), etc. do.
+ *
+ * @param new_state State to set.
+ */
+static void set_state(enum device_state new_state)
+{
+#ifdef CR50_DEBUG_AP_STATE
+ /* Print all state transitions. May spam the console. */
+ if (state != new_state)
+ CPRINTS("AP %s -> %s",
+ device_state_name(state), device_state_name(new_state));
+#endif
+ state = new_state;
+}
+
+/**
+ * Set AP to the off state
+ */
+static void set_ap_off(void)
+{
+ CPRINTS("AP off");
+ set_state(DEVICE_STATE_OFF);
+
+ /*
+ * If TPM is configured then the INT_AP_L signal is used as a low pulse
+ * trigger to sync transactions with the host. By default Cr50 is
+ * driving this line high, but when the AP powers off, the 1.8V rail
+ * that it's pulled up to will be off and cause excessive power to be
+ * consumed by the Cr50. Set INT_AP_L as an input while the AP is
+ * powered off.
+ */
+ gpio_set_flags(GPIO_INT_AP_L, GPIO_INPUT);
+
+ disable_ccd_uart(UART_AP);
+
+ /*
+ * We don't enable deep sleep on ARM devices yet, as its processing
+ * there will require more support on the AP side than is available
+ * now.
+ *
+ * Note: Presence of platform reset is a poor indicator of deep sleep
+ * support. It happens to be correlated with ARM vs x86 at present.
+ */
+ if (board_deep_sleep_allowed())
+ enable_deep_sleep();
+}
+
+/**
+ * Move the AP to the ON state
+ */
+static void set_ap_on(void)
+{
+ CPRINTS("AP on");
+ set_state(DEVICE_STATE_ON);
+
+ /*
+ * AP is powering up, set the host sync signal to output and set it
+ * high which is the default level.
+ */
+ gpio_set_flags(GPIO_INT_AP_L, GPIO_OUT_HIGH);
+ gpio_set_level(GPIO_INT_AP_L, 1);
+
+ enable_ccd_uart(UART_AP);
+
+ if (board_deep_sleep_allowed())
+ disable_deep_sleep();
+}
+
+/**
+ * Handle moving the AP to the OFF state from a deferred interrupt handler.
+ *
+ * Needs to make additional state checks to avoid double-on in case ap_detect()
+ * has run in the meantime.
+ */
+void set_ap_on_deferred(void)
+{
+ /* If we were debouncing ON->OFF, cancel it because we're still on */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_ON);
+
+ /* If AP isn't already on, make it so */
+ if (state != DEVICE_STATE_ON)
+ set_ap_on();
+}
+DECLARE_DEFERRED(set_ap_on_deferred);
+
+/**
+ * Interrupt handler for AP detect asserted
+ */
+void ap_detect_asserted(enum gpio_signal signal)
+{
+ gpio_disable_interrupt(GPIO_DETECT_AP);
+ hook_call_deferred(&set_ap_on_deferred_data, 0);
+}
+
+/**
+ * Detect state machine
+ */
+static void ap_detect(void)
+{
+ int detect;
+
+ if (board_detect_ap_with_tpm_rst()) {
+ /* AP is detected if platform reset is deasserted */
+ detect = gpio_get_level(GPIO_TPM_RST_L);
+ } else {
+ /* Disable interrupts if we had them on for debouncing */
+ gpio_disable_interrupt(GPIO_DETECT_AP);
+
+ /* AP is detected if it's driving its UART TX signal */
+ detect = gpio_get_level(GPIO_DETECT_AP);
+ }
+
+ /* Handle detecting device */
+ if (detect) {
+ /*
+ * If we were debouncing ON->OFF, cancel debouncing and go back
+ * to the ON state.
+ */
+ if (state == DEVICE_STATE_DEBOUNCING)
+ set_state(DEVICE_STATE_ON);
+
+ /* If we're already ON, done */
+ if (state == DEVICE_STATE_ON)
+ return;
+
+ if (board_detect_ap_with_tpm_rst()) {
+ /*
+ * The platform reset handler has not run yet;
+ * otherwise, it would have already turned the AP on
+ * and we wouldn't get here.
+ *
+ * This can happen if the hook task calls ap_detect()
+ * before deferred_tpm_rst_isr(). In this case, the
+ * deferred handler is already pending so calling the
+ * ISR has no effect.
+ *
+ * But we may actually have missed the edge. In that
+ * case, calling the ISR makes sure we don't miss the
+ * reset. It will call set_ap_on_deferred() to move
+ * the AP to the ON state.
+ */
+ CPRINTS("AP detect calling tpm_rst_deasserted()");
+ tpm_rst_deasserted(GPIO_TPM_RST_L);
+ } else {
+ /* We're responsible for setting the AP state to ON */
+ set_ap_on();
+ }
+
+ return;
+ }
+
+ /* AP wasn't detected. If we're already off, done. */
+ if (state == DEVICE_STATE_OFF)
+ return;
+
+ /* If we were debouncing, we're now sure we're off */
+ if (state == DEVICE_STATE_DEBOUNCING ||
+ state == DEVICE_STATE_INIT_DEBOUNCING) {
+ set_ap_off();
+ return;
+ }
+
+ /*
+ * Otherwise, we were on before and haven't detected the AP. But we
+ * don't know if that's because the AP is actually off, or because the
+ * AP UART is sending a 0-bit or temporarily asserting platform reset.
+ * So start debouncing.
+ */
+ if (state == DEVICE_STATE_INIT)
+ set_state(DEVICE_STATE_INIT_DEBOUNCING);
+ else
+ set_state(DEVICE_STATE_DEBOUNCING);
+
+ /* If we're using AP UART RX for detect, enable its interrupt */
+ if (!board_detect_ap_with_tpm_rst())
+ gpio_enable_interrupt(GPIO_DETECT_AP);
+}
+DECLARE_HOOK(HOOK_SECOND, ap_detect, HOOK_PRIO_DEFAULT);
diff --git a/board/cr50/board.c b/board/cr50/board.c
index e72a88a8c1..27ec67fbd2 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -546,11 +546,6 @@ void board_configure_deep_sleep_wakepins(void)
static void deferred_tpm_rst_isr(void);
DECLARE_DEFERRED(deferred_tpm_rst_isr);
-int ap_is_on(void)
-{
- return device_get_state(DEVICE_AP) == DEVICE_STATE_ON;
-}
-
static void configure_board_specific_gpios(void)
{
/* Add a pullup to sys_rst_l */
@@ -569,15 +564,9 @@ static void configure_board_specific_gpios(void)
* plt_rst_l is on diom3, and sys_rst_l is on diom0.
*/
if (board_use_plt_rst()) {
- /* Use plt_rst_l for device detect purposes. */
- device_states[DEVICE_AP].detect = GPIO_TPM_RST_L;
-
/* Use plt_rst_l as the tpm reset signal. */
GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM3_SEL);
- /* No interrupts from AP UART TX state change are needed. */
- gpio_disable_interrupt(GPIO_DETECT_AP);
-
/* Enable the input */
GWRITE_FIELD(PINMUX, DIOM3_CTL, IE, 1);
@@ -597,9 +586,6 @@ static void configure_board_specific_gpios(void)
/* Enable powerdown exit on DIOM3 */
GWRITE_FIELD(PINMUX, EXITEN0, DIOM3, 1);
} else {
- /* Use AP UART TX for device detect purposes. */
- device_states[DEVICE_AP].detect = GPIO_DETECT_AP;
-
/* Use sys_rst_l as the tpm reset signal. */
GWRITE(PINMUX, GPIO1_GPIO0_SEL, GC_PINMUX_DIOM0_SEL);
/* Enable the input */
@@ -731,7 +717,6 @@ static void board_init(void)
/* Enable GPIO interrupts for device state machines */
gpio_enable_interrupt(GPIO_TPM_RST_L);
- gpio_enable_interrupt(GPIO_DETECT_AP);
gpio_enable_interrupt(GPIO_DETECT_SERVO);
/*
@@ -826,18 +811,21 @@ int flash_regions_to_enable(struct g_flash_region *regions,
return 3;
}
+/**
+ * Deferred TPM reset interrupt handling
+ *
+ * This is always called from the HOOK task.
+ */
static void deferred_tpm_rst_isr(void)
{
CPRINTS("%s", __func__);
/*
- * If the board has platform reset, move the AP into DEVICE_STATE_ON.
- * If we're transitioning the AP from OFF or UNKNOWN, also trigger the
- * resume handler.
+ * If the board uses TPM reset to detect the AP, connect AP. This is
+ * the only way those boards connect; they don't examine AP UART TX.
*/
- if (board_detect_ap_with_tpm_rst() &&
- device_state_changed(DEVICE_AP, DEVICE_STATE_ON))
- hook_notify(HOOK_CHIPSET_RESUME);
+ if (board_detect_ap_with_tpm_rst())
+ set_ap_on_deferred();
/*
* If no reboot request is posted, OR if the other RW's header is not
@@ -863,7 +851,12 @@ static void deferred_tpm_rst_isr(void)
system_reset(SYSTEM_RESET_MANUALLY_TRIGGERED | SYSTEM_RESET_HARD);
}
-/* This is the interrupt handler to react to TPM_RST_L */
+/**
+ * Handle TPM_RST_L deasserting
+ *
+ * This can also be called explicitly from AP detection, if it thinks the
+ * interrupt handler missed the rising edge.
+ */
void tpm_rst_deasserted(enum gpio_signal signal)
{
hook_call_deferred(&deferred_tpm_rst_isr_data, 0);
@@ -1104,22 +1097,6 @@ static void servo_deferred(void)
}
DECLARE_DEFERRED(servo_deferred);
-/**
- * Deferred handler for debouncing AP presence detect falling.
- *
- * This is called if DETECT_AP has been low long enough.
- */
-static void ap_deferred(void)
-{
- /*
- * If the AP was still in DEVICE_STATE_UNKNOWN, move it to
- * DEVICE_STATE_OFF and trigger the shutdown hook.
- */
- if (device_powered_off(DEVICE_AP))
- hook_notify(HOOK_CHIPSET_SHUTDOWN);
-}
-DECLARE_DEFERRED(ap_deferred);
-
/* Note: this must EXACTLY match enum device_type! */
struct device_config device_states[] = {
[DEVICE_SERVO] = {
@@ -1128,11 +1105,6 @@ struct device_config device_states[] = {
.detect = GPIO_DETECT_SERVO,
.name = "Servo"
},
- [DEVICE_AP] = {
- .state = DEVICE_STATE_UNKNOWN,
- .deferred = &ap_deferred_data,
- .name = "AP"
- },
};
BUILD_ASSERT(ARRAY_SIZE(device_states) == DEVICE_COUNT);
@@ -1169,47 +1141,9 @@ static void servo_attached(void)
*/
void device_state_on(enum gpio_signal signal)
{
- /*
- * On boards with plt_rst_l the ap state is detected with tpm_rst_l.
- * Make sure we don't disable the tpm reset interrupt
- * tpm_rst_deasserted(), which is what actually handles that interrupt.
- */
- if (signal != GPIO_TPM_RST_L)
- gpio_disable_interrupt(signal);
+ gpio_disable_interrupt(signal);
switch (signal) {
- case GPIO_TPM_RST_L:
- /*
- * Boards using tpm_rst_l have no AP state interrupt that will
- * trigger device_state_on, so this will only get called when
- * we poll the AP state and see that the detect signal is high,
- * but the device state is not 'on'.
- *
- * Boards using tpm_rst_l to detect the AP state use the tpm
- * reset handler to set the AP state to 'on'. If we managed to
- * get to this point, the tpm reset handler has not run yet.
- * This should only happen if there is a race between the board
- * state polling and a scheduled call to
- * deferred_tpm_rst_isr_data, but it may be because we missed
- * the rising edge. Notify the handler again just in case we
- * missed the edge to make sure we reset the tpm and update the
- * state. If there is already a pending call, then this call
- * won't affect it, because subsequent calls to to
- * hook_call_deferred just change the delay for the call, and
- * we are setting the delay to asap.
- */
- CPRINTS("%s: tpm_rst_isr hasn't set the AP state to 'on'.",
- __func__);
- hook_call_deferred(&deferred_tpm_rst_isr_data, 0);
- break;
- case GPIO_DETECT_AP:
- /*
- * Turn the AP device on. If it was previously unknown or
- * off, notify the resume hook.
- */
- if (device_state_changed(DEVICE_AP, DEVICE_STATE_ON))
- hook_notify(HOOK_CHIPSET_RESUME);
- break;
case GPIO_DETECT_SERVO:
servo_attached();
break;
@@ -1274,19 +1208,8 @@ void board_update_device_state(enum device_type device)
*/
device_set_state(device, DEVICE_STATE_UNKNOWN);
- /*
- * The possible devices at this point are AP, EC, and SERVO.
- *
- * If the device is AP, it may use the TPM interrupt line as
- * presence detect. In that case, we don't want to mess with
- * the interrupt enable; it should already be enabled.
- *
- * EC and SERVO use UART lines muxed to GPIOs for their detect
- * signals; we own those GPIOs, so need to enable their
- * interrupts explicitly.
- */
- if ((device != DEVICE_AP) || !board_detect_ap_with_tpm_rst())
- gpio_enable_interrupt(device_states[device].detect);
+ /* Enable servo detect interrupt */
+ gpio_enable_interrupt(device_states[device].detect);
/*
* The signal is low now, but this could be just a (EC, AP, or
@@ -1303,57 +1226,6 @@ void board_update_device_state(enum device_type device)
}
}
-/**
- * AP shutdown handler
- *
- * This is triggered by the deferred debounce handler for AP_DETECT when we're
- * sure the AP has actually shut down.
- */
-static void ap_shutdown(void)
-{
- /*
- * If I2C TPM is configured then the INT_AP_L signal is used as
- * a low pulse trigger to sync I2C transactions with the
- * host. By default Cr50 is driving this line high, but when the
- * AP powers off, the 1.8V rail that it's pulled up to will be
- * off and cause exessive power to be consumed by the Cr50. Set
- * INT_AP_L as an input while the AP is powered off.
- */
- gpio_set_flags(GPIO_INT_AP_L, GPIO_INPUT);
-
- disable_ccd_uart(UART_AP);
-
- /*
- * We don't enable deep sleep on ARM devices yet, as its processing
- * there will require more support on the AP side than is available
- * now.
- */
- if (board_deep_sleep_allowed())
- enable_deep_sleep();
-}
-DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, ap_shutdown, HOOK_PRIO_DEFAULT);
-
-/**
- * AP resume handler
- *
- * This is triggered by AP_DETECT interrupt handler (which may be a GPIO
- * attached to the UART RX line from the AP, or the TPM interrupt signal).
- */
-static void ap_resume(void)
-{
- /*
- * AP is powering up, set the I2C host sync signal to output and set
- * it high which is the default level.
- */
- gpio_set_flags(GPIO_INT_AP_L, GPIO_OUT_HIGH);
- gpio_set_level(GPIO_INT_AP_L, 1);
-
- enable_ccd_uart(UART_AP);
-
- disable_deep_sleep();
-}
-DECLARE_HOOK(HOOK_CHIPSET_RESUME, ap_resume, HOOK_PRIO_DEFAULT);
-
/*
* This function duplicates some of the functionality in chip/g/gpio.c in order
* to configure a given strap pin to be either a low gpio output, a gpio input
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 0f59e48d56..6a350d0e28 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -173,8 +173,7 @@ enum usb_strings {
/* Device indexes for devices that require debouncing */
enum device_type {
- DEVICE_AP = 0,
- DEVICE_SERVO,
+ DEVICE_SERVO = 0,
DEVICE_COUNT
};
@@ -246,6 +245,7 @@ enum nvmem_vars {
void board_configure_deep_sleep_wakepins(void);
/* Interrupt handler */
void tpm_rst_deasserted(enum gpio_signal signal);
+void ap_detect_asserted(enum gpio_signal signal);
void ec_detect_asserted(enum gpio_signal signal);
void device_state_on(enum gpio_signal signal);
void post_reboot_request(void);
@@ -281,6 +281,7 @@ int board_is_first_factory_boot(void);
void enable_ccd_uart(int uart);
void disable_ccd_uart(int uart);
+void print_ap_state(void);
void print_ec_state(void);
int ap_is_on(void);
@@ -288,6 +289,8 @@ int ec_is_on(void);
int rdd_is_connected(void);
int servo_is_connected(void);
+void set_ap_on_deferred(void);
+
/* Returns True if chip is brought up in a factory test harness. */
int chip_factory_mode(void);
diff --git a/board/cr50/build.mk b/board/cr50/build.mk
index dd99f31380..68bf4a0d03 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -29,7 +29,7 @@ dirs-y += chip/$(CHIP)/dcrypto
dirs-y += $(BDIR)/tpm2
# Objects that we need to build
-board-y = board.o ec_state.o
+board-y = board.o ap_state.o ec_state.o
board-${CONFIG_RDD} += rdd.o
board-${CONFIG_USB_SPI} += usb_spi.o
board-${CONFIG_USB_I2C} += usb_i2c.o
diff --git a/board/cr50/gpio.inc b/board/cr50/gpio.inc
index 0bd90555af..dcf5b38561 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -56,7 +56,7 @@
* system resets.
*/
GPIO_INT(TPM_RST_L, PIN(1, 0), GPIO_INT_RISING, tpm_rst_deasserted)
-GPIO_INT(DETECT_AP, PIN(1, 1), GPIO_INT_HIGH, device_state_on)
+GPIO_INT(DETECT_AP, PIN(1, 1), GPIO_INT_HIGH, ap_detect_asserted)
GPIO_INT(DETECT_EC, PIN(1, 2), GPIO_INT_HIGH, ec_detect_asserted)
/*
* DETECT_SERVO and EC_TX_CR50_RX pins must NOT be changed without also changing
diff --git a/board/cr50/rdd.c b/board/cr50/rdd.c
index a0ad76cb22..03b1785d90 100644
--- a/board/cr50/rdd.c
+++ b/board/cr50/rdd.c
@@ -185,6 +185,7 @@ static int command_ccd(int argc, char **argv)
return EC_ERROR_PARAM1;
}
+ print_ap_state();
print_ec_state();
print_rdd_state();