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