1 /*********************************************************************** 2 * 3 * (C) Copyright 2004 4 * DENX Software Engineering 5 * Wolfgang Denk, wd@denx.de 6 * 7 * PS/2 multiplexer driver 8 * 9 * Originally from linux source (drivers/char/ps2mult.c) 10 * 11 * Uses simple serial driver (ps2ser.c) to access the multiplexer 12 * Used by PS/2 keyboard driver (pc_keyb.c) 13 * 14 ***********************************************************************/ 15 16 #include <common.h> 17 18 #include <pc_keyb.h> 19 #include <asm/atomic.h> 20 #include <ps2mult.h> 21 22 /* #define DEBUG_MULT */ 23 /* #define DEBUG_KEYB */ 24 25 #define KBD_STAT_DEFAULT (KBD_STAT_SELFTEST | KBD_STAT_UNLOCKED) 26 27 #define PRINTF(format, args...) printf("ps2mult.c: " format, ## args) 28 29 #ifdef DEBUG_MULT 30 #define PRINTF_MULT(format, args...) printf("PS2MULT: " format, ## args) 31 #else 32 #define PRINTF_MULT(format, args...) 33 #endif 34 35 #ifdef DEBUG_KEYB 36 #define PRINTF_KEYB(format, args...) printf("KEYB: " format, ## args) 37 #else 38 #define PRINTF_KEYB(format, args...) 39 #endif 40 41 42 static ulong start_time; 43 static int init_done = 0; 44 45 static int received_escape = 0; 46 static int received_bsync = 0; 47 static int received_selector = 0; 48 49 static int kbd_command_active = 0; 50 static int mouse_command_active = 0; 51 static int ctl_command_active = 0; 52 53 static u_char command_byte = 0; 54 55 static void (*keyb_handler)(void *dev_id); 56 57 static u_char ps2mult_buf [PS2BUF_SIZE]; 58 static atomic_t ps2mult_buf_cnt; 59 static int ps2mult_buf_in_idx; 60 static int ps2mult_buf_out_idx; 61 62 static u_char ps2mult_buf_status [PS2BUF_SIZE]; 63 64 #ifndef CONFIG_BOARD_EARLY_INIT_R 65 #error #define CONFIG_BOARD_EARLY_INIT_R and call ps2mult_early_init() in board_early_init_r() 66 #endif 67 void ps2mult_early_init (void) 68 { 69 start_time = get_timer(0); 70 } 71 72 static void ps2mult_send_byte(u_char byte, u_char sel) 73 { 74 ps2ser_putc(sel); 75 76 if (sel == PS2MULT_KB_SELECTOR) { 77 PRINTF_MULT("0x%02x send KEYBOARD\n", byte); 78 kbd_command_active = 1; 79 } else { 80 PRINTF_MULT("0x%02x send MOUSE\n", byte); 81 mouse_command_active = 1; 82 } 83 84 switch (byte) { 85 case PS2MULT_ESCAPE: 86 case PS2MULT_BSYNC: 87 case PS2MULT_KB_SELECTOR: 88 case PS2MULT_MS_SELECTOR: 89 case PS2MULT_SESSION_START: 90 case PS2MULT_SESSION_END: 91 ps2ser_putc(PS2MULT_ESCAPE); 92 break; 93 default: 94 break; 95 } 96 97 ps2ser_putc(byte); 98 } 99 100 static void ps2mult_receive_byte(u_char byte, u_char sel) 101 { 102 u_char status = KBD_STAT_DEFAULT; 103 104 #if 1 /* Ignore mouse in U-Boot */ 105 if (sel == PS2MULT_MS_SELECTOR) return; 106 #endif 107 108 if (sel == PS2MULT_KB_SELECTOR) { 109 if (kbd_command_active) { 110 if (!received_bsync) { 111 PRINTF_MULT("0x%02x lost KEYBOARD !!!\n", byte); 112 return; 113 } else { 114 kbd_command_active = 0; 115 received_bsync = 0; 116 } 117 } 118 PRINTF_MULT("0x%02x receive KEYBOARD\n", byte); 119 status |= KBD_STAT_IBF | KBD_STAT_OBF; 120 } else { 121 if (mouse_command_active) { 122 if (!received_bsync) { 123 PRINTF_MULT("0x%02x lost MOUSE !!!\n", byte); 124 return; 125 } else { 126 mouse_command_active = 0; 127 received_bsync = 0; 128 } 129 } 130 PRINTF_MULT("0x%02x receive MOUSE\n", byte); 131 status |= KBD_STAT_IBF | KBD_STAT_OBF | KBD_STAT_MOUSE_OBF; 132 } 133 134 if (atomic_read(&ps2mult_buf_cnt) < PS2BUF_SIZE) { 135 ps2mult_buf_status[ps2mult_buf_in_idx] = status; 136 ps2mult_buf[ps2mult_buf_in_idx++] = byte; 137 ps2mult_buf_in_idx &= (PS2BUF_SIZE - 1); 138 atomic_inc(&ps2mult_buf_cnt); 139 } else { 140 PRINTF("buffer overflow\n"); 141 } 142 143 if (received_bsync) { 144 PRINTF("unexpected BSYNC\n"); 145 received_bsync = 0; 146 } 147 } 148 149 void ps2mult_callback (int in_cnt) 150 { 151 int i; 152 u_char byte; 153 static int keyb_handler_active = 0; 154 155 if (!init_done) { 156 return; 157 } 158 159 for (i = 0; i < in_cnt; i ++) { 160 byte = ps2ser_getc(); 161 162 if (received_escape) { 163 ps2mult_receive_byte(byte, received_selector); 164 received_escape = 0; 165 } else switch (byte) { 166 case PS2MULT_ESCAPE: 167 PRINTF_MULT("ESCAPE receive\n"); 168 received_escape = 1; 169 break; 170 171 case PS2MULT_BSYNC: 172 PRINTF_MULT("BSYNC receive\n"); 173 received_bsync = 1; 174 break; 175 176 case PS2MULT_KB_SELECTOR: 177 case PS2MULT_MS_SELECTOR: 178 PRINTF_MULT("%s receive\n", 179 byte == PS2MULT_KB_SELECTOR ? "KB_SEL" : "MS_SEL"); 180 received_selector = byte; 181 break; 182 183 case PS2MULT_SESSION_START: 184 case PS2MULT_SESSION_END: 185 PRINTF_MULT("%s receive\n", 186 byte == PS2MULT_SESSION_START ? 187 "SESSION_START" : "SESSION_END"); 188 break; 189 190 default: 191 ps2mult_receive_byte(byte, received_selector); 192 } 193 } 194 195 if (keyb_handler && !keyb_handler_active && 196 atomic_read(&ps2mult_buf_cnt)) { 197 keyb_handler_active = 1; 198 keyb_handler(NULL); 199 keyb_handler_active = 0; 200 } 201 } 202 203 u_char ps2mult_read_status(void) 204 { 205 u_char byte; 206 207 if (atomic_read(&ps2mult_buf_cnt) == 0) { 208 ps2ser_check(); 209 } 210 211 if (atomic_read(&ps2mult_buf_cnt)) { 212 byte = ps2mult_buf_status[ps2mult_buf_out_idx]; 213 } else { 214 byte = KBD_STAT_DEFAULT; 215 } 216 PRINTF_KEYB("read_status()=0x%02x\n", byte); 217 return byte; 218 } 219 220 u_char ps2mult_read_input(void) 221 { 222 u_char byte = 0; 223 224 if (atomic_read(&ps2mult_buf_cnt) == 0) { 225 ps2ser_check(); 226 } 227 228 if (atomic_read(&ps2mult_buf_cnt)) { 229 byte = ps2mult_buf[ps2mult_buf_out_idx++]; 230 ps2mult_buf_out_idx &= (PS2BUF_SIZE - 1); 231 atomic_dec(&ps2mult_buf_cnt); 232 } 233 PRINTF_KEYB("read_input()=0x%02x\n", byte); 234 return byte; 235 } 236 237 void ps2mult_write_output(u_char val) 238 { 239 int i; 240 241 PRINTF_KEYB("write_output(0x%02x)\n", val); 242 243 for (i = 0; i < KBD_TIMEOUT; i++) { 244 if (!kbd_command_active && !mouse_command_active) { 245 break; 246 } 247 udelay(1000); 248 ps2ser_check(); 249 } 250 251 if (kbd_command_active) { 252 PRINTF("keyboard command not acknoledged\n"); 253 kbd_command_active = 0; 254 } 255 256 if (mouse_command_active) { 257 PRINTF("mouse command not acknoledged\n"); 258 mouse_command_active = 0; 259 } 260 261 if (ctl_command_active) { 262 switch (ctl_command_active) { 263 case KBD_CCMD_WRITE_MODE: 264 /* Scan code conversion not supported */ 265 command_byte = val & ~KBD_MODE_KCC; 266 break; 267 268 case KBD_CCMD_WRITE_AUX_OBUF: 269 ps2mult_receive_byte(val, PS2MULT_MS_SELECTOR); 270 break; 271 272 case KBD_CCMD_WRITE_MOUSE: 273 ps2mult_send_byte(val, PS2MULT_MS_SELECTOR); 274 break; 275 276 default: 277 PRINTF("invalid controller command\n"); 278 break; 279 } 280 281 ctl_command_active = 0; 282 return; 283 } 284 285 ps2mult_send_byte(val, PS2MULT_KB_SELECTOR); 286 } 287 288 void ps2mult_write_command(u_char val) 289 { 290 ctl_command_active = 0; 291 292 PRINTF_KEYB("write_command(0x%02x)\n", val); 293 294 switch (val) { 295 case KBD_CCMD_READ_MODE: 296 ps2mult_receive_byte(command_byte, PS2MULT_KB_SELECTOR); 297 break; 298 299 case KBD_CCMD_WRITE_MODE: 300 ctl_command_active = val; 301 break; 302 303 case KBD_CCMD_MOUSE_DISABLE: 304 break; 305 306 case KBD_CCMD_MOUSE_ENABLE: 307 break; 308 309 case KBD_CCMD_SELF_TEST: 310 ps2mult_receive_byte(0x55, PS2MULT_KB_SELECTOR); 311 break; 312 313 case KBD_CCMD_KBD_TEST: 314 ps2mult_receive_byte(0x00, PS2MULT_KB_SELECTOR); 315 break; 316 317 case KBD_CCMD_KBD_DISABLE: 318 break; 319 320 case KBD_CCMD_KBD_ENABLE: 321 break; 322 323 case KBD_CCMD_WRITE_AUX_OBUF: 324 ctl_command_active = val; 325 break; 326 327 case KBD_CCMD_WRITE_MOUSE: 328 ctl_command_active = val; 329 break; 330 331 default: 332 PRINTF("invalid controller command\n"); 333 break; 334 } 335 } 336 337 static int ps2mult_getc_w (void) 338 { 339 int res = -1; 340 int i; 341 342 for (i = 0; i < KBD_TIMEOUT; i++) { 343 if (ps2ser_check()) { 344 res = ps2ser_getc(); 345 break; 346 } 347 udelay(1000); 348 } 349 350 switch (res) { 351 case PS2MULT_KB_SELECTOR: 352 case PS2MULT_MS_SELECTOR: 353 received_selector = res; 354 break; 355 default: 356 break; 357 } 358 359 return res; 360 } 361 362 int ps2mult_init (void) 363 { 364 int byte; 365 int kbd_found = 0; 366 int mouse_found = 0; 367 368 while (get_timer(start_time) < CONFIG_PS2MULT_DELAY); 369 370 ps2ser_init(); 371 372 ps2ser_putc(PS2MULT_SESSION_START); 373 374 ps2ser_putc(PS2MULT_KB_SELECTOR); 375 ps2ser_putc(KBD_CMD_RESET); 376 377 do { 378 byte = ps2mult_getc_w(); 379 } while (byte >= 0 && byte != KBD_REPLY_ACK); 380 381 if (byte == KBD_REPLY_ACK) { 382 byte = ps2mult_getc_w(); 383 if (byte == 0xaa) { 384 kbd_found = 1; 385 puts("keyboard"); 386 } 387 } 388 389 if (!kbd_found) { 390 while (byte >= 0) { 391 byte = ps2mult_getc_w(); 392 } 393 } 394 395 #if 1 /* detect mouse */ 396 ps2ser_putc(PS2MULT_MS_SELECTOR); 397 ps2ser_putc(AUX_RESET); 398 399 do { 400 byte = ps2mult_getc_w(); 401 } while (byte >= 0 && byte != AUX_ACK); 402 403 if (byte == AUX_ACK) { 404 byte = ps2mult_getc_w(); 405 if (byte == 0xaa) { 406 byte = ps2mult_getc_w(); 407 if (byte == 0x00) { 408 mouse_found = 1; 409 puts(", mouse"); 410 } 411 } 412 } 413 414 if (!mouse_found) { 415 while (byte >= 0) { 416 byte = ps2mult_getc_w(); 417 } 418 } 419 #endif 420 421 if (mouse_found || kbd_found) { 422 if (!received_selector) { 423 if (mouse_found) { 424 received_selector = PS2MULT_MS_SELECTOR; 425 } else { 426 received_selector = PS2MULT_KB_SELECTOR; 427 } 428 } 429 430 init_done = 1; 431 } else { 432 puts("No device found"); 433 } 434 435 puts("\n"); 436 437 #if 0 /* for testing */ 438 { 439 int i; 440 u_char key[] = { 441 0x1f, 0x12, 0x14, 0x12, 0x31, 0x2f, 0x39, /* setenv */ 442 0x1f, 0x14, 0x20, 0x17, 0x31, 0x39, /* stdin */ 443 0x1f, 0x12, 0x13, 0x17, 0x1e, 0x26, 0x1c, /* serial */ 444 }; 445 446 for (i = 0; i < sizeof (key); i++) { 447 ps2mult_receive_byte (key[i], PS2MULT_KB_SELECTOR); 448 ps2mult_receive_byte (key[i] | 0x80, PS2MULT_KB_SELECTOR); 449 } 450 } 451 #endif 452 453 return init_done ? 0 : -1; 454 } 455 456 int ps2mult_request_irq(void (*handler)(void *)) 457 { 458 keyb_handler = handler; 459 460 return 0; 461 } 462