summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-10-17 13:10:13 -0700
committerGerrit <chrome-bot@google.com>2012-10-17 15:23:52 -0700
commitc0640ee296acbf8ac3d4cf5246c60056015909c8 (patch)
tree367db2d7e31c5f7256400b4bb2a421d0227809de
parent8f73372cefb375c60e0003e0c5839f014e2ca4fa (diff)
downloadchrome-ec-c0640ee296acbf8ac3d4cf5246c60056015909c8.tar.gz
link: don't rely on host-write interrupt status
This works around a potential LM4 chip problem where edges on the FRMH status bit don't always trigger interrupts. The workaround is to look at FRMH for each channel in the interrupt handler rather than the interrupt status, and to trigger the interrupt every 250ms to sweep up any missed writes. We already do this for port 80 writes; this just extends the workaround to all channels. BUG=chrome-os-partner:13965 BRANCH=link TEST=manual - boot system - EC console should show a number of HC lines for host command - EC console should show a number of ACPI queries - switch to root shell; keyboard should work - ectool version should work Change-Id: If02d685519c69ee88c055c8374a6c655a277e637 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/35871 Reviewed-by: Simon Glass <sjg@chromium.org>
-rw-r--r--board/link/ec.tasklist1
-rw-r--r--chip/lm4/lpc.c91
2 files changed, 56 insertions, 36 deletions
diff --git a/board/link/ec.tasklist b/board/link/ec.tasklist
index 8ad82b6427..9ee637efa4 100644
--- a/board/link/ec.tasklist
+++ b/board/link/ec.tasklist
@@ -16,6 +16,7 @@
*/
#define CONFIG_TASK_LIST \
TASK(WATCHDOG, watchdog_task, NULL, WATCHDOG_TASK_STACK_SIZE) \
+ TASK(LPC, lpc_task, NULL, TASK_STACK_SIZE) \
TASK(VBOOTHASH, vboot_hash_task, NULL, LARGER_TASK_STACK_SIZE) \
TASK(LIGHTBAR, lightbar_task, NULL, TASK_STACK_SIZE) \
TASK(POWERSTATE, charge_state_machine_task, NULL, TASK_STACK_SIZE) \
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
index 21314267b2..2d61336653 100644
--- a/chip/lm4/lpc.c
+++ b/chip/lm4/lpc.c
@@ -236,7 +236,7 @@ void lpc_keyboard_resume_irq(void)
int lpc_comx_has_char(void)
{
- return LM4_LPC_ST(LPC_CH_COMX) & 0x02;
+ return LM4_LPC_ST(LPC_CH_COMX) & LM4_LPC_ST_FRMH;
}
@@ -450,10 +450,28 @@ DECLARE_HOST_COMMAND(EC_CMD_ACPI_QUERY_EVENT,
EC_VER_MASK(0));
-/* Handle an incoming host command */
-static void handle_host_command(int cmd)
+/**
+ * Handle write to host command I/O ports.
+ *
+ * @param is_cmd Is write command (1) or data (0)?
+ */
+static void handle_host_write(int is_cmd)
{
- host_cmd_args.command = cmd;
+ /* Ignore data writes */
+ if (!is_cmd) {
+ LM4_LPC_ST(LPC_CH_CMD) &= ~LM4_LPC_ST_FRMH;
+ return;
+ }
+
+ /* Set the busy bit */
+ LM4_LPC_ST(LPC_CH_CMD) |= LM4_LPC_ST_BUSY;
+
+ /*
+ * Read the command byte. This clears the FRMH bit in
+ * the status byte.
+ */
+ host_cmd_args.command = LPC_POOL_CMD[0];
+
host_cmd_args.result = EC_RES_SUCCESS;
host_cmd_args.send_response = lpc_send_response;
host_cmd_args.flags = lpc_host_args->flags;
@@ -515,28 +533,21 @@ static void handle_host_command(int cmd)
static void lpc_interrupt(void)
{
uint32_t mis = LM4_LPC_LPCMIS;
+ uint32_t st;
/* Clear the interrupt bits we're handling */
LM4_LPC_LPCIC = mis;
#ifdef CONFIG_TASK_HOSTCMD
/* Handle ACPI command and data writes */
- if (mis & LM4_LPC_INT_MASK(LPC_CH_ACPI, 4))
- handle_acpi_write(1);
- if (mis & LM4_LPC_INT_MASK(LPC_CH_ACPI, 2))
- handle_acpi_write(0);
+ st = LM4_LPC_ST(LPC_CH_ACPI);
+ if (st & LM4_LPC_ST_FRMH)
+ handle_acpi_write(st & LM4_LPC_ST_CMD);
/* Handle user command writes */
- if (mis & LM4_LPC_INT_MASK(LPC_CH_CMD, 4)) {
- /* Set the busy bit */
- LM4_LPC_ST(LPC_CH_CMD) |= LM4_LPC_ST_BUSY;
-
- /*
- * Read the command byte. This clears the FRMH bit in the
- * status byte.
- */
- handle_host_command(LPC_POOL_CMD[0]);
- }
+ st = LM4_LPC_ST(LPC_CH_CMD);
+ if (st & LM4_LPC_ST_FRMH)
+ handle_host_write(st & LM4_LPC_ST_CMD);
#endif
/*
@@ -550,31 +561,26 @@ static void lpc_interrupt(void)
port_80_write(LPC_POOL_PORT80[0]);
#ifdef CONFIG_TASK_I8042CMD
- /* Handle port 60 command (CH3MIS2) and data (CH3MIS1) */
- if (mis & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 2)) {
- /* Read the data byte and pass to the i8042 handler.
- * This clears the FRMH bit in the status byte. */
- i8042_receives_data(LPC_POOL_KEYBOARD[0]);
- }
- if (mis & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 4)) {
- /* Read the command byte and pass to the i8042 handler.
- * This clears the FRMH bit in the status byte. */
- i8042_receives_command(LPC_POOL_KEYBOARD[0]);
+ /* Handle keyboard interface writes */
+ st = LM4_LPC_ST(LPC_CH_KEYBOARD);
+ if (st & LM4_LPC_ST_FRMH) {
+ if (st & LM4_LPC_ST_CMD)
+ i8042_receives_command(LPC_POOL_KEYBOARD[0]);
+ else
+ i8042_receives_data(LPC_POOL_KEYBOARD[0]);
}
+
if (mis & LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 1)) {
- /* Host picks up the data, try to send remaining bytes */
+ /* Host read data; wake up task to send remaining bytes */
task_wake(TASK_ID_I8042CMD);
}
#endif
/* Handle COMx */
- if (mis & LM4_LPC_INT_MASK(LPC_CH_COMX, 2)) {
- /* Handle host writes */
- if (lpc_comx_has_char()) {
- /* Copy a character to the UART if there's space */
- if (uart_comx_putc_ok())
- uart_comx_putc(lpc_comx_get_char());
- }
+ if (lpc_comx_has_char()) {
+ /* Copy a character to the UART if there's space */
+ if (uart_comx_putc_ok())
+ uart_comx_putc(lpc_comx_get_char());
}
/* Debugging: print changes to LPC0RESET */
@@ -800,3 +806,16 @@ static int lpc_resume(void)
return EC_SUCCESS;
}
DECLARE_HOOK(HOOK_CHIPSET_RESUME, lpc_resume, HOOK_PRIO_DEFAULT);
+
+void lpc_task(void)
+{
+ while (1) {
+ usleep(250000);
+ /*
+ * Make sure pending LPC interrupts have been processed.
+ * This works around a LM4 bug where host writes sometimes
+ * don't trigger interrupts. See crosbug.com/p/13965.
+ */
+ task_trigger_irq(LM4_IRQ_LPC);
+ }
+}