xref: /openbsd/sys/arch/luna88k/dev/siotty.c (revision 5dea098c)
1 /* $OpenBSD: siotty.c,v 1.25 2021/01/09 02:34:21 aoyama Exp $ */
2 /* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */
3 
4 /*-
5  * Copyright (c) 2000 The NetBSD Foundation, Inc.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Tohru Nishimura.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *    notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *    notice, this list of conditions and the following disclaimer in the
18  *    documentation and/or other materials provided with the distribution.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
24  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30  * POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/device.h>
36 #include <sys/conf.h>
37 #include <sys/ioctl.h>
38 #include <sys/malloc.h>
39 #include <sys/proc.h>
40 #include <sys/tty.h>
41 #include <sys/uio.h>
42 #include <sys/fcntl.h>
43 #include <dev/cons.h>
44 
45 #include <machine/board.h>
46 #include <machine/cpu.h>
47 
48 #include <luna88k/dev/sioreg.h>
49 #include <luna88k/dev/siovar.h>
50 
51 #define	TIOCM_BREAK 01000 /* non standard use */
52 
53 static const u_int8_t ch0_regs[6] = {
54 	WR0_RSTINT,				/* reset E/S interrupt */
55 	WR1_RXALLS | WR1_TXENBL,		/* Rx per char, Tx */
56 	0,					/* */
57 	WR3_RX8BIT | WR3_RXENBL,		/* Rx */
58 	WR4_BAUD96 | WR4_STOP1,			/* Tx/Rx */
59 	WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */
60 };
61 
62 static const struct speedtab siospeedtab[] = {
63 	{ 2400,	WR4_BAUD24, },
64 	{ 4800,	WR4_BAUD48, },
65 	{ 9600,	WR4_BAUD96, },
66 	{ -1,	0, },
67 };
68 
69 struct siotty_softc {
70 	struct device	sc_dev;
71 	struct tty	*sc_tty;
72 	struct sioreg	*sc_ctl;
73 	u_int 		sc_flags;
74 	u_int8_t	sc_wr[6];
75 	void		*sc_si;		/* software interrupt handler */
76 	u_int		sc_hwflags;
77 #define	SIOTTY_HW_CONSOLE	0x0001
78 
79 	u_int8_t	*sc_rbuf;
80 	u_int8_t	*sc_rbufend;
81 	u_int8_t * volatile sc_rbget;
82 	u_int8_t * volatile sc_rbput;
83 	volatile u_int	sc_rbavail;
84 
85 	u_int8_t	*sc_tba;
86 	u_int		sc_tbc;
87 
88 	bool		sc_rx_ready;
89 	bool		sc_tx_busy;
90 	bool		sc_tx_done;
91 };
92 
93 #define	SIOTTY_RING_SIZE	2048
94 u_int siotty_rbuf_size = SIOTTY_RING_SIZE;
95 
96 cdev_decl(sio);
97 void siostart(struct tty *);
98 int  sioparam(struct tty *, struct termios *);
99 void siottyintr(void *);
100 void siottysoft(void *);
101 void siotty_rxsoft(struct siotty_softc *, struct tty *);
102 void siotty_txsoft(struct siotty_softc *, struct tty *);
103 int  siomctl(struct siotty_softc *, int, int);
104 
105 int  siotty_match(struct device *, void *, void *);
106 void siotty_attach(struct device *, struct device *, void *);
107 
108 const struct cfattach siotty_ca = {
109 	sizeof(struct siotty_softc), siotty_match, siotty_attach
110 };
111 
112 struct cfdriver siotty_cd = {
113 	NULL, "siotty", DV_TTY
114 };
115 
116 int
117 siotty_match(struct device *parent, void *cf, void *aux)
118 {
119 	struct sio_attach_args *args = aux;
120 
121 	if (args->channel != 0) /* XXX allow tty on Ch.B XXX */
122 		return 0;
123 	return 1;
124 }
125 
126 void
127 siotty_attach(struct device *parent, struct device *self, void *aux)
128 {
129 	struct sio_softc *siosc = (void *)parent;
130 	struct siotty_softc *sc = (void *)self;
131 	struct sio_attach_args *args = aux;
132 	int channel;
133 	struct tty *tp;
134 
135 	channel = args->channel;
136 	sc->sc_ctl = &siosc->sc_ctl[channel];
137 	memcpy(sc->sc_wr, ch0_regs, sizeof(ch0_regs));
138 	siosc->sc_intrhand[channel].ih_func = siottyintr;
139 	siosc->sc_intrhand[channel].ih_arg = sc;
140 	if (args->hwflags == 1)
141 		sc->sc_hwflags |= SIOTTY_HW_CONSOLE;
142 
143 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE) {
144 		printf(" (console)");
145 		sc->sc_flags = TIOCFLAG_SOFTCAR;
146 	} else {
147 		setsioreg(sc->sc_ctl, WR0, WR0_CHANRST);
148 		setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1);
149 		setsioreg(sc->sc_ctl, WR2B, 0);
150 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
151 		setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
152 		setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
153 		setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
154 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
155 	}
156 	setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */
157 
158 	printf("\n");
159 
160 	sc->sc_rbuf = malloc(siotty_rbuf_size * 2, M_DEVBUF, M_NOWAIT);
161 	if (sc->sc_rbuf == NULL) {
162 		printf("%s: unable to allocate ring buffer\n",
163 		    (sc->sc_dev).dv_xname);
164 		return;
165 	}
166 	sc->sc_rbufend = sc->sc_rbuf + (siotty_rbuf_size * 2);
167 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
168 	sc->sc_rbavail = siotty_rbuf_size;
169 
170 	tp = ttymalloc(0);
171 	tp->t_oproc = siostart;
172 	tp->t_param = sioparam;
173 	tp->t_hwiflow = NULL /* XXX siohwiflow XXX */;
174 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE)
175 		tp->t_dev = cn_tab->cn_dev;
176 	sc->sc_tty = tp;
177 
178 	sc->sc_si = softintr_establish(IPL_SOFTTTY, siottysoft, sc);
179 }
180 
181 /*--------------------  low level routine --------------------*/
182 
183 void
184 siottyintr(void *arg)
185 {
186 	struct siotty_softc *sc;
187 	struct sioreg *sio;
188 	u_int8_t *put, *end;
189 	u_int8_t c;
190 	u_int16_t rr;
191 	int cc;
192 
193 	sc = arg;
194 	end = sc->sc_rbufend;
195 	put = sc->sc_rbput;
196 	cc = sc->sc_rbavail;
197 
198 	sio = sc->sc_ctl;
199 	rr = getsiocsr(sio);
200 	if (rr & RR_RXRDY) {
201 		do {
202 			if (cc > 0) {
203 				c = sio->sio_data;
204 				put[0] = c;
205 				put[1] = rr & 0xff;
206 				put += 2;
207 				if (put >= end)
208 					put = sc->sc_rbuf;
209 				cc--;
210 			}
211 			if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY))
212 				sio->sio_cmd = WR0_ERRRST;
213 
214 			sc->sc_rbput = put;
215 			sc->sc_rbavail = cc;
216 			sc->sc_rx_ready = true;
217 		} while ((rr = getsiocsr(sio)) & RR_RXRDY);
218 	}
219 	if (rr & RR_TXRDY) {
220 		sio->sio_cmd = WR0_RSTPEND;
221 		if (sc->sc_tbc > 0) {
222 			sio->sio_data = *sc->sc_tba;
223 			sc->sc_tba++;
224 			sc->sc_tbc--;
225 		} else {
226 			if (sc->sc_tx_busy) {
227 				sc->sc_tx_busy = false;
228 				sc->sc_tx_done = true;
229 			}
230 		}
231 	}
232 	softintr_schedule(sc->sc_si);
233 }
234 
235 void
236 siottysoft(void *arg)
237 {
238 	struct siotty_softc *sc;
239 	struct tty *tp;
240 
241 	sc = arg;
242 	tp = sc->sc_tty;
243 
244 	if (sc->sc_rx_ready) {
245 		sc->sc_rx_ready = false;
246 		siotty_rxsoft(sc, tp);
247 	}
248 	if (sc->sc_tx_done) {
249 		sc->sc_tx_done = false;
250 		siotty_txsoft(sc, tp);
251 	}
252 }
253 
254 void
255 siotty_rxsoft(struct siotty_softc *sc, struct tty *tp)
256 {
257 	uint8_t *get, *end;
258 	u_int cc, scc;
259 	unsigned int code;
260 	uint8_t stat;
261 	int s;
262 
263 	end = sc->sc_rbufend;
264 	get = sc->sc_rbget;
265 	scc = cc = siotty_rbuf_size - sc->sc_rbavail;
266 
267 	if (cc == siotty_rbuf_size) {
268 		printf("%s: rx buffer overflow\n", (sc->sc_dev).dv_xname);
269 	}
270 
271 	while (cc > 0) {
272 		code = get[0];
273 		stat = get[1];
274 		if ((stat & RR_FRAMING) != 0)
275 			code |= TTY_FE;
276 		else if ((stat & RR_PARITY) != 0)
277 			code |= TTY_PE;
278 
279 		(*linesw[tp->t_line].l_rint)(code, tp);
280 		get += 2;
281 		if (get >= end)
282 			get = sc->sc_rbuf;
283 		cc--;
284 	}
285 
286 	if (cc != scc) {
287 		s = spltty();
288 		sc->sc_rbget = get;
289 		sc->sc_rbavail += scc - cc;
290 		splx(s);
291 	}
292 }
293 
294 void
295 siotty_txsoft(struct siotty_softc *sc, struct tty *tp)
296 {
297 
298 	tp->t_state &= ~TS_BUSY;
299 	if ((tp->t_state & TS_FLUSH) != 0)
300 		tp->t_state &= ~TS_FLUSH;
301 	else
302 		ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
303 	(*linesw[tp->t_line].l_start)(tp);
304 }
305 
306 void
307 siostart(struct tty *tp)
308 {
309 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
310 	int s;
311 	u_int8_t *tba;
312 	int tbc;
313 
314 	s = spltty();
315 	if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP))
316 		goto out;
317 	ttwakeupwr(tp);
318 	if (tp->t_outq.c_cc == 0)
319 		goto out;
320 
321 	tp->t_state |= TS_BUSY;
322 
323 	tba = tp->t_outq.c_cf;
324 	tbc = ndqb(&tp->t_outq, 0);
325 
326 	sc->sc_tba = tba;
327 	sc->sc_tbc = tbc;
328 	sc->sc_tx_busy = true;
329 
330 	sc->sc_ctl->sio_data = *sc->sc_tba;
331 	sc->sc_tba++;
332 	sc->sc_tbc--;
333 out:
334 	splx(s);
335 }
336 
337 int
338 siostop(struct tty *tp, int flag)
339 {
340 	int s;
341 
342 	s = spltty();
343 	if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) {
344 		/*
345 		 * Device is transmitting; must stop it.
346 		 */
347 		tp->t_state |= TS_FLUSH;
348 	}
349 	splx(s);
350 	return (0);
351 }
352 
353 int
354 sioparam(struct tty *tp, struct termios *t)
355 {
356 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
357 	int wr4, s;
358 
359 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
360 		return EINVAL;
361 	wr4 = ttspeedtab(t->c_ospeed, siospeedtab);
362 	if (wr4 < 0)
363 		return EINVAL;
364 
365 	if (sc->sc_flags & TIOCFLAG_SOFTCAR) {
366 		t->c_cflag |= CLOCAL;
367 		t->c_cflag &= ~HUPCL;
368 	}
369 	if (sc->sc_flags & TIOCFLAG_CLOCAL)
370 		t->c_cflag |= CLOCAL;
371 
372 	/*
373 	 * If there were no changes, don't do anything.  This avoids dropping
374 	 * input and improves performance when all we did was frob things like
375 	 * VMIN and VTIME.
376 	 */
377 	if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag)
378 		return 0;
379 
380 	tp->t_ispeed = t->c_ispeed;
381 	tp->t_ospeed = t->c_ospeed;
382 	tp->t_cflag = t->c_cflag;
383 
384 	sc->sc_wr[WR3] &= 0x3f;
385 	sc->sc_wr[WR5] &= 0x9f;
386 	switch (tp->t_cflag & CSIZE) {
387 	case CS7:
388 		sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT;
389 		break;
390 	case CS8:
391 		sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT;
392 		break;
393 	}
394 	if (tp->t_cflag & PARENB) {
395 		wr4 |= WR4_PARENAB;
396 		if ((tp->t_cflag & PARODD) == 0)
397 			wr4 |= WR4_EPARITY;
398 	}
399 	wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1;
400 	sc->sc_wr[WR4] = wr4;
401 
402 	s = spltty();
403 	setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
404 	setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
405 	setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
406 	splx(s);
407 
408 	return 0;
409 }
410 
411 int
412 siomctl(struct siotty_softc *sc, int control, int op)
413 {
414 	int val, s, wr5, rr;
415 
416 	val = 0;
417 	if (control & TIOCM_BREAK)
418 		val |= WR5_BREAK;
419 	if (control & TIOCM_DTR)
420 		val |= WR5_DTR;
421 	if (control & TIOCM_RTS)
422 		val |= WR5_RTS;
423 	s = spltty();
424 	wr5 = sc->sc_wr[WR5];
425 	switch (op) {
426 	case DMSET:
427 		wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS);
428 		/* FALLTHROUGH */
429 	case DMBIS:
430 		wr5 |= val;
431 		break;
432 	case DMBIC:
433 		wr5 &= ~val;
434 		break;
435 	case DMGET:
436 		val = 0;
437 		rr = getsiocsr(sc->sc_ctl);
438 		if (wr5 & WR5_DTR)
439 			val |= TIOCM_DTR;
440 		if (wr5 & WR5_RTS)
441 			val |= TIOCM_RTS;
442 		if (rr & RR_CTS)
443 			val |= TIOCM_CTS;
444 		if (rr & RR_DCD)
445 			val |= TIOCM_CD;
446 		goto done;
447 	}
448 	sc->sc_wr[WR5] = wr5;
449 	setsioreg(sc->sc_ctl, WR5, wr5);
450 	val = 0;
451 done:
452 	splx(s);
453 	return val;
454 }
455 
456 /*--------------------  cdevsw[] interface --------------------*/
457 
458 int
459 sioopen(dev_t dev, int flag, int mode, struct proc *p)
460 {
461 	struct siotty_softc *sc;
462 	struct tty *tp;
463 	int s;
464 
465 	if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL)
466 		return ENXIO;
467 
468 	tp = sc->sc_tty;
469 
470 	if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE)
471 	    && suser(p) != 0)
472 		return EBUSY;
473 
474 	if ((tp->t_state & TS_ISOPEN) == 0) {
475 		struct termios t;
476 
477 		tp->t_dev = dev;
478 		t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
479 		t.c_cflag = TTYDEF_CFLAG;
480 		tp->t_ospeed = 0; /* force register update */
481 		(void)sioparam(tp, &t);
482 		tp->t_iflag = TTYDEF_IFLAG;
483 		tp->t_oflag = TTYDEF_OFLAG;
484 		tp->t_lflag = TTYDEF_LFLAG;
485 		ttychars(tp);
486 		ttsetwater(tp);
487 		/* raise RTS and DTR here; but, DTR lead is not wired */
488 		/* then check DCD condition; but, DCD lead is not wired */
489 #if 0
490 		if ((sc->sc_flags & TIOCFLAG_SOFTCAR)
491 		    || (tp->t_cflag & MDMBUF)
492 		    || (getsiocsr(sc->sc_ctl) & RR_DCD))
493 			tp->t_state |= TS_CARR_ON;
494 		else
495 			tp->t_state &= ~TS_CARR_ON;
496 #else
497 		tp->t_state |= TS_CARR_ON; /* assume detected all the time */
498 #endif
499 		s = spltty();
500 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
501 		sc->sc_rbavail = siotty_rbuf_size;
502 		splx(s);
503 	}
504 
505 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
506 }
507 
508 int
509 sioclose(dev_t dev, int flag, int mode, struct proc *p)
510 {
511 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
512 	struct tty *tp = sc->sc_tty;
513 	int s;
514 
515 	(*linesw[tp->t_line].l_close)(tp, flag, p);
516 
517 	s = spltty();
518 	siomctl(sc, TIOCM_BREAK, DMBIC);
519 #if 0 /* because unable to feed DTR signal */
520 	if ((tp->t_cflag & HUPCL)
521 	    || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) {
522 		siomctl(sc, TIOCM_DTR, DMBIC);
523 		/* Yield CPU time to others for 1 second, then ... */
524 		siomctl(sc, TIOCM_DTR, DMBIS);
525 	}
526 #endif
527 	splx(s);
528 	return ttyclose(tp);
529 }
530 
531 int
532 sioread(dev_t dev, struct uio *uio, int flag)
533 {
534 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
535 	struct tty *tp = sc->sc_tty;
536 
537 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
538 }
539 
540 int
541 siowrite(dev_t dev, struct uio *uio, int flag)
542 {
543 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
544 	struct tty *tp = sc->sc_tty;
545 
546 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
547 }
548 
549 int
550 sioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
551 {
552 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
553 	struct tty *tp = sc->sc_tty;
554 	int error;
555 
556 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
557 	if (error >= 0)
558 		return error;
559 
560 	error = ttioctl(tp, cmd, data, flag, p);
561 	if (error >= 0)
562 		return error;
563 
564 	/* the last resort for TIOC ioctl traversing */
565 	switch (cmd) {
566 	case TIOCSBRK: /* Set the hardware into BREAK condition */
567 		siomctl(sc, TIOCM_BREAK, DMBIS);
568 		break;
569 	case TIOCCBRK: /* Clear the hardware BREAK condition */
570 		siomctl(sc, TIOCM_BREAK, DMBIC);
571 		break;
572 	case TIOCSDTR: /* Assert DTR signal */
573 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS);
574 		break;
575 	case TIOCCDTR: /* Clear DTR signal */
576 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC);
577 		break;
578 	case TIOCMSET: /* Set modem state replacing current one */
579 		siomctl(sc, *(int *)data, DMSET);
580 		break;
581 	case TIOCMGET: /* Return current modem state */
582 		*(int *)data = siomctl(sc, 0, DMGET);
583 		break;
584 	case TIOCMBIS: /* Set individual bits of modem state */
585 		siomctl(sc, *(int *)data, DMBIS);
586 		break;
587 	case TIOCMBIC: /* Clear individual bits of modem state */
588 		siomctl(sc, *(int *)data, DMBIC);
589 		break;
590 	case TIOCSFLAGS: /* Instruct how serial port behaves */
591 		error = suser(p);
592 		if (error != 0)
593 			return EPERM;
594 		sc->sc_flags = *(int *)data;
595 		break;
596 	case TIOCGFLAGS: /* Return current serial port state */
597 		*(int *)data = sc->sc_flags;
598 		break;
599 	default:
600 		return ENOTTY;
601 	}
602 	return 0;
603 }
604 
605 /* ARSGUSED */
606 struct tty *
607 siotty(dev_t dev)
608 {
609 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
610 
611 	return sc->sc_tty;
612 }
613 
614 /*--------------------  miscellaneous routines --------------------*/
615 
616 /* EXPORT */ void
617 setsioreg(struct sioreg *sio, int regno, int val)
618 {
619 	if (regno != 0)
620 		sio->sio_cmd = regno;	/* DELAY(); */
621 	sio->sio_cmd = val;		/* DELAY(); */
622 }
623 
624 /* EXPORT */ int
625 getsiocsr(struct sioreg *sio)
626 {
627 	int val;
628 
629 	val = sio->sio_stat << 8;	/* DELAY(); */
630 	sio->sio_cmd = 1;		/* DELAY(); */
631 	val |= sio->sio_stat;		/* DELAY(); */
632 	return val;
633 }
634 
635 /*---------------------  console interface ----------------------*/
636 
637 void syscnattach(int);
638 int  syscngetc(dev_t);
639 void syscnputc(dev_t, int);
640 
641 struct consdev syscons = {
642 	NULL,
643 	NULL,
644 	syscngetc,
645 	syscnputc,
646 	nullcnpollc,
647 	NULL,
648 	NODEV,
649 	CN_HIGHPRI,
650 };
651 
652 /* EXPORT */ void
653 syscnattach(int channel)
654 {
655 /*
656  * Channel A is immediately initialized with 9600N1 right after cold
657  * boot/reset/poweron.  ROM monitor emits one line message on CH.A.
658  */
659 	struct sioreg *sio;
660 	sio = (struct sioreg *)OBIO_SIO + channel;
661 
662 	syscons.cn_dev = makedev(12, channel);
663 	cn_tab = &syscons;
664 
665 	setsioreg(sio, WR0, WR0_CHANRST);
666 	setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1);
667 	setsioreg(sio, WR2B, 0);
668 	setsioreg(sio, WR0, ch0_regs[WR0]);
669 	setsioreg(sio, WR4, ch0_regs[WR4]);
670 	setsioreg(sio, WR3, ch0_regs[WR3]);
671 	setsioreg(sio, WR5, ch0_regs[WR5]);
672 	setsioreg(sio, WR0, ch0_regs[WR0]);
673 }
674 
675 /* EXPORT */ int
676 syscngetc(dev_t dev)
677 {
678 	struct sioreg *sio;
679 	int s, c;
680 
681 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
682 	s = splhigh();
683 	while ((getsiocsr(sio) & RR_RXRDY) == 0)
684 		;
685 	c = sio->sio_data;
686 	splx(s);
687 
688 	return c;
689 }
690 
691 /* EXPORT */ void
692 syscnputc(dev_t dev, int c)
693 {
694 	struct sioreg *sio;
695 	int s;
696 
697 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
698 	s = splhigh();
699 	while ((getsiocsr(sio) & RR_TXRDY) == 0)
700 		;
701 	sio->sio_cmd = WR0_RSTPEND;
702 	sio->sio_data = c;
703 	splx(s);
704 }
705