xref: /openbsd-src/sys/dev/fdt/amluart.c (revision 467651fa4a81e090f223fc0a4a50188f6aedcd0f)
1*467651faSkettenis /*	$OpenBSD: amluart.c,v 1.4 2022/07/15 17:14:49 kettenis Exp $	*/
29d94a3b6Skettenis /*
39d94a3b6Skettenis  * Copyright (c) 2019 Mark Kettenis <kettenis@openbsd.org>
49d94a3b6Skettenis  *
59d94a3b6Skettenis  * Permission to use, copy, modify, and distribute this software for any
69d94a3b6Skettenis  * purpose with or without fee is hereby granted, provided that the above
79d94a3b6Skettenis  * copyright notice and this permission notice appear in all copies.
89d94a3b6Skettenis  *
99d94a3b6Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109d94a3b6Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119d94a3b6Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129d94a3b6Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139d94a3b6Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149d94a3b6Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159d94a3b6Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169d94a3b6Skettenis  */
179d94a3b6Skettenis 
189d94a3b6Skettenis #include <sys/param.h>
199d94a3b6Skettenis #include <sys/conf.h>
209d94a3b6Skettenis #include <sys/fcntl.h>
219d94a3b6Skettenis #include <sys/proc.h>
229d94a3b6Skettenis #include <sys/systm.h>
239d94a3b6Skettenis #include <sys/tty.h>
249d94a3b6Skettenis 
259d94a3b6Skettenis #include <machine/bus.h>
269d94a3b6Skettenis #include <machine/fdt.h>
279d94a3b6Skettenis 
289d94a3b6Skettenis #include <dev/cons.h>
299d94a3b6Skettenis 
309d94a3b6Skettenis #include <dev/ofw/fdt.h>
319d94a3b6Skettenis #include <dev/ofw/openfirm.h>
329d94a3b6Skettenis 
339d94a3b6Skettenis #define UART_WFIFO			0x0000
349d94a3b6Skettenis #define UART_RFIFO			0x0004
359d94a3b6Skettenis #define UART_CONTROL			0x0008
369d94a3b6Skettenis #define  UART_CONTROL_TX_INT		(1 << 28)
379d94a3b6Skettenis #define  UART_CONTROL_RX_INT		(1 << 27)
389d94a3b6Skettenis #define  UART_CONTROL_CLEAR_ERROR	(1 << 24)
399d94a3b6Skettenis #define UART_STATUS			0x000c
409d94a3b6Skettenis #define  UART_STATUS_RX_FIFO_OVERFLOW	(1 << 24)
419d94a3b6Skettenis #define  UART_STATUS_TX_FIFO_FULL	(1 << 21)
429d94a3b6Skettenis #define  UART_STATUS_RX_FIFO_EMPTY	(1 << 20)
439d94a3b6Skettenis #define  UART_STATUS_FRAME_ERROR	(1 << 17)
449d94a3b6Skettenis #define  UART_STATUS_PARITY_ERROR	(1 << 16)
459d94a3b6Skettenis #define  UART_STATUS_ERROR 		(1 << 24 | 0x7 << 16)
469d94a3b6Skettenis #define UART_MISC			0x0010
479d94a3b6Skettenis #define  UART_MISC_TX_INT_CNT_MASK	(0xff << 16)
489d94a3b6Skettenis #define  UART_MISC_TX_INT_CNT_SHIFT	16
499d94a3b6Skettenis #define  UART_MISC_RX_INT_CNT_MASK	(0xff << 0)
509d94a3b6Skettenis #define  UART_MISC_RX_INT_CNT_SHIFT	0
519d94a3b6Skettenis 
529d94a3b6Skettenis #define UART_SPACE			24
539d94a3b6Skettenis 
549d94a3b6Skettenis #define HREAD4(sc, reg)							\
559d94a3b6Skettenis 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
569d94a3b6Skettenis #define HWRITE4(sc, reg, val)						\
579d94a3b6Skettenis 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
589d94a3b6Skettenis #define HSET4(sc, reg, bits)						\
599d94a3b6Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
609d94a3b6Skettenis #define HCLR4(sc, reg, bits)						\
619d94a3b6Skettenis 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
629d94a3b6Skettenis 
639d94a3b6Skettenis cdev_decl(com);
649d94a3b6Skettenis cdev_decl(amluart);
659d94a3b6Skettenis 
669d94a3b6Skettenis #define DEVUNIT(x)	(minor(x) & 0x7f)
679d94a3b6Skettenis #define DEVCUA(x)	(minor(x) & 0x80)
689d94a3b6Skettenis 
699d94a3b6Skettenis struct cdevsw amluartdev = cdev_tty_init(3, amluart);
709d94a3b6Skettenis 
719d94a3b6Skettenis struct amluart_softc {
729d94a3b6Skettenis 	struct device		sc_dev;
739d94a3b6Skettenis 	bus_space_tag_t		sc_iot;
749d94a3b6Skettenis 	bus_space_handle_t	sc_ioh;
759d94a3b6Skettenis 
769d94a3b6Skettenis 	struct soft_intrhand	*sc_si;
779d94a3b6Skettenis 	void			*sc_ih;
789d94a3b6Skettenis 
799d94a3b6Skettenis 	struct tty		*sc_tty;
809d94a3b6Skettenis 	int			sc_conspeed;
819d94a3b6Skettenis 	int			sc_floods;
829d94a3b6Skettenis 	int			sc_overflows;
839d94a3b6Skettenis 	int			sc_halt;
849d94a3b6Skettenis 	int			sc_cua;
859d94a3b6Skettenis 	int	 		*sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend;
869d94a3b6Skettenis #define AMLUART_IBUFSIZE	128
879d94a3b6Skettenis #define AMLUART_IHIGHWATER	100
889d94a3b6Skettenis 	int			sc_ibufs[2][AMLUART_IBUFSIZE];
899d94a3b6Skettenis };
909d94a3b6Skettenis 
919d94a3b6Skettenis int	amluart_match(struct device *, void *, void *);
929d94a3b6Skettenis void	amluart_attach(struct device *, struct device *, void *);
939d94a3b6Skettenis 
949d94a3b6Skettenis struct cfdriver amluart_cd = {
959d94a3b6Skettenis 	NULL, "amluart", DV_TTY
969d94a3b6Skettenis };
979d94a3b6Skettenis 
989fdf0c62Smpi const struct cfattach amluart_ca = {
999d94a3b6Skettenis 	sizeof(struct amluart_softc), amluart_match, amluart_attach
1009d94a3b6Skettenis };
1019d94a3b6Skettenis 
1029d94a3b6Skettenis bus_space_tag_t	amluartconsiot;
1039d94a3b6Skettenis bus_space_handle_t amluartconsioh;
1049d94a3b6Skettenis 
1059d94a3b6Skettenis struct amluart_softc *amluart_sc(dev_t);
1069d94a3b6Skettenis 
1079d94a3b6Skettenis int	amluart_intr(void *);
1089d94a3b6Skettenis void	amluart_softintr(void *);
1099d94a3b6Skettenis void	amluart_start(struct tty *);
1109d94a3b6Skettenis 
1119d94a3b6Skettenis int	amluartcnattach(bus_space_tag_t, bus_addr_t);
1129d94a3b6Skettenis int	amluartcngetc(dev_t);
1139d94a3b6Skettenis void	amluartcnputc(dev_t, int);
1149d94a3b6Skettenis void	amluartcnpollc(dev_t, int);
1159d94a3b6Skettenis 
1169d94a3b6Skettenis void
amluart_init_cons(void)1179d94a3b6Skettenis amluart_init_cons(void)
1189d94a3b6Skettenis {
1199d94a3b6Skettenis 	struct fdt_reg reg;
1209d94a3b6Skettenis 	void *node;
1219d94a3b6Skettenis 
1229d94a3b6Skettenis 	if ((node = fdt_find_cons("amlogic,meson-gx-uart")) == NULL)
1239d94a3b6Skettenis 		return;
1249d94a3b6Skettenis 	if (fdt_get_reg(node, 0, &reg))
1259d94a3b6Skettenis 		return;
1269d94a3b6Skettenis 
1279d94a3b6Skettenis 	amluartcnattach(fdt_cons_bs_tag, reg.addr);
1289d94a3b6Skettenis }
1299d94a3b6Skettenis 
1309d94a3b6Skettenis int
amluart_match(struct device * parent,void * match,void * aux)1319d94a3b6Skettenis amluart_match(struct device *parent, void *match, void *aux)
1329d94a3b6Skettenis {
1339d94a3b6Skettenis 	struct fdt_attach_args *faa = aux;
1349d94a3b6Skettenis 
1359d94a3b6Skettenis 	return OF_is_compatible(faa->fa_node, "amlogic,meson-gx-uart");
1369d94a3b6Skettenis }
1379d94a3b6Skettenis 
1389d94a3b6Skettenis void
amluart_attach(struct device * parent,struct device * self,void * aux)1399d94a3b6Skettenis amluart_attach(struct device *parent, struct device *self, void *aux)
1409d94a3b6Skettenis {
1419d94a3b6Skettenis 	struct amluart_softc *sc = (struct amluart_softc *)self;
1429d94a3b6Skettenis 	struct fdt_attach_args *faa = aux;
1439d94a3b6Skettenis 	uint32_t reg;
1449d94a3b6Skettenis 	int maj;
1459d94a3b6Skettenis 
1469d94a3b6Skettenis 	if (faa->fa_nreg < 1) {
1479d94a3b6Skettenis 		printf(": no registers\n");
1489d94a3b6Skettenis 		return;
1499d94a3b6Skettenis 	}
1509d94a3b6Skettenis 
1519d94a3b6Skettenis 	sc->sc_iot = faa->fa_iot;
1529d94a3b6Skettenis 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1539d94a3b6Skettenis 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1549d94a3b6Skettenis 		printf(": can't map registers\n");
1559d94a3b6Skettenis 		return;
1569d94a3b6Skettenis 	}
1579d94a3b6Skettenis 
1589d94a3b6Skettenis 	if (faa->fa_node == stdout_node) {
1599d94a3b6Skettenis 		/* Locate the major number. */
1609d94a3b6Skettenis 		for (maj = 0; maj < nchrdev; maj++)
1619d94a3b6Skettenis 			if (cdevsw[maj].d_open == amluartopen)
1629d94a3b6Skettenis 				break;
1639d94a3b6Skettenis 		cn_tab->cn_dev = makedev(maj, sc->sc_dev.dv_unit);
1649d94a3b6Skettenis 		sc->sc_conspeed = stdout_speed;
1659d94a3b6Skettenis 		printf(": console");
1669d94a3b6Skettenis 	}
1679d94a3b6Skettenis 
1689d94a3b6Skettenis 	sc->sc_si = softintr_establish(IPL_TTY, amluart_softintr, sc);
1699d94a3b6Skettenis 	if (sc->sc_si == NULL) {
1709d94a3b6Skettenis 		printf(": can't establish soft interrupt\n");
1719d94a3b6Skettenis 		return;
1729d94a3b6Skettenis 	}
1739d94a3b6Skettenis 
1749d94a3b6Skettenis 	sc->sc_ih = fdt_intr_establish_idx(faa->fa_node, 0, IPL_TTY,
1759d94a3b6Skettenis 	    amluart_intr, sc, sc->sc_dev.dv_xname);
1769d94a3b6Skettenis 	if (sc->sc_ih == NULL) {
1779d94a3b6Skettenis 		printf(": can't establish hard interrupt\n");
1789d94a3b6Skettenis 		return;
1799d94a3b6Skettenis 	}
1809d94a3b6Skettenis 
1819d94a3b6Skettenis 	printf("\n");
1829d94a3b6Skettenis 
1839d94a3b6Skettenis 	/*
1849d94a3b6Skettenis 	 * Generate interrupts if the Tx FIFO is half-empty or if
1859d94a3b6Skettenis 	 * there is anything in the Rx FIFO.
1869d94a3b6Skettenis 	 */
1879d94a3b6Skettenis 	reg = HREAD4(sc, UART_MISC);
1889d94a3b6Skettenis 	reg &= ~UART_MISC_TX_INT_CNT_MASK;
1899d94a3b6Skettenis 	reg |= (32 << UART_MISC_TX_INT_CNT_SHIFT);
1909d94a3b6Skettenis 	reg &= ~UART_MISC_RX_INT_CNT_MASK;
1919d94a3b6Skettenis 	reg |= (1 << UART_MISC_RX_INT_CNT_SHIFT);
1929d94a3b6Skettenis 	HWRITE4(sc, UART_MISC, reg);
1939d94a3b6Skettenis }
1949d94a3b6Skettenis 
1959d94a3b6Skettenis int
amluart_intr(void * arg)1969d94a3b6Skettenis amluart_intr(void *arg)
1979d94a3b6Skettenis {
1989d94a3b6Skettenis 	struct amluart_softc *sc = arg;
1999d94a3b6Skettenis 	struct tty *tp = sc->sc_tty;
2009d94a3b6Skettenis 	int *p;
2019d94a3b6Skettenis 	u_int32_t stat;
2029d94a3b6Skettenis 	u_char c;
2039d94a3b6Skettenis 	int handled = 0;
2049d94a3b6Skettenis 
2059d94a3b6Skettenis 	if (tp == NULL)
2069d94a3b6Skettenis 		return 0;
2079d94a3b6Skettenis 
2089d94a3b6Skettenis 	stat = HREAD4(sc, UART_STATUS);
2099d94a3b6Skettenis 	if (!ISSET(stat, UART_STATUS_TX_FIFO_FULL) &&
2109d94a3b6Skettenis 	    ISSET(tp->t_state, TS_BUSY)) {
2119d94a3b6Skettenis 		CLR(tp->t_state, TS_BUSY | TS_FLUSH);
2129d94a3b6Skettenis 		if (sc->sc_halt > 0)
2139d94a3b6Skettenis 			wakeup(&tp->t_outq);
2149d94a3b6Skettenis 		(*linesw[tp->t_line].l_start)(tp);
2159d94a3b6Skettenis 		handled = 1;
2169d94a3b6Skettenis 	}
2179d94a3b6Skettenis 
2189d94a3b6Skettenis 	p = sc->sc_ibufp;
2199d94a3b6Skettenis 	while (!ISSET(stat, UART_STATUS_RX_FIFO_EMPTY)) {
2209d94a3b6Skettenis 		c = HREAD4(sc, UART_RFIFO);
2219d94a3b6Skettenis 		if (ISSET(stat, UART_STATUS_FRAME_ERROR))
2229d94a3b6Skettenis 			c |= TTY_FE;
2239d94a3b6Skettenis 		if (ISSET(stat, UART_STATUS_PARITY_ERROR))
2249d94a3b6Skettenis 			c |= TTY_PE;
2259d94a3b6Skettenis 		if (ISSET(stat, UART_STATUS_RX_FIFO_OVERFLOW))
2269d94a3b6Skettenis 			sc->sc_overflows++;
2279d94a3b6Skettenis 
2289d94a3b6Skettenis 		if (p >= sc->sc_ibufend)
2299d94a3b6Skettenis 			sc->sc_floods++;
2309d94a3b6Skettenis 		else
2319d94a3b6Skettenis 			*p++ = c;
2329d94a3b6Skettenis 
2339d94a3b6Skettenis 		if (stat & UART_STATUS_ERROR)
2349d94a3b6Skettenis 			HSET4(sc, UART_CONTROL, UART_CONTROL_CLEAR_ERROR);
2359d94a3b6Skettenis 		stat = HREAD4(sc, UART_STATUS);
2369d94a3b6Skettenis 		handled = 1;
2379d94a3b6Skettenis 	}
2389d94a3b6Skettenis 	if (sc->sc_ibufp != p) {
2399d94a3b6Skettenis 		sc->sc_ibufp = p;
2409d94a3b6Skettenis 		softintr_schedule(sc->sc_si);
2419d94a3b6Skettenis 	}
2429d94a3b6Skettenis 
2439d94a3b6Skettenis 	return handled;
2449d94a3b6Skettenis }
2459d94a3b6Skettenis 
2469d94a3b6Skettenis void
amluart_softintr(void * arg)2479d94a3b6Skettenis amluart_softintr(void *arg)
2489d94a3b6Skettenis {
2499d94a3b6Skettenis 	struct amluart_softc *sc = arg;
2509d94a3b6Skettenis 	struct tty *tp = sc->sc_tty;
2519d94a3b6Skettenis 	int *ibufp, *ibufend;
2529d94a3b6Skettenis 	int s;
2539d94a3b6Skettenis 
2549d94a3b6Skettenis 	if (sc->sc_ibufp == sc->sc_ibuf)
2559d94a3b6Skettenis 		return;
2569d94a3b6Skettenis 
2579d94a3b6Skettenis 	s = spltty();
2589d94a3b6Skettenis 
2599d94a3b6Skettenis 	ibufp = sc->sc_ibuf;
2609d94a3b6Skettenis 	ibufend = sc->sc_ibufp;
2619d94a3b6Skettenis 
2629d94a3b6Skettenis 	if (ibufp == ibufend) {
2639d94a3b6Skettenis 		splx(s);
2649d94a3b6Skettenis 		return;
2659d94a3b6Skettenis 	}
2669d94a3b6Skettenis 
2679d94a3b6Skettenis 	sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ?
2689d94a3b6Skettenis 	    sc->sc_ibufs[1] : sc->sc_ibufs[0];
2699d94a3b6Skettenis 	sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
2709d94a3b6Skettenis 	sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
2719d94a3b6Skettenis 
2729d94a3b6Skettenis 	if (tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) {
2739d94a3b6Skettenis 		splx(s);
2749d94a3b6Skettenis 		return;
2759d94a3b6Skettenis 	}
2769d94a3b6Skettenis 
2779d94a3b6Skettenis 	splx(s);
2789d94a3b6Skettenis 
279*467651faSkettenis 	while (ibufp < ibufend) {
280*467651faSkettenis 		int i = *ibufp++;
281*467651faSkettenis #ifdef DDB
282*467651faSkettenis 		if (tp->t_dev == cn_tab->cn_dev) {
283*467651faSkettenis 			int j = db_rint(i);
284*467651faSkettenis 
285*467651faSkettenis 			if (j == 1)	/* Escape received, skip */
286*467651faSkettenis 				continue;
287*467651faSkettenis 			if (j == 2)	/* Second char wasn't 'D' */
288*467651faSkettenis 				(*linesw[tp->t_line].l_rint)(27, tp);
289*467651faSkettenis 		}
290*467651faSkettenis #endif
291*467651faSkettenis 		(*linesw[tp->t_line].l_rint)(i, tp);
292*467651faSkettenis 	}
2939d94a3b6Skettenis }
2949d94a3b6Skettenis 
2959d94a3b6Skettenis int
amluart_param(struct tty * tp,struct termios * t)2969d94a3b6Skettenis amluart_param(struct tty *tp, struct termios *t)
2979d94a3b6Skettenis {
2989d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
2999d94a3b6Skettenis 	int ospeed = t->c_ospeed;
3009d94a3b6Skettenis 
3019d94a3b6Skettenis 	/* Check requested parameters. */
3029d94a3b6Skettenis 	if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
3039d94a3b6Skettenis 		return EINVAL;
3049d94a3b6Skettenis 
3059d94a3b6Skettenis 	switch (ISSET(t->c_cflag, CSIZE)) {
3069d94a3b6Skettenis 	case CS5:
3079d94a3b6Skettenis 	case CS6:
3089d94a3b6Skettenis 	case CS7:
3099d94a3b6Skettenis 		return EINVAL;
3109d94a3b6Skettenis 	case CS8:
3119d94a3b6Skettenis 		break;
3129d94a3b6Skettenis 	}
3139d94a3b6Skettenis 
3149d94a3b6Skettenis 	if (ospeed != 0) {
3159d94a3b6Skettenis 		while (ISSET(tp->t_state, TS_BUSY)) {
3169d94a3b6Skettenis 			int error;
3179d94a3b6Skettenis 
3189d94a3b6Skettenis 			sc->sc_halt++;
3199d94a3b6Skettenis 			error = ttysleep(tp, &tp->t_outq,
3209d94a3b6Skettenis 			    TTOPRI | PCATCH, "amluprm");
3219d94a3b6Skettenis 			sc->sc_halt--;
3229d94a3b6Skettenis 			if (error) {
3239d94a3b6Skettenis 				amluart_start(tp);
3249d94a3b6Skettenis 				return error;
3259d94a3b6Skettenis 			}
3269d94a3b6Skettenis 		}
3279d94a3b6Skettenis 	}
3289d94a3b6Skettenis 
3299d94a3b6Skettenis 	tp->t_ispeed = t->c_ispeed;
3309d94a3b6Skettenis 	tp->t_ospeed = t->c_ospeed;
3319d94a3b6Skettenis 	tp->t_cflag = t->c_cflag;
3329d94a3b6Skettenis 
3339d94a3b6Skettenis 	/* Just to be sure... */
3349d94a3b6Skettenis 	amluart_start(tp);
3359d94a3b6Skettenis 	return 0;
3369d94a3b6Skettenis }
3379d94a3b6Skettenis 
3389d94a3b6Skettenis void
amluart_start(struct tty * tp)3399d94a3b6Skettenis amluart_start(struct tty *tp)
3409d94a3b6Skettenis {
3419d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(tp->t_dev);
3429d94a3b6Skettenis 	int stat;
3439d94a3b6Skettenis 	int s;
3449d94a3b6Skettenis 
3459d94a3b6Skettenis 	s = spltty();
3469d94a3b6Skettenis 	if (ISSET(tp->t_state, TS_BUSY))
3479d94a3b6Skettenis 		goto out;
3489d94a3b6Skettenis 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP) || sc->sc_halt > 0)
3499d94a3b6Skettenis 		goto out;
3509d94a3b6Skettenis 	ttwakeupwr(tp);
3519d94a3b6Skettenis 	if (tp->t_outq.c_cc == 0)
3529d94a3b6Skettenis 		goto out;
3539d94a3b6Skettenis 	SET(tp->t_state, TS_BUSY);
3549d94a3b6Skettenis 
3559d94a3b6Skettenis 	stat = HREAD4(sc, UART_STATUS);
3569d94a3b6Skettenis 	while ((stat & UART_STATUS_TX_FIFO_FULL) == 0) {
3579d94a3b6Skettenis 		HWRITE4(sc, UART_WFIFO, getc(&tp->t_outq));
3589d94a3b6Skettenis 		stat = HREAD4(sc, UART_STATUS);
3599d94a3b6Skettenis 	}
3609d94a3b6Skettenis out:
3619d94a3b6Skettenis 	splx(s);
3629d94a3b6Skettenis }
3639d94a3b6Skettenis 
3649d94a3b6Skettenis int
amluartopen(dev_t dev,int flag,int mode,struct proc * p)3659d94a3b6Skettenis amluartopen(dev_t dev, int flag, int mode, struct proc *p)
3669d94a3b6Skettenis {
3679d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(dev);
3689d94a3b6Skettenis 	struct tty *tp;
3699d94a3b6Skettenis 	int error;
3709d94a3b6Skettenis 	int s;
3719d94a3b6Skettenis 
3729d94a3b6Skettenis 	if (sc == NULL)
3739d94a3b6Skettenis 		return ENXIO;
3749d94a3b6Skettenis 
3759d94a3b6Skettenis 	s = spltty();
3769d94a3b6Skettenis 	if (sc->sc_tty == NULL)
3779d94a3b6Skettenis 		tp = sc->sc_tty = ttymalloc(0);
3789d94a3b6Skettenis 	else
3799d94a3b6Skettenis 		tp = sc->sc_tty;
3809d94a3b6Skettenis 	splx(s);
3819d94a3b6Skettenis 
3829d94a3b6Skettenis 	tp->t_oproc = amluart_start;
3839d94a3b6Skettenis 	tp->t_param = amluart_param;
3849d94a3b6Skettenis 	tp->t_dev = dev;
3859d94a3b6Skettenis 
3869d94a3b6Skettenis 	if (!ISSET(tp->t_state, TS_ISOPEN)) {
3879d94a3b6Skettenis 		SET(tp->t_state, TS_WOPEN);
3889d94a3b6Skettenis 		ttychars(tp);
3899d94a3b6Skettenis 		tp->t_iflag = TTYDEF_IFLAG;
3909d94a3b6Skettenis 		tp->t_oflag = TTYDEF_OFLAG;
3919d94a3b6Skettenis 		tp->t_cflag = TTYDEF_CFLAG;
3929d94a3b6Skettenis 		tp->t_lflag = TTYDEF_LFLAG;
3939d94a3b6Skettenis 		tp->t_ispeed = tp->t_ospeed =
3949d94a3b6Skettenis 		    sc->sc_conspeed ? sc->sc_conspeed : B115200;
3959d94a3b6Skettenis 
3969d94a3b6Skettenis 		s = spltty();
3979d94a3b6Skettenis 
3989d94a3b6Skettenis 		amluart_param(tp, &tp->t_termios);
3999d94a3b6Skettenis 		ttsetwater(tp);
4009d94a3b6Skettenis 
4019d94a3b6Skettenis 		sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0];
4029d94a3b6Skettenis 		sc->sc_ibufhigh = sc->sc_ibuf + AMLUART_IHIGHWATER;
4039d94a3b6Skettenis 		sc->sc_ibufend = sc->sc_ibuf + AMLUART_IBUFSIZE;
4049d94a3b6Skettenis 
4059d94a3b6Skettenis 		/* Enable interrupts */
4069d94a3b6Skettenis 		HSET4(sc, UART_CONTROL,
4079d94a3b6Skettenis 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
4089d94a3b6Skettenis 
4099d94a3b6Skettenis 		/* No carrier detect support. */
4109d94a3b6Skettenis 		SET(tp->t_state, TS_CARR_ON);
4119ca4957bSjan 	} else if (ISSET(tp->t_state, TS_XCLUDE) && suser(p) != 0)
4129d94a3b6Skettenis 		return EBUSY;
4139d94a3b6Skettenis 	else
4149d94a3b6Skettenis 		s = spltty();
4159d94a3b6Skettenis 
4169d94a3b6Skettenis 	if (DEVCUA(dev)) {
4179d94a3b6Skettenis 		if (ISSET(tp->t_state, TS_ISOPEN)) {
4189d94a3b6Skettenis 			/* Ah, but someone already is dialed in... */
4199d94a3b6Skettenis 			splx(s);
4209d94a3b6Skettenis 			return EBUSY;
4219d94a3b6Skettenis 		}
4229d94a3b6Skettenis 		sc->sc_cua = 1;		/* We go into CUA mode. */
4239d94a3b6Skettenis 	} else {
4249d94a3b6Skettenis 		if (ISSET(flag, O_NONBLOCK) && sc->sc_cua) {
4259d94a3b6Skettenis 			/* Opening TTY non-blocking... but the CUA is busy. */
4269d94a3b6Skettenis 			splx(s);
4279d94a3b6Skettenis 			return EBUSY;
4289d94a3b6Skettenis 		} else {
4299d94a3b6Skettenis 			while (sc->sc_cua) {
4309d94a3b6Skettenis 				SET(tp->t_state, TS_WOPEN);
4319d94a3b6Skettenis 				error = ttysleep(tp, &tp->t_rawq,
4329d94a3b6Skettenis 				    TTIPRI | PCATCH, ttopen);
4339d94a3b6Skettenis 				/*
4349d94a3b6Skettenis 				 * If TS_WOPEN has been reset, that means the
4359d94a3b6Skettenis 				 * cua device has been closed.
4369d94a3b6Skettenis 				 * We don't want to fail in that case,
4379d94a3b6Skettenis 				 * so just go around again.
4389d94a3b6Skettenis 				 */
4399d94a3b6Skettenis 				if (error && ISSET(tp->t_state, TS_WOPEN)) {
4409d94a3b6Skettenis 					CLR(tp->t_state, TS_WOPEN);
4419d94a3b6Skettenis 					splx(s);
4429d94a3b6Skettenis 					return error;
4439d94a3b6Skettenis 				}
4449d94a3b6Skettenis 			}
4459d94a3b6Skettenis 		}
4469d94a3b6Skettenis 	}
4479d94a3b6Skettenis 	splx(s);
4489d94a3b6Skettenis 
4499d94a3b6Skettenis 	return (*linesw[tp->t_line].l_open)(dev, tp, p);
4509d94a3b6Skettenis }
4519d94a3b6Skettenis 
4529d94a3b6Skettenis int
amluartclose(dev_t dev,int flag,int mode,struct proc * p)4539d94a3b6Skettenis amluartclose(dev_t dev, int flag, int mode, struct proc *p)
4549d94a3b6Skettenis {
4559d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(dev);
4569d94a3b6Skettenis 	struct tty *tp = sc->sc_tty;
4579d94a3b6Skettenis 	int s;
4589d94a3b6Skettenis 
4599d94a3b6Skettenis 	if (!ISSET(tp->t_state, TS_ISOPEN))
4609d94a3b6Skettenis 		return 0;
4619d94a3b6Skettenis 
4629d94a3b6Skettenis 	(*linesw[tp->t_line].l_close)(tp, flag, p);
4639d94a3b6Skettenis 	s = spltty();
4649d94a3b6Skettenis 	if (!ISSET(tp->t_state, TS_WOPEN)) {
4659d94a3b6Skettenis 		/* Disable interrupts */
4669d94a3b6Skettenis 		HCLR4(sc, UART_CONTROL,
4679d94a3b6Skettenis 		    UART_CONTROL_TX_INT | UART_CONTROL_RX_INT);
4689d94a3b6Skettenis 	}
4699d94a3b6Skettenis 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
4709d94a3b6Skettenis 	sc->sc_cua = 0;
4719d94a3b6Skettenis 	splx(s);
4729d94a3b6Skettenis 	ttyclose(tp);
4739d94a3b6Skettenis 
4749d94a3b6Skettenis 	return 0;
4759d94a3b6Skettenis }
4769d94a3b6Skettenis 
4779d94a3b6Skettenis int
amluartread(dev_t dev,struct uio * uio,int flag)4789d94a3b6Skettenis amluartread(dev_t dev, struct uio *uio, int flag)
4799d94a3b6Skettenis {
4809d94a3b6Skettenis 	struct tty *tp = amluarttty(dev);
4819d94a3b6Skettenis 
4829d94a3b6Skettenis 	if (tp == NULL)
4839d94a3b6Skettenis 		return ENODEV;
4849d94a3b6Skettenis 
4859d94a3b6Skettenis 	return (*linesw[tp->t_line].l_read)(tp, uio, flag);
4869d94a3b6Skettenis }
4879d94a3b6Skettenis 
4889d94a3b6Skettenis int
amluartwrite(dev_t dev,struct uio * uio,int flag)4899d94a3b6Skettenis amluartwrite(dev_t dev, struct uio *uio, int flag)
4909d94a3b6Skettenis {
4919d94a3b6Skettenis 	struct tty *tp = amluarttty(dev);
4929d94a3b6Skettenis 
4939d94a3b6Skettenis 	if (tp == NULL)
4949d94a3b6Skettenis 		return ENODEV;
4959d94a3b6Skettenis 
4969d94a3b6Skettenis 	return (*linesw[tp->t_line].l_write)(tp, uio, flag);
4979d94a3b6Skettenis }
4989d94a3b6Skettenis 
4999d94a3b6Skettenis int
amluartioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)5009d94a3b6Skettenis amluartioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
5019d94a3b6Skettenis {
5029d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(dev);
5039d94a3b6Skettenis 	struct tty *tp;
5049d94a3b6Skettenis 	int error;
5059d94a3b6Skettenis 
5069d94a3b6Skettenis 	if (sc == NULL)
5079d94a3b6Skettenis 		return ENODEV;
5089d94a3b6Skettenis 
5099d94a3b6Skettenis 	tp = sc->sc_tty;
5109d94a3b6Skettenis 	if (tp == NULL)
5119d94a3b6Skettenis 		return ENXIO;
5129d94a3b6Skettenis 
5139d94a3b6Skettenis 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
5149d94a3b6Skettenis 	if (error >= 0)
5159d94a3b6Skettenis 		return error;
5169d94a3b6Skettenis 
5179d94a3b6Skettenis 	error = ttioctl(tp, cmd, data, flag, p);
5189d94a3b6Skettenis 	if (error >= 0)
5199d94a3b6Skettenis 		return error;
5209d94a3b6Skettenis 
5219d94a3b6Skettenis 	switch(cmd) {
5229d94a3b6Skettenis 	case TIOCSBRK:
5239d94a3b6Skettenis 	case TIOCCBRK:
5249d94a3b6Skettenis 	case TIOCSDTR:
5259d94a3b6Skettenis 	case TIOCCDTR:
5269d94a3b6Skettenis 	case TIOCMSET:
5279d94a3b6Skettenis 	case TIOCMBIS:
5289d94a3b6Skettenis 	case TIOCMBIC:
5299d94a3b6Skettenis 	case TIOCMGET:
5309d94a3b6Skettenis 	case TIOCGFLAGS:
5319d94a3b6Skettenis 		break;
5329d94a3b6Skettenis 	case TIOCSFLAGS:
5339d94a3b6Skettenis 		error = suser(p);
5349d94a3b6Skettenis 		if (error != 0)
5359d94a3b6Skettenis 			return EPERM;
5369d94a3b6Skettenis 		break;
5379d94a3b6Skettenis 	default:
5389d94a3b6Skettenis 		return ENOTTY;
5399d94a3b6Skettenis 	}
5409d94a3b6Skettenis 
5419d94a3b6Skettenis 	return 0;
5429d94a3b6Skettenis }
5439d94a3b6Skettenis 
5449d94a3b6Skettenis int
amluartstop(struct tty * tp,int flag)5459d94a3b6Skettenis amluartstop(struct tty *tp, int flag)
5469d94a3b6Skettenis {
5479d94a3b6Skettenis 	return 0;
5489d94a3b6Skettenis }
5499d94a3b6Skettenis 
5509d94a3b6Skettenis struct tty *
amluarttty(dev_t dev)5519d94a3b6Skettenis amluarttty(dev_t dev)
5529d94a3b6Skettenis {
5539d94a3b6Skettenis 	struct amluart_softc *sc = amluart_sc(dev);
5549d94a3b6Skettenis 
5559d94a3b6Skettenis 	if (sc == NULL)
5569d94a3b6Skettenis 		return NULL;
5579d94a3b6Skettenis 	return sc->sc_tty;
5589d94a3b6Skettenis }
5599d94a3b6Skettenis 
5609d94a3b6Skettenis struct amluart_softc *
amluart_sc(dev_t dev)5619d94a3b6Skettenis amluart_sc(dev_t dev)
5629d94a3b6Skettenis {
5639d94a3b6Skettenis 	int unit = DEVUNIT(dev);
5649d94a3b6Skettenis 
5659d94a3b6Skettenis 	if (unit >= amluart_cd.cd_ndevs)
5669d94a3b6Skettenis 		return NULL;
5679d94a3b6Skettenis 	return (struct amluart_softc *)amluart_cd.cd_devs[unit];
5689d94a3b6Skettenis }
5699d94a3b6Skettenis 
5709d94a3b6Skettenis int
amluartcnattach(bus_space_tag_t iot,bus_addr_t iobase)5719d94a3b6Skettenis amluartcnattach(bus_space_tag_t iot, bus_addr_t iobase)
5729d94a3b6Skettenis {
5739d94a3b6Skettenis 	static struct consdev amluartcons = {
5749d94a3b6Skettenis 		NULL, NULL, amluartcngetc, amluartcnputc, amluartcnpollc, NULL,
5759d94a3b6Skettenis 		NODEV, CN_MIDPRI
5769d94a3b6Skettenis 	};
5779d94a3b6Skettenis 	int maj;
5789d94a3b6Skettenis 
5799d94a3b6Skettenis 	amluartconsiot = iot;
5809d94a3b6Skettenis 	if (bus_space_map(iot, iobase, UART_SPACE, 0, &amluartconsioh))
5819d94a3b6Skettenis 		return ENOMEM;
5829d94a3b6Skettenis 
5839d94a3b6Skettenis 	/* Look for major of com(4) to replace. */
5849d94a3b6Skettenis 	for (maj = 0; maj < nchrdev; maj++)
5859d94a3b6Skettenis 		if (cdevsw[maj].d_open == comopen)
5869d94a3b6Skettenis 			break;
5879d94a3b6Skettenis 	if (maj == nchrdev)
5889d94a3b6Skettenis 		return ENXIO;
5899d94a3b6Skettenis 
5909d94a3b6Skettenis 	cn_tab = &amluartcons;
5919d94a3b6Skettenis 	cn_tab->cn_dev = makedev(maj, 0);
5929d94a3b6Skettenis 	cdevsw[maj] = amluartdev; 	/* KLUDGE */
5939d94a3b6Skettenis 
5949d94a3b6Skettenis 	return 0;
5959d94a3b6Skettenis }
5969d94a3b6Skettenis 
5979d94a3b6Skettenis int
amluartcngetc(dev_t dev)5989d94a3b6Skettenis amluartcngetc(dev_t dev)
5999d94a3b6Skettenis {
6009d94a3b6Skettenis 	uint8_t c;
6019d94a3b6Skettenis 
6029d94a3b6Skettenis 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
6039d94a3b6Skettenis 	    UART_STATUS_RX_FIFO_EMPTY)
6049d94a3b6Skettenis 		CPU_BUSY_CYCLE();
6059d94a3b6Skettenis 	c = bus_space_read_4(amluartconsiot, amluartconsioh, UART_RFIFO);
6069d94a3b6Skettenis 	return c;
6079d94a3b6Skettenis }
6089d94a3b6Skettenis 
6099d94a3b6Skettenis void
amluartcnputc(dev_t dev,int c)6109d94a3b6Skettenis amluartcnputc(dev_t dev, int c)
6119d94a3b6Skettenis {
6129d94a3b6Skettenis 	while (bus_space_read_4(amluartconsiot, amluartconsioh, UART_STATUS) &
6139d94a3b6Skettenis 	    UART_STATUS_TX_FIFO_FULL)
6149d94a3b6Skettenis 		CPU_BUSY_CYCLE();
6159d94a3b6Skettenis 	bus_space_write_4(amluartconsiot, amluartconsioh, UART_WFIFO, c);
6169d94a3b6Skettenis }
6179d94a3b6Skettenis 
6189d94a3b6Skettenis void
amluartcnpollc(dev_t dev,int on)6199d94a3b6Skettenis amluartcnpollc(dev_t dev, int on)
6209d94a3b6Skettenis {
6219d94a3b6Skettenis }
622