xref: /openbsd-src/sys/arch/luna88k/dev/siotty.c (revision dfc190eaa6096fd689c021dccb9da083a5aaa735)
1*dfc190eaSaoyama /* $OpenBSD: siotty.c,v 1.25 2021/01/09 02:34:21 aoyama Exp $ */
202b01b5eSaoyama /* $NetBSD: siotty.c,v 1.9 2002/03/17 19:40:43 atatat Exp $ */
302b01b5eSaoyama 
402b01b5eSaoyama /*-
502b01b5eSaoyama  * Copyright (c) 2000 The NetBSD Foundation, Inc.
602b01b5eSaoyama  * All rights reserved.
702b01b5eSaoyama  *
802b01b5eSaoyama  * This code is derived from software contributed to The NetBSD Foundation
902b01b5eSaoyama  * by Tohru Nishimura.
1002b01b5eSaoyama  *
1102b01b5eSaoyama  * Redistribution and use in source and binary forms, with or without
1202b01b5eSaoyama  * modification, are permitted provided that the following conditions
1302b01b5eSaoyama  * are met:
1402b01b5eSaoyama  * 1. Redistributions of source code must retain the above copyright
1502b01b5eSaoyama  *    notice, this list of conditions and the following disclaimer.
1602b01b5eSaoyama  * 2. Redistributions in binary form must reproduce the above copyright
1702b01b5eSaoyama  *    notice, this list of conditions and the following disclaimer in the
1802b01b5eSaoyama  *    documentation and/or other materials provided with the distribution.
1902b01b5eSaoyama  *
2002b01b5eSaoyama  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
2102b01b5eSaoyama  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
2202b01b5eSaoyama  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
2302b01b5eSaoyama  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2402b01b5eSaoyama  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2502b01b5eSaoyama  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2602b01b5eSaoyama  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2702b01b5eSaoyama  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2802b01b5eSaoyama  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2902b01b5eSaoyama  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3002b01b5eSaoyama  * POSSIBILITY OF SUCH DAMAGE.
3102b01b5eSaoyama  */
3202b01b5eSaoyama 
3302b01b5eSaoyama #include <sys/param.h>
3402b01b5eSaoyama #include <sys/systm.h>
3502b01b5eSaoyama #include <sys/device.h>
3602b01b5eSaoyama #include <sys/conf.h>
3702b01b5eSaoyama #include <sys/ioctl.h>
3865958cb6Saoyama #include <sys/malloc.h>
3902b01b5eSaoyama #include <sys/proc.h>
4002b01b5eSaoyama #include <sys/tty.h>
4102b01b5eSaoyama #include <sys/uio.h>
4202b01b5eSaoyama #include <sys/fcntl.h>
4302b01b5eSaoyama #include <dev/cons.h>
4402b01b5eSaoyama 
458aed167bSmiod #include <machine/board.h>
4602b01b5eSaoyama #include <machine/cpu.h>
4702b01b5eSaoyama 
4802b01b5eSaoyama #include <luna88k/dev/sioreg.h>
4902b01b5eSaoyama #include <luna88k/dev/siovar.h>
5002b01b5eSaoyama 
5102b01b5eSaoyama #define	TIOCM_BREAK 01000 /* non standard use */
5202b01b5eSaoyama 
5302b01b5eSaoyama static const u_int8_t ch0_regs[6] = {
5402b01b5eSaoyama 	WR0_RSTINT,				/* reset E/S interrupt */
5502b01b5eSaoyama 	WR1_RXALLS | WR1_TXENBL,		/* Rx per char, Tx */
5602b01b5eSaoyama 	0,					/* */
5702b01b5eSaoyama 	WR3_RX8BIT | WR3_RXENBL,		/* Rx */
5802b01b5eSaoyama 	WR4_BAUD96 | WR4_STOP1,			/* Tx/Rx */
5902b01b5eSaoyama 	WR5_TX8BIT | WR5_TXENBL | WR5_DTR | WR5_RTS, /* Tx */
6002b01b5eSaoyama };
6102b01b5eSaoyama 
6265cd0f3cSmickey static const struct speedtab siospeedtab[] = {
6302b01b5eSaoyama 	{ 2400,	WR4_BAUD24, },
6402b01b5eSaoyama 	{ 4800,	WR4_BAUD48, },
6502b01b5eSaoyama 	{ 9600,	WR4_BAUD96, },
6602b01b5eSaoyama 	{ -1,	0, },
6702b01b5eSaoyama };
6802b01b5eSaoyama 
6902b01b5eSaoyama struct siotty_softc {
7002b01b5eSaoyama 	struct device	sc_dev;
7102b01b5eSaoyama 	struct tty	*sc_tty;
7202b01b5eSaoyama 	struct sioreg	*sc_ctl;
7302b01b5eSaoyama 	u_int 		sc_flags;
7402b01b5eSaoyama 	u_int8_t	sc_wr[6];
7565958cb6Saoyama 	void		*sc_si;		/* software interrupt handler */
7665958cb6Saoyama 	u_int		sc_hwflags;
7765958cb6Saoyama #define	SIOTTY_HW_CONSOLE	0x0001
7865958cb6Saoyama 
7965958cb6Saoyama 	u_int8_t	*sc_rbuf;
8065958cb6Saoyama 	u_int8_t	*sc_rbufend;
8165958cb6Saoyama 	u_int8_t * volatile sc_rbget;
8265958cb6Saoyama 	u_int8_t * volatile sc_rbput;
8365958cb6Saoyama 	volatile u_int	sc_rbavail;
8465958cb6Saoyama 
8565958cb6Saoyama 	u_int8_t	*sc_tba;
8665958cb6Saoyama 	u_int		sc_tbc;
8765958cb6Saoyama 
8865958cb6Saoyama 	bool		sc_rx_ready;
8965958cb6Saoyama 	bool		sc_tx_busy;
9065958cb6Saoyama 	bool		sc_tx_done;
9102b01b5eSaoyama };
9202b01b5eSaoyama 
9365958cb6Saoyama #define	SIOTTY_RING_SIZE	2048
9465958cb6Saoyama u_int siotty_rbuf_size = SIOTTY_RING_SIZE;
9565958cb6Saoyama 
9602b01b5eSaoyama cdev_decl(sio);
9702b01b5eSaoyama void siostart(struct tty *);
9802b01b5eSaoyama int  sioparam(struct tty *, struct termios *);
99b6c895f4Saoyama void siottyintr(void *);
10065958cb6Saoyama void siottysoft(void *);
10165958cb6Saoyama void siotty_rxsoft(struct siotty_softc *, struct tty *);
10265958cb6Saoyama void siotty_txsoft(struct siotty_softc *, struct tty *);
10302b01b5eSaoyama int  siomctl(struct siotty_softc *, int, int);
10402b01b5eSaoyama 
10502b01b5eSaoyama int  siotty_match(struct device *, void *, void *);
10602b01b5eSaoyama void siotty_attach(struct device *, struct device *, void *);
10702b01b5eSaoyama 
10802b01b5eSaoyama const struct cfattach siotty_ca = {
10902b01b5eSaoyama 	sizeof(struct siotty_softc), siotty_match, siotty_attach
11002b01b5eSaoyama };
11102b01b5eSaoyama 
11210e8dd75Smiod struct cfdriver siotty_cd = {
11302b01b5eSaoyama 	NULL, "siotty", DV_TTY
11402b01b5eSaoyama };
11502b01b5eSaoyama 
11602b01b5eSaoyama int
siotty_match(struct device * parent,void * cf,void * aux)11780bc78ddSaoyama siotty_match(struct device *parent, void *cf, void *aux)
11802b01b5eSaoyama {
11902b01b5eSaoyama 	struct sio_attach_args *args = aux;
12002b01b5eSaoyama 
12102b01b5eSaoyama 	if (args->channel != 0) /* XXX allow tty on Ch.B XXX */
12202b01b5eSaoyama 		return 0;
12302b01b5eSaoyama 	return 1;
12402b01b5eSaoyama }
12502b01b5eSaoyama 
12602b01b5eSaoyama void
siotty_attach(struct device * parent,struct device * self,void * aux)12780bc78ddSaoyama siotty_attach(struct device *parent, struct device *self, void *aux)
12802b01b5eSaoyama {
129b6c895f4Saoyama 	struct sio_softc *siosc = (void *)parent;
13002b01b5eSaoyama 	struct siotty_softc *sc = (void *)self;
13102b01b5eSaoyama 	struct sio_attach_args *args = aux;
132b6c895f4Saoyama 	int channel;
13365958cb6Saoyama 	struct tty *tp;
13402b01b5eSaoyama 
135b6c895f4Saoyama 	channel = args->channel;
136b6c895f4Saoyama 	sc->sc_ctl = &siosc->sc_ctl[channel];
137b6c895f4Saoyama 	memcpy(sc->sc_wr, ch0_regs, sizeof(ch0_regs));
138b6c895f4Saoyama 	siosc->sc_intrhand[channel].ih_func = siottyintr;
139b6c895f4Saoyama 	siosc->sc_intrhand[channel].ih_arg = sc;
14065958cb6Saoyama 	if (args->hwflags == 1)
14165958cb6Saoyama 		sc->sc_hwflags |= SIOTTY_HW_CONSOLE;
14202b01b5eSaoyama 
14365958cb6Saoyama 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE) {
14402b01b5eSaoyama 		printf(" (console)");
14502b01b5eSaoyama 		sc->sc_flags = TIOCFLAG_SOFTCAR;
14665958cb6Saoyama 	} else {
14702b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR0, WR0_CHANRST);
14802b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR2A, WR2_VEC86 | WR2_INTR_1);
14902b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR2B, 0);
15002b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
15102b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
15202b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
15302b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
15402b01b5eSaoyama 		setsioreg(sc->sc_ctl, WR0, sc->sc_wr[WR0]);
15502b01b5eSaoyama 	}
15602b01b5eSaoyama 	setsioreg(sc->sc_ctl, WR1, sc->sc_wr[WR1]); /* now interrupt driven */
15702b01b5eSaoyama 
15802b01b5eSaoyama 	printf("\n");
15965958cb6Saoyama 
16065958cb6Saoyama 	sc->sc_rbuf = malloc(siotty_rbuf_size * 2, M_DEVBUF, M_NOWAIT);
16165958cb6Saoyama 	if (sc->sc_rbuf == NULL) {
16265958cb6Saoyama 		printf("%s: unable to allocate ring buffer\n",
16365958cb6Saoyama 		    (sc->sc_dev).dv_xname);
16465958cb6Saoyama 		return;
16565958cb6Saoyama 	}
16665958cb6Saoyama 	sc->sc_rbufend = sc->sc_rbuf + (siotty_rbuf_size * 2);
16765958cb6Saoyama 	sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
16865958cb6Saoyama 	sc->sc_rbavail = siotty_rbuf_size;
16965958cb6Saoyama 
17065958cb6Saoyama 	tp = ttymalloc(0);
17165958cb6Saoyama 	tp->t_oproc = siostart;
17265958cb6Saoyama 	tp->t_param = sioparam;
17365958cb6Saoyama 	tp->t_hwiflow = NULL /* XXX siohwiflow XXX */;
17465958cb6Saoyama 	if (sc->sc_hwflags & SIOTTY_HW_CONSOLE)
17565958cb6Saoyama 		tp->t_dev = cn_tab->cn_dev;
17665958cb6Saoyama 	sc->sc_tty = tp;
17765958cb6Saoyama 
17865958cb6Saoyama 	sc->sc_si = softintr_establish(IPL_SOFTTTY, siottysoft, sc);
17902b01b5eSaoyama }
18002b01b5eSaoyama 
18102b01b5eSaoyama /*--------------------  low level routine --------------------*/
18202b01b5eSaoyama 
18302b01b5eSaoyama void
siottyintr(void * arg)184b6c895f4Saoyama siottyintr(void *arg)
18502b01b5eSaoyama {
18602b01b5eSaoyama 	struct siotty_softc *sc;
18702b01b5eSaoyama 	struct sioreg *sio;
18865958cb6Saoyama 	u_int8_t *put, *end;
18965958cb6Saoyama 	u_int8_t c;
19065958cb6Saoyama 	u_int16_t rr;
19165958cb6Saoyama 	int cc;
19202b01b5eSaoyama 
19365958cb6Saoyama 	sc = arg;
19465958cb6Saoyama 	end = sc->sc_rbufend;
19565958cb6Saoyama 	put = sc->sc_rbput;
19665958cb6Saoyama 	cc = sc->sc_rbavail;
19765958cb6Saoyama 
19802b01b5eSaoyama 	sio = sc->sc_ctl;
19902b01b5eSaoyama 	rr = getsiocsr(sio);
20002b01b5eSaoyama 	if (rr & RR_RXRDY) {
20102b01b5eSaoyama 		do {
20265958cb6Saoyama 			if (cc > 0) {
20365958cb6Saoyama 				c = sio->sio_data;
20465958cb6Saoyama 				put[0] = c;
20565958cb6Saoyama 				put[1] = rr & 0xff;
20665958cb6Saoyama 				put += 2;
20765958cb6Saoyama 				if (put >= end)
20865958cb6Saoyama 					put = sc->sc_rbuf;
20965958cb6Saoyama 				cc--;
21065958cb6Saoyama 			}
21165958cb6Saoyama 			if (rr & (RR_FRAMING | RR_OVERRUN | RR_PARITY))
21202b01b5eSaoyama 				sio->sio_cmd = WR0_ERRRST;
21365958cb6Saoyama 
21465958cb6Saoyama 			sc->sc_rbput = put;
21565958cb6Saoyama 			sc->sc_rbavail = cc;
21665958cb6Saoyama 			sc->sc_rx_ready = true;
21702b01b5eSaoyama 		} while ((rr = getsiocsr(sio)) & RR_RXRDY);
21802b01b5eSaoyama 	}
21902b01b5eSaoyama 	if (rr & RR_TXRDY) {
22002b01b5eSaoyama 		sio->sio_cmd = WR0_RSTPEND;
22165958cb6Saoyama 		if (sc->sc_tbc > 0) {
22265958cb6Saoyama 			sio->sio_data = *sc->sc_tba;
22365958cb6Saoyama 			sc->sc_tba++;
22465958cb6Saoyama 			sc->sc_tbc--;
22565958cb6Saoyama 		} else {
22665958cb6Saoyama 			if (sc->sc_tx_busy) {
22765958cb6Saoyama 				sc->sc_tx_busy = false;
22865958cb6Saoyama 				sc->sc_tx_done = true;
22965958cb6Saoyama 			}
23065958cb6Saoyama 		}
23165958cb6Saoyama 	}
23265958cb6Saoyama 	softintr_schedule(sc->sc_si);
23365958cb6Saoyama }
23465958cb6Saoyama 
23565958cb6Saoyama void
siottysoft(void * arg)23665958cb6Saoyama siottysoft(void *arg)
23765958cb6Saoyama {
23865958cb6Saoyama 	struct siotty_softc *sc;
23965958cb6Saoyama 	struct tty *tp;
24065958cb6Saoyama 
24165958cb6Saoyama 	sc = arg;
24265958cb6Saoyama 	tp = sc->sc_tty;
24365958cb6Saoyama 
24465958cb6Saoyama 	if (sc->sc_rx_ready) {
24565958cb6Saoyama 		sc->sc_rx_ready = false;
24665958cb6Saoyama 		siotty_rxsoft(sc, tp);
24765958cb6Saoyama 	}
24865958cb6Saoyama 	if (sc->sc_tx_done) {
24965958cb6Saoyama 		sc->sc_tx_done = false;
25065958cb6Saoyama 		siotty_txsoft(sc, tp);
25165958cb6Saoyama 	}
25265958cb6Saoyama }
25365958cb6Saoyama 
25465958cb6Saoyama void
siotty_rxsoft(struct siotty_softc * sc,struct tty * tp)25565958cb6Saoyama siotty_rxsoft(struct siotty_softc *sc, struct tty *tp)
25665958cb6Saoyama {
25765958cb6Saoyama 	uint8_t *get, *end;
25865958cb6Saoyama 	u_int cc, scc;
25965958cb6Saoyama 	unsigned int code;
26065958cb6Saoyama 	uint8_t stat;
26165958cb6Saoyama 	int s;
26265958cb6Saoyama 
26365958cb6Saoyama 	end = sc->sc_rbufend;
26465958cb6Saoyama 	get = sc->sc_rbget;
26565958cb6Saoyama 	scc = cc = siotty_rbuf_size - sc->sc_rbavail;
26665958cb6Saoyama 
26765958cb6Saoyama 	if (cc == siotty_rbuf_size) {
26865958cb6Saoyama 		printf("%s: rx buffer overflow\n", (sc->sc_dev).dv_xname);
26965958cb6Saoyama 	}
27065958cb6Saoyama 
27165958cb6Saoyama 	while (cc > 0) {
27265958cb6Saoyama 		code = get[0];
27365958cb6Saoyama 		stat = get[1];
27465958cb6Saoyama 		if ((stat & RR_FRAMING) != 0)
27565958cb6Saoyama 			code |= TTY_FE;
27665958cb6Saoyama 		else if ((stat & RR_PARITY) != 0)
27765958cb6Saoyama 			code |= TTY_PE;
27865958cb6Saoyama 
27965958cb6Saoyama 		(*linesw[tp->t_line].l_rint)(code, tp);
28065958cb6Saoyama 		get += 2;
28165958cb6Saoyama 		if (get >= end)
28265958cb6Saoyama 			get = sc->sc_rbuf;
28365958cb6Saoyama 		cc--;
28465958cb6Saoyama 	}
28565958cb6Saoyama 
28665958cb6Saoyama 	if (cc != scc) {
28765958cb6Saoyama 		s = spltty();
28865958cb6Saoyama 		sc->sc_rbget = get;
28965958cb6Saoyama 		sc->sc_rbavail += scc - cc;
29065958cb6Saoyama 		splx(s);
29165958cb6Saoyama 	}
29265958cb6Saoyama }
29365958cb6Saoyama 
29465958cb6Saoyama void
siotty_txsoft(struct siotty_softc * sc,struct tty * tp)29565958cb6Saoyama siotty_txsoft(struct siotty_softc *sc, struct tty *tp)
29665958cb6Saoyama {
29765958cb6Saoyama 
29865958cb6Saoyama 	tp->t_state &= ~TS_BUSY;
29965958cb6Saoyama 	if ((tp->t_state & TS_FLUSH) != 0)
30065958cb6Saoyama 		tp->t_state &= ~TS_FLUSH;
30165958cb6Saoyama 	else
30265958cb6Saoyama 		ndflush(&tp->t_outq, (int)(sc->sc_tba - tp->t_outq.c_cf));
30302b01b5eSaoyama 	(*linesw[tp->t_line].l_start)(tp);
30402b01b5eSaoyama }
30502b01b5eSaoyama 
30602b01b5eSaoyama void
siostart(struct tty * tp)30780bc78ddSaoyama siostart(struct tty *tp)
30802b01b5eSaoyama {
30902b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
31065958cb6Saoyama 	int s;
31165958cb6Saoyama 	u_int8_t *tba;
31265958cb6Saoyama 	int tbc;
31302b01b5eSaoyama 
31402b01b5eSaoyama 	s = spltty();
31502b01b5eSaoyama 	if (tp->t_state & (TS_BUSY|TS_TIMEOUT|TS_TTSTOP))
31602b01b5eSaoyama 		goto out;
3174cc8800eSnicm 	ttwakeupwr(tp);
31802b01b5eSaoyama 	if (tp->t_outq.c_cc == 0)
31902b01b5eSaoyama 		goto out;
32002b01b5eSaoyama 
32102b01b5eSaoyama 	tp->t_state |= TS_BUSY;
32265958cb6Saoyama 
32365958cb6Saoyama 	tba = tp->t_outq.c_cf;
32465958cb6Saoyama 	tbc = ndqb(&tp->t_outq, 0);
32565958cb6Saoyama 
32665958cb6Saoyama 	sc->sc_tba = tba;
32765958cb6Saoyama 	sc->sc_tbc = tbc;
32865958cb6Saoyama 	sc->sc_tx_busy = true;
32965958cb6Saoyama 
33065958cb6Saoyama 	sc->sc_ctl->sio_data = *sc->sc_tba;
33165958cb6Saoyama 	sc->sc_tba++;
33265958cb6Saoyama 	sc->sc_tbc--;
33302b01b5eSaoyama out:
33402b01b5eSaoyama 	splx(s);
33502b01b5eSaoyama }
33602b01b5eSaoyama 
33702b01b5eSaoyama int
siostop(struct tty * tp,int flag)33880bc78ddSaoyama siostop(struct tty *tp, int flag)
33902b01b5eSaoyama {
34002b01b5eSaoyama 	int s;
34102b01b5eSaoyama 
34202b01b5eSaoyama 	s = spltty();
34302b01b5eSaoyama 	if (TS_BUSY == (tp->t_state & (TS_BUSY|TS_TTSTOP))) {
34402b01b5eSaoyama 		/*
34502b01b5eSaoyama 		 * Device is transmitting; must stop it.
34602b01b5eSaoyama 		 */
34702b01b5eSaoyama 		tp->t_state |= TS_FLUSH;
34802b01b5eSaoyama 	}
34902b01b5eSaoyama 	splx(s);
35002b01b5eSaoyama 	return (0);
35102b01b5eSaoyama }
35202b01b5eSaoyama 
35302b01b5eSaoyama int
sioparam(struct tty * tp,struct termios * t)35480bc78ddSaoyama sioparam(struct tty *tp, struct termios *t)
35502b01b5eSaoyama {
35602b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(tp->t_dev)];
35702b01b5eSaoyama 	int wr4, s;
35802b01b5eSaoyama 
35902b01b5eSaoyama 	if (t->c_ispeed && t->c_ispeed != t->c_ospeed)
36002b01b5eSaoyama 		return EINVAL;
36102b01b5eSaoyama 	wr4 = ttspeedtab(t->c_ospeed, siospeedtab);
36202b01b5eSaoyama 	if (wr4 < 0)
36302b01b5eSaoyama 		return EINVAL;
36402b01b5eSaoyama 
36502b01b5eSaoyama 	if (sc->sc_flags & TIOCFLAG_SOFTCAR) {
36602b01b5eSaoyama 		t->c_cflag |= CLOCAL;
36702b01b5eSaoyama 		t->c_cflag &= ~HUPCL;
36802b01b5eSaoyama 	}
36902b01b5eSaoyama 	if (sc->sc_flags & TIOCFLAG_CLOCAL)
37002b01b5eSaoyama 		t->c_cflag |= CLOCAL;
37102b01b5eSaoyama 
37202b01b5eSaoyama 	/*
37302b01b5eSaoyama 	 * If there were no changes, don't do anything.  This avoids dropping
37402b01b5eSaoyama 	 * input and improves performance when all we did was frob things like
37502b01b5eSaoyama 	 * VMIN and VTIME.
37602b01b5eSaoyama 	 */
37702b01b5eSaoyama 	if (tp->t_ospeed == t->c_ospeed && tp->t_cflag == t->c_cflag)
37802b01b5eSaoyama 		return 0;
37902b01b5eSaoyama 
38002b01b5eSaoyama 	tp->t_ispeed = t->c_ispeed;
38102b01b5eSaoyama 	tp->t_ospeed = t->c_ospeed;
38202b01b5eSaoyama 	tp->t_cflag = t->c_cflag;
38302b01b5eSaoyama 
38402b01b5eSaoyama 	sc->sc_wr[WR3] &= 0x3f;
38502b01b5eSaoyama 	sc->sc_wr[WR5] &= 0x9f;
38602b01b5eSaoyama 	switch (tp->t_cflag & CSIZE) {
38702b01b5eSaoyama 	case CS7:
38802b01b5eSaoyama 		sc->sc_wr[WR3] |= WR3_RX7BIT; sc->sc_wr[WR5] |= WR5_TX7BIT;
38902b01b5eSaoyama 		break;
39002b01b5eSaoyama 	case CS8:
39102b01b5eSaoyama 		sc->sc_wr[WR3] |= WR3_RX8BIT; sc->sc_wr[WR5] |= WR5_TX8BIT;
39202b01b5eSaoyama 		break;
39302b01b5eSaoyama 	}
39402b01b5eSaoyama 	if (tp->t_cflag & PARENB) {
39502b01b5eSaoyama 		wr4 |= WR4_PARENAB;
39602b01b5eSaoyama 		if ((tp->t_cflag & PARODD) == 0)
39702b01b5eSaoyama 			wr4 |= WR4_EPARITY;
39802b01b5eSaoyama 	}
39902b01b5eSaoyama 	wr4 |= (tp->t_cflag & CSTOPB) ? WR4_STOP2 : WR4_STOP1;
40002b01b5eSaoyama 	sc->sc_wr[WR4] = wr4;
40102b01b5eSaoyama 
40202b01b5eSaoyama 	s = spltty();
40302b01b5eSaoyama 	setsioreg(sc->sc_ctl, WR4, sc->sc_wr[WR4]);
40402b01b5eSaoyama 	setsioreg(sc->sc_ctl, WR3, sc->sc_wr[WR3]);
40502b01b5eSaoyama 	setsioreg(sc->sc_ctl, WR5, sc->sc_wr[WR5]);
40602b01b5eSaoyama 	splx(s);
40702b01b5eSaoyama 
40802b01b5eSaoyama 	return 0;
40902b01b5eSaoyama }
41002b01b5eSaoyama 
41102b01b5eSaoyama int
siomctl(struct siotty_softc * sc,int control,int op)41280bc78ddSaoyama siomctl(struct siotty_softc *sc, int control, int op)
41302b01b5eSaoyama {
41402b01b5eSaoyama 	int val, s, wr5, rr;
41502b01b5eSaoyama 
41602b01b5eSaoyama 	val = 0;
41702b01b5eSaoyama 	if (control & TIOCM_BREAK)
41802b01b5eSaoyama 		val |= WR5_BREAK;
41902b01b5eSaoyama 	if (control & TIOCM_DTR)
42002b01b5eSaoyama 		val |= WR5_DTR;
42102b01b5eSaoyama 	if (control & TIOCM_RTS)
42202b01b5eSaoyama 		val |= WR5_RTS;
42302b01b5eSaoyama 	s = spltty();
42402b01b5eSaoyama 	wr5 = sc->sc_wr[WR5];
42502b01b5eSaoyama 	switch (op) {
42602b01b5eSaoyama 	case DMSET:
42702b01b5eSaoyama 		wr5 &= ~(WR5_BREAK|WR5_DTR|WR5_RTS);
428341cd9feSjsg 		/* FALLTHROUGH */
42902b01b5eSaoyama 	case DMBIS:
43002b01b5eSaoyama 		wr5 |= val;
43102b01b5eSaoyama 		break;
43202b01b5eSaoyama 	case DMBIC:
43302b01b5eSaoyama 		wr5 &= ~val;
43402b01b5eSaoyama 		break;
43502b01b5eSaoyama 	case DMGET:
43602b01b5eSaoyama 		val = 0;
43702b01b5eSaoyama 		rr = getsiocsr(sc->sc_ctl);
43802b01b5eSaoyama 		if (wr5 & WR5_DTR)
43902b01b5eSaoyama 			val |= TIOCM_DTR;
44002b01b5eSaoyama 		if (wr5 & WR5_RTS)
44102b01b5eSaoyama 			val |= TIOCM_RTS;
44202b01b5eSaoyama 		if (rr & RR_CTS)
44302b01b5eSaoyama 			val |= TIOCM_CTS;
44402b01b5eSaoyama 		if (rr & RR_DCD)
44502b01b5eSaoyama 			val |= TIOCM_CD;
44602b01b5eSaoyama 		goto done;
44702b01b5eSaoyama 	}
44802b01b5eSaoyama 	sc->sc_wr[WR5] = wr5;
44902b01b5eSaoyama 	setsioreg(sc->sc_ctl, WR5, wr5);
45002b01b5eSaoyama 	val = 0;
45102b01b5eSaoyama done:
45202b01b5eSaoyama 	splx(s);
45302b01b5eSaoyama 	return val;
45402b01b5eSaoyama }
45502b01b5eSaoyama 
45602b01b5eSaoyama /*--------------------  cdevsw[] interface --------------------*/
45702b01b5eSaoyama 
45802b01b5eSaoyama int
sioopen(dev_t dev,int flag,int mode,struct proc * p)45980bc78ddSaoyama sioopen(dev_t dev, int flag, int mode, struct proc *p)
46002b01b5eSaoyama {
46102b01b5eSaoyama 	struct siotty_softc *sc;
46202b01b5eSaoyama 	struct tty *tp;
46365958cb6Saoyama 	int s;
46402b01b5eSaoyama 
46502b01b5eSaoyama 	if ((sc = siotty_cd.cd_devs[minor(dev)]) == NULL)
46602b01b5eSaoyama 		return ENXIO;
46765958cb6Saoyama 
46865958cb6Saoyama 	tp = sc->sc_tty;
46965958cb6Saoyama 
47065958cb6Saoyama 	if ((tp->t_state & TS_ISOPEN) && (tp->t_state & TS_XCLUDE)
4713e676399Smpi 	    && suser(p) != 0)
47202b01b5eSaoyama 		return EBUSY;
47302b01b5eSaoyama 
47402b01b5eSaoyama 	if ((tp->t_state & TS_ISOPEN) == 0) {
47502b01b5eSaoyama 		struct termios t;
47602b01b5eSaoyama 
47765958cb6Saoyama 		tp->t_dev = dev;
47802b01b5eSaoyama 		t.c_ispeed = t.c_ospeed = TTYDEF_SPEED;
47902b01b5eSaoyama 		t.c_cflag = TTYDEF_CFLAG;
48002b01b5eSaoyama 		tp->t_ospeed = 0; /* force register update */
48102b01b5eSaoyama 		(void)sioparam(tp, &t);
48202b01b5eSaoyama 		tp->t_iflag = TTYDEF_IFLAG;
48302b01b5eSaoyama 		tp->t_oflag = TTYDEF_OFLAG;
48402b01b5eSaoyama 		tp->t_lflag = TTYDEF_LFLAG;
48502b01b5eSaoyama 		ttychars(tp);
48602b01b5eSaoyama 		ttsetwater(tp);
48702b01b5eSaoyama 		/* raise RTS and DTR here; but, DTR lead is not wired */
48802b01b5eSaoyama 		/* then check DCD condition; but, DCD lead is not wired */
48902b01b5eSaoyama #if 0
49002b01b5eSaoyama 		if ((sc->sc_flags & TIOCFLAG_SOFTCAR)
49102b01b5eSaoyama 		    || (tp->t_cflag & MDMBUF)
49202b01b5eSaoyama 		    || (getsiocsr(sc->sc_ctl) & RR_DCD))
49302b01b5eSaoyama 			tp->t_state |= TS_CARR_ON;
49402b01b5eSaoyama 		else
49502b01b5eSaoyama 			tp->t_state &= ~TS_CARR_ON;
49665958cb6Saoyama #else
49765958cb6Saoyama 		tp->t_state |= TS_CARR_ON; /* assume detected all the time */
49802b01b5eSaoyama #endif
49965958cb6Saoyama 		s = spltty();
50065958cb6Saoyama 		sc->sc_rbput = sc->sc_rbget = sc->sc_rbuf;
50165958cb6Saoyama 		sc->sc_rbavail = siotty_rbuf_size;
50265958cb6Saoyama 		splx(s);
50302b01b5eSaoyama 	}
50402b01b5eSaoyama 
50579f6c33aStedu 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
50602b01b5eSaoyama }
50702b01b5eSaoyama 
50802b01b5eSaoyama int
sioclose(dev_t dev,int flag,int mode,struct proc * p)50980bc78ddSaoyama sioclose(dev_t dev, int flag, int mode, struct proc *p)
51002b01b5eSaoyama {
51102b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
51202b01b5eSaoyama 	struct tty *tp = sc->sc_tty;
51302b01b5eSaoyama 	int s;
51402b01b5eSaoyama 
51579f6c33aStedu 	(*linesw[tp->t_line].l_close)(tp, flag, p);
51602b01b5eSaoyama 
51702b01b5eSaoyama 	s = spltty();
51802b01b5eSaoyama 	siomctl(sc, TIOCM_BREAK, DMBIC);
51902b01b5eSaoyama #if 0 /* because unable to feed DTR signal */
52002b01b5eSaoyama 	if ((tp->t_cflag & HUPCL)
52102b01b5eSaoyama 	    || tp->t_wopen || (tp->t_state & TS_ISOPEN) == 0) {
52202b01b5eSaoyama 		siomctl(sc, TIOCM_DTR, DMBIC);
52302b01b5eSaoyama 		/* Yield CPU time to others for 1 second, then ... */
52402b01b5eSaoyama 		siomctl(sc, TIOCM_DTR, DMBIS);
52502b01b5eSaoyama 	}
52602b01b5eSaoyama #endif
52702b01b5eSaoyama 	splx(s);
52802b01b5eSaoyama 	return ttyclose(tp);
52902b01b5eSaoyama }
53002b01b5eSaoyama 
53102b01b5eSaoyama int
sioread(dev_t dev,struct uio * uio,int flag)53280bc78ddSaoyama sioread(dev_t dev, struct uio *uio, int flag)
53302b01b5eSaoyama {
53402b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
53502b01b5eSaoyama 	struct tty *tp = sc->sc_tty;
53602b01b5eSaoyama 
53702b01b5eSaoyama 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
53802b01b5eSaoyama }
53902b01b5eSaoyama 
54002b01b5eSaoyama int
siowrite(dev_t dev,struct uio * uio,int flag)54180bc78ddSaoyama siowrite(dev_t dev, struct uio *uio, int flag)
54202b01b5eSaoyama {
54302b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
54402b01b5eSaoyama 	struct tty *tp = sc->sc_tty;
54502b01b5eSaoyama 
54602b01b5eSaoyama 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
54702b01b5eSaoyama }
54802b01b5eSaoyama 
54902b01b5eSaoyama int
sioioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)55080bc78ddSaoyama sioioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
55102b01b5eSaoyama {
55202b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
55302b01b5eSaoyama 	struct tty *tp = sc->sc_tty;
55402b01b5eSaoyama 	int error;
55502b01b5eSaoyama 
55602b01b5eSaoyama 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
55702b01b5eSaoyama 	if (error >= 0)
55802b01b5eSaoyama 		return error;
55902b01b5eSaoyama 
56002b01b5eSaoyama 	error = ttioctl(tp, cmd, data, flag, p);
56102b01b5eSaoyama 	if (error >= 0)
56202b01b5eSaoyama 		return error;
56302b01b5eSaoyama 
564e4c489dbSjca 	/* the last resort for TIOC ioctl traversing */
56502b01b5eSaoyama 	switch (cmd) {
56602b01b5eSaoyama 	case TIOCSBRK: /* Set the hardware into BREAK condition */
56702b01b5eSaoyama 		siomctl(sc, TIOCM_BREAK, DMBIS);
56802b01b5eSaoyama 		break;
56902b01b5eSaoyama 	case TIOCCBRK: /* Clear the hardware BREAK condition */
57002b01b5eSaoyama 		siomctl(sc, TIOCM_BREAK, DMBIC);
57102b01b5eSaoyama 		break;
57202b01b5eSaoyama 	case TIOCSDTR: /* Assert DTR signal */
57302b01b5eSaoyama 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIS);
57402b01b5eSaoyama 		break;
57502b01b5eSaoyama 	case TIOCCDTR: /* Clear DTR signal */
57602b01b5eSaoyama 		siomctl(sc, TIOCM_DTR|TIOCM_RTS, DMBIC);
57702b01b5eSaoyama 		break;
57802b01b5eSaoyama 	case TIOCMSET: /* Set modem state replacing current one */
57902b01b5eSaoyama 		siomctl(sc, *(int *)data, DMSET);
58002b01b5eSaoyama 		break;
58102b01b5eSaoyama 	case TIOCMGET: /* Return current modem state */
58202b01b5eSaoyama 		*(int *)data = siomctl(sc, 0, DMGET);
58302b01b5eSaoyama 		break;
58402b01b5eSaoyama 	case TIOCMBIS: /* Set individual bits of modem state */
58502b01b5eSaoyama 		siomctl(sc, *(int *)data, DMBIS);
58602b01b5eSaoyama 		break;
58702b01b5eSaoyama 	case TIOCMBIC: /* Clear individual bits of modem state */
58802b01b5eSaoyama 		siomctl(sc, *(int *)data, DMBIC);
58902b01b5eSaoyama 		break;
59002b01b5eSaoyama 	case TIOCSFLAGS: /* Instruct how serial port behaves */
5913e676399Smpi 		error = suser(p);
5922b84db25Smiod 		if (error != 0)
5932b84db25Smiod 			return EPERM;
59402b01b5eSaoyama 		sc->sc_flags = *(int *)data;
59502b01b5eSaoyama 		break;
59602b01b5eSaoyama 	case TIOCGFLAGS: /* Return current serial port state */
59702b01b5eSaoyama 		*(int *)data = sc->sc_flags;
59802b01b5eSaoyama 		break;
59902b01b5eSaoyama 	default:
60002b01b5eSaoyama 		return ENOTTY;
60102b01b5eSaoyama 	}
60202b01b5eSaoyama 	return 0;
60302b01b5eSaoyama }
60402b01b5eSaoyama 
60502b01b5eSaoyama /* ARSGUSED */
60602b01b5eSaoyama struct tty *
siotty(dev_t dev)60780bc78ddSaoyama siotty(dev_t dev)
60802b01b5eSaoyama {
60902b01b5eSaoyama 	struct siotty_softc *sc = siotty_cd.cd_devs[minor(dev)];
61002b01b5eSaoyama 
61102b01b5eSaoyama 	return sc->sc_tty;
61202b01b5eSaoyama }
61302b01b5eSaoyama 
61402b01b5eSaoyama /*--------------------  miscellaneous routines --------------------*/
61502b01b5eSaoyama 
61602b01b5eSaoyama /* EXPORT */ void
setsioreg(struct sioreg * sio,int regno,int val)61780bc78ddSaoyama setsioreg(struct sioreg *sio, int regno, int val)
61802b01b5eSaoyama {
61902b01b5eSaoyama 	if (regno != 0)
62002b01b5eSaoyama 		sio->sio_cmd = regno;	/* DELAY(); */
62102b01b5eSaoyama 	sio->sio_cmd = val;		/* DELAY(); */
62202b01b5eSaoyama }
62302b01b5eSaoyama 
62402b01b5eSaoyama /* EXPORT */ int
getsiocsr(struct sioreg * sio)62580bc78ddSaoyama getsiocsr(struct sioreg *sio)
62602b01b5eSaoyama {
62702b01b5eSaoyama 	int val;
62802b01b5eSaoyama 
62902b01b5eSaoyama 	val = sio->sio_stat << 8;	/* DELAY(); */
63002b01b5eSaoyama 	sio->sio_cmd = 1;		/* DELAY(); */
63102b01b5eSaoyama 	val |= sio->sio_stat;		/* DELAY(); */
63202b01b5eSaoyama 	return val;
63302b01b5eSaoyama }
63402b01b5eSaoyama 
63502b01b5eSaoyama /*---------------------  console interface ----------------------*/
63602b01b5eSaoyama 
63702b01b5eSaoyama void syscnattach(int);
63802b01b5eSaoyama int  syscngetc(dev_t);
63902b01b5eSaoyama void syscnputc(dev_t, int);
64002b01b5eSaoyama 
64102b01b5eSaoyama struct consdev syscons = {
64202b01b5eSaoyama 	NULL,
64302b01b5eSaoyama 	NULL,
64402b01b5eSaoyama 	syscngetc,
64502b01b5eSaoyama 	syscnputc,
64602b01b5eSaoyama 	nullcnpollc,
64702b01b5eSaoyama 	NULL,
64802b01b5eSaoyama 	NODEV,
649713cf266Sjsing 	CN_HIGHPRI,
65002b01b5eSaoyama };
65102b01b5eSaoyama 
65202b01b5eSaoyama /* EXPORT */ void
syscnattach(int channel)65380bc78ddSaoyama syscnattach(int channel)
65402b01b5eSaoyama {
65502b01b5eSaoyama /*
65602b01b5eSaoyama  * Channel A is immediately initialized with 9600N1 right after cold
65702b01b5eSaoyama  * boot/reset/poweron.  ROM monitor emits one line message on CH.A.
65802b01b5eSaoyama  */
65902b01b5eSaoyama 	struct sioreg *sio;
6608aed167bSmiod 	sio = (struct sioreg *)OBIO_SIO + channel;
66102b01b5eSaoyama 
66202b01b5eSaoyama 	syscons.cn_dev = makedev(12, channel);
66302b01b5eSaoyama 	cn_tab = &syscons;
66402b01b5eSaoyama 
66502b01b5eSaoyama 	setsioreg(sio, WR0, WR0_CHANRST);
66602b01b5eSaoyama 	setsioreg(sio, WR2A, WR2_VEC86 | WR2_INTR_1);
66702b01b5eSaoyama 	setsioreg(sio, WR2B, 0);
66802b01b5eSaoyama 	setsioreg(sio, WR0, ch0_regs[WR0]);
66902b01b5eSaoyama 	setsioreg(sio, WR4, ch0_regs[WR4]);
67002b01b5eSaoyama 	setsioreg(sio, WR3, ch0_regs[WR3]);
67102b01b5eSaoyama 	setsioreg(sio, WR5, ch0_regs[WR5]);
67202b01b5eSaoyama 	setsioreg(sio, WR0, ch0_regs[WR0]);
67302b01b5eSaoyama }
67402b01b5eSaoyama 
67502b01b5eSaoyama /* EXPORT */ int
syscngetc(dev_t dev)67680bc78ddSaoyama syscngetc(dev_t dev)
67702b01b5eSaoyama {
67802b01b5eSaoyama 	struct sioreg *sio;
67902b01b5eSaoyama 	int s, c;
68002b01b5eSaoyama 
6818aed167bSmiod 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
68202b01b5eSaoyama 	s = splhigh();
68302b01b5eSaoyama 	while ((getsiocsr(sio) & RR_RXRDY) == 0)
68402b01b5eSaoyama 		;
68502b01b5eSaoyama 	c = sio->sio_data;
68602b01b5eSaoyama 	splx(s);
68702b01b5eSaoyama 
68802b01b5eSaoyama 	return c;
68902b01b5eSaoyama }
69002b01b5eSaoyama 
69102b01b5eSaoyama /* EXPORT */ void
syscnputc(dev_t dev,int c)69280bc78ddSaoyama syscnputc(dev_t dev, int c)
69302b01b5eSaoyama {
69402b01b5eSaoyama 	struct sioreg *sio;
69502b01b5eSaoyama 	int s;
69602b01b5eSaoyama 
6978aed167bSmiod 	sio = (struct sioreg *)OBIO_SIO + ((int)dev & 0x1);
69802b01b5eSaoyama 	s = splhigh();
69902b01b5eSaoyama 	while ((getsiocsr(sio) & RR_TXRDY) == 0)
70002b01b5eSaoyama 		;
70102b01b5eSaoyama 	sio->sio_cmd = WR0_RSTPEND;
70202b01b5eSaoyama 	sio->sio_data = c;
70302b01b5eSaoyama 	splx(s);
70402b01b5eSaoyama }
705