1 /* $OpenBSD: comkbd_ebus.c,v 1.24 2021/10/24 17:05:03 mpi Exp $ */
2
3 /*
4 * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 *
28 * Effort sponsored in part by the Defense Advanced Research Projects
29 * Agency (DARPA) and Air Force Research Laboratory, Air Force
30 * Materiel Command, USAF, under agreement number F30602-01-2-0537.
31 *
32 */
33
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/proc.h>
37 #include <sys/device.h>
38 #include <sys/conf.h>
39 #include <sys/ioctl.h>
40 #include <sys/malloc.h>
41 #include <sys/tty.h>
42 #include <sys/time.h>
43 #include <sys/kernel.h>
44 #include <sys/syslog.h>
45
46 #include <machine/bus.h>
47 #include <machine/autoconf.h>
48 #include <machine/openfirm.h>
49
50 #include <sparc64/dev/ebusreg.h>
51 #include <sparc64/dev/ebusvar.h>
52
53 #include <dev/wscons/wsconsio.h>
54 #include <dev/wscons/wskbdvar.h>
55
56 #include <dev/sun/sunkbdreg.h>
57 #include <dev/sun/sunkbdvar.h>
58
59 #include <dev/ic/comreg.h>
60 #include <dev/ic/comvar.h>
61 #include <dev/ic/ns16550reg.h>
62
63 #include <dev/cons.h>
64
65 #define COMK_RX_RING 64
66 #define COMK_TX_RING 64
67
68 struct comkbd_softc {
69 struct sunkbd_softc sc_base;
70
71 bus_space_tag_t sc_iot; /* bus tag */
72 bus_space_handle_t sc_ioh; /* bus handle */
73 void *sc_ih, *sc_si; /* interrupt vectors */
74
75 u_int sc_rxcnt;
76 u_int8_t sc_rxbuf[COMK_RX_RING];
77 u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput;
78
79 u_int sc_txcnt;
80 u_int8_t sc_txbuf[COMK_TX_RING];
81 u_int8_t *sc_txbeg, *sc_txend, *sc_txget, *sc_txput;
82
83 u_int8_t sc_ier;
84 };
85
86 #define COM_WRITE(sc,r,v) \
87 bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
88 #define COM_READ(sc,r) \
89 bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
90
91 int comkbd_match(struct device *, void *, void *);
92 void comkbd_attach(struct device *, struct device *, void *);
93 int comkbd_iskbd(int);
94
95 /* wskbd glue */
96 void comkbd_cnpollc(void *, int);
97 void comkbd_cngetc(void *, u_int *, int *);
98
99 /* internals */
100 int comkbd_enqueue(void *, u_int8_t *, u_int);
101 int comkbd_init(struct comkbd_softc *);
102 void comkbd_putc(struct comkbd_softc *, u_int8_t);
103 int comkbd_intr(void *);
104 void comkbd_soft(void *);
105
106 const struct cfattach comkbd_ca = {
107 sizeof(struct comkbd_softc), comkbd_match, comkbd_attach
108 };
109
110 struct cfdriver comkbd_cd = {
111 NULL, "comkbd", DV_DULL
112 };
113
114 const char *comkbd_names[] = {
115 "su",
116 "su_pnp",
117 NULL
118 };
119
120 struct wskbd_consops comkbd_consops = {
121 comkbd_cngetc,
122 comkbd_cnpollc
123 };
124
125 int
comkbd_iskbd(int node)126 comkbd_iskbd(int node)
127 {
128 if (OF_getproplen(node, "keyboard") == 0)
129 return (10);
130 return (0);
131 }
132
133 int
comkbd_match(struct device * parent,void * match,void * aux)134 comkbd_match(struct device *parent, void *match, void *aux)
135 {
136 struct ebus_attach_args *ea = aux;
137 int i;
138
139 for (i = 0; comkbd_names[i]; i++)
140 if (strcmp(ea->ea_name, comkbd_names[i]) == 0)
141 return (comkbd_iskbd(ea->ea_node));
142
143 if (strcmp(ea->ea_name, "serial") == 0) {
144 char compat[80];
145
146 if ((i = OF_getproplen(ea->ea_node, "compatible")) &&
147 OF_getprop(ea->ea_node, "compatible", compat,
148 sizeof(compat)) == i) {
149 if (strcmp(compat, "su16550") == 0 ||
150 strcmp(compat, "su") == 0)
151 return (comkbd_iskbd(ea->ea_node));
152 }
153 }
154 return (0);
155 }
156
157 void
comkbd_attach(struct device * parent,struct device * self,void * aux)158 comkbd_attach(struct device *parent, struct device *self, void *aux)
159 {
160 struct comkbd_softc *sc = (void *)self;
161 struct sunkbd_softc *ss = (void *)sc;
162 struct ebus_attach_args *ea = aux;
163 struct wskbddev_attach_args a;
164 int console;
165
166 ss->sc_sendcmd = comkbd_enqueue;
167 timeout_set(&ss->sc_bellto, sunkbd_bellstop, sc);
168
169 sc->sc_iot = ea->ea_memtag;
170
171 sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf;
172 sc->sc_rxend = sc->sc_rxbuf + COMK_RX_RING;
173 sc->sc_rxcnt = 0;
174
175 sc->sc_txget = sc->sc_txput = sc->sc_txbeg = sc->sc_txbuf;
176 sc->sc_txend = sc->sc_txbuf + COMK_TX_RING;
177 sc->sc_txcnt = 0;
178
179 console = (ea->ea_node == OF_instance_to_package(OF_stdin()));
180
181 sc->sc_si = softintr_establish(IPL_TTY, comkbd_soft, sc);
182 if (sc->sc_si == NULL) {
183 printf(": can't get soft intr\n");
184 return;
185 }
186
187 /* Use prom address if available, otherwise map it. */
188 if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
189 BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) {
190 sc->sc_iot = ea->ea_memtag;
191 } else if (ebus_bus_map(ea->ea_memtag, 0,
192 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
193 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
194 sc->sc_iot = ea->ea_memtag;
195 } else if (ebus_bus_map(ea->ea_iotag, 0,
196 EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
197 ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
198 sc->sc_iot = ea->ea_iotag;
199 } else {
200 printf(": can't map register space\n");
201 return;
202 }
203
204 sc->sc_ih = bus_intr_establish(sc->sc_iot,
205 ea->ea_intrs[0], IPL_TTY, 0, comkbd_intr, sc, self->dv_xname);
206 if (sc->sc_ih == NULL) {
207 printf(": can't get hard intr\n");
208 return;
209 }
210
211 if (comkbd_init(sc) == 0) {
212 return;
213 }
214
215 ss->sc_click =
216 strcmp(getpropstring(optionsnode, "keyboard-click?"), "true") == 0;
217 sunkbd_setclick(ss, ss->sc_click);
218
219 a.console = console;
220 if (ISTYPE5(ss->sc_layout)) {
221 a.keymap = &sunkbd5_keymapdata;
222 #ifndef SUNKBD5_LAYOUT
223 if (ss->sc_layout < MAXSUNLAYOUT &&
224 sunkbd_layouts[ss->sc_layout] != -1)
225 sunkbd5_keymapdata.layout =
226 sunkbd_layouts[ss->sc_layout];
227 #endif
228 } else {
229 a.keymap = &sunkbd_keymapdata;
230 #ifndef SUNKBD_LAYOUT
231 if (ss->sc_layout < MAXSUNLAYOUT &&
232 sunkbd_layouts[ss->sc_layout] != -1)
233 sunkbd_keymapdata.layout =
234 sunkbd_layouts[ss->sc_layout];
235 #endif
236 }
237 a.accessops = &sunkbd_accessops;
238 a.accesscookie = sc;
239
240 if (console) {
241 cn_tab->cn_dev = makedev(77, ss->sc_dev.dv_unit); /* XXX */
242 cn_tab->cn_pollc = wskbd_cnpollc;
243 cn_tab->cn_getc = wskbd_cngetc;
244 wskbd_cnattach(&comkbd_consops, sc, a.keymap);
245 sc->sc_ier = IER_ETXRDY | IER_ERXRDY;
246 COM_WRITE(sc, com_ier, sc->sc_ier);
247 COM_READ(sc, com_iir);
248 COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS);
249 }
250
251 sunkbd_attach(ss, &a);
252 }
253
254 void
comkbd_cnpollc(void * vsc,int on)255 comkbd_cnpollc(void *vsc, int on)
256 {
257 }
258
259 void
comkbd_cngetc(void * v,u_int * type,int * data)260 comkbd_cngetc(void *v, u_int *type, int *data)
261 {
262 struct comkbd_softc *sc = v;
263 int s;
264 u_int8_t c;
265
266 s = splhigh();
267 while (1) {
268 if (COM_READ(sc, com_lsr) & LSR_RXRDY)
269 break;
270 }
271 c = COM_READ(sc, com_data);
272 COM_READ(sc, com_iir);
273 splx(s);
274
275 sunkbd_decode(c, type, data);
276 }
277
278 void
comkbd_putc(struct comkbd_softc * sc,u_int8_t c)279 comkbd_putc(struct comkbd_softc *sc, u_int8_t c)
280 {
281 int s, timo;
282
283 s = splhigh();
284
285 timo = 150000;
286 while (--timo) {
287 if (COM_READ(sc, com_lsr) & LSR_TXRDY)
288 break;
289 }
290
291 COM_WRITE(sc, com_data, c);
292 bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, COM_NPORTS,
293 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
294
295 timo = 150000;
296 while (--timo) {
297 if (COM_READ(sc, com_lsr) & LSR_TXRDY)
298 break;
299 }
300
301 splx(s);
302 }
303
304 int
comkbd_enqueue(void * v,u_int8_t * buf,u_int buflen)305 comkbd_enqueue(void *v, u_int8_t *buf, u_int buflen)
306 {
307 struct comkbd_softc *sc = v;
308 int s;
309 u_int i;
310
311 s = spltty();
312
313 /* See if there is room... */
314 if ((sc->sc_txcnt + buflen) > COMK_TX_RING) {
315 splx(s);
316 return (-1);
317 }
318
319 for (i = 0; i < buflen; i++) {
320 *sc->sc_txget = *buf;
321 buf++;
322 sc->sc_txcnt++;
323 sc->sc_txget++;
324 if (sc->sc_txget == sc->sc_txend)
325 sc->sc_txget = sc->sc_txbeg;
326 }
327
328 comkbd_soft(sc);
329
330 splx(s);
331 return (0);
332 }
333
334 void
comkbd_soft(void * vsc)335 comkbd_soft(void *vsc)
336 {
337 struct comkbd_softc *sc = vsc;
338 struct sunkbd_softc *ss = (void *)sc;
339 u_int8_t cbuf[SUNKBD_MAX_INPUT_SIZE], *cptr;
340 u_int8_t c;
341
342 cptr = cbuf;
343 while (sc->sc_rxcnt) {
344 *cptr++ = *sc->sc_rxget;
345 if (++sc->sc_rxget == sc->sc_rxend)
346 sc->sc_rxget = sc->sc_rxbeg;
347 sc->sc_rxcnt--;
348 if (cptr - cbuf == sizeof cbuf) {
349 sunkbd_input(ss, cbuf, cptr - cbuf);
350 cptr = cbuf;
351 }
352 }
353 if (cptr != cbuf)
354 sunkbd_input(ss, cbuf, cptr - cbuf);
355
356 if (sc->sc_txcnt) {
357 c = sc->sc_ier | IER_ETXRDY;
358 if (c != sc->sc_ier) {
359 COM_WRITE(sc, com_ier, c);
360 sc->sc_ier = c;
361 }
362 if (COM_READ(sc, com_lsr) & LSR_TXRDY) {
363 sc->sc_txcnt--;
364 COM_WRITE(sc, com_data, *sc->sc_txput);
365 if (++sc->sc_txput == sc->sc_txend)
366 sc->sc_txput = sc->sc_txbeg;
367 }
368 }
369 }
370
371 int
comkbd_intr(void * vsc)372 comkbd_intr(void *vsc)
373 {
374 struct comkbd_softc *sc = vsc;
375 u_int8_t iir, lsr, data;
376 int needsoft = 0;
377
378 /* Nothing to do */
379 iir = COM_READ(sc, com_iir);
380 if (iir & IIR_NOPEND)
381 return (0);
382
383 for (;;) {
384 lsr = COM_READ(sc, com_lsr);
385 if (lsr & LSR_RXRDY) {
386 needsoft = 1;
387
388 do {
389 data = COM_READ(sc, com_data);
390 if (sc->sc_rxcnt != COMK_RX_RING) {
391 *sc->sc_rxput = data;
392 if (++sc->sc_rxput == sc->sc_rxend)
393 sc->sc_rxput = sc->sc_rxbeg;
394 sc->sc_rxcnt++;
395 }
396 lsr = COM_READ(sc, com_lsr);
397 } while (lsr & LSR_RXRDY);
398 }
399
400 if (lsr & LSR_TXRDY) {
401 if (sc->sc_txcnt == 0) {
402 /* Nothing further to send */
403 sc->sc_ier &= ~IER_ETXRDY;
404 COM_WRITE(sc, com_ier, sc->sc_ier);
405 } else
406 needsoft = 1;
407 }
408
409 iir = COM_READ(sc, com_iir);
410 if (iir & IIR_NOPEND)
411 break;
412 }
413
414 if (needsoft)
415 softintr_schedule(sc->sc_si);
416
417 return (1);
418 }
419
420 int
comkbd_init(struct comkbd_softc * sc)421 comkbd_init(struct comkbd_softc *sc)
422 {
423 struct sunkbd_softc *ss = (void *)sc;
424 u_int8_t stat, c;
425 int tries;
426
427 for (tries = 5; tries != 0; tries--) {
428 int ltries;
429
430 ss->sc_leds = 0;
431 ss->sc_layout = -1;
432
433 /* Send reset request */
434 comkbd_putc(sc, SKBD_CMD_RESET);
435
436 ltries = 1000;
437 while (--ltries > 0) {
438 stat = COM_READ(sc,com_lsr);
439 if (stat & LSR_RXRDY) {
440 c = COM_READ(sc, com_data);
441
442 sunkbd_raw(ss, c);
443 if (ss->sc_kbdstate == SKBD_STATE_RESET)
444 break;
445 }
446 DELAY(1000);
447 }
448 if (ltries == 0)
449 continue;
450
451 /* Wait for reset to finish. */
452 ltries = 1000;
453 while (--ltries > 0) {
454 stat = COM_READ(sc, com_lsr);
455 if (stat & LSR_RXRDY) {
456 c = COM_READ(sc, com_data);
457 sunkbd_raw(ss, c);
458 if (ss->sc_kbdstate == SKBD_STATE_GETKEY)
459 break;
460 }
461 DELAY(1000);
462 }
463 if (ltries == 0)
464 continue;
465
466 /* Some Sun<=>PS/2 converters need some delay here */
467 DELAY(5000);
468
469 /* Send layout request */
470 comkbd_putc(sc, SKBD_CMD_LAYOUT);
471
472 ltries = 1000;
473 while (--ltries > 0) {
474 stat = COM_READ(sc, com_lsr);
475 if (stat & LSR_RXRDY) {
476 c = COM_READ(sc, com_data);
477 sunkbd_raw(ss, c);
478 if (ss->sc_layout != -1)
479 break;
480 }
481 DELAY(1000);
482 }
483 if (ltries != 0)
484 break;
485 }
486 if (tries == 0)
487 printf(": no keyboard\n");
488 else
489 printf(": layout %d\n", ss->sc_layout);
490
491 return tries;
492 }
493