1*16b195c8SJean-Christophe PLAGNIOL-VILLARD /*********************************************************************** 2*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 3*16b195c8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2004 4*16b195c8SJean-Christophe PLAGNIOL-VILLARD * DENX Software Engineering 5*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Wolfgang Denk, wd@denx.de 6*16b195c8SJean-Christophe PLAGNIOL-VILLARD * All rights reserved. 7*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 8*16b195c8SJean-Christophe PLAGNIOL-VILLARD * PS/2 keyboard driver 9*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 10*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Originally from linux source (drivers/char/pc_keyb.c) 11*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 12*16b195c8SJean-Christophe PLAGNIOL-VILLARD ***********************************************************************/ 13*16b195c8SJean-Christophe PLAGNIOL-VILLARD 14*16b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h> 15*16b195c8SJean-Christophe PLAGNIOL-VILLARD 16*16b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef CONFIG_PS2KBD 17*16b195c8SJean-Christophe PLAGNIOL-VILLARD 18*16b195c8SJean-Christophe PLAGNIOL-VILLARD #include <keyboard.h> 19*16b195c8SJean-Christophe PLAGNIOL-VILLARD #include <pc_keyb.h> 20*16b195c8SJean-Christophe PLAGNIOL-VILLARD 21*16b195c8SJean-Christophe PLAGNIOL-VILLARD #undef KBG_DEBUG 22*16b195c8SJean-Christophe PLAGNIOL-VILLARD 23*16b195c8SJean-Christophe PLAGNIOL-VILLARD #ifdef KBG_DEBUG 24*16b195c8SJean-Christophe PLAGNIOL-VILLARD #define PRINTF(fmt,args...) printf (fmt ,##args) 25*16b195c8SJean-Christophe PLAGNIOL-VILLARD #else 26*16b195c8SJean-Christophe PLAGNIOL-VILLARD #define PRINTF(fmt,args...) 27*16b195c8SJean-Christophe PLAGNIOL-VILLARD #endif 28*16b195c8SJean-Christophe PLAGNIOL-VILLARD 29*16b195c8SJean-Christophe PLAGNIOL-VILLARD 30*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 31*16b195c8SJean-Christophe PLAGNIOL-VILLARD * This reads the keyboard status port, and does the 32*16b195c8SJean-Christophe PLAGNIOL-VILLARD * appropriate action. 33*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 34*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 35*16b195c8SJean-Christophe PLAGNIOL-VILLARD static unsigned char handle_kbd_event(void) 36*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 37*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned char status = kbd_read_status(); 38*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned int work = 10000; 39*16b195c8SJean-Christophe PLAGNIOL-VILLARD 40*16b195c8SJean-Christophe PLAGNIOL-VILLARD while ((--work > 0) && (status & KBD_STAT_OBF)) { 41*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned char scancode; 42*16b195c8SJean-Christophe PLAGNIOL-VILLARD 43*16b195c8SJean-Christophe PLAGNIOL-VILLARD scancode = kbd_read_input(); 44*16b195c8SJean-Christophe PLAGNIOL-VILLARD 45*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* Error bytes must be ignored to make the 46*16b195c8SJean-Christophe PLAGNIOL-VILLARD Synaptics touchpads compaq use work */ 47*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* Ignore error bytes */ 48*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (!(status & (KBD_STAT_GTO | KBD_STAT_PERR))) { 49*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status & KBD_STAT_MOUSE_OBF) 50*16b195c8SJean-Christophe PLAGNIOL-VILLARD ; /* not supported: handle_mouse_event(scancode); */ 51*16b195c8SJean-Christophe PLAGNIOL-VILLARD else 52*16b195c8SJean-Christophe PLAGNIOL-VILLARD handle_scancode(scancode); 53*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 54*16b195c8SJean-Christophe PLAGNIOL-VILLARD status = kbd_read_status(); 55*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 56*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (!work) 57*16b195c8SJean-Christophe PLAGNIOL-VILLARD PRINTF("pc_keyb: controller jammed (0x%02X).\n", status); 58*16b195c8SJean-Christophe PLAGNIOL-VILLARD return status; 59*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 60*16b195c8SJean-Christophe PLAGNIOL-VILLARD 61*16b195c8SJean-Christophe PLAGNIOL-VILLARD 62*16b195c8SJean-Christophe PLAGNIOL-VILLARD static int kbd_read_data(void) 63*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 64*16b195c8SJean-Christophe PLAGNIOL-VILLARD int val; 65*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned char status; 66*16b195c8SJean-Christophe PLAGNIOL-VILLARD 67*16b195c8SJean-Christophe PLAGNIOL-VILLARD val=-1; 68*16b195c8SJean-Christophe PLAGNIOL-VILLARD status = kbd_read_status(); 69*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status & KBD_STAT_OBF) { 70*16b195c8SJean-Christophe PLAGNIOL-VILLARD val = kbd_read_input(); 71*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) 72*16b195c8SJean-Christophe PLAGNIOL-VILLARD val = -2; 73*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 74*16b195c8SJean-Christophe PLAGNIOL-VILLARD return val; 75*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 76*16b195c8SJean-Christophe PLAGNIOL-VILLARD 77*16b195c8SJean-Christophe PLAGNIOL-VILLARD static int kbd_wait_for_input(void) 78*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 79*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned long timeout; 80*16b195c8SJean-Christophe PLAGNIOL-VILLARD int val; 81*16b195c8SJean-Christophe PLAGNIOL-VILLARD 82*16b195c8SJean-Christophe PLAGNIOL-VILLARD timeout = KBD_TIMEOUT; 83*16b195c8SJean-Christophe PLAGNIOL-VILLARD val=kbd_read_data(); 84*16b195c8SJean-Christophe PLAGNIOL-VILLARD while(val < 0) { 85*16b195c8SJean-Christophe PLAGNIOL-VILLARD if(timeout--==0) 86*16b195c8SJean-Christophe PLAGNIOL-VILLARD return -1; 87*16b195c8SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 88*16b195c8SJean-Christophe PLAGNIOL-VILLARD val=kbd_read_data(); 89*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 90*16b195c8SJean-Christophe PLAGNIOL-VILLARD return val; 91*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 92*16b195c8SJean-Christophe PLAGNIOL-VILLARD 93*16b195c8SJean-Christophe PLAGNIOL-VILLARD 94*16b195c8SJean-Christophe PLAGNIOL-VILLARD static int kb_wait(void) 95*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 96*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned long timeout = KBC_TIMEOUT * 10; 97*16b195c8SJean-Christophe PLAGNIOL-VILLARD 98*16b195c8SJean-Christophe PLAGNIOL-VILLARD do { 99*16b195c8SJean-Christophe PLAGNIOL-VILLARD unsigned char status = handle_kbd_event(); 100*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (!(status & KBD_STAT_IBF)) 101*16b195c8SJean-Christophe PLAGNIOL-VILLARD return 0; /* ok */ 102*16b195c8SJean-Christophe PLAGNIOL-VILLARD udelay(1000); 103*16b195c8SJean-Christophe PLAGNIOL-VILLARD timeout--; 104*16b195c8SJean-Christophe PLAGNIOL-VILLARD } while (timeout); 105*16b195c8SJean-Christophe PLAGNIOL-VILLARD return 1; 106*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 107*16b195c8SJean-Christophe PLAGNIOL-VILLARD 108*16b195c8SJean-Christophe PLAGNIOL-VILLARD static void kbd_write_command_w(int data) 109*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 110*16b195c8SJean-Christophe PLAGNIOL-VILLARD if(kb_wait()) 111*16b195c8SJean-Christophe PLAGNIOL-VILLARD PRINTF("timeout in kbd_write_command_w\n"); 112*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command(data); 113*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 114*16b195c8SJean-Christophe PLAGNIOL-VILLARD 115*16b195c8SJean-Christophe PLAGNIOL-VILLARD static void kbd_write_output_w(int data) 116*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 117*16b195c8SJean-Christophe PLAGNIOL-VILLARD if(kb_wait()) 118*16b195c8SJean-Christophe PLAGNIOL-VILLARD PRINTF("timeout in kbd_write_output_w\n"); 119*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output(data); 120*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 121*16b195c8SJean-Christophe PLAGNIOL-VILLARD 122*16b195c8SJean-Christophe PLAGNIOL-VILLARD static void kbd_send_data(unsigned char data) 123*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 124*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(data); 125*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_wait_for_input(); 126*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 127*16b195c8SJean-Christophe PLAGNIOL-VILLARD 128*16b195c8SJean-Christophe PLAGNIOL-VILLARD 129*16b195c8SJean-Christophe PLAGNIOL-VILLARD static char * kbd_initialize(void) 130*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 131*16b195c8SJean-Christophe PLAGNIOL-VILLARD int status; 132*16b195c8SJean-Christophe PLAGNIOL-VILLARD 133*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 134*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Test the keyboard interface. 135*16b195c8SJean-Christophe PLAGNIOL-VILLARD * This seems to be the only way to get it going. 136*16b195c8SJean-Christophe PLAGNIOL-VILLARD * If the test is successful a x55 is placed in the input buffer. 137*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 138*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command_w(KBD_CCMD_SELF_TEST); 139*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != 0x55) 140*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: failed self test"; 141*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 142*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Perform a keyboard interface test. This causes the controller 143*16b195c8SJean-Christophe PLAGNIOL-VILLARD * to test the keyboard clock and data lines. The results of the 144*16b195c8SJean-Christophe PLAGNIOL-VILLARD * test are placed in the input buffer. 145*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 146*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command_w(KBD_CCMD_KBD_TEST); 147*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != 0x00) 148*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: interface failed self test"; 149*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 150*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Enable the keyboard by allowing the keyboard clock to run. 151*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 152*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command_w(KBD_CCMD_KBD_ENABLE); 153*16b195c8SJean-Christophe PLAGNIOL-VILLARD 154*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 155*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Reset keyboard. If the read times out 156*16b195c8SJean-Christophe PLAGNIOL-VILLARD * then the assumption is that no keyboard is 157*16b195c8SJean-Christophe PLAGNIOL-VILLARD * plugged into the machine. 158*16b195c8SJean-Christophe PLAGNIOL-VILLARD * This defaults the keyboard to scan-code set 2. 159*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 160*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Set up to try again if the keyboard asks for RESEND. 161*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 162*16b195c8SJean-Christophe PLAGNIOL-VILLARD do { 163*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(KBD_CMD_RESET); 164*16b195c8SJean-Christophe PLAGNIOL-VILLARD status = kbd_wait_for_input(); 165*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status == KBD_REPLY_ACK) 166*16b195c8SJean-Christophe PLAGNIOL-VILLARD break; 167*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status != KBD_REPLY_RESEND) { 168*16b195c8SJean-Christophe PLAGNIOL-VILLARD PRINTF("status: %X\n",status); 169*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: reset failed, no ACK"; 170*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 171*16b195c8SJean-Christophe PLAGNIOL-VILLARD } while (1); 172*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != KBD_REPLY_POR) 173*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: reset failed, no POR"; 174*16b195c8SJean-Christophe PLAGNIOL-VILLARD 175*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 176*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Set keyboard controller mode. During this, the keyboard should be 177*16b195c8SJean-Christophe PLAGNIOL-VILLARD * in the disabled state. 178*16b195c8SJean-Christophe PLAGNIOL-VILLARD * 179*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Set up to try again if the keyboard asks for RESEND. 180*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 181*16b195c8SJean-Christophe PLAGNIOL-VILLARD do { 182*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(KBD_CMD_DISABLE); 183*16b195c8SJean-Christophe PLAGNIOL-VILLARD status = kbd_wait_for_input(); 184*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status == KBD_REPLY_ACK) 185*16b195c8SJean-Christophe PLAGNIOL-VILLARD break; 186*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (status != KBD_REPLY_RESEND) 187*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: disable keyboard: no ACK"; 188*16b195c8SJean-Christophe PLAGNIOL-VILLARD } while (1); 189*16b195c8SJean-Christophe PLAGNIOL-VILLARD 190*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command_w(KBD_CCMD_WRITE_MODE); 191*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(KBD_MODE_KBD_INT 192*16b195c8SJean-Christophe PLAGNIOL-VILLARD | KBD_MODE_SYS 193*16b195c8SJean-Christophe PLAGNIOL-VILLARD | KBD_MODE_DISABLE_MOUSE 194*16b195c8SJean-Christophe PLAGNIOL-VILLARD | KBD_MODE_KCC); 195*16b195c8SJean-Christophe PLAGNIOL-VILLARD 196*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* AMCC powerpc portables need this to use scan-code set 1 -- Cort */ 197*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_command_w(KBD_CCMD_READ_MODE); 198*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (!(kbd_wait_for_input() & KBD_MODE_KCC)) { 199*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 200*16b195c8SJean-Christophe PLAGNIOL-VILLARD * If the controller does not support conversion, 201*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Set the keyboard to scan-code set 1. 202*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 203*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(0xF0); 204*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_wait_for_input(); 205*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(0x01); 206*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_wait_for_input(); 207*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 208*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(KBD_CMD_ENABLE); 209*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != KBD_REPLY_ACK) 210*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: enable keyboard: no ACK"; 211*16b195c8SJean-Christophe PLAGNIOL-VILLARD 212*16b195c8SJean-Christophe PLAGNIOL-VILLARD /* 213*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Finally, set the typematic rate to maximum. 214*16b195c8SJean-Christophe PLAGNIOL-VILLARD */ 215*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(KBD_CMD_SET_RATE); 216*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != KBD_REPLY_ACK) 217*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: Set rate: no ACK"; 218*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_write_output_w(0x00); 219*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (kbd_wait_for_input() != KBD_REPLY_ACK) 220*16b195c8SJean-Christophe PLAGNIOL-VILLARD return "Kbd: Set rate: no ACK"; 221*16b195c8SJean-Christophe PLAGNIOL-VILLARD return NULL; 222*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 223*16b195c8SJean-Christophe PLAGNIOL-VILLARD 224*16b195c8SJean-Christophe PLAGNIOL-VILLARD static void kbd_interrupt(void *dev_id) 225*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 226*16b195c8SJean-Christophe PLAGNIOL-VILLARD handle_kbd_event(); 227*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 228*16b195c8SJean-Christophe PLAGNIOL-VILLARD 229*16b195c8SJean-Christophe PLAGNIOL-VILLARD /****************************************************************** 230*16b195c8SJean-Christophe PLAGNIOL-VILLARD * Init 231*16b195c8SJean-Christophe PLAGNIOL-VILLARD ******************************************************************/ 232*16b195c8SJean-Christophe PLAGNIOL-VILLARD 233*16b195c8SJean-Christophe PLAGNIOL-VILLARD int kbd_init_hw(void) 234*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 235*16b195c8SJean-Christophe PLAGNIOL-VILLARD char* result; 236*16b195c8SJean-Christophe PLAGNIOL-VILLARD 237*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_request_region(); 238*16b195c8SJean-Christophe PLAGNIOL-VILLARD 239*16b195c8SJean-Christophe PLAGNIOL-VILLARD result=kbd_initialize(); 240*16b195c8SJean-Christophe PLAGNIOL-VILLARD if (result==NULL) { 241*16b195c8SJean-Christophe PLAGNIOL-VILLARD PRINTF("AT Keyboard initialized\n"); 242*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_request_irq(kbd_interrupt); 243*16b195c8SJean-Christophe PLAGNIOL-VILLARD return (1); 244*16b195c8SJean-Christophe PLAGNIOL-VILLARD } else { 245*16b195c8SJean-Christophe PLAGNIOL-VILLARD printf("%s\n",result); 246*16b195c8SJean-Christophe PLAGNIOL-VILLARD return (-1); 247*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 248*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 249*16b195c8SJean-Christophe PLAGNIOL-VILLARD 250*16b195c8SJean-Christophe PLAGNIOL-VILLARD void pckbd_leds(unsigned char leds) 251*16b195c8SJean-Christophe PLAGNIOL-VILLARD { 252*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_send_data(KBD_CMD_SET_LEDS); 253*16b195c8SJean-Christophe PLAGNIOL-VILLARD kbd_send_data(leds); 254*16b195c8SJean-Christophe PLAGNIOL-VILLARD } 255*16b195c8SJean-Christophe PLAGNIOL-VILLARD 256*16b195c8SJean-Christophe PLAGNIOL-VILLARD #endif /* CONFIG_PS2KBD */ 257