cr50: monitor the state of Servo, the EC, and AP

There are a couple of issues that cr50 has when it cannot know the state
of servo, the EC, and the AP. This change adds support so we can detect
when the AP or EC has been powered on and when servo has been connected.
It uses the UART RX signals to monitor the power state of the AP and EC.
The TX signals are used to monitor the state of servo.

BUG=chrome-os-partner:52056,chrome-os-partner:52322
BRANCH=none
TEST=verify device states are correct when the AP and EC are powered on
	or off and when Servo is attached or detached

Change-Id: Id0a2281b65cb367ecc8d0ca2f9a576672318a5fb
Signed-off-by: Mary Ruthven <mruthven@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/344019
This commit is contained in:
Mary Ruthven
2016-05-10 12:50:31 -07:00
committed by chrome-bot
parent cebf8734d9
commit 1d7984ad20
9 changed files with 295 additions and 7 deletions

View File

@@ -6,6 +6,7 @@
#include "common.h"
#include "console.h"
#include "dcrypto/dcrypto.h"
#include "device_state.h"
#include "ec_version.h"
#include "flash_config.h"
#include "gpio.h"
@@ -15,6 +16,7 @@
#include "nvmem.h"
#include "task.h"
#include "trng.h"
#include "uartn.h"
#include "usb_descriptor.h"
#include "usb_hid.h"
#include "util.h"
@@ -218,3 +220,178 @@ void nvmem_compute_sha(uint8_t *p_buf, int num_bytes,
sha1_digest);
memcpy(p_sha, sha1_digest, sha_len);
}
static void device_state_changed(enum device_type device,
enum device_state state)
{
device_set_state(device, state);
/* Disable interrupts */
gpio_disable_interrupt(device_states[device].detect_on);
gpio_disable_interrupt(device_states[device].detect_off);
/*
* We've determined the device state, so cancel any deferred callbacks.
*/
hook_call_deferred(device_states[device].deferred, -1);
}
/*
* If both UARTs are enabled we cant tell anything about the
* state of servo, so disable servo detection.
*/
static int servo_state_unknown(void)
{
if (uartn_enabled(UART_AP) && uartn_enabled(UART_EC)) {
device_state_changed(DEVICE_SERVO_EC, DEVICE_STATE_UNKNOWN);
device_state_changed(DEVICE_SERVO_AP, DEVICE_STATE_UNKNOWN);
return 1;
}
return 0;
}
static void servo_deferred(void)
{
if (servo_state_unknown() ||
(device_get_state(DEVICE_SERVO_AP) == DEVICE_STATE_ON) ||
(device_get_state(DEVICE_SERVO_EC) == DEVICE_STATE_ON))
return;
device_state_changed(DEVICE_SERVO_AP, DEVICE_STATE_OFF);
device_state_changed(DEVICE_SERVO_EC, DEVICE_STATE_OFF);
}
DECLARE_DEFERRED(servo_deferred);
static void ap_deferred(void)
{
if (device_get_state(DEVICE_AP) == DEVICE_STATE_ON)
return;
device_state_changed(DEVICE_AP, DEVICE_STATE_OFF);
}
DECLARE_DEFERRED(ap_deferred);
static void ec_deferred(void)
{
if (device_get_state(DEVICE_EC) == DEVICE_STATE_ON)
return;
device_state_changed(DEVICE_EC, DEVICE_STATE_OFF);
}
DECLARE_DEFERRED(ec_deferred);
struct device_config device_states[] = {
[DEVICE_SERVO_AP] = {
.deferred = &servo_deferred_data,
.detect_on = GPIO_SERVO_UART1_ON,
.detect_off = GPIO_SERVO_UART1_OFF
},
[DEVICE_SERVO_EC] = {
.deferred = &servo_deferred_data,
.detect_on = GPIO_SERVO_UART2_ON,
.detect_off = GPIO_SERVO_UART2_OFF
},
[DEVICE_AP] = {
.deferred = &ap_deferred_data,
.detect_on = GPIO_AP_ON,
.detect_off = GPIO_AP_OFF
},
[DEVICE_EC] = {
.deferred = &ec_deferred_data,
.detect_on = GPIO_EC_ON,
.detect_off = GPIO_EC_OFF
},
};
BUILD_ASSERT(ARRAY_SIZE(device_states) == DEVICE_COUNT);
void device_state_on(enum gpio_signal signal)
{
switch (signal) {
case GPIO_AP_ON:
device_state_changed(DEVICE_AP, DEVICE_STATE_ON);
break;
case GPIO_EC_ON:
device_state_changed(DEVICE_EC, DEVICE_STATE_ON);
break;
default:
if (servo_state_unknown())
return;
device_state_changed(DEVICE_SERVO_EC, DEVICE_STATE_ON);
device_state_changed(DEVICE_SERVO_AP, DEVICE_STATE_ON);
}
}
void device_state_off(enum gpio_signal signal)
{
switch (signal) {
case GPIO_AP_OFF:
board_update_device_state(DEVICE_AP);
break;
case GPIO_EC_OFF:
board_update_device_state(DEVICE_EC);
break;
default:
board_update_device_state(DEVICE_SERVO_EC);
board_update_device_state(DEVICE_SERVO_AP);
}
}
void board_update_device_state(enum device_type device)
{
int state;
if (device == DEVICE_SERVO_EC || device == DEVICE_SERVO_AP) {
/*
* If either AP UART TX or EC UART TX are pulled high when
* cr50 uart is not enabled, then servo is attached
*/
state = (!uartn_enabled(UART_AP) &&
gpio_get_level(GPIO_SERVO_UART1_ON)) ||
(!uartn_enabled(UART_EC) &&
gpio_get_level(GPIO_SERVO_UART2_ON));
} else
state = gpio_get_level(device_states[device].detect_on);
/*
* If the device is currently on set its state immediately. If it
* thinks the device is powered off debounce the signal.
*/
if (state)
device_state_changed(device, DEVICE_STATE_ON);
else {
device_set_state(device, DEVICE_STATE_UNKNOWN);
gpio_enable_interrupt(device_states[device].detect_on);
/*
* Wait a bit. If cr50 detects this device is ever powered on
* during this time then the status wont be set to powered off.
*/
hook_call_deferred(device_states[device].deferred, 50);
}
}
static int command_devices(int argc, char **argv)
{
int state = device_get_state(DEVICE_AP);
ccprintf("AP %s\n",
state == DEVICE_STATE_ON ? "on" :
state == DEVICE_STATE_OFF ? "off" : "unknown");
state = device_get_state(DEVICE_SERVO_AP);
ccprintf("SERVO_AP %s\n",
state == DEVICE_STATE_ON ? "on" :
state == DEVICE_STATE_OFF ? "off" : "unknown");
state = device_get_state(DEVICE_SERVO_EC);
ccprintf("SERVO_EC %s\n",
state == DEVICE_STATE_ON ? "on" :
state == DEVICE_STATE_OFF ? "off" : "unknown");
state = device_get_state(DEVICE_EC);
ccprintf("EC %s\n",
state == DEVICE_STATE_ON ? "on" :
state == DEVICE_STATE_OFF ? "off" : "unknown");
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(devices, command_devices,
"",
"Get the AP, EC, and servo device states",
NULL);

View File

@@ -35,6 +35,9 @@
/* Go to sleep when nothing else is happening */
#define CONFIG_LOW_POWER_IDLE
/* Detect the states of other devices */
#define CONFIG_DEVICE_STATE
/* Enable debug cable detection */
#define CONFIG_RDD
@@ -100,8 +103,20 @@ enum usb_strings {
USB_STR_COUNT
};
/* Device indexes */
enum device_type {
DEVICE_SERVO_AP = 0,
DEVICE_SERVO_EC,
DEVICE_AP,
DEVICE_EC,
DEVICE_COUNT
};
/* Interrupt handler */
void sys_rst_asserted(enum gpio_signal signal);
void device_state_on(enum gpio_signal signal);
void device_state_off(enum gpio_signal signal);
#endif /* !__ASSEMBLER__ */

View File

@@ -14,8 +14,16 @@
* TODO: Remove this internal pullup at some point. It's only here so that
* boards that don't have an external pullup don't trigger due to noise.
*/
GPIO_INT(SYS_RST_L_IN, PIN(1, 0), GPIO_INT_FALLING | GPIO_PULL_UP,
GPIO_INT(SYS_RST_L_IN, PIN(1, 0), GPIO_INT_FALLING | GPIO_PULL_UP,
sys_rst_asserted)
GPIO_INT(AP_ON, PIN(1, 1), GPIO_INT_RISING, device_state_on)
GPIO_INT(EC_ON, PIN(1, 2), GPIO_INT_RISING, device_state_on)
GPIO_INT(SERVO_UART1_ON, PIN(1, 3), GPIO_INT_RISING, device_state_on)
GPIO_INT(SERVO_UART2_ON, PIN(1, 4), GPIO_INT_RISING, device_state_on)
GPIO_INT(AP_OFF, PIN(1, 5), GPIO_INT_FALLING, device_state_off)
GPIO_INT(EC_OFF, PIN(1, 6), GPIO_INT_FALLING, device_state_off)
GPIO_INT(SERVO_UART1_OFF, PIN(1, 7), GPIO_INT_FALLING, device_state_off)
GPIO_INT(SERVO_UART2_OFF, PIN(1, 8), GPIO_INT_FALLING, device_state_off)
/* Pull this low to interrupt the AP */
GPIO(INT_AP_L, PIN(0, 0), GPIO_INPUT)
@@ -74,6 +82,15 @@ PINMUX(FUNC(UART0_RX), A1, DIO_INPUT | DIO_WAKE_LOW)
PINMUX(FUNC(UART1_RX), A3, DIO_INPUT) /* AP console */
PINMUX(FUNC(UART2_RX), B6, DIO_INPUT) /* EC console */
PINMUX(GPIO(AP_ON), A3, DIO_INPUT)
PINMUX(GPIO(AP_OFF), A3, DIO_INPUT)
PINMUX(GPIO(EC_ON), B6, DIO_INPUT)
PINMUX(GPIO(EC_OFF), B6, DIO_INPUT)
PINMUX(GPIO(SERVO_UART1_ON), A7, DIO_INPUT)
PINMUX(GPIO(SERVO_UART1_OFF), A7, DIO_INPUT)
PINMUX(GPIO(SERVO_UART2_ON), B5, DIO_INPUT)
PINMUX(GPIO(SERVO_UART2_OFF), B5, DIO_INPUT)
/* I2C pins are bi-directional */
PINMUX(FUNC(I2C0_SCL), B0, DIO_INPUT)
PINMUX(FUNC(I2C0_SDA), B1, DIO_INPUT)

View File

@@ -7,8 +7,17 @@
#include "gpio.h"
#include "rdd.h"
#include "registers.h"
#include "uartn.h"
#include "usb_api.h"
/* If the UART TX is enabled the pinmux select will have a non-zero value */
int uartn_enabled(int uart)
{
if (uart == UART_AP)
return GREAD(PINMUX, DIOA7_SEL);
return GREAD(PINMUX, DIOB5_SEL);
}
static void usart_tx_connect(void)
{
GWRITE(PINMUX, DIOA7_SEL, GC_PINMUX_UART1_TX_SEL);
@@ -17,7 +26,7 @@ static void usart_tx_connect(void)
static void usart_tx_disconnect(void)
{
GWRITE(PINMUX, DIOA7_SEL, GC_PINMUX_DIOA3_SEL_DEFAULT);
GWRITE(PINMUX, DIOA7_SEL, GC_PINMUX_DIOA7_SEL_DEFAULT);
GWRITE(PINMUX, DIOB5_SEL, GC_PINMUX_DIOB5_SEL_DEFAULT);
}
@@ -44,19 +53,17 @@ void rdd_detached(void)
static int command_uart(int argc, char **argv)
{
static int enabled;
if (argc > 1) {
if (!strcasecmp("enable", argv[1])) {
enabled = 1;
usart_tx_connect();
} else if (!strcasecmp("disable", argv[1])) {
enabled = 0;
usart_tx_disconnect();
}
}
ccprintf("UART %s\n", enabled ? "enabled" : "disabled");
ccprintf("EC UART %s\nAP UART %s\n",
uartn_enabled(UART_EC) ? "enabled" : "disabled",
uartn_enabled(UART_AP) ? "enabled" : "disabled");
return EC_SUCCESS;
}
DECLARE_CONSOLE_COMMAND(uart, command_uart,

View File

@@ -74,4 +74,7 @@ void uartn_tx_start(int uart);
* Disable the UART transmit interrupt.
*/
void uartn_tx_stop(int uart);
/* Get UART output status */
int uartn_enabled(int uart);
#endif /* __CROS_EC_UARTN_H */

View File

@@ -38,6 +38,7 @@ common-$(CONFIG_COMMON_PANIC_OUTPUT)+=panic_output.o
common-$(CONFIG_COMMON_RUNTIME)+=hooks.o main.o system.o shared_mem.o
common-$(CONFIG_COMMON_TIMER)+=timer.o
common-$(CONFIG_CRC8)+= crc8.o
common-$(CONFIG_DEVICE_STATE)+=device_state.o
common-$(CONFIG_EXTENSION_COMMAND)+=extension.o
common-$(CONFIG_EXTPOWER_GPIO)+=extpower_gpio.o
common-$(CONFIG_FANS)+=fan.o pwm.o

29
common/device_state.c Normal file
View File

@@ -0,0 +1,29 @@
/* Copyright 2016 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.
*/
#include "device_state.h"
#include "hooks.h"
int device_get_state(enum device_type device)
{
return device_states[device].state;
}
void device_set_state(enum device_type device, enum device_state state)
{
if (device_states[device].state == state)
return;
device_states[device].state = state;
}
static void check_device_state(void)
{
int i;
for (i = 0; i < DEVICE_COUNT; i++)
board_update_device_state(i);
}
DECLARE_HOOK(HOOK_SECOND, check_device_state, HOOK_PRIO_DEFAULT);

View File

@@ -738,6 +738,9 @@
/*****************************************************************************/
/* Monitor the states of other devices */
#undef CONFIG_DEVICE_STATE
/* Support DMA transfers inside the EC */
#undef CONFIG_DMA

36
include/device_state.h Normal file
View File

@@ -0,0 +1,36 @@
/* Copyright 2016 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.
*/
#include "gpio.h"
#ifndef __CROS_DEVICE_STATE_H
#define __CROS_DEVICE_STATE_H
/* Device state indexes */
enum device_state {
DEVICE_STATE_UNKNOWN = 0,
DEVICE_STATE_OFF,
DEVICE_STATE_ON,
DEVICE_STATE_COUNT,
};
struct device_config {
enum device_state state;
int state_change;
const struct deferred_data *deferred;
enum gpio_signal detect_on;
enum gpio_signal detect_off;
};
enum device_type;
extern struct device_config device_states[];
int device_get_state(enum device_type device);
void device_set_state(enum device_type device, enum device_state state);
void board_update_device_state(enum device_type device);
#endif /* __CROS_DEVICE_STATE_H */