diff options
Diffstat (limited to 'drivers/input/pc_keyb.c')
-rw-r--r-- | drivers/input/pc_keyb.c | 256 |
1 files changed, 256 insertions, 0 deletions
diff --git a/drivers/input/pc_keyb.c b/drivers/input/pc_keyb.c new file mode 100644 index 0000000..81d3e98 --- /dev/null +++ b/drivers/input/pc_keyb.c @@ -0,0 +1,256 @@ +/*********************************************************************** + * + * (C) Copyright 2004 + * DENX Software Engineering + * Wolfgang Denk, wd@denx.de + * All rights reserved. + * + * PS/2 keyboard driver + * + * Originally from linux source (drivers/char/pc_keyb.c) + * + ***********************************************************************/ + +#include <common.h> + +#ifdef CONFIG_PS2KBD + +#include <keyboard.h> +#include <pc_keyb.h> + +#undef KBG_DEBUG + +#ifdef KBG_DEBUG +#define PRINTF(fmt,args...) printf (fmt ,##args) +#else +#define PRINTF(fmt,args...) +#endif + + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + */ +static unsigned char handle_kbd_event(void) +{ + unsigned char status = kbd_read_status(); + unsigned int work = 10000; + + while ((--work > 0) && (status & KBD_STAT_OBF)) { + unsigned char scancode; + + scancode = kbd_read_input(); + + /* Error bytes must be ignored to make the + Synaptics touchpads compaq use work */ + /* Ignore error bytes */ + if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) { + if (status & KBD_STAT_MOUSE_OBF) + ; /* not supported: handle_mouse_event(scancode); */ + else + handle_scancode(scancode); + } + status = kbd_read_status(); + } + if (!work) + PRINTF("pc_keyb: controller jammed (0x%02X).\n", status); + return status; +} + + +static int kbd_read_data(void) +{ + int val; + unsigned char status; + + val=-1; + status = kbd_read_status(); + if (status & KBD_STAT_OBF) { + val = kbd_read_input(); + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) + val = -2; + } + return val; +} + +static int kbd_wait_for_input(void) +{ + unsigned long timeout; + int val; + + timeout = KBD_TIMEOUT; + val=kbd_read_data(); + while(val < 0) { + if(timeout--==0) + return -1; + udelay(1000); + val=kbd_read_data(); + } + return val; +} + + +static int kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT * 10; + + do { + unsigned char status = handle_kbd_event(); + if (!(status & KBD_STAT_IBF)) + return 0; /* ok */ + udelay(1000); + timeout--; + } while (timeout); + return 1; +} + +static void kbd_write_command_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_command_w\n"); + kbd_write_command(data); +} + +static void kbd_write_output_w(int data) +{ + if(kb_wait()) + PRINTF("timeout in kbd_write_output_w\n"); + kbd_write_output(data); +} + +static void kbd_send_data(unsigned char data) +{ + kbd_write_output_w(data); + kbd_wait_for_input(); +} + + +static char * kbd_initialize(void) +{ + int status; + + /* + * Test the keyboard interface. + * This seems to be the only way to get it going. + * If the test is successful a x55 is placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_SELF_TEST); + if (kbd_wait_for_input() != 0x55) + return "Kbd: failed self test"; + /* + * Perform a keyboard interface test. This causes the controller + * to test the keyboard clock and data lines. The results of the + * test are placed in the input buffer. + */ + kbd_write_command_w(KBD_CCMD_KBD_TEST); + if (kbd_wait_for_input() != 0x00) + return "Kbd: interface failed self test"; + /* + * Enable the keyboard by allowing the keyboard clock to run. + */ + kbd_write_command_w(KBD_CCMD_KBD_ENABLE); + + /* + * Reset keyboard. If the read times out + * then the assumption is that no keyboard is + * plugged into the machine. + * This defaults the keyboard to scan-code set 2. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_RESET); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) { + PRINTF("status: %X\n",status); + return "Kbd: reset failed, no ACK"; + } + } while (1); + if (kbd_wait_for_input() != KBD_REPLY_POR) + return "Kbd: reset failed, no POR"; + + /* + * Set keyboard controller mode. During this, the keyboard should be + * in the disabled state. + * + * Set up to try again if the keyboard asks for RESEND. + */ + do { + kbd_write_output_w(KBD_CMD_DISABLE); + status = kbd_wait_for_input(); + if (status == KBD_REPLY_ACK) + break; + if (status != KBD_REPLY_RESEND) + return "Kbd: disable keyboard: no ACK"; + } while (1); + + kbd_write_command_w(KBD_CCMD_WRITE_MODE); + kbd_write_output_w(KBD_MODE_KBD_INT + | KBD_MODE_SYS + | KBD_MODE_DISABLE_MOUSE + | KBD_MODE_KCC); + + /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */ + kbd_write_command_w(KBD_CCMD_READ_MODE); + if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { + /* + * If the controller does not support conversion, + * Set the keyboard to scan-code set 1. + */ + kbd_write_output_w(0xF0); + kbd_wait_for_input(); + kbd_write_output_w(0x01); + kbd_wait_for_input(); + } + kbd_write_output_w(KBD_CMD_ENABLE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: enable keyboard: no ACK"; + + /* + * Finally, set the typematic rate to maximum. + */ + kbd_write_output_w(KBD_CMD_SET_RATE); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + kbd_write_output_w(0x00); + if (kbd_wait_for_input() != KBD_REPLY_ACK) + return "Kbd: Set rate: no ACK"; + return NULL; +} + +static void kbd_interrupt(void *dev_id) +{ + handle_kbd_event(); +} + +/****************************************************************** + * Init + ******************************************************************/ + +int kbd_init_hw(void) +{ + char* result; + + kbd_request_region(); + + result=kbd_initialize(); + if (result==NULL) { + PRINTF("AT Keyboard initialized\n"); + kbd_request_irq(kbd_interrupt); + return (1); + } else { + printf("%s\n",result); + return (-1); + } +} + +void pckbd_leds(unsigned char leds) +{ + kbd_send_data(KBD_CMD_SET_LEDS); + kbd_send_data(leds); +} + +#endif /* CONFIG_PS2KBD */ |