summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLouis Yung-Chieh Lo <yjlou@chromium.org>2012-01-20 14:33:15 +0800
committerLouis Yung-Chieh Lo <yjlou@chromium.org>2012-02-02 20:51:45 +0800
commit7e8d739b3885a9180e0cf3fd01eae13779440832 (patch)
tree6012aeda0974c97f1b0c897e8da060f08981c8f0
parent965987eeac617d0efc932242b697e3ed55b6a667 (diff)
downloadchrome-ec-7e8d739b3885a9180e0cf3fd01eae13779440832.tar.gz
Fix the missing IRQ problem.
The problem comes from the different assumption of interrupt mode in EC and the PCH. The PCH assumes IRQ1 is edge-triggered and triggered at a rising edge. However, the auto-IRQ functino of EC is level-triggered and uses low-active to assert an IRQ. This makes the deadlock so that the kernel never gets an interrupt until a byte is manually pulled from host. So, the solution is manually firing an IRQ_1 to host after EC puts a byte to port 0x60. Note that the auto IRQ needs to be disabled in order to avoid the interference with manual IRQ generation. This CL also moves chip specific code to lm4/lpc.c and handle some minor keyboard commands. BUG=none TEST=on hacked baord. Change-Id: Ib57f5a4d749cb019e4c3c00da110054c4f335c7b
-rw-r--r--chip/lm4/lpc.c40
-rw-r--r--common/i8042.c31
-rw-r--r--common/keyboard.c117
-rw-r--r--include/i8042.h13
-rw-r--r--include/lpc.h13
5 files changed, 184 insertions, 30 deletions
diff --git a/chip/lm4/lpc.c b/chip/lm4/lpc.c
index 70db8dbe19..fae068343f 100644
--- a/chip/lm4/lpc.c
+++ b/chip/lm4/lpc.c
@@ -37,6 +37,30 @@ static void configure_gpio(void)
}
+/* Manually generates an IRQ to host (edge-trigger).
+ *
+ * For SERIRQ quite mode, we need to set LM4_LPC_LPCIRQCTL twice.
+ * The first one is to assert IRQ (pull low), and then the second one is
+ * to de-assert it. This generates a pulse (high-low-high) for an IRQ.
+ *
+ * Note that the irq_num == 0 would set the AH bit (Active High).
+ */
+void lpc_manual_irq(int irq_num) {
+ uint32_t common_bits =
+ 0x00000004 | /* PULSE */
+ 0x00000002 | /* ONCHG - for quiet mode */
+ 0x00000001; /* SND - send immediately */
+
+ while (LM4_LPC_LPCIRQCTL & 1); /* wait until SND is cleared */
+ LM4_LPC_LPCIRQCTL = (1 << (irq_num + 16)) | common_bits;
+
+ while (LM4_LPC_LPCIRQCTL & 1); /* wait until SND is cleared */
+ LM4_LPC_LPCIRQCTL = common_bits; /* generate a all-high frame to
+ * simulate a rising edge. */
+ while (LM4_LPC_LPCIRQCTL & 1); /* wait until SND is cleared */
+}
+
+
int lpc_init(void)
{
volatile uint32_t scratch __attribute__((unused));
@@ -47,6 +71,7 @@ int lpc_init(void)
LM4_LPC_LPCIM = 0;
LM4_LPC_LPCCTL = 0;
+ LM4_LPC_LPCIRQCTL = 0;
/* Configure GPIOs */
configure_gpio();
@@ -83,7 +108,7 @@ int lpc_init(void)
* data writes, pool bytes 0(data)/1(cmd) */
LM4_LPC_ADR(LPC_CH_KEYBOARD) = 0x60;
LM4_LPC_CTL(LPC_CH_KEYBOARD) = (1 << 24/* IRQSEL1 */) |
- (1 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
+ (0 << 18/* IRQEN1 */) | (LPC_POOL_OFFS_KEYBOARD << (5 - 1));
LM4_LPC_ST(LPC_CH_KEYBOARD) = 0;
/* Unmask interrupt for host command/data writes and data reads */
LM4_LPC_LPCIM |= LM4_LPC_INT_MASK(LPC_CH_KEYBOARD, 7);
@@ -148,6 +173,19 @@ void lpc_send_host_response(int slot, int status)
}
+/* Return true if the TOH is still set */
+int lpc_keyboard_has_char() {
+ return (LM4_LPC_ST(LPC_CH_KEYBOARD) & (1 << 0 /* TOH */)) ? 1 : 0;
+}
+
+void lpc_keyboard_put_char(uint8_t chr, int send_irq) {
+ LPC_POOL_KEYBOARD[1] = chr;
+ if (send_irq) {
+ lpc_manual_irq(1); /* IRQ#1 */
+ }
+}
+
+
int lpc_comx_has_char(void)
{
return LM4_LPC_ST(LPC_CH_COMX) & 0x02;
diff --git a/common/i8042.c b/common/i8042.c
index a4fea378c7..785e6a761e 100644
--- a/common/i8042.c
+++ b/common/i8042.c
@@ -9,8 +9,7 @@
#include "common.h"
#include "i8042.h"
#include "keyboard.h"
-/* TODO: Code in common.c should not directly access chip registers */
-#include "registers.h"
+#include "lpc.h"
#include "task.h"
#include "timer.h"
#include "uart.h"
@@ -32,14 +31,17 @@ static int tail_to_buffer = 0;
#define HOST_BUFFER_SIZE (16)
static uint8_t to_host_buffer[HOST_BUFFER_SIZE];
+static int i8042_irq_enabled = 0;
+
+/* Reset all i8042 buffer */
void i8042_init()
{
head_to_buffer = tail_to_buffer = 0;
- LM4_LPC_ST(LPC_CH_KEYBOARD) = 0; /* clear the TOH bit */
}
+/* Called by the chip-specific code when host sedns a byte to port 0x60. */
void i8042_receives_data(int data)
{
int ret_len;
@@ -52,6 +54,7 @@ void i8042_receives_data(int data)
}
+/* Called by the chip-specific code when host sedns a byte to port 0x64. */
void i8042_receives_command(int cmd)
{
int ret_len;
@@ -64,6 +67,7 @@ void i8042_receives_command(int cmd)
}
+/* Called by EC common code to send bytes to host via port 0x60. */
static void enq_to_host(int len, uint8_t *to_host)
{
int from, to;
@@ -80,6 +84,18 @@ static void enq_to_host(int len, uint8_t *to_host)
}
+/* Called by common/keyboard.c when the host wants to receive keyboard IRQ
+ * (or not).
+ */
+void i8042_enable_keyboard_irq(void) {
+ i8042_irq_enabled = 1;
+}
+
+void i8042_disable_keyboard_irq(void) {
+ i8042_irq_enabled = 0;
+}
+
+
void i8042_command_task(void)
{
while (1) {
@@ -99,10 +115,11 @@ void i8042_command_task(void)
/* if the host still didn't read that away,
try next time. */
- if (LM4_LPC_ST(LPC_CH_KEYBOARD) & (1 << 0 /* TOH */)) {
+ if (lpc_keyboard_has_char()) {
#if I8042_DEBUG >= 5
uart_printf("[%d] i8042_command_task() "
- "cannot send to host due to TOH\n",
+ "cannot send to host due to host "
+ "havn't taken away.\n",
get_time().le.lo);
#endif
break;
@@ -114,8 +131,8 @@ void i8042_command_task(void)
(head_to_buffer + 1) % HOST_BUFFER_SIZE;
/* end of atomic protection */
- /* Write to host. TOH is set automatically. */
- LPC_POOL_KEYBOARD[1] = chr;
+ /* Write to host. */
+ lpc_keyboard_put_char(chr, i8042_irq_enabled);
#if I8042_DEBUG >= 4
uart_printf("[%d] i8042_command_task() "
"sends to host: 0x%02x\n",
diff --git a/common/keyboard.c b/common/keyboard.c
index fad9d2eda5..268a824110 100644
--- a/common/keyboard.c
+++ b/common/keyboard.c
@@ -27,13 +27,14 @@
/*
* i8042 global settings.
*/
-static int i8042_enabled = 0; /* default the keyboard is disabled. */
+static int keyboard_enabled = 0; /* default the keyboard is disabled. */
static uint8_t resend_command[MAX_SCAN_CODE_LEN];
static uint8_t resend_command_len = 0;
static uint8_t controller_ram_address;
static uint8_t controller_ram[0x20] = {
- I8042_AUX_DIS, /* the so called "command byte" */
- /* 0x01 - 0x1f are controller RAM */
+ /* the so called "command byte" */
+ I8042_XLATE | I8042_AUX_DIS | I8042_KBD_DIS,
+ /* 0x01 - 0x1f are controller RAM */
};
/*
@@ -121,6 +122,12 @@ static enum ec_error_list matrix_callback(
*len = 0;
+ if (controller_ram[0] & I8042_XLATE) {
+ /* If the keyboard translation is enabled,
+ * then always generates set 1. */
+ code_set = SCANCODE_SET_1;
+ }
+
switch (code_set) {
case SCANCODE_SET_1:
make_code = scancode_set1[row][col];
@@ -215,6 +222,53 @@ void keyboard_state_changed(int row, int col, int is_pressed) {
}
+void keyboard_enable(int enable) {
+ if (!keyboard_enabled && enable) {
+ /* enable */
+ } else if (keyboard_enabled && !enable) {
+ /* disable */
+ reset_rate_and_delay();
+ clean_underlying_buffer();
+ }
+ keyboard_enabled = enable;
+}
+
+
+uint8_t read_ctl_ram(uint8_t addr) {
+ ASSERT(addr < 0x20); // Controller RAM is only 32 bytes.
+
+ return controller_ram[addr];
+}
+
+
+/* Manipulates the controller_ram[]. Some bits change may trigger internal
+ * state change.
+ */
+void update_ctl_ram(uint8_t addr, uint8_t data) {
+ uint8_t orig;
+
+ ASSERT(addr < 0x20); // Controller RAM is only 32 bytes.
+ orig = controller_ram[addr];
+ controller_ram[addr] = data;
+#if KEYBOARD_DEBUG >= 5
+ uart_printf("Set CTR_RAM[0x%02x]=0x%02x (old:0x%02x)\n",
+ addr, data, orig);
+#endif
+
+ if (addr == 0x00) { /* the controller RAM */
+ /* Handle the I8042_KBD_DIS bit */
+ keyboard_enable(!(data & I8042_KBD_DIS));
+
+ /* Handle the I8042_ENIRQ1 bit */
+ if (!(orig & I8042_ENIRQ1) && (data & I8042_ENIRQ1)) {
+ i8042_enable_keyboard_irq();
+ } else if ((orig & I8042_ENIRQ1) && !(data & I8042_ENIRQ1)) {
+ i8042_disable_keyboard_irq();
+ }
+ }
+}
+
+
enum {
STATE_NORMAL = 0,
STATE_SCANCODE,
@@ -237,7 +291,7 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
switch (data_port_state) {
case STATE_SCANCODE:
#if KEYBOARD_DEBUG >= 5
- uart_printf("Eaten by STATE_SCANCODE\n");
+ uart_printf("Eaten by STATE_SCANCODE: 0x%02x\n", data);
#endif
if (data == SCANCODE_GET_SET) {
output[out_len++] = I8042_RET_ACK;
@@ -261,22 +315,27 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
break;
case STATE_WRITE_CMD_BYTE:
- controller_ram[controller_ram_address] = data;
#if KEYBOARD_DEBUG >= 5
- uart_printf("Set command_bytes[0x%02x]=0x%02x\n",
- controller_ram_address,
- controller_ram[controller_ram_address]);
+ uart_printf("Eaten by STATE_WRITE_CMD_BYTE: 0x%02x\n", data);
#endif
+ update_ctl_ram(controller_ram_address, data);
output[out_len++] = I8042_RET_ACK;
data_port_state = STATE_NORMAL;
break;
case STATE_ECHO_MOUSE:
+#if KEYBOARD_DEBUG >= 5
+ uart_printf("Eaten by STATE_ECHO_MOUSE: 0x%02x\n", data);
+#endif
+ output[out_len++] = I8042_RET_ACK;
output[out_len++] = data;
data_port_state = STATE_NORMAL;
break;
case STATE_SEND_TO_MOUSE:
+#if KEYBOARD_DEBUG >= 5
+ uart_printf("Eaten by STATE_SEND_TO_MOUSE: 0x%02x\n", data);
+#endif
data_port_state = STATE_NORMAL;
break;
@@ -294,6 +353,11 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
data_port_state = STATE_SETLEDS;
break;
+ case I8042_CMD_DIAG_ECHO:
+ output[out_len++] = I8042_RET_ACK;
+ output[out_len++] = I8042_CMD_DIAG_ECHO;
+ break;
+
case I8042_CMD_GETID: /* fall-thru */
case I8042_CMD_OK_GETID:
output[out_len++] = I8042_RET_ACK;
@@ -314,13 +378,12 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
case I8042_CMD_ENABLE:
output[out_len++] = I8042_RET_ACK;
- i8042_enabled = 1;
- clean_underlying_buffer();
+ keyboard_enable(1);
break;
case I8042_CMD_RESET_DIS:
output[out_len++] = I8042_RET_ACK;
- i8042_enabled = 0;
+ keyboard_enable(0);
reset_rate_and_delay();
clean_underlying_buffer();
break;
@@ -333,7 +396,7 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
case I8042_CMD_RESET_BAT:
output[out_len++] = I8042_RET_ACK;
- i8042_enabled = 0;
+ keyboard_enable(0);
output[out_len++] = I8042_RET_BAT;
output[out_len++] = I8042_RET_BAT;
break;
@@ -357,9 +420,6 @@ int handle_keyboard_data(uint8_t data, uint8_t *output) {
case I8042_CMD_EX_ENABLE:
default:
output[out_len++] = I8042_RET_NAK;
- i8042_enabled = 0;
- reset_rate_and_delay();
- clean_underlying_buffer();
#if KEYBOARD_DEBUG >= 1
uart_printf("Unsupported i8042 data 0x%02x.\n", data);
#endif
@@ -389,7 +449,7 @@ int handle_keyboard_command(uint8_t command, uint8_t *output) {
#endif
switch (command) {
case I8042_READ_CMD_BYTE:
- output[out_len++] = controller_ram[0];
+ output[out_len++] = read_ctl_ram(0);
break;
case I8042_WRITE_CMD_BYTE:
@@ -398,19 +458,27 @@ int handle_keyboard_command(uint8_t command, uint8_t *output) {
break;
case I8042_DIS_KB:
- i8042_enabled = 0;
+ keyboard_enable(0);
break;
case I8042_ENA_KB:
- i8042_enabled = 1;
+ keyboard_enable(1);
+ break;
+
+ case I8042_RESET_SELF_TEST:
+ output[out_len++] = 0x55; // Self test success.
break;
case I8042_DIS_MOUSE:
- controller_ram[0] |= I8042_AUX_DIS;
+ update_ctl_ram(0, read_ctl_ram(0) | I8042_AUX_DIS);
break;
case I8042_ENA_MOUSE:
- controller_ram[0] &= ~I8042_AUX_DIS;
+ update_ctl_ram(0, read_ctl_ram(0) & ~I8042_AUX_DIS);
+ break;
+
+ case I8042_TEST_MOUSE:
+ output[out_len++] = 0; // no error detected
break;
case I8042_ECHO_MOUSE:
@@ -424,19 +492,22 @@ int handle_keyboard_command(uint8_t command, uint8_t *output) {
default:
if (command >= I8042_READ_CTL_RAM &&
command <= I8042_READ_CTL_RAM_END) {
- output[out_len++] = controller_ram[command - 0x20];
+ output[out_len++] = read_ctl_ram(command - 0x20);
} else if (command >= I8042_WRITE_CTL_RAM &&
command <= I8042_WRITE_CTL_RAM_END) {
data_port_state = STATE_WRITE_CMD_BYTE;
controller_ram_address = command - 0x60;
+ } else if (command >= I8042_PULSE_START &&
+ command <= I8042_PULSE_END) {
+ /* Pulse Output Bit. Not implemented. Ignore it. */
} else {
#if KEYBOARD_DEBUG >= 1
uart_printf("Unsupported cmd:[0x%02x]\n", command);
#endif
- i8042_enabled = 0;
reset_rate_and_delay();
clean_underlying_buffer();
output[out_len++] = I8042_RET_NAK;
+ data_port_state = STATE_NORMAL;
}
break;
}
@@ -451,6 +522,8 @@ static int command_codeset(int argc, char **argv)
if (argc == 1) {
uart_printf("Current scancode set: %d\n", scancode_set);
+ uart_printf("I8042_XLATE: %d\n",
+ controller_ram[0] & I8042_XLATE ? 1 : 0);
} else if (argc == 2) {
set = strtoi(argv[1], NULL, 0);
switch (set) {
diff --git a/include/i8042.h b/include/i8042.h
index 440b19ad70..c3d858bee1 100644
--- a/include/i8042.h
+++ b/include/i8042.h
@@ -63,6 +63,9 @@
#define I8042_ENA_KB 0xae
#define I8042_ECHO_MOUSE 0xd3 /* expect a byte on port 0x60 */
#define I8042_SEND_TO_MOUSE 0xd4 /* expect a byte on port 0x60 */
+#define I8042_PULSE_START 0xf0
+#define I8042_PULSE_END 0xfd
+#define I8042_SYSTEM_RESET 0xfe
/* port 0x60 return value */
#define I8042_RET_BAT 0xaa
@@ -79,9 +82,12 @@
#define I8042_RET_ERR 0xff
/* port 64 - command byte bits */
+#define I8042_XLATE (1 << 6)
#define I8042_AUX_DIS (1 << 5)
+#define I8042_KBD_DIS (1 << 4)
#define I8042_SYS_FLAG (1 << 2)
#define I8042_ENIRQ12 (1 << 1)
+#define I8042_ENIRQ1 (1 << 0)
void i8042_init(void);
@@ -98,6 +104,13 @@ void i8042_receives_data(int data);
void i8042_receives_command(int cmd);
+/* Called by common/keyboard.c when the host doesn't want to receive
+ * keyboard IRQ.
+ */
+void i8042_enable_keyboard_irq(void);
+void i8042_disable_keyboard_irq(void);
+
+
/* Send the scan code to the host. The EC lib will push the scan code bytes
* to host via port 0x60 and assert the IBF flag to trigger an interrupt.
* The EC lib must queue them if the host cannot read the previous byte away
diff --git a/include/lpc.h b/include/lpc.h
index 8cf87668f6..94b92b41bb 100644
--- a/include/lpc.h
+++ b/include/lpc.h
@@ -10,6 +10,13 @@
#include "common.h"
+
+/* Manually generates an IRQ to host.
+ * Note that the irq_num == 0 would set the AH bit (Active High).
+ */
+void lpc_manual_irq(int irq_num);
+
+
/* Initializes the LPC module. */
int lpc_init(void);
@@ -25,6 +32,12 @@ uint8_t *lpc_get_host_range(int slot);
* commands, 1 for usermode-originated commands. */
void lpc_send_host_response(int slot, int status);
+/* Return true if the TOH is still set */
+int lpc_keyboard_has_char(void);
+
+/* Send a byte to host via port 0x60 and asserts IRQ if specified. */
+void lpc_keyboard_put_char(uint8_t chr, int send_irq);
+
/* Returns non-zero if the COMx interface has received a character. */
int lpc_comx_has_char(void);