1/* 2 * linux/drivers/char/ec3104_keyb.c 3 * 4 * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org> 5 * 6 * based on linux/drivers/char/pc_keyb.c, which had the following comments: 7 * 8 * Separation of the PC low-level part by Geert Uytterhoeven, May 1997 9 * See keyboard.c for the whole history. 10 * 11 * Major cleanup by Martin Mares, May 1997 12 * 13 * Combined the keyboard and PS/2 mouse handling into one file, 14 * because they share the same hardware. 15 * Johan Myreen <jem@iki.fi> 1998-10-08. 16 * 17 * Code fixes to handle mouse ACKs properly. 18 * C. Scott Ananian <cananian@alumni.princeton.edu> 1999-01-29. 19 */ 20/* EC3104 note: 21 * This code was written without any documentation about the EC3104 chip. While 22 * I hope I got most of the basic functionality right, the register names I use 23 * are most likely completely different from those in the chip documentation. 24 * 25 * If you have any further information about the EC3104, please tell me 26 * (prumpf@tux.org). 27 */ 28 29 30#include <linux/spinlock.h> 31#include <linux/sched.h> 32#include <linux/interrupt.h> 33#include <linux/tty.h> 34#include <linux/mm.h> 35#include <linux/signal.h> 36#include <linux/init.h> 37#include <linux/kbd_ll.h> 38#include <linux/delay.h> 39#include <linux/random.h> 40#include <linux/poll.h> 41#include <linux/miscdevice.h> 42#include <linux/slab.h> 43#include <linux/kbd_kern.h> 44#include <linux/bitops.h> 45 46#include <asm/keyboard.h> 47#include <asm/uaccess.h> 48#include <asm/irq.h> 49#include <asm/system.h> 50#include <asm/ec3104.h> 51 52#include <asm/io.h> 53 54/* Some configuration switches are present in the include file... */ 55 56#include <linux/pc_keyb.h> 57 58#define MSR_CTS 0x10 59#define MCR_RTS 0x02 60#define LSR_DR 0x01 61#define LSR_BOTH_EMPTY 0x60 62 63static struct e5_struct { 64 u8 packet[8]; 65 int pos; 66 int length; 67 68 u8 cached_mcr; 69 u8 last_msr; 70} ec3104_keyb; 71 72/* Simple translation table for the SysRq keys */ 73 74 75#ifdef CONFIG_MAGIC_SYSRQ 76unsigned char ec3104_kbd_sysrq_xlate[128] = 77 "\000\0331234567890-=\177\t" /* 0x00 - 0x0f */ 78 "qwertyuiop[]\r\000as" /* 0x10 - 0x1f */ 79 "dfghjkl;'`\000\\zxcv" /* 0x20 - 0x2f */ 80 "bnm,./\000*\000 \000\201\202\203\204\205" /* 0x30 - 0x3f */ 81 "\206\207\210\211\212\000\000789-456+1" /* 0x40 - 0x4f */ 82 "230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */ 83 "\r\000/"; /* 0x60 - 0x6f */ 84#endif 85 86static void kbd_write_command_w(int data); 87static void kbd_write_output_w(int data); 88#ifdef CONFIG_PSMOUSE 89static void aux_write_ack(int val); 90static void __aux_write_ack(int val); 91#endif 92 93static DEFINE_SPINLOCK(kbd_controller_lock); 94static unsigned char handle_kbd_event(void); 95 96/* used only by send_data - set by keyboard_interrupt */ 97static volatile unsigned char reply_expected; 98static volatile unsigned char acknowledge; 99static volatile unsigned char resend; 100 101 102int ec3104_kbd_setkeycode(unsigned int scancode, unsigned int keycode) 103{ 104 return 0; 105} 106 107int ec3104_kbd_getkeycode(unsigned int scancode) 108{ 109 return 0; 110} 111 112 113/* yes, it probably would be faster to use an array. I don't care. */ 114 115static inline unsigned char ec3104_scan2key(unsigned char scancode) 116{ 117 switch (scancode) { 118 case 1: /* '`' */ 119 return 41; 120 121 case 2 ... 27: 122 return scancode; 123 124 case 28: /* '\\' */ 125 return 43; 126 127 case 29 ... 39: 128 return scancode + 1; 129 130 case 40: /* '\r' */ 131 return 28; 132 133 case 41 ... 50: 134 return scancode + 3; 135 136 case 51: /* ' ' */ 137 return 57; 138 139 case 52: /* escape */ 140 return 1; 141 142 case 54: /* insert/delete (labelled delete) */ 143 /* this should arguably be 110, but I'd like to have ctrl-alt-del 144 * working with a standard keymap */ 145 return 111; 146 147 case 55: /* left */ 148 return 105; 149 case 56: /* home */ 150 return 102; 151 case 57: /* end */ 152 return 107; 153 case 58: /* up */ 154 return 103; 155 case 59: /* down */ 156 return 108; 157 case 60: /* pgup */ 158 return 104; 159 case 61: /* pgdown */ 160 return 109; 161 case 62: /* right */ 162 return 106; 163 164 case 79 ... 88: /* f1 - f10 */ 165 return scancode - 20; 166 167 case 89 ... 90: /* f11 - f12 */ 168 return scancode - 2; 169 170 case 91: /* left shift */ 171 return 42; 172 173 case 92: /* right shift */ 174 return 54; 175 176 case 93: /* left alt */ 177 return 56; 178 case 94: /* right alt */ 179 return 100; 180 case 95: /* left ctrl */ 181 return 29; 182 case 96: /* right ctrl */ 183 return 97; 184 185 case 97: /* caps lock */ 186 return 58; 187 case 102: /* left windows */ 188 return 125; 189 case 103: /* right windows */ 190 return 126; 191 192 case 106: /* Fn */ 193 /* this is wrong. */ 194 return 84; 195 196 default: 197 return 0; 198 } 199} 200 201int ec3104_kbd_translate(unsigned char scancode, unsigned char *keycode, 202 char raw_mode) 203{ 204 scancode &= 0x7f; 205 206 *keycode = ec3104_scan2key(scancode); 207 208 return 1; 209} 210 211char ec3104_kbd_unexpected_up(unsigned char keycode) 212{ 213 return 0200; 214} 215 216static inline void handle_keyboard_event(unsigned char scancode) 217{ 218#ifdef CONFIG_VT 219 handle_scancode(scancode, !(scancode & 0x80)); 220#endif 221 tasklet_schedule(&keyboard_tasklet); 222} 223 224void ec3104_kbd_leds(unsigned char leds) 225{ 226} 227 228static u8 e5_checksum(u8 *packet, int count) 229{ 230 int i; 231 u8 sum = 0; 232 233 for (i=0; i<count; i++) 234 sum ^= packet[i]; 235 236 if (sum & 0x80) 237 sum ^= 0xc0; 238 239 return sum; 240} 241 242static void e5_wait_for_cts(struct e5_struct *k) 243{ 244 u8 msr; 245 246 do { 247 msr = ctrl_inb(EC3104_SER4_MSR); 248 } while (!(msr & MSR_CTS)); 249} 250 251 252static void e5_send_byte(u8 byte, struct e5_struct *k) 253{ 254 u8 status; 255 256 do { 257 status = ctrl_inb(EC3104_SER4_LSR); 258 } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); 259 260 printk("<%02x>", byte); 261 262 ctrl_outb(byte, EC3104_SER4_DATA); 263 264 do { 265 status = ctrl_inb(EC3104_SER4_LSR); 266 } while ((status & LSR_BOTH_EMPTY) != LSR_BOTH_EMPTY); 267 268} 269 270static int e5_send_packet(u8 *packet, int count, struct e5_struct *k) 271{ 272 int i; 273 274 disable_irq(EC3104_IRQ_SER4); 275 276 if (k->cached_mcr & MCR_RTS) { 277 printk("e5_send_packet: too slow\n"); 278 enable_irq(EC3104_IRQ_SER4); 279 return -EAGAIN; 280 } 281 282 k->cached_mcr |= MCR_RTS; 283 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 284 285 e5_wait_for_cts(k); 286 287 printk("p: "); 288 289 for(i=0; i<count; i++) 290 e5_send_byte(packet[i], k); 291 292 e5_send_byte(e5_checksum(packet, count), k); 293 294 printk("\n"); 295 296 udelay(1500); 297 298 k->cached_mcr &= ~MCR_RTS; 299 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 300 301 set_current_state(TASK_UNINTERRUPTIBLE); 302 303 304 305 enable_irq(EC3104_IRQ_SER4); 306 307 308 309 return 0; 310} 311 312/* 313 * E5 packets we know about: 314 * E5->host 0x80 0x05 <checksum> - resend packet 315 * host->E5 0x83 0x43 <contrast> - set LCD contrast 316 * host->E5 0x85 0x41 0x02 <brightness> 0x02 - set LCD backlight 317 * E5->host 0x87 <ps2 packet> 0x00 <checksum> - external PS2 318 * E5->host 0x88 <scancode> <checksum> - key press 319 */ 320 321static void e5_receive(struct e5_struct *k) 322{ 323 k->packet[k->pos++] = ctrl_inb(EC3104_SER4_DATA); 324 325 if (k->pos == 1) { 326 switch(k->packet[0]) { 327 case 0x80: 328 k->length = 3; 329 break; 330 331 case 0x87: /* PS2 ext */ 332 k->length = 6; 333 break; 334 335 case 0x88: /* keyboard */ 336 k->length = 3; 337 break; 338 339 default: 340 k->length = 1; 341 printk(KERN_WARNING "unknown E5 packet %02x\n", 342 k->packet[0]); 343 } 344 } 345 346 if (k->pos == k->length) { 347 int i; 348 349 if (e5_checksum(k->packet, k->length) != 0) 350 printk(KERN_WARNING "E5: wrong checksum\n"); 351 352 353 switch(k->packet[0]) { 354 case 0x80: 355 case 0x88: 356 handle_keyboard_event(k->packet[1]); 357 break; 358 } 359 360 k->pos = k->length = 0; 361 } 362} 363 364static void ec3104_keyb_interrupt(int irq, void *data) 365{ 366 struct e5_struct *k = &ec3104_keyb; 367 u8 msr, lsr; 368 369 msr = ctrl_inb(EC3104_SER4_MSR); 370 371 if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { 372 if (k->cached_mcr & MCR_RTS) 373 printk("confused: RTS already high\n"); 374 /* CTS went high. Send RTS. */ 375 k->cached_mcr |= MCR_RTS; 376 377 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 378 } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { 379 /* CTS went low. */ 380 if (!(k->cached_mcr & MCR_RTS)) 381 printk("confused: RTS already low\n"); 382 383 k->cached_mcr &= ~MCR_RTS; 384 385 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 386 } 387 388 k->last_msr = msr; 389 390 lsr = ctrl_inb(EC3104_SER4_LSR); 391 392 if (lsr & LSR_DR) 393 e5_receive(k); 394} 395 396static void ec3104_keyb_clear_state(void) 397{ 398 struct e5_struct *k = &ec3104_keyb; 399 u8 msr, lsr; 400 401 /* we want CTS to be low */ 402 k->last_msr = 0; 403 404 for (;;) { 405 msleep(100); 406 407 msr = ctrl_inb(EC3104_SER4_MSR); 408 409 lsr = ctrl_inb(EC3104_SER4_LSR); 410 411 if (lsr & LSR_DR) { 412 e5_receive(k); 413 continue; 414 } 415 416 if ((msr & MSR_CTS) && !(k->last_msr & MSR_CTS)) { 417 if (k->cached_mcr & MCR_RTS) 418 printk("confused: RTS already high\n"); 419 /* CTS went high. Send RTS. */ 420 k->cached_mcr |= MCR_RTS; 421 422 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 423 } else if ((!(msr & MSR_CTS)) && (k->last_msr & MSR_CTS)) { 424 /* CTS went low. */ 425 if (!(k->cached_mcr & MCR_RTS)) 426 printk("confused: RTS already low\n"); 427 428 k->cached_mcr &= ~MCR_RTS; 429 430 ctrl_outb(k->cached_mcr, EC3104_SER4_MCR); 431 } else 432 break; 433 434 k->last_msr = msr; 435 436 continue; 437 } 438} 439 440void __init ec3104_kbd_init_hw(void) 441{ 442 ec3104_keyb.last_msr = ctrl_inb(EC3104_SER4_MSR); 443 ec3104_keyb.cached_mcr = ctrl_inb(EC3104_SER4_MCR); 444 445 ec3104_keyb_clear_state(); 446 447 /* Ok, finally allocate the IRQ, and off we go.. */ 448 request_irq(EC3104_IRQ_SER4, ec3104_keyb_interrupt, 0, "keyboard", NULL); 449} 450