1 /* $OpenBSD: gsckbc.c,v 1.22 2023/07/25 10:00:44 miod Exp $ */
2 /*
3 * Copyright (c) 2003, Miodrag Vallat.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF MIND,
21 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 /*
28 * Derived from /sys/dev/ic/pckbd.c under the following terms:
29 * OpenBSD: pckbc.c,v 1.5 2002/06/09 00:58:03 nordin Exp
30 * NetBSD: pckbc.c,v 1.5 2000/06/09 04:58:35 soda Exp
31 */
32 /*
33 * Copyright (c) 1998
34 * Matthias Drochner. All rights reserved.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed for the NetBSD Project
47 * by Matthias Drochner.
48 * 4. The name of the author may not be used to endorse or promote products
49 * derived from this software without specific prior written permission.
50 *
51 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
52 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
53 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
54 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
55 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
56 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
57 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
58 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
59 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
60 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
61 */
62
63 /*
64 * Driver for the PS/2-like keyboard and mouse ports found on 712 and 715
65 * models, among others.
66 *
67 * Contrary to the ``pckbc'' port set found on other arches, the
68 * keyboard and mouse port are two separate entities on the snakes, and
69 * they are driven by a custom chip not 8042-compatible.
70 */
71
72 #include "pckbd.h"
73
74 #include <sys/param.h>
75 #include <sys/systm.h>
76 #include <sys/device.h>
77 #include <sys/kernel.h>
78 #include <sys/malloc.h>
79 #include <sys/proc.h>
80
81 #include <machine/autoconf.h>
82 #include <machine/bus.h>
83 #include <machine/intr.h>
84 #include <machine/iomod.h>
85
86 #include <hppa/dev/cpudevs.h>
87 #include <hppa/gsc/gscbusvar.h>
88
89 #include <hppa/gsc/gsckbcreg.h>
90 #include <dev/ic/pckbcvar.h>
91
92 #include <dev/pckbc/pckbdreg.h> /* constants for probe magic */
93 #include <dev/pckbc/pckbdvar.h>
94
95 int gsckbc_match(struct device *, void *, void *);
96 void gsckbc_attach(struct device *, struct device *, void *);
97
98 struct gsckbc_softc {
99 struct pckbc_softc sc_pckbc;
100
101 void *sc_ih;
102 int sc_type;
103 };
104
105 const struct cfattach gsckbc_ca = {
106 sizeof(struct gsckbc_softc), gsckbc_match, gsckbc_attach
107 };
108
109 struct cfdriver gsckbc_cd = {
110 NULL, "gsckbc", DV_DULL
111 };
112
113 /* descriptor for one device command */
114 struct pckbc_devcmd {
115 TAILQ_ENTRY(pckbc_devcmd) next;
116 int flags;
117 #define KBC_CMDFLAG_SYNC 1 /* give descriptor back to caller */
118 #define KBC_CMDFLAG_SLOW 2
119 u_char cmd[4];
120 int cmdlen, cmdidx, retries;
121 u_char response[4];
122 int status, responselen, responseidx;
123 };
124
125 /* data per slave device */
126 struct pckbc_slotdata {
127 int polling; /* don't read data port in interrupt handler */
128 TAILQ_HEAD(, pckbc_devcmd) cmdqueue; /* active commands */
129 TAILQ_HEAD(, pckbc_devcmd) freequeue; /* free commands */
130 #define NCMD 5
131 struct pckbc_devcmd cmds[NCMD];
132 };
133
134 #define CMD_IN_QUEUE(q) (TAILQ_FIRST(&(q)->cmdqueue) != NULL)
135 /* Force polling mode behaviour for boot -a XXX */
136 #define IS_POLLING(q) ((q)->polling || cold)
137
138 void pckbc_init_slotdata(struct pckbc_slotdata *);
139 int pckbc_attach_slot(struct pckbc_softc *, pckbc_slot_t);
140 int pckbc_submatch(struct device *, void *, void *);
141 int pckbcprint(void *, const char *);
142
143 int pckbc_wait_output(bus_space_tag_t, bus_space_handle_t);
144 int pckbc_send_devcmd(struct pckbc_internal *, pckbc_slot_t,
145 u_char);
146 void pckbc_poll_cmd1(struct pckbc_internal *, pckbc_slot_t,
147 struct pckbc_devcmd *);
148
149 void pckbc_cleanqueue(struct pckbc_slotdata *);
150 void pckbc_cleanup(void *);
151 int pckbc_cmdresponse(struct pckbc_internal *, pckbc_slot_t, u_char);
152 void pckbc_start(struct pckbc_internal *, pckbc_slot_t);
153 int gsckbcintr(void *);
154
155 const char *pckbc_slot_names[] = { "kbd", "mouse" };
156
157 #define KBC_DEVCMD_ACK 0xfa
158 #define KBC_DEVCMD_RESEND 0xfe
159
160 #define KBD_DELAY DELAY(8)
161
162 int
gsckbc_match(struct device * parent,void * match,void * aux)163 gsckbc_match(struct device *parent, void *match, void *aux)
164 {
165 struct gsc_attach_args *ga = aux;
166 bus_space_handle_t ioh;
167 u_int8_t rv;
168
169 if (ga->ga_type.iodc_type != HPPA_TYPE_FIO ||
170 ga->ga_type.iodc_sv_model != HPPA_FIO_GPCIO)
171 return (0);
172
173 /* Map the i/o space. */
174 if (bus_space_map(ga->ga_ca.ca_iot, ga->ga_ca.ca_hpa,
175 KBMAPSIZE, 0, &ioh))
176 return 0;
177
178 rv = bus_space_read_1(ga->ga_ca.ca_iot, ioh, KBIDP);
179 bus_space_unmap(ga->ga_ca.ca_iot, ioh, KBMAPSIZE);
180
181 if (rv == PCKBC_KBD_SLOT || rv == PCKBC_AUX_SLOT)
182 return (1); /* keyboard or mouse port */
183
184 return (0);
185 }
186
187 /*
188 * Attachment helper functions
189 */
190
191 /* state machine values */
192 #define PROBE_SUCCESS 0
193 #define PROBE_TIMEOUT 1
194 #define PROBE_RETRANS 2
195 #define PROBE_NOACK 3
196
197 int probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
198 int probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply);
199 int probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
200 int probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte);
201 int probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh);
202
203 #define PROBE_TRIES 1000
204
205 int
probe_readtmo(bus_space_tag_t iot,bus_space_handle_t ioh,int * reply)206 probe_readtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
207 {
208 int numtries = PROBE_TRIES;
209
210 while (numtries--) {
211 if (bus_space_read_1(iot, ioh, KBSTATP) &
212 (KBS_DIB | KBS_TERR | KBS_PERR))
213 break;
214 DELAY(500);
215 }
216
217 if (numtries <= 0)
218 return (PROBE_TIMEOUT);
219
220 if (bus_space_read_1(iot, ioh, KBSTATP) & (KBS_PERR | KBS_TERR)) {
221 if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
222 bus_space_write_1(iot, ioh, KBRESETP, 0xff);
223 bus_space_write_1(iot, ioh, KBRESETP, 0x00);
224 bus_space_write_1(iot, ioh, KBCMDP,
225 bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
226 return (PROBE_TIMEOUT);
227 }
228
229 *reply = bus_space_read_1(iot, ioh, KBDATAP);
230 if (!(bus_space_read_1(iot, ioh, KBSTATP) & KBS_DIB)) {
231 bus_space_write_1(iot, ioh, KBRESETP, 0xff);
232 bus_space_write_1(iot, ioh, KBRESETP, 0x00);
233 bus_space_write_1(iot, ioh, KBCMDP,
234 bus_space_read_1(iot, ioh, KBCMDP) | KBCP_ENABLE);
235 if (probe_sendtmo(iot, ioh, KBR_RESEND))
236 return (PROBE_TIMEOUT);
237 else
238 return (PROBE_RETRANS);
239 } else
240 return (PROBE_SUCCESS);
241 } else {
242 *reply = bus_space_read_1(iot, ioh, KBDATAP);
243 return (PROBE_SUCCESS);
244 }
245 }
246
247 int
probe_readretry(bus_space_tag_t iot,bus_space_handle_t ioh,int * reply)248 probe_readretry(bus_space_tag_t iot, bus_space_handle_t ioh, int *reply)
249 {
250 int read_status;
251 int retrans = KB_MAX_RETRANS;
252
253 do {
254 read_status = probe_readtmo(iot, ioh, reply);
255 } while ((read_status == PROBE_RETRANS) && retrans--);
256
257 return (read_status);
258 }
259
260 int
probe_sendtmo(bus_space_tag_t iot,bus_space_handle_t ioh,int cmdbyte)261 probe_sendtmo(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
262 {
263 int numtries = PROBE_TRIES;
264
265 while (numtries--) {
266 if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) == 0)
267 break;
268 DELAY(500);
269 }
270
271 if (numtries <= 0)
272 return (1);
273
274 bus_space_write_1(iot, ioh, KBDATAP, cmdbyte);
275 bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
276 return (0);
277 }
278
279 int
probe_sendack(bus_space_tag_t iot,bus_space_handle_t ioh,int cmdbyte)280 probe_sendack(bus_space_tag_t iot, bus_space_handle_t ioh, int cmdbyte)
281 {
282 int retranscount;
283 int reply;
284
285 for (retranscount = 0; retranscount < KB_MAX_RETRANS; retranscount++) {
286 if (probe_sendtmo(iot, ioh, cmdbyte))
287 return (PROBE_TIMEOUT);
288 if (probe_readretry(iot, ioh, &reply))
289 return (PROBE_TIMEOUT);
290
291 switch (reply) {
292 case KBR_ACK:
293 return (PROBE_SUCCESS);
294 case KBR_RESEND:
295 break;
296 default:
297 return (PROBE_NOACK);
298 }
299 }
300 return (PROBE_TIMEOUT);
301
302 }
303
304 int
probe_ident(bus_space_tag_t iot,bus_space_handle_t ioh)305 probe_ident(bus_space_tag_t iot, bus_space_handle_t ioh)
306 {
307 int status;
308
309 bus_space_write_1(iot, ioh, KBRESETP, 0);
310 bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
311 DELAY(0x20000); /* XXX why 0x? */
312 bus_space_write_1(iot, ioh, KBCMDP, 0);
313 DELAY(20000);
314 bus_space_write_1(iot, ioh, KBRESETP, 0);
315 bus_space_write_1(iot, ioh, KBCMDP, KBCP_DIAG);
316 DELAY(20000);
317
318 status = probe_sendack(iot, ioh, KBC_DISABLE);
319 switch (status) {
320 case PROBE_TIMEOUT:
321 if (bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD) {
322 bus_space_write_1(iot, ioh, KBRESETP, 0);
323 bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
324 }
325 return (-1);
326 case PROBE_NOACK:
327 return (-1);
328 }
329
330 if (probe_sendack(iot, ioh, KBC_ID) != PROBE_SUCCESS)
331 return (-1);
332
333 if (probe_readretry(iot, ioh, &status))
334 return (-1);
335
336 switch (status) {
337 case KBR_MOUSE_ID:
338 return PCKBC_AUX_SLOT;
339 case KBR_KBD_ID1:
340 if (probe_readretry(iot, ioh, &status))
341 return (-1);
342 if (status == KBR_KBD_ID2) {
343 if (probe_sendack(iot, ioh, KBC_ENABLE) ==
344 PROBE_TIMEOUT) {
345 bus_space_write_1(iot, ioh, KBRESETP, 0);
346 bus_space_write_1(iot, ioh, KBCMDP,
347 KBCP_ENABLE);
348 }
349 return PCKBC_KBD_SLOT;
350 }
351 }
352 return (-1);
353 }
354
355 void
gsckbc_attach(struct device * parent,struct device * self,void * aux)356 gsckbc_attach(struct device *parent, struct device *self, void *aux)
357 {
358 struct gsc_attach_args *ga = aux;
359 struct gsckbc_softc *gsc = (void *)self;
360 struct pckbc_softc *sc = &gsc->sc_pckbc;
361 struct pckbc_internal *t;
362 bus_space_tag_t iot;
363 bus_space_handle_t ioh;
364 int ident;
365
366 iot = ga->ga_ca.ca_iot;
367
368 if (bus_space_map(iot, ga->ga_ca.ca_hpa, KBMAPSIZE, 0, &ioh))
369 panic("gsckbc_attach: couldn't map port");
370
371 gsc->sc_type = bus_space_read_1(iot, ioh, KBIDP);
372
373 switch (gsc->sc_type) {
374 case PCKBC_KBD_SLOT:
375 case PCKBC_AUX_SLOT:
376 break;
377 default:
378 printf(": unknown port type %x\n", gsc->sc_type);
379 /* play nice and don't really attach. */
380 bus_space_unmap(iot, ioh, KBMAPSIZE);
381 return;
382 }
383
384 gsc->sc_ih = gsc_intr_establish((struct gsc_softc *)parent,
385 ga->ga_ca.ca_irq, IPL_TTY, gsckbcintr, sc, sc->sc_dv.dv_xname);
386 if (gsc->sc_ih == NULL) {
387 printf(": can't establish interrupt\n");
388 bus_space_unmap(iot, ioh, KBMAPSIZE);
389 return;
390 }
391
392 printf("\n");
393
394 t = malloc(sizeof(*t), M_DEVBUF, M_WAITOK | M_ZERO);
395 t->t_iot = iot;
396 /* XXX it does not make sense to only map two ports here */
397 t->t_ioh_d = t->t_ioh_c = ioh;
398 t->t_addr = ga->ga_ca.ca_hpa;
399 t->t_sc = sc;
400 timeout_set(&t->t_cleanup, pckbc_cleanup, t);
401 sc->id = t;
402
403 /*
404 * Reset port and probe device, if plugged
405 */
406 ident = probe_ident(iot, ioh);
407 if (ident != gsc->sc_type) {
408 /* don't whine for unplugged ports */
409 if (ident != -1)
410 printf("%s: expecting device type %d, got %d\n",
411 sc->sc_dv.dv_xname, gsc->sc_type, ident);
412 } else {
413 #if (NPCKBD > 0)
414 if (gsc->sc_type == PCKBC_KBD_SLOT &&
415 ga->ga_dp.dp_mod == PAGE0->mem_kbd.pz_dp.dp_mod &&
416 bcmp(ga->ga_dp.dp_bc, PAGE0->mem_kbd.pz_dp.dp_bc, 6) == 0)
417 pckbd_cnattach(t);
418 #endif
419 pckbc_attach_slot(sc, gsc->sc_type);
420 }
421 }
422
423 /*
424 * pckbc-like interfaces
425 */
426
427 int
pckbc_wait_output(iot,ioh)428 pckbc_wait_output(iot, ioh)
429 bus_space_tag_t iot;
430 bus_space_handle_t ioh;
431 {
432 u_int i;
433
434 for (i = 100000; i; i--) {
435 if ((bus_space_read_1(iot, ioh, KBSTATP) & KBS_OCMD)) {
436 KBD_DELAY;
437 } else
438 return (1);
439 }
440 return (0);
441 }
442
443 int
pckbc_send_cmd(iot,ioh,val)444 pckbc_send_cmd(iot, ioh, val)
445 bus_space_tag_t iot;
446 bus_space_handle_t ioh;
447 u_char val;
448 {
449 if (!pckbc_wait_output(iot, ioh))
450 return (0);
451 bus_space_write_1(iot, ioh, KBOUTP, val);
452 bus_space_write_1(iot, ioh, KBCMDP, KBCP_ENABLE);
453 return (1);
454 }
455
456 /* XXX logic */
457 int
pckbc_poll_data1(iot,ioh,ioh_c,slot,checkaux)458 pckbc_poll_data1(iot, ioh, ioh_c, slot, checkaux)
459 bus_space_tag_t iot;
460 bus_space_handle_t ioh, ioh_c;
461 pckbc_slot_t slot;
462 int checkaux; /* ignored on hppa */
463 {
464 int i;
465 u_char stat;
466
467 /* if 1 port read takes 1us (?), this polls for 100ms */
468 for (i = 100000; i; i--) {
469 stat = bus_space_read_1(iot, ioh, KBSTATP);
470 if (stat & KBS_DIB) {
471 KBD_DELAY;
472 return bus_space_read_1(iot, ioh, KBDATAP);
473 }
474 }
475 return (-1);
476 }
477
478 int
pckbc_send_devcmd(t,slot,val)479 pckbc_send_devcmd(t, slot, val)
480 struct pckbc_internal *t;
481 pckbc_slot_t slot;
482 u_char val;
483 {
484 return pckbc_send_cmd(t->t_iot, t->t_ioh_d, val);
485 }
486
487 int
pckbc_submatch(parent,match,aux)488 pckbc_submatch(parent, match, aux)
489 struct device *parent;
490 void *match;
491 void *aux;
492 {
493 struct cfdata *cf = match;
494 struct pckbc_attach_args *pa = aux;
495
496 if (cf->cf_loc[PCKBCCF_SLOT] != PCKBCCF_SLOT_DEFAULT &&
497 cf->cf_loc[PCKBCCF_SLOT] != pa->pa_slot)
498 return (0);
499 return ((*cf->cf_attach->ca_match)(parent, cf, aux));
500 }
501
502 int
pckbc_attach_slot(sc,slot)503 pckbc_attach_slot(sc, slot)
504 struct pckbc_softc *sc;
505 pckbc_slot_t slot;
506 {
507 struct pckbc_internal *t = sc->id;
508 struct pckbc_attach_args pa;
509 int found;
510
511 pa.pa_tag = t;
512 pa.pa_slot = slot;
513 found = (config_found_sm((struct device *)sc, &pa,
514 pckbcprint, pckbc_submatch) != NULL);
515
516 if (found && !t->t_slotdata[slot]) {
517 t->t_slotdata[slot] = malloc(sizeof(struct pckbc_slotdata),
518 M_DEVBUF, M_NOWAIT);
519 if (t->t_slotdata[slot] == NULL)
520 return 0;
521 pckbc_init_slotdata(t->t_slotdata[slot]);
522 }
523 return (found);
524 }
525
526 int
pckbcprint(aux,pnp)527 pckbcprint(aux, pnp)
528 void *aux;
529 const char *pnp;
530 {
531 #if 0 /* hppa having devices for each slot, this is barely useful */
532 struct pckbc_attach_args *pa = aux;
533
534 if (!pnp)
535 printf(" (%s slot)", pckbc_slot_names[pa->pa_slot]);
536 #endif
537 return (QUIET);
538 }
539
540 void
pckbc_init_slotdata(q)541 pckbc_init_slotdata(q)
542 struct pckbc_slotdata *q;
543 {
544 int i;
545 TAILQ_INIT(&q->cmdqueue);
546 TAILQ_INIT(&q->freequeue);
547
548 for (i = 0; i < NCMD; i++) {
549 TAILQ_INSERT_TAIL(&q->freequeue, &(q->cmds[i]), next);
550 }
551 q->polling = 0;
552 }
553
554 void
pckbc_flush(self,slot)555 pckbc_flush(self, slot)
556 pckbc_tag_t self;
557 pckbc_slot_t slot;
558 {
559 struct pckbc_internal *t = self;
560
561 pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
562 }
563
564 int
pckbc_poll_data(self,slot)565 pckbc_poll_data(self, slot)
566 pckbc_tag_t self;
567 pckbc_slot_t slot;
568 {
569 struct pckbc_internal *t = self;
570 struct pckbc_slotdata *q = t->t_slotdata[slot];
571 int c;
572
573 c = pckbc_poll_data1(t->t_iot, t->t_ioh_d, t->t_ioh_d, slot, 0);
574 if (c != -1 && q && CMD_IN_QUEUE(q)) {
575 /* we jumped into a running command - try to
576 deliver the response */
577 if (pckbc_cmdresponse(t, slot, c))
578 return (-1);
579 }
580 return (c);
581 }
582
583 int
pckbc_xt_translation(self,table)584 pckbc_xt_translation(self, table)
585 pckbc_tag_t self;
586 int *table;
587 {
588 /* Translation isn't supported... */
589 return (-1);
590 }
591
592 void
pckbc_slot_enable(self,slot,on)593 pckbc_slot_enable(self, slot, on)
594 pckbc_tag_t self;
595 pckbc_slot_t slot;
596 int on;
597 {
598 /* can't enable slots here as they are different devices */
599 }
600
601 void
pckbc_set_poll(self,slot,on)602 pckbc_set_poll(self, slot, on)
603 pckbc_tag_t self;
604 pckbc_slot_t slot;
605 int on;
606 {
607 struct pckbc_internal *t = (struct pckbc_internal *)self;
608
609 t->t_slotdata[slot]->polling = on;
610
611 if (!on) {
612 int s;
613
614 /*
615 * If disabling polling on a device that's been configured,
616 * make sure there are no bytes left in the FIFO, holding up
617 * the interrupt line. Otherwise we won't get any further
618 * interrupts.
619 */
620 if (t->t_sc) {
621 s = spltty();
622 gsckbcintr(t->t_sc);
623 splx(s);
624 }
625 }
626 }
627
628 /*
629 * Pass command to device, poll for ACK and data.
630 * to be called at spltty()
631 */
632 void
pckbc_poll_cmd1(t,slot,cmd)633 pckbc_poll_cmd1(t, slot, cmd)
634 struct pckbc_internal *t;
635 pckbc_slot_t slot;
636 struct pckbc_devcmd *cmd;
637 {
638 bus_space_tag_t iot = t->t_iot;
639 bus_space_handle_t ioh = t->t_ioh_d;
640 int i, c = 0;
641
642 while (cmd->cmdidx < cmd->cmdlen) {
643 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
644 printf("pckbc_cmd: send error\n");
645 cmd->status = EIO;
646 return;
647 }
648 for (i = 10; i; i--) { /* 1s ??? */
649 c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
650 if (c != -1)
651 break;
652 }
653
654 if (c == KBC_DEVCMD_ACK) {
655 cmd->cmdidx++;
656 continue;
657 }
658 if (c == KBC_DEVCMD_RESEND) {
659 #ifdef PCKBCDEBUG
660 printf("pckbc_cmd: RESEND\n");
661 #endif
662 if (cmd->retries++ < KB_MAX_RETRANS)
663 continue;
664 else {
665 #ifdef PCKBCDEBUG
666 printf("pckbc: cmd failed\n");
667 #endif
668 cmd->status = EIO;
669 return;
670 }
671 }
672 if (c == -1) {
673 #ifdef PCKBCDEBUG
674 printf("pckbc_cmd: timeout\n");
675 #endif
676 cmd->status = EIO;
677 return;
678 }
679 #ifdef PCKBCDEBUG
680 printf("pckbc_cmd: lost 0x%x\n", c);
681 #endif
682 }
683
684 while (cmd->responseidx < cmd->responselen) {
685 if (cmd->flags & KBC_CMDFLAG_SLOW)
686 i = 100; /* 10s ??? */
687 else
688 i = 10; /* 1s ??? */
689 while (i--) {
690 c = pckbc_poll_data1(iot, ioh, ioh, slot, 0);
691 if (c != -1)
692 break;
693 }
694 if (c == -1) {
695 #ifdef PCKBCDEBUG
696 printf("pckbc_cmd: no data\n");
697 #endif
698 cmd->status = ETIMEDOUT;
699 return;
700 } else
701 cmd->response[cmd->responseidx++] = c;
702 }
703 }
704
705 /* for use in autoconfiguration */
706 int
pckbc_poll_cmd(self,slot,cmd,len,responselen,respbuf,slow)707 pckbc_poll_cmd(self, slot, cmd, len, responselen, respbuf, slow)
708 pckbc_tag_t self;
709 pckbc_slot_t slot;
710 u_char *cmd;
711 int len, responselen;
712 u_char *respbuf;
713 int slow;
714 {
715 struct pckbc_devcmd nc;
716
717 if ((len > 4) || (responselen > 4))
718 return (EINVAL);
719
720 bzero(&nc, sizeof(nc));
721 bcopy(cmd, nc.cmd, len);
722 nc.cmdlen = len;
723 nc.responselen = responselen;
724 nc.flags = (slow ? KBC_CMDFLAG_SLOW : 0);
725
726 pckbc_poll_cmd1(self, slot, &nc);
727
728 if (nc.status == 0 && respbuf)
729 bcopy(nc.response, respbuf, responselen);
730
731 return (nc.status);
732 }
733
734 /*
735 * Clean up a command queue, throw away everything.
736 */
737 void
pckbc_cleanqueue(q)738 pckbc_cleanqueue(q)
739 struct pckbc_slotdata *q;
740 {
741 struct pckbc_devcmd *cmd;
742 #ifdef PCKBCDEBUG
743 int i;
744 #endif
745
746 while ((cmd = TAILQ_FIRST(&q->cmdqueue))) {
747 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
748 #ifdef PCKBCDEBUG
749 printf("pckbc_cleanqueue: removing");
750 for (i = 0; i < cmd->cmdlen; i++)
751 printf(" %02x", cmd->cmd[i]);
752 printf("\n");
753 #endif
754 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
755 }
756 }
757
758 /*
759 * Timeout error handler: clean queues and data port.
760 * XXX could be less invasive.
761 */
762 void
pckbc_cleanup(self)763 pckbc_cleanup(self)
764 void *self;
765 {
766 struct pckbc_internal *t = self;
767 int s;
768
769 printf("pckbc: command timeout\n");
770
771 s = spltty();
772
773 if (t->t_slotdata[PCKBC_KBD_SLOT])
774 pckbc_cleanqueue(t->t_slotdata[PCKBC_KBD_SLOT]);
775 if (t->t_slotdata[PCKBC_AUX_SLOT])
776 pckbc_cleanqueue(t->t_slotdata[PCKBC_AUX_SLOT]);
777
778 while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
779 KBD_DELAY;
780 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
781 }
782
783 /* reset KBC? */
784
785 splx(s);
786 }
787
788 /*
789 * Pass command to device during normal operation.
790 * to be called at spltty()
791 */
792 void
pckbc_start(t,slot)793 pckbc_start(t, slot)
794 struct pckbc_internal *t;
795 pckbc_slot_t slot;
796 {
797 struct pckbc_slotdata *q = t->t_slotdata[slot];
798 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
799
800 if (IS_POLLING(q)) {
801 do {
802 pckbc_poll_cmd1(t, slot, cmd);
803 if (cmd->status)
804 printf("pckbc_start: command error\n");
805
806 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
807 if (cmd->flags & KBC_CMDFLAG_SYNC)
808 wakeup(cmd);
809 else {
810 timeout_del(&t->t_cleanup);
811 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
812 }
813 cmd = TAILQ_FIRST(&q->cmdqueue);
814 } while (cmd);
815 return;
816 }
817
818 if (!pckbc_send_devcmd(t, slot, cmd->cmd[cmd->cmdidx])) {
819 printf("pckbc_start: send error\n");
820 /* XXX what now? */
821 return;
822 }
823 }
824
825 /*
826 * Handle command responses coming in asynchronously,
827 * return nonzero if valid response.
828 * to be called at spltty()
829 */
830 int
pckbc_cmdresponse(t,slot,data)831 pckbc_cmdresponse(t, slot, data)
832 struct pckbc_internal *t;
833 pckbc_slot_t slot;
834 u_char data;
835 {
836 struct pckbc_slotdata *q = t->t_slotdata[slot];
837 struct pckbc_devcmd *cmd = TAILQ_FIRST(&q->cmdqueue);
838
839 #ifdef DIAGNOSTIC
840 if (!cmd)
841 panic("pckbc_cmdresponse: no active command");
842 #endif
843 if (cmd->cmdidx < cmd->cmdlen) {
844 if (data != KBC_DEVCMD_ACK && data != KBC_DEVCMD_RESEND)
845 return (0);
846
847 if (data == KBC_DEVCMD_RESEND) {
848 if (cmd->retries++ < KB_MAX_RETRANS) {
849 /* try again last command */
850 goto restart;
851 } else {
852 #ifdef PCKBCDEBUG
853 printf("pckbc: cmd failed\n");
854 #endif
855 cmd->status = EIO;
856 /* dequeue */
857 }
858 } else {
859 if (++cmd->cmdidx < cmd->cmdlen)
860 goto restart;
861 if (cmd->responselen)
862 return (1);
863 /* else dequeue */
864 }
865 } else if (cmd->responseidx < cmd->responselen) {
866 cmd->response[cmd->responseidx++] = data;
867 if (cmd->responseidx < cmd->responselen)
868 return (1);
869 /* else dequeue */
870 } else
871 return (0);
872
873 /* dequeue: */
874 TAILQ_REMOVE(&q->cmdqueue, cmd, next);
875 if (cmd->flags & KBC_CMDFLAG_SYNC)
876 wakeup(cmd);
877 else {
878 timeout_del(&t->t_cleanup);
879 TAILQ_INSERT_TAIL(&q->freequeue, cmd, next);
880 }
881 if (!CMD_IN_QUEUE(q))
882 return (1);
883 restart:
884 pckbc_start(t, slot);
885 return (1);
886 }
887
888 /*
889 * Put command into the device's command queue, return zero or errno.
890 */
891 int
pckbc_enqueue_cmd(self,slot,cmd,len,responselen,sync,respbuf)892 pckbc_enqueue_cmd(self, slot, cmd, len, responselen, sync, respbuf)
893 pckbc_tag_t self;
894 pckbc_slot_t slot;
895 u_char *cmd;
896 int len, responselen, sync;
897 u_char *respbuf;
898 {
899 struct pckbc_internal *t = self;
900 struct pckbc_slotdata *q = t->t_slotdata[slot];
901 struct pckbc_devcmd *nc;
902 int s, isactive, res = 0;
903
904 if ((len > 4) || (responselen > 4))
905 return (EINVAL);
906 s = spltty();
907 nc = TAILQ_FIRST(&q->freequeue);
908 if (nc) {
909 TAILQ_REMOVE(&q->freequeue, nc, next);
910 }
911 splx(s);
912 if (!nc)
913 return (ENOMEM);
914
915 bzero(nc, sizeof(*nc));
916 bcopy(cmd, nc->cmd, len);
917 nc->cmdlen = len;
918 nc->responselen = responselen;
919 nc->flags = (sync ? KBC_CMDFLAG_SYNC : 0);
920
921 s = spltty();
922
923 if (IS_POLLING(q) && sync) {
924 /*
925 * XXX We should poll until the queue is empty.
926 * But we don't come here normally, so make
927 * it simple and throw away everything.
928 */
929 pckbc_cleanqueue(q);
930 }
931
932 isactive = CMD_IN_QUEUE(q);
933 TAILQ_INSERT_TAIL(&q->cmdqueue, nc, next);
934 if (!isactive)
935 pckbc_start(t, slot);
936
937 if (IS_POLLING(q))
938 res = (sync ? nc->status : 0);
939 else if (sync) {
940 if ((res = tsleep_nsec(nc, 0, "kbccmd", SEC_TO_NSEC(1)))) {
941 TAILQ_REMOVE(&q->cmdqueue, nc, next);
942 pckbc_cleanup(t);
943 } else
944 res = nc->status;
945 } else
946 timeout_add_sec(&t->t_cleanup, 1);
947
948 if (sync) {
949 if (respbuf)
950 bcopy(nc->response, respbuf, responselen);
951 TAILQ_INSERT_TAIL(&q->freequeue, nc, next);
952 }
953
954 splx(s);
955
956 return (res);
957 }
958
959 void
pckbc_set_inputhandler(self,slot,func,arg,name)960 pckbc_set_inputhandler(self, slot, func, arg, name)
961 pckbc_tag_t self;
962 pckbc_slot_t slot;
963 pckbc_inputfcn func;
964 void *arg;
965 char *name;
966 {
967 struct pckbc_internal *t = (struct pckbc_internal *)self;
968 struct pckbc_softc *sc = t->t_sc;
969
970 if (slot >= PCKBC_NSLOTS)
971 panic("pckbc_set_inputhandler: bad slot %d", slot);
972
973 sc->inputhandler[slot] = func;
974 sc->inputarg[slot] = arg;
975 sc->subname[slot] = name;
976 }
977
978 int
gsckbcintr(void * v)979 gsckbcintr(void *v)
980 {
981 struct gsckbc_softc *gsc = v;
982 struct pckbc_softc *sc = (struct pckbc_softc *)gsc;
983 struct pckbc_internal *t = sc->id;
984 pckbc_slot_t slot;
985 struct pckbc_slotdata *q;
986 int served = 0, data;
987
988 while (bus_space_read_1(t->t_iot, t->t_ioh_d, KBSTATP) & KBS_DIB) {
989 served = 1;
990
991 slot = gsc->sc_type;
992 q = t->t_slotdata[slot];
993
994 if (!q) {
995 /* XXX do something for live insertion? */
996 #ifdef PCKBCDEBUG
997 printf("gsckbcintr: no dev for slot %d\n", slot);
998 #endif
999 KBD_DELAY;
1000 (void) bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
1001 continue;
1002 }
1003
1004 if (IS_POLLING(q))
1005 break; /* pckbc_poll_data() will get it */
1006
1007 KBD_DELAY;
1008 data = bus_space_read_1(t->t_iot, t->t_ioh_d, KBDATAP);
1009
1010 if (CMD_IN_QUEUE(q) && pckbc_cmdresponse(t, slot, data))
1011 continue;
1012
1013 if (sc->inputhandler[slot])
1014 (*sc->inputhandler[slot])(sc->inputarg[slot], data);
1015 #ifdef PCKBCDEBUG
1016 else
1017 printf("gsckbcintr: slot %d lost %d\n", slot, data);
1018 #endif
1019 }
1020
1021 return (served);
1022 }
1023