summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2016-05-10 12:50:31 -0700
committerchrome-bot <chrome-bot@chromium.org>2016-05-27 18:08:50 -0700
commit1d7984ad20a33b6062a96b498d06fa8266d52014 (patch)
tree74c98efe8b7b57eeb4a6f5743fd280e291ec583c
parentcebf8734d96d8c6a44185ee1b7d38b4d42efd36a (diff)
downloadchrome-ec-1d7984ad20a33b6062a96b498d06fa8266d52014.tar.gz
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
-rw-r--r--board/cr50/board.c177
-rw-r--r--board/cr50/board.h15
-rw-r--r--board/cr50/gpio.inc19
-rw-r--r--board/cr50/rdd.c19
-rw-r--r--chip/g/uartn.h3
-rw-r--r--common/build.mk1
-rw-r--r--common/device_state.c29
-rw-r--r--include/config.h3
-rw-r--r--include/device_state.h36
9 files changed, 295 insertions, 7 deletions
diff --git a/board/cr50/board.c b/board/cr50/board.c
index ebe2f65937..6263232349 100644
--- a/board/cr50/board.c
+++ b/board/cr50/board.c
@@ -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);
diff --git a/board/cr50/board.h b/board/cr50/board.h
index 56fdb7ab91..0aeffdc55b 100644
--- a/board/cr50/board.h
+++ b/board/cr50/board.h
@@ -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__ */
diff --git a/board/cr50/gpio.inc b/board/cr50/gpio.inc
index 20bda41d98..47a16478bc 100644
--- a/board/cr50/gpio.inc
+++ b/board/cr50/gpio.inc
@@ -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)
diff --git a/board/cr50/rdd.c b/board/cr50/rdd.c
index ed30094bf3..6fd76146cd 100644
--- a/board/cr50/rdd.c
+++ b/board/cr50/rdd.c
@@ -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,
diff --git a/chip/g/uartn.h b/chip/g/uartn.h
index 12d0f53bff..d6acc29210 100644
--- a/chip/g/uartn.h
+++ b/chip/g/uartn.h
@@ -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 */
diff --git a/common/build.mk b/common/build.mk
index be72a44979..bfb4e6aef7 100644
--- a/common/build.mk
+++ b/common/build.mk
@@ -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
diff --git a/common/device_state.c b/common/device_state.c
new file mode 100644
index 0000000000..2b3f969d4c
--- /dev/null
+++ b/common/device_state.c
@@ -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);
diff --git a/include/config.h b/include/config.h
index bedb99a6a3..2c4fa6ccc4 100644
--- a/include/config.h
+++ b/include/config.h
@@ -738,6 +738,9 @@
/*****************************************************************************/
+/* Monitor the states of other devices */
+#undef CONFIG_DEVICE_STATE
+
/* Support DMA transfers inside the EC */
#undef CONFIG_DMA
diff --git a/include/device_state.h b/include/device_state.h
new file mode 100644
index 0000000000..1af3972bfb
--- /dev/null
+++ b/include/device_state.h
@@ -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 */