Merge "Fix the missing IRQ problem."

This commit is contained in:
Louis Yung-Chieh Lo
2012-02-02 05:11:25 -08:00
committed by Gerrit Code Review
5 changed files with 184 additions and 30 deletions

View File

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

View File

@@ -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",

View File

@@ -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) {

View File

@@ -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

View File

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