diff options
author | Randall Spangler <rspangler@chromium.org> | 2012-10-17 13:10:13 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-10-17 15:23:52 -0700 |
commit | c0640ee296acbf8ac3d4cf5246c60056015909c8 (patch) | |
tree | 367db2d7e31c5f7256400b4bb2a421d0227809de | |
parent | 8f73372cefb375c60e0003e0c5839f014e2ca4fa (diff) | |
download | chrome-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.tasklist | 1 | ||||
-rw-r--r-- | chip/lm4/lpc.c | 91 |
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); + } +} |