xref: /openbsd-src/sys/arch/luna88k/dev/siotty.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /* $OpenBSD: siotty.c,v 1.16 2014/06/07 11:55:35 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/proc.h>
39 #include <sys/tty.h>
40 #include <sys/uio.h>
41 #include <sys/fcntl.h>
42 #include <dev/cons.h>
43 
44 #include <machine/cpu.h>
45 
46 #include <luna88k/dev/sioreg.h>
47 #include <luna88k/dev/siovar.h>
48 
49 #define	TIOCM_BREAK 01000 /* non standard use */
50 
51 static const u_int8_t ch0_regs[6] = {
52 	WR0_RSTINT,				/* reset E/S interrupt */
53 	WR1_RXALLS | WR1_TXENBL,	 	/* Rx per char, Tx */
54 	0,					/* */
55 	WR3_RX8BIT | WR3_RXENBL,		/* Rx */
56 	WR4_BAUD96 | WR4_STOP1,			/* Tx/Rx */
57 	WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */
58 };
59 
60 static const struct speedtab siospeedtab[] = {
61 	{ 2400,	WR4_BAUD24, },
62 	{ 4800,	WR4_BAUD48, },
63 	{ 9600,	WR4_BAUD96, },
64 	{ -1,	0, },
65 };
66 
67 struct siotty_softc {
68 	struct device	sc_dev;
69 	struct tty	*sc_tty;
70 	struct sioreg	*sc_ctl;
71 	u_int 		sc_flags;
72 	u_int8_t	sc_wr[6];
73 };
74 
75 cdev_decl(sio);
76 void siostart(struct tty *);
77 int  sioparam(struct tty *, struct termios *);
78 void siottyintr(int);
79 int  siomctl(struct siotty_softc *, int, int);
80 
81 int  siotty_match(struct device *, void *, void *);
82 void siotty_attach(struct device *, struct device *, void *);
83 
84 const struct cfattach siotty_ca = {
85 	sizeof(struct siotty_softc), siotty_match, siotty_attach
86 };
87 
88 struct cfdriver siotty_cd = {
89         NULL, "siotty", DV_TTY
90 };
91 
92 int
93 siotty_match(struct device *parent, void *cf, void *aux)
94 {
95 	struct sio_attach_args *args = aux;
96 
97 	if (args->channel != 0) /* XXX allow tty on Ch.B XXX */
98 		return 0;
99 	return 1;
100 }
101 
102 void
103 siotty_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct sio_softc *scp = (void *)parent;
106 	struct siotty_softc *sc = (void *)self;
107 	struct sio_attach_args *args = aux;
108 
109 	sc->sc_ctl = (struct sioreg *)scp->scp_ctl + args->channel;
110 	bcopy(ch0_regs, sc->sc_wr, sizeof(ch0_regs));
111 	scp->scp_intr[args->channel] = siottyintr;
112 
113 	if (args->hwflags == 1) {
114 		printf(" (console)");
115 		sc->sc_flags = TIOCFLAG_SOFTCAR;
116 	}
117 	else {
118 		setsioreg(sc->sc_ctl, WR0, WR0_CHANRST);
119 		setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1);
120 		setsioreg(sc->sc_ctl, WR2B, 0);
121 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
122 		setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
123 		setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
124 		setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
125 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
126 	}
127 	setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */
128 
129 	printf("\n");
130 }
131 
132 /*--------------------  low level routine --------------------*/
133 
134 void
135 siottyintr(int chan)
136 {
137 	struct siotty_softc *sc;
138 	struct sioreg *sio;
139 	struct tty *tp;
140 	unsigned int code;
141 	int rr;
142 
143 	if (chan >= siotty_cd.cd_ndevs)
144 		return;
145 	sc = siotty_cd.cd_devs[chan];
146 	tp = sc->sc_tty;
147 	sio = sc->sc_ctl;
148 	rr = getsiocsr(sio);
149 	if (rr & RR_RXRDY) {
150 		do {
151 			code = sio->sio_data;
152 			if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY)) {
153 				sio->sio_cmd = WR0_ERRRST;
154 				if (sio->sio_stat & RR_FRAMING)
155 					code |= TTY_FE;
156 				else if (sio->sio_stat & RR_PARITY)
157 					code |= TTY_PE;
158 			}
159 			if (tp == NULL || (tp->t_state & TS_ISOPEN) == 0)
160 				continue;
161 #if 0 && defined(DDB) /* ?!?! fails to resume ?!?! */
162 			if ((rr & RR_BREAK) && tp->t_dev == cn_tab->cn_dev) {
163 				if (db_console)
164 					Debugger();
165 				return;
166 			}
167 #endif
168 			(*linesw[tp->t_line].l_rint)(code, tp);
169 		} while ((rr = getsiocsr(sio)) & RR_RXRDY);
170 	}
171 	if (rr & RR_TXRDY) {
172 		sio->sio_cmd = WR0_RSTPEND;
173 		if (tp != NULL) {
174 			tp->t_state &= ~(TS_BUSY|TS_FLUSH);
175 			(*linesw[tp->t_line].l_start)(tp);
176 		}
177 	}
178 }
179 
180 void
181 siostart(struct tty *tp)
182 {
183 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
184 	int s, c;
185 
186 	s = spltty();
187 	if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP))
188 		goto out;
189 	ttwakeupwr(tp);
190 	if (tp->t_outq.c_cc == 0)
191 		goto out;
192 
193 	tp->t_state |= TS_BUSY;
194 	while (getsiocsr(sc->sc_ctl) & RR_TXRDY) {
195 		if ((c = getc(&tp->t_outq)) == -1)
196 			break;
197 		sc->sc_ctl->sio_data = c;
198 	}
199 out:
200 	splx(s);
201 }
202 
203 int
204 siostop(struct tty *tp, int flag)
205 {
206 	int s;
207 
208         s = spltty();
209         if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) {
210                 /*
211                  * Device is transmitting; must stop it.
212                  */
213 		tp->t_state |= TS_FLUSH;
214         }
215         splx(s);
216 	return (0);
217 }
218 
219 int
220 sioparam(struct tty *tp, struct termios *t)
221 {
222 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
223 	int wr4, s;
224 
225 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
226 		return EINVAL;
227 	wr4 = ttspeedtab(t->c_ospeed, siospeedtab);
228 	if (wr4 < 0)
229 		return EINVAL;
230 
231 	if (sc->sc_flags & TIOCFLAG_SOFTCAR) {
232 		t->c_cflag |= CLOCAL;
233 		t->c_cflag &= ~HUPCL;
234 	}
235 	if (sc->sc_flags & TIOCFLAG_CLOCAL)
236 		t->c_cflag |= CLOCAL;
237 
238 	/*
239 	 * If there were no changes, don't do anything.  This avoids dropping
240 	 * input and improves performance when all we did was frob things like
241 	 * VMIN and VTIME.
242 	 */
243 	if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag)
244 		return 0;
245 
246 	tp->t_ispeed = t->c_ispeed;
247 	tp->t_ospeed = t->c_ospeed;
248 	tp->t_cflag = t->c_cflag;
249 
250 	sc->sc_wr[WR3] &= 0x3f;
251 	sc->sc_wr[WR5] &= 0x9f;
252 	switch (tp->t_cflag & CSIZE) {
253 	case CS7:
254 		sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT;
255 		break;
256 	case CS8:
257 		sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT;
258 		break;
259 	}
260 	if (tp->t_cflag & PARENB) {
261 		wr4 |= WR4_PARENAB;
262 		if ((tp->t_cflag & PARODD) == 0)
263 			wr4 |= WR4_EPARITY;
264 	}
265 	wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1;
266 	sc->sc_wr[WR4] = wr4;
267 
268 	s = spltty();
269 	setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
270 	setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
271 	setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
272 	splx(s);
273 
274 	return 0;
275 }
276 
277 int
278 siomctl(struct siotty_softc *sc, int control, int op)
279 {
280 	int val, s, wr5, rr;
281 
282 	val = 0;
283 	if (control & TIOCM_BREAK)
284 		val |= WR5_BREAK;
285 	if (control & TIOCM_DTR)
286 		val |= WR5_DTR;
287 	if (control & TIOCM_RTS)
288 		val |= WR5_RTS;
289 	s = spltty();
290 	wr5 = sc->sc_wr[WR5];
291 	switch (op) {
292 	case DMSET:
293 		wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS);
294 		/* FALLTHROUGH */
295 	case DMBIS:
296 		wr5 |= val;
297 		break;
298 	case DMBIC:
299 		wr5 &= ~val;
300 		break;
301 	case DMGET:
302 		val = 0;
303 		rr = getsiocsr(sc->sc_ctl);
304 		if (wr5 & WR5_DTR)
305 			val |= TIOCM_DTR;
306 		if (wr5 & WR5_RTS)
307 			val |= TIOCM_RTS;
308 		if (rr & RR_CTS)
309 			val |= TIOCM_CTS;
310 		if (rr & RR_DCD)
311 			val |= TIOCM_CD;
312 		goto done;
313 	}
314 	sc->sc_wr[WR5] = wr5;
315 	setsioreg(sc->sc_ctl, WR5, wr5);
316 	val = 0;
317 done:
318 	splx(s);
319 	return val;
320 }
321 
322 /*--------------------  cdevsw[] interface --------------------*/
323 
324 int
325 sioopen(dev_t dev, int flag, int mode, struct proc *p)
326 {
327 	struct siotty_softc *sc;
328 	struct tty *tp;
329 	int error;
330 
331 	if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL)
332 		return ENXIO;
333 	if ((tp = sc->sc_tty) == NULL) {
334 		tp = sc->sc_tty = ttymalloc(0);
335 	}
336 	else if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE)
337 	    && suser(p, 0) != 0)
338 		return EBUSY;
339 
340 	tp->t_oproc = siostart;
341 	tp->t_param = sioparam;
342 	tp->t_hwiflow = NULL /* XXX siohwiflow XXX */;
343 	tp->t_dev = dev;
344 	if ((tp->t_state & TS_ISOPEN) == 0) {
345 		struct termios t;
346 
347 		t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
348 		t.c_cflag = TTYDEF_CFLAG;
349 		tp->t_ospeed = 0; /* force register update */
350 		(void)sioparam(tp, &t);
351 		tp->t_iflag = TTYDEF_IFLAG;
352 		tp->t_oflag = TTYDEF_OFLAG;
353 		tp->t_lflag = TTYDEF_LFLAG;
354 		ttychars(tp);
355 		ttsetwater(tp);
356 		/* raise RTS and DTR here; but, DTR lead is not wired */
357 		/* then check DCD condition; but, DCD lead is not wired */
358 		tp->t_state |= TS_CARR_ON; /* assume detected all the time */
359 #if 0
360 		if ((sc->sc_flags & TIOCFLAG_SOFTCAR)
361 		    || (tp->t_cflag & MDMBUF)
362 		    || (getsiocsr(sc->sc_ctl) & RR_DCD))
363 			tp->t_state |= TS_CARR_ON;
364 		else
365 			tp->t_state &= ~TS_CARR_ON;
366 #endif
367 	}
368 
369 	error = ttyopen(dev, tp, p);
370 	if (error > 0)
371 		return error;
372 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
373 }
374 
375 int
376 sioclose(dev_t dev, int flag, int mode, struct proc *p)
377 {
378 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
379 	struct tty *tp = sc->sc_tty;
380 	int s;
381 
382 	(*linesw[tp->t_line].l_close)(tp, flag, p);
383 
384 	s = spltty();
385 	siomctl(sc, TIOCM_BREAK, DMBIC);
386 #if 0 /* because unable to feed DTR signal */
387 	if ((tp->t_cflag & HUPCL)
388 	    || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) {
389 		siomctl(sc, TIOCM_DTR, DMBIC);
390 		/* Yield CPU time to others for 1 second, then ... */
391 		siomctl(sc, TIOCM_DTR, DMBIS);
392 	}
393 #endif
394 	splx(s);
395 	return ttyclose(tp);
396 }
397 
398 int
399 sioread(dev_t dev, struct uio *uio, int flag)
400 {
401 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
402 	struct tty *tp = sc->sc_tty;
403 
404 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
405 }
406 
407 int
408 siowrite(dev_t dev, struct uio *uio, int flag)
409 {
410 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
411 	struct tty *tp = sc->sc_tty;
412 
413 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
414 }
415 
416 #if 0
417 int
418 sioselect(dev, events, p)
419 	dev_t dev;
420 	int events;
421 	struct proc *p;
422 {
423 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
424 	struct tty *tp = sc->sc_tty;
425 
426 	return ((*linesw[tp->t_line].l_select)(tp, events, p));
427 
428 }
429 #endif
430 
431 int
432 sioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
433 {
434 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
435 	struct tty *tp = sc->sc_tty;
436 	int error;
437 
438 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
439 	if (error >= 0)
440 		return error;
441 
442 	error = ttioctl(tp, cmd, data, flag, p);
443 	if (error >= 0)
444 		return error;
445 
446 	/* the last resort for TIOC ioctl tranversing */
447 	switch (cmd) {
448 	case TIOCSBRK: /* Set the hardware into BREAK condition */
449 		siomctl(sc, TIOCM_BREAK, DMBIS);
450 		break;
451 	case TIOCCBRK: /* Clear the hardware BREAK condition */
452 		siomctl(sc, TIOCM_BREAK, DMBIC);
453 		break;
454 	case TIOCSDTR: /* Assert DTR signal */
455 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS);
456 		break;
457 	case TIOCCDTR: /* Clear DTR signal */
458 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC);
459 		break;
460 	case TIOCMSET: /* Set modem state replacing current one */
461 		siomctl(sc, *(int *)data, DMSET);
462 		break;
463 	case TIOCMGET: /* Return current modem state */
464 		*(int *)data = siomctl(sc, 0, DMGET);
465 		break;
466 	case TIOCMBIS: /* Set individual bits of modem state */
467 		siomctl(sc, *(int *)data, DMBIS);
468 		break;
469 	case TIOCMBIC: /* Clear individual bits of modem state */
470 		siomctl(sc, *(int *)data, DMBIC);
471 		break;
472 	case TIOCSFLAGS: /* Instruct how serial port behaves */
473 		error = suser(p, 0);
474 		if (error != 0)
475 			return EPERM;
476 		sc->sc_flags = *(int *)data;
477 		break;
478 	case TIOCGFLAGS: /* Return current serial port state */
479 		*(int *)data = sc->sc_flags;
480 		break;
481 	default:
482 		return ENOTTY;
483 	}
484 	return 0;
485 }
486 
487 /* ARSGUSED */
488 struct tty *
489 siotty(dev_t dev)
490 {
491 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
492 
493 	return sc->sc_tty;
494 }
495 
496 /*--------------------  miscellaneous routines --------------------*/
497 
498 /* EXPORT */ void
499 setsioreg(struct sioreg *sio, int regno, int val)
500 {
501 	if (regno != 0)
502 		sio->sio_cmd = regno;	/* DELAY(); */
503 	sio->sio_cmd = val;		/* DELAY(); */
504 }
505 
506 /* EXPORT */ int
507 getsiocsr(struct sioreg *sio)
508 {
509 	int val;
510 
511 	val = sio->sio_stat << 8;	/* DELAY(); */
512 	sio->sio_cmd = 1;		/* DELAY(); */
513 	val |= sio->sio_stat;		/* DELAY(); */
514 	return val;
515 }
516 
517 /*---------------------  console interface ----------------------*/
518 
519 void syscnattach(int);
520 int  syscngetc(dev_t);
521 void syscnputc(dev_t, int);
522 
523 struct consdev syscons = {
524 	NULL,
525 	NULL,
526 	syscngetc,
527 	syscnputc,
528 	nullcnpollc,
529 	NULL,
530 	NODEV,
531 	CN_HIGHPRI,
532 };
533 
534 /* EXPORT */ void
535 syscnattach(int channel)
536 {
537 /*
538  * Channel A is immediately initialized with 9600N1 right after cold
539  * boot/reset/poweron.  ROM monitor emits one line message on CH.A.
540  */
541 	struct sioreg *sio;
542 	sio = (struct sioreg *)0x51000000 + channel;
543 
544 	syscons.cn_dev = makedev(12, channel);
545 	cn_tab = &syscons;
546 
547 #if 0
548 	setsioreg(sio, WR0, WR0_CHANRST);
549 	setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1);
550 	setsioreg(sio, WR2B, 0);
551 	setsioreg(sio, WR0, ch0_regs[WR0]);
552 	setsioreg(sio, WR4, ch0_regs[WR4]);
553 	setsioreg(sio, WR3, ch0_regs[WR3]);
554 	setsioreg(sio, WR5, ch0_regs[WR5]);
555 	setsioreg(sio, WR0, ch0_regs[WR0]);
556 #endif
557 }
558 
559 /* EXPORT */ int
560 syscngetc(dev_t dev)
561 {
562 	struct sioreg *sio;
563 	int s, c;
564 
565 	sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1);
566 	s = splhigh();
567 	while ((getsiocsr(sio) & RR_RXRDY) == 0)
568 		;
569 	c = sio->sio_data;
570 	splx(s);
571 
572 	return c;
573 }
574 
575 /* EXPORT */ void
576 syscnputc(dev_t dev, int c)
577 {
578 	struct sioreg *sio;
579 	int s;
580 
581 	sio = (struct sioreg *)0x51000000 + ((int)dev & 0x1);
582 	s = splhigh();
583 	while ((getsiocsr(sio) & RR_TXRDY) == 0)
584 		;
585 	sio->sio_cmd = WR0_RSTPEND;
586 	sio->sio_data = c;
587 	splx(s);
588 }
589