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 4d3c8c1776)
Reviewed-on: https://chromium-review.googlesource.com/900557
Commit-Queue: Vadim Bendebury <vbendeb@chromium.org>
Tested-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
Mary Ruthven 2018-01-24 10:46:27 -08:00 committed by ChromeOS Commit Bot
parent 0b84f505b6
commit d798dd3181
7 changed files with 172 additions and 115 deletions

View File

@ -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);
@ -99,99 +99,44 @@ static void set_ap_on(void)
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 (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);

133
board/cr50/ap_uart_state.c Normal file
View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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();