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