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