xref: /openbsd-src/sys/arch/sparc64/dev/comkbd_ebus.c (revision eb7eaf8de3ff431d305450f61b441e5460c82246)
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