summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2018-01-24 10:46:27 -0800
committerChromeOS Commit Bot <chromeos-commit-bot@chromium.org>2018-02-03 18:36:14 +0000
commitd798dd31813d978a9f26258b21c623945e217940 (patch)
tree1001e443101e8611ca64f072ad339237ee05aa77
parent0b84f505b6b3269b57c728c3fc04616307e4319a (diff)
downloadchrome-ec-d798dd31813d978a9f26258b21c623945e217940.tar.gz
cr50: create ap_uart state machine
This change creates a state machine to handle ap uart detection. It removes all of the ap_uart stuff from ap_state.c and moves it to ap_uart_state.c. All boards will now use ap_uart to enable/disable ap uart and tpm_rst_l to detect the ap state. Separate ap uart detection from ap detection, so we can disable the ap uart without enabling deep sleep. If the ap is in S3 on ARM devices, Cr50 wont be in deep sleep, but the AP UART RX signal wont be pulled up. In this case we need cr50 ap rx to be disabled and deep sleep to be disabled. BUG=b:35647982 BRANCH=cr50 TEST=run firmware_Cr50DeviceState on scalet and electro Change-Id: I81336a9e232df8d44b325eef59327a1c06a80cba Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/884307 Reviewed-by: Randall Spangler <rspangler@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> (cherry picked from commit 4d3c8c1776f4055d999deea6593f499b588430f2) Reviewed-on: https://chromium-review.googlesource.com/900557 Commit-Queue: Vadim Bendebury <vbendeb@chromium.org> Tested-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--board/cr50/ap_state.c102
-rw-r--r--board/cr50/ap_uart_state.c133
-rw-r--r--board/cr50/board.c33
-rw-r--r--board/cr50/board.h6
-rw-r--r--board/cr50/build.mk7
-rw-r--r--board/cr50/gpio.inc1
-rw-r--r--board/cr50/rdd.c5
7 files changed, 172 insertions, 115 deletions
diff --git a/board/cr50/ap_state.c b/board/cr50/ap_state.c
index 5bf764ec7c..d4f549940a 100644
--- a/board/cr50/ap_state.c
+++ b/board/cr50/ap_state.c
@@ -81,7 +81,7 @@ static void set_ap_off(void)
/**
* Move the AP to the ON state
*/
-static void set_ap_on(void)
+void set_ap_on(void)
{
CPRINTS("AP on");
set_state(DEVICE_STATE_ON);
@@ -100,98 +100,43 @@ static void set_ap_on(void)
}
/**
- * 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 (gpio_get_level(GPIO_TPM_RST_L)) {
/*
+ * It is important to check if the AP is already 'on' here, so
+ * we don't call tpm_rst_deasserted() when the AP is already on.
+ *
* 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 AP is already on, nothing needs to be 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;
- } else if (!board_detect_ap_with_tpm_rst()) {
/*
- * If the signal is low, cr50 will enter the debouncing state or
- * off. Either way, cr50 is waiting for the signal to go high.
- * Reenable the interrupt, so cr50 can immediately detect it
- * instead of relying on the 1s polling.
+ * 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() to move the AP to the ON state.
*/
- gpio_enable_interrupt(GPIO_DETECT_AP);
+ CPRINTS("AP detect calling tpm_rst_deasserted()");
+ tpm_rst_deasserted(GPIO_TPM_RST_L);
+ return;
}
- /* AP wasn't detected. If we're already off, done. */
+ /* TPM_RST_L is asserted. If we're already off, done. */
if (state == DEVICE_STATE_OFF)
return;
@@ -203,10 +148,9 @@ static void ap_detect(void)
}
/*
- * 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.
+ * Otherwise, we were on before and haven't detected the AP off. We
+ * don't know if thats because the AP is actually off, or because the
+ * TPM_RST_L signal is being pulsed for a short reset. Start debouncing.
*/
if (state == DEVICE_STATE_INIT)
set_state(DEVICE_STATE_INIT_DEBOUNCING);
diff --git a/board/cr50/ap_uart_state.c b/board/cr50/ap_uart_state.c
new file mode 100644
index 0000000000..42e2feeab4
--- /dev/null
+++ b/board/cr50/ap_uart_state.c
@@ -0,0 +1,133 @@
+/* Copyright 2018 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 UART state machine
+ */
+#include "ccd_config.h"
+#include "common.h"
+#include "console.h"
+#include "gpio.h"
+#include "hooks.h"
+#include "uart_bitbang.h"
+#include "uartn.h"
+
+#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
+
+static enum device_state state = DEVICE_STATE_INIT;
+
+void print_ap_uart_state(void)
+{
+ ccprintf("AP UART: %s\n", device_state_name(state));
+}
+
+int ap_uart_is_on(void)
+{
+ /* Debouncing and on are both still on */
+ return (state == DEVICE_STATE_DEBOUNCING || state == DEVICE_STATE_ON);
+}
+
+/**
+ * Set the AP UART 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_uart_on(), etc. do.
+ *
+ * @param new_state State to set.
+ */
+static void set_state(enum device_state new_state)
+{
+#ifdef CR50_DEBUG_AP_UART_STATE
+ /* Print all state transitions. May spam the console. */
+ if (state != new_state)
+ CPRINTS("AP UART %s -> %s",
+ device_state_name(state), device_state_name(new_state));
+#endif
+ state = new_state;
+}
+
+/* Move the AP UART to the OFF state. */
+static void set_ap_uart_off(void)
+{
+ CPRINTS("AP UART off");
+ set_state(DEVICE_STATE_OFF);
+
+ ccd_update_state();
+}
+
+/**
+ * Move the AP UART to the ON state.
+ *
+ * This can be deferred from the interrupt handler, or called from the state
+ * machine which also runs in HOOK task, so it needs to check the current state
+ * to determine whether we're already on.
+ */
+static void set_ap_uart_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 we're already on, done */
+ if (state == DEVICE_STATE_ON)
+ return;
+
+ /* We were previously off */
+ CPRINTS("AP UART on");
+ set_state(DEVICE_STATE_ON);
+
+ ccd_update_state();
+}
+DECLARE_DEFERRED(set_ap_uart_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_uart_on_deferred_data, 0);
+}
+
+/**
+ * Detect state machine
+ */
+static void ap_uart_detect(void)
+{
+ /* Disable interrupts if we had them on for debouncing */
+ gpio_disable_interrupt(GPIO_DETECT_AP);
+
+ /* If the AP UART signal is high, make sure it's on */
+ if (gpio_get_level(GPIO_DETECT_AP)) {
+ hook_call_deferred(&set_ap_uart_on_deferred_data, 0);
+ return;
+ }
+
+ /*
+ * Make sure the interrupt is enabled. We will need to detect the on
+ * transition if we enter the off or debouncing state
+ */
+ gpio_enable_interrupt(GPIO_DETECT_AP);
+
+ /* AP UART 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_uart_off();
+ return;
+ }
+
+ /*
+ * Otherwise, we were on or initializing, and we're not sure if the AP
+ * UART is actually off or just sending a 0-bit. So start debouncing.
+ */
+ if (state == DEVICE_STATE_INIT)
+ set_state(DEVICE_STATE_INIT_DEBOUNCING);
+ else
+ set_state(DEVICE_STATE_DEBOUNCING);
+}
+DECLARE_HOOK(HOOK_SECOND, ap_uart_detect, HOOK_PRIO_DEFAULT);
diff --git a/board/cr50/board.c b/board/cr50/board.c
index 04b64d1dac..28f6ceca20 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -144,15 +144,6 @@ int board_deep_sleep_allowed(void)
return !(board_properties & BOARD_DEEP_SLEEP_DISABLED);
}
-/*
- * If the board doesn't use CR50_RX_AP_TX to determine AP state, it uses
- * TPM_RST_L.
- */
-int board_detect_ap_with_tpm_rst(void)
-{
- return !(board_properties & BOARD_DETECT_AP_WITH_UART);
-}
-
int board_rst_pullup_needed(void)
{
return !!(board_properties & BOARD_NEEDS_SYS_RST_PULL_UP);
@@ -524,17 +515,6 @@ void board_configure_deep_sleep_wakepins(void)
/* enable powerdown exit */
GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1);
}
-
- if (!board_detect_ap_with_tpm_rst()) {
- /*
- * DIOA3 is GPIO_DETECT_AP which is used to detect if the AP
- * is in S0. If the AP is in s0, cr50 should not be in deep
- * sleep so wake up.
- */
- GWRITE_FIELD(PINMUX, EXITEDGE0, DIOA3, 0); /* level sensitive */
- GWRITE_FIELD(PINMUX, EXITINV0, DIOA3, 0); /* wake on high */
- GWRITE_FIELD(PINMUX, EXITEN0, DIOA3, 1);
- }
}
static void deferred_tpm_rst_isr(void);
@@ -592,12 +572,6 @@ static void configure_board_specific_gpios(void)
/* Enable powerdown exit on DIOM0 */
GWRITE_FIELD(PINMUX, EXITEN0, DIOM0, 1);
}
- if (!board_detect_ap_with_tpm_rst()) {
- /* Use AP UART TX as the DETECT AP signal. */
- GWRITE(PINMUX, GPIO1_GPIO1_SEL, GC_PINMUX_DIOA3_SEL);
- /* Enable the input */
- GWRITE_FIELD(PINMUX, DIOA3_CTL, IE, 1);
- }
}
void decrement_retry_counter(void)
@@ -824,11 +798,10 @@ static void deferred_tpm_rst_isr(void)
CPRINTS("%s", __func__);
/*
- * 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.
+ * TPM reset is used to detect the AP, connect AP. Let the AP state
+ * machine know the AP is on.
*/
- if (board_detect_ap_with_tpm_rst())
- set_ap_on_deferred();
+ set_ap_on();
/*
* If no reboot request is posted, OR if the other RW's header is not
diff --git a/board/cr50/board.h b/board/cr50/board.h
index e76caed5dc..180f0a8349 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -260,8 +260,6 @@ int board_rst_pullup_needed(void);
int board_tpm_uses_i2c(void);
int board_tpm_uses_spi(void);
int board_id_is_mismatched(void);
-/* Use TPM_RST_L to detect the AP state instead of the uart */
-int board_detect_ap_with_tpm_rst(void);
/* Allow for deep sleep to be enabled on AP shutdown */
int board_deep_sleep_allowed(void);
@@ -278,15 +276,17 @@ int usb_i2c_board_enable(void);
void usb_i2c_board_disable(void);
void print_ap_state(void);
+void print_ap_uart_state(void);
void print_ec_state(void);
void print_servo_state(void);
int ap_is_on(void);
+int ap_uart_is_on(void);
int ec_is_on(void);
int ec_is_rx_allowed(void);
int servo_is_connected(void);
-void set_ap_on_deferred(void);
+void set_ap_on(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 15e1f1f2a3..049e29eafc 100644
--- a/board/cr50/build.mk
+++ b/board/cr50/build.mk
@@ -29,7 +29,12 @@ dirs-y += chip/$(CHIP)/dcrypto
dirs-y += $(BDIR)/tpm2
# Objects that we need to build
-board-y = board.o ap_state.o ec_state.o power_button.o servo_state.o
+board-y = board.o
+board-y += ap_state.o
+board-y += ec_state.o
+board-y += power_button.o
+board-y += servo_state.o
+board-y += ap_uart_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 8288735a3f..8c698e549f 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -173,6 +173,7 @@ PINMUX(FUNC(UART2_RX), B6, DIO_INPUT) /* EC console */
* driving the cr50 uart TX at the same time as servo is driving those pins may
* damage both servo and cr50.
*/
+PINMUX(GPIO(DETECT_AP), A3, DIO_INPUT)
PINMUX(GPIO(DETECT_EC), B6, DIO_INPUT)
PINMUX(GPIO(EC_TX_CR50_RX), B6, DIO_INPUT)
PINMUX(GPIO(DETECT_SERVO), B5, DIO_INPUT)
diff --git a/board/cr50/rdd.c b/board/cr50/rdd.c
index b616299c89..d59ca7d803 100644
--- a/board/cr50/rdd.c
+++ b/board/cr50/rdd.c
@@ -94,7 +94,7 @@ void uartn_tx_connect(int uart)
if (!ccd_is_cap_enabled(CCD_CAP_GSC_TX_AP_RX))
return;
- if (!ap_is_on())
+ if (!ap_uart_is_on())
return;
uart_select_tx(UART_AP, GC_PINMUX_UART1_TX_SEL);
@@ -218,7 +218,7 @@ static void ccd_state_change_hook(void)
/* Start out by figuring what flags we might want enabled */
/* Enable EC/AP UART RX if that device is on */
- if (ap_is_on())
+ if (ap_uart_is_on())
flags_want |= CCD_ENABLE_UART_AP;
if (ec_is_rx_allowed())
flags_want |= CCD_ENABLE_UART_EC;
@@ -397,6 +397,7 @@ static void print_ccd_ports_blocked(void)
static int command_ccd_state(int argc, char **argv)
{
print_ap_state();
+ print_ap_uart_state();
print_ec_state();
print_rdd_state();
print_servo_state();