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);