xref: /openbsd-src/sys/arch/sparc64/dev/comkbd_ebus.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: comkbd_ebus.c,v 1.21 2011/03/18 21:01:17 miod 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/file.h>
40 #include <sys/ioctl.h>
41 #include <sys/malloc.h>
42 #include <sys/tty.h>
43 #include <sys/time.h>
44 #include <sys/kernel.h>
45 #include <sys/syslog.h>
46 
47 #include <machine/bus.h>
48 #include <machine/autoconf.h>
49 #include <machine/openfirm.h>
50 
51 #include <sparc64/dev/ebusreg.h>
52 #include <sparc64/dev/ebusvar.h>
53 
54 #include <dev/wscons/wsconsio.h>
55 #include <dev/wscons/wskbdvar.h>
56 
57 #include <dev/sun/sunkbdreg.h>
58 #include <dev/sun/sunkbdvar.h>
59 
60 #include <dev/ic/comreg.h>
61 #include <dev/ic/comvar.h>
62 #include <dev/ic/ns16550reg.h>
63 
64 #include <dev/cons.h>
65 
66 #define	COMK_RX_RING	64
67 #define	COMK_TX_RING	64
68 
69 struct comkbd_softc {
70 	struct sunkbd_softc	sc_base;
71 
72 	bus_space_tag_t sc_iot;		/* bus tag */
73 	bus_space_handle_t sc_ioh;	/* bus handle */
74 	void *sc_ih, *sc_si;		/* interrupt vectors */
75 
76 	u_int sc_rxcnt;
77 	u_int8_t sc_rxbuf[COMK_RX_RING];
78 	u_int8_t *sc_rxbeg, *sc_rxend, *sc_rxget, *sc_rxput;
79 
80 	u_int sc_txcnt;
81 	u_int8_t sc_txbuf[COMK_TX_RING];
82 	u_int8_t *sc_txbeg, *sc_txend, *sc_txget, *sc_txput;
83 
84 	u_int8_t sc_ier;
85 };
86 
87 #define	COM_WRITE(sc,r,v) \
88     bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (r), (v))
89 #define	COM_READ(sc,r) \
90     bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (r))
91 
92 int comkbd_match(struct device *, void *, void *);
93 void comkbd_attach(struct device *, struct device *, void *);
94 int comkbd_iskbd(int);
95 
96 /* wskbd glue */
97 void comkbd_cnpollc(void *, int);
98 void comkbd_cngetc(void *, u_int *, int *);
99 
100 /* internals */
101 int comkbd_enqueue(void *, u_int8_t *, u_int);
102 int comkbd_init(struct comkbd_softc *);
103 void comkbd_putc(struct comkbd_softc *, u_int8_t);
104 int comkbd_intr(void *);
105 void comkbd_soft(void *);
106 
107 struct cfattach comkbd_ca = {
108 	sizeof(struct comkbd_softc), comkbd_match, comkbd_attach
109 };
110 
111 struct cfdriver comkbd_cd = {
112 	NULL, "comkbd", DV_DULL
113 };
114 
115 const char *comkbd_names[] = {
116 	"su",
117 	"su_pnp",
118 	NULL
119 };
120 
121 struct wskbd_consops comkbd_consops = {
122 	comkbd_cngetc,
123 	comkbd_cnpollc
124 };
125 
126 int
127 comkbd_iskbd(node)
128 	int node;
129 {
130 	if (OF_getproplen(node, "keyboard") == 0)
131 		return (10);
132 	return (0);
133 }
134 
135 int
136 comkbd_match(parent, match, aux)
137 	struct device *parent;
138 	void *match;
139 	void *aux;
140 {
141 	struct ebus_attach_args *ea = aux;
142 	int i;
143 
144 	for (i = 0; comkbd_names[i]; i++)
145 		if (strcmp(ea->ea_name, comkbd_names[i]) == 0)
146 			return (comkbd_iskbd(ea->ea_node));
147 
148 	if (strcmp(ea->ea_name, "serial") == 0) {
149 		char compat[80];
150 
151 		if ((i = OF_getproplen(ea->ea_node, "compatible")) &&
152 		    OF_getprop(ea->ea_node, "compatible", compat,
153 			sizeof(compat)) == i) {
154 			if (strcmp(compat, "su16550") == 0 ||
155 			    strcmp(compat, "su") == 0)
156 				return (comkbd_iskbd(ea->ea_node));
157 		}
158 	}
159 	return (0);
160 }
161 
162 void
163 comkbd_attach(parent, self, aux)
164 	struct device *parent, *self;
165 	void *aux;
166 {
167 	struct comkbd_softc *sc = (void *)self;
168 	struct sunkbd_softc *ss = (void *)sc;
169 	struct ebus_attach_args *ea = aux;
170 	struct wskbddev_attach_args a;
171 	int console;
172 
173 	ss->sc_sendcmd = comkbd_enqueue;
174 	timeout_set(&ss->sc_bellto, sunkbd_bellstop, sc);
175 
176 	sc->sc_iot = ea->ea_memtag;
177 
178 	sc->sc_rxget = sc->sc_rxput = sc->sc_rxbeg = sc->sc_rxbuf;
179 	sc->sc_rxend = sc->sc_rxbuf + COMK_RX_RING;
180 	sc->sc_rxcnt = 0;
181 
182 	sc->sc_txget = sc->sc_txput = sc->sc_txbeg = sc->sc_txbuf;
183 	sc->sc_txend = sc->sc_txbuf + COMK_TX_RING;
184 	sc->sc_txcnt = 0;
185 
186 	console = (ea->ea_node == OF_instance_to_package(OF_stdin()));
187 
188 	sc->sc_si = softintr_establish(IPL_TTY, comkbd_soft, sc);
189 	if (sc->sc_si == NULL) {
190 		printf(": can't get soft intr\n");
191 		return;
192 	}
193 
194 	/* Use prom address if available, otherwise map it. */
195 	if (ea->ea_nvaddrs && bus_space_map(ea->ea_memtag, ea->ea_vaddrs[0], 0,
196 	    BUS_SPACE_MAP_PROMADDRESS, &sc->sc_ioh) == 0) {
197 		sc->sc_iot = ea->ea_memtag;
198 	} else if (ebus_bus_map(ea->ea_memtag, 0,
199 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
200 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
201 		sc->sc_iot = ea->ea_memtag;
202 	} else if (ebus_bus_map(ea->ea_iotag, 0,
203 	    EBUS_PADDR_FROM_REG(&ea->ea_regs[0]),
204 	    ea->ea_regs[0].size, 0, 0, &sc->sc_ioh) == 0) {
205 		sc->sc_iot = ea->ea_iotag;
206 	} else {
207 		printf(": can't map register space\n");
208                 return;
209 	}
210 
211 	sc->sc_ih = bus_intr_establish(sc->sc_iot,
212 	    ea->ea_intrs[0], IPL_TTY, 0, comkbd_intr, sc, self->dv_xname);
213 	if (sc->sc_ih == NULL) {
214 		printf(": can't get hard intr\n");
215 		return;
216 	}
217 
218 	if (comkbd_init(sc) == 0) {
219 		return;
220 	}
221 
222 	ss->sc_click =
223 	    strcmp(getpropstring(optionsnode, "keyboard-click?"), "true") == 0;
224 	sunkbd_setclick(ss, ss->sc_click);
225 
226 	a.console = console;
227 	if (ISTYPE5(ss->sc_layout)) {
228 		a.keymap = &sunkbd5_keymapdata;
229 #ifndef SUNKBD5_LAYOUT
230 		if (ss->sc_layout < MAXSUNLAYOUT &&
231 		    sunkbd_layouts[ss->sc_layout] != -1)
232 			sunkbd5_keymapdata.layout =
233 			    sunkbd_layouts[ss->sc_layout];
234 #endif
235 	} else {
236 		a.keymap = &sunkbd_keymapdata;
237 #ifndef SUNKBD_LAYOUT
238 		if (ss->sc_layout < MAXSUNLAYOUT &&
239 		    sunkbd_layouts[ss->sc_layout] != -1)
240 			sunkbd_keymapdata.layout =
241 			    sunkbd_layouts[ss->sc_layout];
242 #endif
243 	}
244 	a.accessops = &sunkbd_accessops;
245 	a.accesscookie = sc;
246 
247 	if (console) {
248 		cn_tab->cn_dev = makedev(77, ss->sc_dev.dv_unit); /* XXX */
249 		cn_tab->cn_pollc = wskbd_cnpollc;
250 		cn_tab->cn_getc = wskbd_cngetc;
251 		wskbd_cnattach(&comkbd_consops, sc, a.keymap);
252 		sc->sc_ier = IER_ETXRDY | IER_ERXRDY;
253 		COM_WRITE(sc, com_ier, sc->sc_ier);
254 		COM_READ(sc, com_iir);
255 		COM_WRITE(sc, com_mcr, MCR_IENABLE | MCR_DTR | MCR_RTS);
256 	}
257 
258 	sunkbd_attach(ss, &a);
259 }
260 
261 void
262 comkbd_cnpollc(vsc, on)
263 	void *vsc;
264 	int on;
265 {
266 }
267 
268 void
269 comkbd_cngetc(v, type, data)
270 	void *v;
271 	u_int *type;
272 	int *data;
273 {
274 	struct comkbd_softc *sc = v;
275 	int s;
276 	u_int8_t c;
277 
278 	s = splhigh();
279 	while (1) {
280 		if (COM_READ(sc, com_lsr) & LSR_RXRDY)
281 			break;
282 	}
283 	c = COM_READ(sc, com_data);
284 	COM_READ(sc, com_iir);
285 	splx(s);
286 
287 	sunkbd_decode(c, type, data);
288 }
289 
290 void
291 comkbd_putc(sc, c)
292 	struct comkbd_softc *sc;
293 	u_int8_t c;
294 {
295 	int s, timo;
296 
297 	s = splhigh();
298 
299 	timo = 150000;
300 	while (--timo) {
301 		if (COM_READ(sc, com_lsr) & LSR_TXRDY)
302 			break;
303 	}
304 
305 	COM_WRITE(sc, com_data, c);
306 	bus_space_barrier(sc->sc_iot, sc->sc_ioh, 0, COM_NPORTS,
307 	    BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
308 
309 	timo = 150000;
310 	while (--timo) {
311 		if (COM_READ(sc, com_lsr) & LSR_TXRDY)
312 			break;
313 	}
314 
315 	splx(s);
316 }
317 
318 int
319 comkbd_enqueue(v, buf, buflen)
320 	void *v;
321 	u_int8_t *buf;
322 	u_int buflen;
323 {
324 	struct comkbd_softc *sc = v;
325 	int s;
326 	u_int i;
327 
328 	s = spltty();
329 
330 	/* See if there is room... */
331 	if ((sc->sc_txcnt + buflen) > COMK_TX_RING) {
332 		splx(s);
333 		return (-1);
334 	}
335 
336 	for (i = 0; i < buflen; i++) {
337 		*sc->sc_txget = *buf;
338 		buf++;
339 		sc->sc_txcnt++;
340 		sc->sc_txget++;
341 		if (sc->sc_txget == sc->sc_txend)
342 			sc->sc_txget = sc->sc_txbeg;
343 	}
344 
345 	comkbd_soft(sc);
346 
347 	splx(s);
348 	return (0);
349 }
350 
351 void
352 comkbd_soft(vsc)
353 	void *vsc;
354 {
355 	struct comkbd_softc *sc = vsc;
356 	struct sunkbd_softc *ss = (void *)sc;
357 	u_int8_t cbuf[SUNKBD_MAX_INPUT_SIZE], *cptr;
358 	u_int8_t c;
359 
360 	cptr = cbuf;
361 	while (sc->sc_rxcnt) {
362 		*cptr++ = *sc->sc_rxget;
363 		if (++sc->sc_rxget == sc->sc_rxend)
364 			sc->sc_rxget = sc->sc_rxbeg;
365 		sc->sc_rxcnt--;
366 		if (cptr - cbuf == sizeof cbuf) {
367 			sunkbd_input(ss, cbuf, cptr - cbuf);
368 			cptr = cbuf;
369 		}
370 	}
371 	if (cptr != cbuf)
372 		sunkbd_input(ss, cbuf, cptr - cbuf);
373 
374 	if (sc->sc_txcnt) {
375 		c = sc->sc_ier | IER_ETXRDY;
376 		if (c != sc->sc_ier) {
377 			COM_WRITE(sc, com_ier, c);
378 			sc->sc_ier = c;
379 		}
380 		if (COM_READ(sc, com_lsr) & LSR_TXRDY) {
381 			sc->sc_txcnt--;
382 			COM_WRITE(sc, com_data, *sc->sc_txput);
383 			if (++sc->sc_txput == sc->sc_txend)
384 				sc->sc_txput = sc->sc_txbeg;
385 		}
386 	}
387 }
388 
389 int
390 comkbd_intr(vsc)
391 	void *vsc;
392 {
393 	struct comkbd_softc *sc = vsc;
394 	u_int8_t iir, lsr, data;
395 	int needsoft = 0;
396 
397 	/* Nothing to do */
398 	iir = COM_READ(sc, com_iir);
399 	if (iir & IIR_NOPEND)
400 		return (0);
401 
402 	for (;;) {
403 		lsr = COM_READ(sc, com_lsr);
404 		if (lsr & LSR_RXRDY) {
405 			needsoft = 1;
406 
407 			do {
408 				data = COM_READ(sc, com_data);
409 				if (sc->sc_rxcnt != COMK_RX_RING) {
410 					*sc->sc_rxput = data;
411 					if (++sc->sc_rxput == sc->sc_rxend)
412 						sc->sc_rxput = sc->sc_rxbeg;
413 					sc->sc_rxcnt++;
414 				}
415 				lsr = COM_READ(sc, com_lsr);
416 			} while (lsr & LSR_RXRDY);
417 		}
418 
419 		if (lsr & LSR_TXRDY) {
420 			if (sc->sc_txcnt == 0) {
421 				/* Nothing further to send */
422 				sc->sc_ier &= ~IER_ETXRDY;
423 				COM_WRITE(sc, com_ier, sc->sc_ier);
424 			} else
425 				needsoft = 1;
426 		}
427 
428 		iir = COM_READ(sc, com_iir);
429 		if (iir & IIR_NOPEND)
430 			break;
431 	}
432 
433 	if (needsoft)
434 		softintr_schedule(sc->sc_si);
435 
436 	return (1);
437 }
438 
439 int
440 comkbd_init(sc)
441 	struct comkbd_softc *sc;
442 {
443 	struct sunkbd_softc *ss = (void *)sc;
444 	u_int8_t stat, c;
445 	int tries;
446 
447 	for (tries = 5; tries != 0; tries--) {
448 		int ltries;
449 
450 		ss->sc_leds = 0;
451 		ss->sc_layout = -1;
452 
453 		/* Send reset request */
454 		comkbd_putc(sc, SKBD_CMD_RESET);
455 
456 		ltries = 1000;
457 		while (--ltries > 0) {
458 			stat = COM_READ(sc,com_lsr);
459 			if (stat & LSR_RXRDY) {
460 				c = COM_READ(sc, com_data);
461 
462 				sunkbd_raw(ss, c);
463 				if (ss->sc_kbdstate == SKBD_STATE_RESET)
464 					break;
465 			}
466 			DELAY(1000);
467 		}
468 		if (ltries == 0)
469 			continue;
470 
471 		/* Wait for reset to finish. */
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_kbdstate == SKBD_STATE_GETKEY)
479 					break;
480 			}
481 			DELAY(1000);
482 		}
483 		if (ltries == 0)
484 			continue;
485 
486 		/* Some Sun<=>PS/2 converters need some delay here */
487 		DELAY(5000);
488 
489 		/* Send layout request */
490 		comkbd_putc(sc, SKBD_CMD_LAYOUT);
491 
492 		ltries = 1000;
493 		while (--ltries > 0) {
494 			stat = COM_READ(sc, com_lsr);
495 			if (stat & LSR_RXRDY) {
496 				c = COM_READ(sc, com_data);
497 				sunkbd_raw(ss, c);
498 				if (ss->sc_layout != -1)
499 					break;
500 			}
501 			DELAY(1000);
502 		}
503 		if (ltries != 0)
504 			break;
505 	}
506 	if (tries == 0)
507 		printf(": no keyboard\n");
508 	else
509 		printf(": layout %d\n", ss->sc_layout);
510 
511 	return tries;
512 }
513