FreeBSD/Linux Kernel Cross Reference
sys/dev/ic/pckbc.c
1 /* $NetBSD: pckbc.c,v 1.32 2004/03/24 17:26:53 drochner Exp $ */
2
3 /*
4 * Copyright (c) 2004 Ben Harris.
5 * Copyright (c) 1998
6 * Matthias Drochner. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pckbc.c,v 1.32 2004/03/24 17:26:53 drochner Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/callout.h>
35 #include <sys/kernel.h>
36 #include <sys/proc.h>
37 #include <sys/device.h>
38 #include <sys/malloc.h>
39 #include <sys/errno.h>
40 #include <sys/queue.h>
41 #include <sys/lock.h>
42
43 #include <machine/bus.h>
44
45 #include <dev/ic/i8042reg.h>
46 #include <dev/ic/pckbcvar.h>
47
48 #include <dev/pckbport/pckbportvar.h>
49
50 #include "rnd.h"
51 #include "locators.h"
52
53 #if NRND > 0
54 #include <sys/rnd.h>
55 #endif
56
57 /* data per slave device */
58 struct pckbc_slotdata {
59 int polling; /* don't process data in interrupt handler */
60 int poll_data; /* data read from inr handler if polling */
61 int poll_stat; /* status read from inr handler if polling */
62 #if NRND > 0
63 rndsource_element_t rnd_source;
64 #endif
65 };
66
67 static void pckbc_init_slotdata __P((struct pckbc_slotdata *));
68 static int pckbc_attach_slot __P((struct pckbc_softc *, pckbc_slot_t));
69
70 struct pckbc_internal pckbc_consdata;
71 int pckbc_console_attached;
72
73 static int pckbc_console;
74 static struct pckbc_slotdata pckbc_cons_slotdata;
75
76 static int pckbc_xt_translation __P((void *, pckbport_slot_t, int));
77 static int pckbc_send_devcmd __P((void *, pckbport_slot_t, u_char));
78 static void pckbc_slot_enable __P((void *, pckbport_slot_t, int));
79 static void pckbc_intr_establish __P((void *, pckbport_slot_t));
80 static void pckbc_set_poll __P((void *, pckbc_slot_t, int on));
81
82 static int pckbc_wait_output __P((bus_space_tag_t, bus_space_handle_t));
83
84 static int pckbc_get8042cmd __P((struct pckbc_internal *));
85 static int pckbc_put8042cmd __P((struct pckbc_internal *));
86
87 void pckbc_cleanqueue __P((struct pckbc_slotdata *));
88 void pckbc_cleanup __P((void *));
89 int pckbc_cmdresponse __P((struct pckbc_internal *, pckbc_slot_t, u_char));
90 void pckbc_start __P((struct pckbc_internal *, pckbc_slot_t));
91
92 const char * const pckbc_slot_names[] = { "kbd", "aux" };
93
94 static struct pckbport_accessops const pckbc_ops = {
95 pckbc_xt_translation,
96 pckbc_send_devcmd,
97 pckbc_poll_data1,
98 pckbc_slot_enable,
99 pckbc_intr_establish,
100 pckbc_set_poll
101 };
102
103 #define KBD_DELAY DELAY(8)
104
105 static inline int
106 pckbc_wait_output(iot, ioh_c)
107 bus_space_tag_t iot;
108 bus_space_handle_t ioh_c;
109 {
110 u_int i;
111
112 for (i = 100000; i; i--)
113 if (!(bus_space_read_1(iot, ioh_c, 0) & KBS_IBF)) {
114 KBD_DELAY;
115 return (1);
116 }
117 return (0);
118 }
119
120 int
121 pckbc_send_cmd(iot, ioh_c, val)
122 bus_space_tag_t iot;
123 bus_space_handle_t ioh_c;
124 u_char val;
125 {
126 if (!pckbc_wait_output(iot, ioh_c))
127 return (0);
128 bus_space_write_1(iot, ioh_c, 0, val);
129 return (1);
130 }
131
132 /*
133 * Note: the spl games here are to deal with some strange PC kbd controllers
134 * in some system configurations.
135 * This is not canonical way to handle polling input.
136 */
137 int
138 pckbc_poll_data1(pt, slot)
139 void *pt;
140 pckbc_slot_t slot;
141 {
142 struct pckbc_internal *t = pt;
143 struct pckbc_slotdata *q = t->t_slotdata[slot];
144 int s;
145 u_char stat, c;
146 int i = 100000; /* if 1 port read takes 1us (?), this polls for 100ms */
147 int checkaux = t->t_haveaux;
148
149 s = splhigh();
150
151 if (q && q->polling && q->poll_data != -1 && q->poll_stat != -1) {
152 stat = q->poll_stat;
153 c = q->poll_data;
154 q->poll_data = -1;
155 q->poll_stat = -1;
156 goto process;
157 }
158
159 for (; i; i--) {
160 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
161 if (stat & KBS_DIB) {
162 KBD_DELAY;
163 c = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
164
165 process:
166 if (checkaux && (stat & 0x20)) { /* aux data */
167 if (slot != PCKBC_AUX_SLOT) {
168 #ifdef PCKBCDEBUG
169 printf("lost aux 0x%x\n", c);
170 #endif
171 continue;
172 }
173 } else {
174 if (slot == PCKBC_AUX_SLOT) {
175 #ifdef PCKBCDEBUG
176 printf("lost kbd 0x%x\n", c);
177 #endif
178 continue;
179 }
180 }
181 splx(s);
182 return (c);
183 }
184 }
185
186 splx(s);
187 return (-1);
188 }
189
190 /*
191 * Get the current command byte.
192 */
193 static int
194 pckbc_get8042cmd(t)
195 struct pckbc_internal *t;
196 {
197 bus_space_tag_t iot = t->t_iot;
198 bus_space_handle_t ioh_c = t->t_ioh_c;
199 int data;
200
201 if (!pckbc_send_cmd(iot, ioh_c, K_RDCMDBYTE))
202 return (0);
203 data = pckbc_poll_data1(t, PCKBC_KBD_SLOT);
204 if (data == -1)
205 return (0);
206 t->t_cmdbyte = data;
207 return (1);
208 }
209
210 /*
211 * Pass command byte to keyboard controller (8042).
212 */
213 static int
214 pckbc_put8042cmd(t)
215 struct pckbc_internal *t;
216 {
217 bus_space_tag_t iot = t->t_iot;
218 bus_space_handle_t ioh_d = t->t_ioh_d;
219 bus_space_handle_t ioh_c = t->t_ioh_c;
220
221 if (!pckbc_send_cmd(iot, ioh_c, K_LDCMDBYTE))
222 return (0);
223 if (!pckbc_wait_output(iot, ioh_c))
224 return (0);
225 bus_space_write_1(iot, ioh_d, 0, t->t_cmdbyte);
226 return (1);
227 }
228
229 static int
230 pckbc_send_devcmd(pt, slot, val)
231 void *pt;
232 pckbc_slot_t slot;
233 u_char val;
234 {
235 struct pckbc_internal *t = pt;
236 bus_space_tag_t iot = t->t_iot;
237 bus_space_handle_t ioh_d = t->t_ioh_d;
238 bus_space_handle_t ioh_c = t->t_ioh_c;
239
240 if (slot == PCKBC_AUX_SLOT) {
241 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXWRITE))
242 return (0);
243 }
244 if (!pckbc_wait_output(iot, ioh_c))
245 return (0);
246 bus_space_write_1(iot, ioh_d, 0, val);
247 return (1);
248 }
249
250 int
251 pckbc_is_console(iot, addr)
252 bus_space_tag_t iot;
253 bus_addr_t addr;
254 {
255 if (pckbc_console && !pckbc_console_attached &&
256 pckbc_consdata.t_iot == iot &&
257 pckbc_consdata.t_addr == addr)
258 return (1);
259 return (0);
260 }
261
262 static int
263 pckbc_attach_slot(sc, slot)
264 struct pckbc_softc *sc;
265 pckbc_slot_t slot;
266 {
267 struct pckbc_internal *t = sc->id;
268 struct pckbc_attach_args pa;
269 void *sdata;
270 struct device *child;
271 int alloced = 0;
272
273 pa.pa_tag = t;
274 pa.pa_slot = slot;
275
276 if (t->t_slotdata[slot] == NULL) {
277 sdata = malloc(sizeof(struct pckbc_slotdata),
278 M_DEVBUF, M_NOWAIT);
279 if (sdata == NULL) {
280 printf("%s: no memory\n", sc->sc_dv.dv_xname);
281 return (0);
282 }
283 t->t_slotdata[slot] = sdata;
284 pckbc_init_slotdata(t->t_slotdata[slot]);
285 alloced++;
286 }
287
288 child = pckbport_attach_slot(&sc->sc_dv, t->t_pt, slot);
289
290 if (child == NULL && alloced) {
291 free(t->t_slotdata[slot], M_DEVBUF);
292 t->t_slotdata[slot] = NULL;
293 }
294
295 #if NRND > 0
296 if (child != NULL && t->t_slotdata[slot] != NULL)
297 rnd_attach_source(&t->t_slotdata[slot]->rnd_source,
298 child->dv_xname, RND_TYPE_TTY, 0);
299 #endif
300 return child != NULL;
301 }
302
303 void
304 pckbc_attach(sc)
305 struct pckbc_softc *sc;
306 {
307 struct pckbc_internal *t;
308 bus_space_tag_t iot;
309 bus_space_handle_t ioh_d, ioh_c;
310 int res;
311 u_char cmdbits = 0;
312
313 t = sc->id;
314 iot = t->t_iot;
315 ioh_d = t->t_ioh_d;
316 ioh_c = t->t_ioh_c;
317
318 t->t_pt = pckbport_attach(t, &pckbc_ops);
319 if (t->t_pt == NULL) {
320 aprint_error(": attach failed\n");
321 return;
322 }
323
324 /* flush */
325 (void) pckbc_poll_data1(t, PCKBC_KBD_SLOT);
326
327 /* set initial cmd byte */
328 if (!pckbc_put8042cmd(t)) {
329 printf("kbc: cmd word write error\n");
330 return;
331 }
332
333 /*
334 * XXX Don't check the keyboard port. There are broken keyboard controllers
335 * which don't pass the test but work normally otherwise.
336 */
337 #if 0
338 /*
339 * check kbd port ok
340 */
341 if (!pckbc_send_cmd(iot, ioh_c, KBC_KBDTEST))
342 return;
343 res = pckbc_poll_data1(t, PCKBC_KBD_SLOT, 0);
344
345 /*
346 * Normally, we should get a "" here.
347 * But there are keyboard controllers behaving differently.
348 */
349 if (res == 0 || res == 0xfa || res == 0x01 || res == 0xab) {
350 #ifdef PCKBCDEBUG
351 if (res != 0)
352 printf("kbc: returned %x on kbd slot test\n", res);
353 #endif
354 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT))
355 cmdbits |= KC8_KENABLE;
356 } else {
357 printf("kbc: kbd port test: %x\n", res);
358 return;
359 }
360 #else
361 if (pckbc_attach_slot(sc, PCKBC_KBD_SLOT))
362 cmdbits |= KC8_KENABLE;
363 #endif /* 0 */
364
365 /*
366 * Check aux port ok.
367 * Avoid KBC_AUXTEST because it hangs some older controllers
368 * (eg UMC880?).
369 */
370 if (!pckbc_send_cmd(iot, ioh_c, KBC_AUXECHO)) {
371 printf("kbc: aux echo error 1\n");
372 goto nomouse;
373 }
374 if (!pckbc_wait_output(iot, ioh_c)) {
375 printf("kbc: aux echo error 2\n");
376 goto nomouse;
377 }
378 t->t_haveaux = 1;
379 bus_space_write_1(iot, ioh_d, 0, 0x5a); /* a random value */
380 res = pckbc_poll_data1(t, PCKBC_AUX_SLOT);
381 if (res != -1) {
382 /*
383 * In most cases, the 0x5a gets echoed.
384 * Some older controllers (Gateway 2000 circa 1993)
385 * return 0xfe here.
386 * We are satisfied if there is anything in the
387 * aux output buffer.
388 */
389 if (pckbc_attach_slot(sc, PCKBC_AUX_SLOT))
390 cmdbits |= KC8_MENABLE;
391 } else {
392 #ifdef PCKBCDEBUG
393 printf("kbc: aux echo test failed\n");
394 #endif
395 t->t_haveaux = 0;
396 }
397
398 nomouse:
399 /* enable needed interrupts */
400 t->t_cmdbyte |= cmdbits;
401 if (!pckbc_put8042cmd(t))
402 printf("kbc: cmd word write error\n");
403 }
404
405 static void
406 pckbc_init_slotdata(q)
407 struct pckbc_slotdata *q;
408 {
409
410 q->polling = 0;
411 }
412
413 /*
414 * switch scancode translation on / off
415 * return nonzero on success
416 */
417 static int
418 pckbc_xt_translation(self, slot, on)
419 void *self;
420 pckbc_slot_t slot;
421 int on;
422 {
423 struct pckbc_internal *t = self;
424 int ison;
425
426 if (slot != PCKBC_KBD_SLOT) {
427 /* translation only for kbd slot */
428 if (on)
429 return (0);
430 else
431 return (1);
432 }
433
434 ison = t->t_cmdbyte & KC8_TRANS;
435 if ((on && ison) || (!on && !ison))
436 return (1);
437
438 t->t_cmdbyte ^= KC8_TRANS;
439 if (!pckbc_put8042cmd(t))
440 return (0);
441
442 /* read back to be sure */
443 if (!pckbc_get8042cmd(t))
444 return (0);
445
446 ison = t->t_cmdbyte & KC8_TRANS;
447 if ((on && ison) || (!on && !ison))
448 return (1);
449 return (0);
450 }
451
452 static const struct pckbc_portcmd {
453 u_char cmd_en, cmd_dis;
454 } pckbc_portcmd[2] = {
455 {
456 KBC_KBDENABLE, KBC_KBDDISABLE,
457 }, {
458 KBC_AUXENABLE, KBC_AUXDISABLE,
459 }
460 };
461
462 void
463 pckbc_slot_enable(self, slot, on)
464 void *self;
465 pckbc_slot_t slot;
466 int on;
467 {
468 struct pckbc_internal *t = (struct pckbc_internal *)self;
469 const struct pckbc_portcmd *cmd;
470
471 cmd = &pckbc_portcmd[slot];
472
473 if (!pckbc_send_cmd(t->t_iot, t->t_ioh_c,
474 on ? cmd->cmd_en : cmd->cmd_dis))
475 printf("pckbc_slot_enable(%d) failed\n", on);
476 }
477
478 static void
479 pckbc_set_poll(self, slot, on)
480 void *self;
481 pckbc_slot_t slot;
482 int on;
483 {
484 struct pckbc_internal *t = (struct pckbc_internal *)self;
485
486 t->t_slotdata[slot]->polling = on;
487
488 if (on) {
489 t->t_slotdata[slot]->poll_data = -1;
490 t->t_slotdata[slot]->poll_stat = -1;
491 } else {
492 int s;
493
494 /*
495 * If disabling polling on a device that's been configured,
496 * make sure there are no bytes left in the FIFO, holding up
497 * the interrupt line. Otherwise we won't get any further
498 * interrupts.
499 */
500 if (t->t_sc) {
501 s = spltty();
502 pckbcintr(t->t_sc);
503 splx(s);
504 }
505 }
506 }
507
508 static void
509 pckbc_intr_establish(pt, slot)
510 void *pt;
511 pckbport_slot_t slot;
512 {
513 struct pckbc_internal *t = pt;
514
515 (*t->t_sc->intr_establish)(t->t_sc, slot);
516 }
517
518 int
519 pckbcintr_hard(vsc)
520 void *vsc;
521 {
522 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
523 struct pckbc_internal *t = sc->id;
524 u_char stat;
525 pckbc_slot_t slot;
526 struct pckbc_slotdata *q;
527 int served = 0, data, next, s;
528
529 for(;;) {
530 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
531 if (!(stat & KBS_DIB))
532 break;
533
534 served = 1;
535
536 slot = (t->t_haveaux && (stat & 0x20)) ?
537 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
538 q = t->t_slotdata[slot];
539
540 if (!q) {
541 /* XXX do something for live insertion? */
542 printf("pckbcintr: no dev for slot %d\n", slot);
543 KBD_DELAY;
544 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
545 continue;
546 }
547
548 KBD_DELAY;
549 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
550
551 #if NRND > 0
552 rnd_add_uint32(&q->rnd_source, (stat<<8)|data);
553 #endif
554
555 if (q->polling) {
556 q->poll_data = data;
557 q->poll_stat = stat;
558 break; /* pckbc_poll_data() will get it */
559 }
560
561 #if 0 /* XXXBJH */
562 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
563 continue;
564 #endif
565
566 s = splhigh();
567 next = (t->rbuf_write+1) % PCKBC_RBUF_SIZE;
568 if (next == t->rbuf_read) {
569 splx(s);
570 break;
571 }
572 t->rbuf[t->rbuf_write].data = data;
573 t->rbuf[t->rbuf_write].slot = slot;
574 t->rbuf_write = next;
575 splx(s);
576 }
577
578 return (served);
579 }
580
581 void
582 pckbcintr_soft(vsc)
583 void *vsc;
584 {
585 struct pckbc_softc *sc = vsc;
586 struct pckbc_internal *t = sc->id;
587 int data, slot, s;
588 #ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
589 int st;
590
591 st = spltty();
592 #endif
593
594 s = splhigh();
595 while (t->rbuf_read != t->rbuf_write) {
596 slot = t->rbuf[t->rbuf_read].slot;
597 data = t->rbuf[t->rbuf_read].data;
598 t->rbuf_read = (t->rbuf_read+1) % PCKBC_RBUF_SIZE;
599 splx(s);
600 pckbportintr(t->t_pt, slot, data);
601 s = splhigh();
602 }
603 splx(s);
604
605
606 #ifndef __GENERIC_SOFT_INTERRUPTS_ALL_LEVELS
607 splx(st);
608 #endif
609 }
610
611 int
612 pckbcintr(vsc)
613 void *vsc;
614 {
615 struct pckbc_softc *sc = (struct pckbc_softc *)vsc;
616 struct pckbc_internal *t = sc->id;
617 u_char stat;
618 pckbc_slot_t slot;
619 struct pckbc_slotdata *q;
620 int served = 0, data;
621
622 for(;;) {
623 stat = bus_space_read_1(t->t_iot, t->t_ioh_c, 0);
624 if (!(stat & KBS_DIB))
625 break;
626
627 served = 1;
628
629 slot = (t->t_haveaux && (stat & 0x20)) ?
630 PCKBC_AUX_SLOT : PCKBC_KBD_SLOT;
631 q = t->t_slotdata[slot];
632
633 KBD_DELAY;
634 data = bus_space_read_1(t->t_iot, t->t_ioh_d, 0);
635
636 #if NRND > 0
637 rnd_add_uint32(&q->rnd_source, (stat<<8)|data);
638 #endif
639
640 pckbportintr(t->t_pt, slot, data);
641 }
642
643 return (served);
644 }
645
646 int
647 pckbc_cnattach(iot, addr, cmd_offset, slot)
648 bus_space_tag_t iot;
649 bus_addr_t addr;
650 bus_size_t cmd_offset;
651 pckbc_slot_t slot;
652 {
653 bus_space_handle_t ioh_d, ioh_c;
654 #ifdef PCKBC_CNATTACH_SELFTEST
655 int reply;
656 #endif
657 int res = 0;
658
659 if (bus_space_map(iot, addr + KBDATAP, 1, 0, &ioh_d))
660 return (ENXIO);
661 if (bus_space_map(iot, addr + cmd_offset, 1, 0, &ioh_c)) {
662 bus_space_unmap(iot, ioh_d, 1);
663 return (ENXIO);
664 }
665
666 memset(&pckbc_consdata, 0, sizeof(pckbc_consdata));
667 pckbc_consdata.t_iot = iot;
668 pckbc_consdata.t_ioh_d = ioh_d;
669 pckbc_consdata.t_ioh_c = ioh_c;
670 pckbc_consdata.t_addr = addr;
671 callout_init(&pckbc_consdata.t_cleanup);
672
673 /* flush */
674 (void) pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT);
675
676 #ifdef PCKBC_CNATTACH_SELFTEST
677 /*
678 * In some machines (e.g. netwinder) pckbc refuses to talk at
679 * all until we request a self-test.
680 */
681 if (!pckbc_send_cmd(iot, ioh_c, KBC_SELFTEST)) {
682 printf("kbc: unable to request selftest\n");
683 res = EIO;
684 goto out;
685 }
686
687 reply = pckbc_poll_data1(&pckbc_consdata, PCKBC_KBD_SLOT);
688 if (reply != 0x55) {
689 printf("kbc: selftest returned 0x%02x\n", reply);
690 res = EIO;
691 goto out;
692 }
693 #endif /* PCKBC_CNATTACH_SELFTEST */
694
695 /* init cmd byte, enable ports */
696 pckbc_consdata.t_cmdbyte = KC8_CPU;
697 if (!pckbc_put8042cmd(&pckbc_consdata)) {
698 printf("kbc: cmd word write error\n");
699 res = EIO;
700 goto out;
701 }
702
703 res = pckbport_cnattach(&pckbc_consdata, &pckbc_ops, slot);
704
705 out:
706 if (res) {
707 bus_space_unmap(iot, pckbc_consdata.t_ioh_d, 1);
708 bus_space_unmap(iot, pckbc_consdata.t_ioh_c, 1);
709 } else {
710 pckbc_consdata.t_slotdata[slot] = &pckbc_cons_slotdata;
711 pckbc_init_slotdata(&pckbc_cons_slotdata);
712 pckbc_console = 1;
713 }
714
715 return (res);
716 }
Cache object: 987152275537a091b1052f6fc2b99141
|