Module Name:    src
Committed By:   jmcneill
Date:           Sun Mar  1 23:39:28 UTC 2015

Modified Files:
        src/sys/arch/arm/amlogic: amlogic_com.c amlogic_comreg.h

Log Message:
UART TTY support.


To generate a diff of this commit:
cvs rdiff -u -r1.2 -r1.3 src/sys/arch/arm/amlogic/amlogic_com.c \
    src/sys/arch/arm/amlogic/amlogic_comreg.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/amlogic/amlogic_com.c
diff -u src/sys/arch/arm/amlogic/amlogic_com.c:1.2 src/sys/arch/arm/amlogic/amlogic_com.c:1.3
--- src/sys/arch/arm/amlogic/amlogic_com.c:1.2	Fri Feb 27 17:35:08 2015
+++ src/sys/arch/arm/amlogic/amlogic_com.c	Sun Mar  1 23:39:28 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: amlogic_com.c,v 1.2 2015/02/27 17:35:08 jmcneill Exp $ */
+/* $NetBSD: amlogic_com.c,v 1.3 2015/03/01 23:39:28 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2013 The NetBSD Foundation, Inc.
@@ -33,15 +33,22 @@
 
 #include <sys/cdefs.h>
 
-__KERNEL_RCSID(1, "$NetBSD: amlogic_com.c,v 1.2 2015/02/27 17:35:08 jmcneill Exp $");
+__KERNEL_RCSID(1, "$NetBSD: amlogic_com.c,v 1.3 2015/03/01 23:39:28 jmcneill Exp $");
+
+#define AMLOGIC_COM_INTRPOLL
+#define AMLOGIC_COM_INTRPOLL_RATE	10
 
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/device.h>
+#include <sys/conf.h>
 #include <sys/intr.h>
 #include <sys/systm.h>
 #include <sys/time.h>
 #include <sys/termios.h>
+#include <sys/kauth.h>
+#include <sys/lwp.h>
+#include <sys/tty.h>
 
 #include <dev/cons.h>
 
@@ -50,32 +57,80 @@ __KERNEL_RCSID(1, "$NetBSD: amlogic_com.
 #include <arm/amlogic/amlogic_comreg.h>
 #include <arm/amlogic/amlogic_comvar.h>
 
-static int amlogic_com_match(device_t, cfdata_t, void *);
-static void amlogic_com_attach(device_t, device_t, void *);
+static int	amlogic_com_match(device_t, cfdata_t, void *);
+static void	amlogic_com_attach(device_t, device_t, void *);
+
+static int	amlogic_com_intr(void *);
+
+#ifdef AMLOGIC_COM_INTRPOLL
+static void	amlogic_com_intrpoll(void *);
+#endif
+
+static int	amlogic_com_cngetc(dev_t);
+static void	amlogic_com_cnputc(dev_t, int);
+static void	amlogic_com_cnpollc(dev_t, int);
+
+static void	amlogic_com_start(struct tty *);
+static int	amlogic_com_param(struct tty *, struct termios *);
+
+extern struct cfdriver amlogiccom_cd;
 
 struct amlogic_com_softc {
 	device_t sc_dev;
 	bus_space_tag_t	sc_bst;
 	bus_space_handle_t sc_bsh;
+	void *sc_ih;
+
+	struct tty *sc_tty;
 
 	int sc_ospeed;
 	tcflag_t sc_cflag;
+
+	u_char sc_buf[1024];
+
+#ifdef AMLOGIC_COM_INTRPOLL
+	callout_t sc_intrpoll_ch;
+#endif
 };
 
 static struct amlogic_com_softc amlogic_com_cnsc;
 
 static struct cnm_state amlogic_com_cnm_state;
 
-static int	amlogic_com_cngetc(dev_t);
-static void	amlogic_com_cnputc(dev_t, int);
-static void	amlogic_com_cnpollc(dev_t, int);
-
 struct consdev amlogic_com_consdev = {
 	.cn_getc = amlogic_com_cngetc,
 	.cn_putc = amlogic_com_cnputc,
 	.cn_pollc = amlogic_com_cnpollc,
+	.cn_dev = NODEV,
+	.cn_pri = CN_NORMAL,
 };
 
+static dev_type_open(amlogic_com_open);
+static dev_type_open(amlogic_com_close);
+static dev_type_read(amlogic_com_read);
+static dev_type_write(amlogic_com_write);
+static dev_type_ioctl(amlogic_com_ioctl);
+static dev_type_tty(amlogic_com_tty);
+static dev_type_poll(amlogic_com_poll);
+static dev_type_stop(amlogic_com_stop);
+
+const struct cdevsw amlogiccom_cdevsw = {
+	.d_open = amlogic_com_open,
+	.d_close = amlogic_com_close,
+	.d_read = amlogic_com_read,
+	.d_write = amlogic_com_write,
+	.d_ioctl = amlogic_com_ioctl,
+	.d_stop = amlogic_com_stop,
+	.d_tty = amlogic_com_tty,
+	.d_poll = amlogic_com_poll,
+	.d_mmap = nommap,
+	.d_kqfilter = ttykqfilter,
+	.d_discard = nodiscard,
+	.d_flag = D_TTY
+};
+
+static int amlogic_com_cmajor = -1;
+
 CFATTACH_DECL_NEW(amlogic_com, sizeof(struct amlogic_com_softc),
 	amlogic_com_match, amlogic_com_attach, NULL, NULL);
 
@@ -92,17 +147,82 @@ amlogic_com_attach(device_t parent, devi
 	struct amlogicio_attach_args * const aio = aux;
 	const struct amlogic_locators * const loc = &aio->aio_loc;
 	const bus_addr_t iobase = AMLOGIC_CORE_BASE + loc->loc_offset;
+	struct tty *tp;
+	int major, minor;
+	uint32_t misc, control;
 
 	sc->sc_dev = self;
 	sc->sc_bst = aio->aio_core_bst;
-	sc->sc_bsh = aio->aio_bsh;
+	bus_space_subregion(aio->aio_core_bst, aio->aio_bsh,
+	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
+
+#ifdef AMLOGIC_COM_INTRPOLL
+	callout_init(&sc->sc_intrpoll_ch, CALLOUT_MPSAFE);
+	callout_setfunc(&sc->sc_intrpoll_ch, amlogic_com_intrpoll, sc);
+#else
+	sc->sc_ih = intr_establish(loc->loc_intr, IPL_SERIAL,
+	    IST_EDGE | IST_MPSAFE, amlogic_com_intr, sc);
+	if (sc->sc_ih == NULL) {
+		aprint_error(": failed to establish interrupt %d\n",
+		    loc->loc_intr);
+		return;
+	}
+#endif
+
+	if (amlogic_com_cmajor == -1) {
+		/* allocate a major number */
+		int bmajor = -1, cmajor = -1;
+		int error = devsw_attach("amlogiccom", NULL, &bmajor,
+		    &amlogiccom_cdevsw, &cmajor);
+		if (error) {
+			aprint_error(": couldn't allocate major number\n");
+			return;
+		}
+		amlogic_com_cmajor = cmajor;
+	}
+
+	major = cdevsw_lookup_major(&amlogiccom_cdevsw);
+	minor = device_unit(self);
+
+	tp = sc->sc_tty = tty_alloc();
+	tp->t_oproc = amlogic_com_start;
+	tp->t_param = amlogic_com_param;
+	tp->t_dev = makedev(major, minor);
+	tp->t_sc = sc;
+	tty_attach(tp);
 
 	aprint_naive("\n");
 	if (amlogic_com_is_console(iobase)) {
-		aprint_normal(": (console)\n");
-	} else {
-		aprint_normal("\n");
+		cn_tab->cn_dev = tp->t_dev;
+		aprint_normal(": console");
 	}
+	aprint_normal("\n");
+
+#ifdef AMLOGIC_COM_INTRPOLL
+	aprint_normal_dev(self, "polling\n");
+#else
+	aprint_normal_dev(self, "interrupting at irq %d\n", loc->loc_intr);
+#endif
+
+	misc = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG);
+	misc &= ~UART_MISC_TX_IRQ_CNT;
+	misc |= __SHIFTIN(0, UART_MISC_TX_IRQ_CNT);
+	misc &= ~UART_MISC_RX_IRQ_CNT;
+	misc |= __SHIFTIN(1, UART_MISC_RX_IRQ_CNT);
+	bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_MISC_REG, misc);
+
+	control = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG);
+	control &= ~UART_CONTROL_TX_INT_EN;
+#ifdef AMLOGIC_COM_INTRPOLL
+	control &= ~UART_CONTROL_RX_INT_EN;
+#else
+	control |= UART_CONTROL_RX_INT_EN;
+#endif
+	bus_space_write_4(sc->sc_bst, sc->sc_bsh, UART_CONTROL_REG, control);
+
+#ifdef AMLOGIC_COM_INTRPOLL
+	callout_schedule(&sc->sc_intrpoll_ch, AMLOGIC_COM_INTRPOLL_RATE);
+#endif
 }
 
 static int
@@ -145,7 +265,7 @@ amlogic_com_cnputc(dev_t dev, int c)
 
 	s = splserial();
 
-	while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_EMPTY) == 0)
+	while ((bus_space_read_4(bst, bsh, UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0)
 		;
 
 	bus_space_write_4(bst, bsh, UART_WFIFO_REG, c);
@@ -182,3 +302,187 @@ amlogic_com_is_console(bus_addr_t iobase
 {
 	return iobase == CONSADDR;
 }
+
+static int
+amlogic_com_open(dev_t dev, int flag, int mode, lwp_t *l)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+
+	if (kauth_authorize_device_tty(l->l_cred,
+	    KAUTH_DEVICE_TTY_OPEN, tp) != 0) {
+		return EBUSY;
+	}
+
+	if ((tp->t_state & TS_ISOPEN) == 0 && tp->t_wopen == 0) {
+		tp->t_dev = dev;
+		ttychars(tp);
+		tp->t_iflag = TTYDEF_IFLAG;
+		tp->t_oflag = TTYDEF_OFLAG;
+		tp->t_cflag = TTYDEF_CFLAG;
+		tp->t_lflag = TTYDEF_LFLAG;
+		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+		ttsetwater(tp);
+	}
+	tp->t_state |= TS_CARR_ON;
+
+	return tp->t_linesw->l_open(dev, tp);
+}
+
+static int
+amlogic_com_close(dev_t dev, int flag, int mode, lwp_t *l)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+
+	tp->t_linesw->l_close(tp, flag);
+	ttyclose(tp);
+
+	return 0;
+}
+
+static int
+amlogic_com_read(dev_t dev, struct uio *uio, int flag)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+
+	return tp->t_linesw->l_read(tp, uio, flag);
+}
+
+static int
+amlogic_com_write(dev_t dev, struct uio *uio, int flag)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+
+	return tp->t_linesw->l_write(tp, uio, flag);
+}
+
+static int
+amlogic_com_poll(dev_t dev, int events, lwp_t *l)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+
+	return tp->t_linesw->l_poll(tp, events, l);
+}
+
+static int
+amlogic_com_ioctl(dev_t dev, u_long cmd, void *data, int flag, lwp_t *l)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+	struct tty *tp = sc->sc_tty;
+	int error;
+
+	error = tp->t_linesw->l_ioctl(tp, cmd, data, flag, l);
+	if (error != EPASSTHROUGH)
+		return error;
+
+	return ttioctl(tp, cmd, data, flag, l);
+}
+
+static struct tty *
+amlogic_com_tty(dev_t dev)
+{
+	struct amlogic_com_softc *sc =
+	    device_lookup_private(&amlogiccom_cd, minor(dev));
+
+	return sc->sc_tty;
+}
+
+static void
+amlogic_com_stop(struct tty *tp, int flag)
+{
+}
+
+static void
+amlogic_com_start(struct tty *tp)
+{
+	struct amlogic_com_softc *sc = tp->t_sc;
+	u_char *p = sc->sc_buf;
+	int s, brem;
+
+	s = spltty();
+
+	if (tp->t_state & (TS_TTSTOP | TS_BUSY | TS_TIMEOUT)) {
+		splx(s);
+		return;
+	}
+	tp->t_state |= TS_BUSY;
+
+	splx(s);
+
+	for (brem = q_to_b(&tp->t_outq, sc->sc_buf, sizeof(sc->sc_buf));
+	     brem > 0;
+	     brem--, p++) {
+		while ((bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+		    UART_STATUS_REG) & UART_STATUS_TX_FULL) != 0)
+			;
+
+		bus_space_write_4(sc->sc_bst, sc->sc_bsh,
+		    UART_WFIFO_REG, *p);
+	}
+
+	s = spltty();
+	tp->t_state &= ~TS_BUSY;
+	if (ttypull(tp)) {
+		tp->t_state |= TS_TIMEOUT;
+		callout_schedule(&tp->t_rstrt_ch, 1);
+	}
+	splx(s);
+}
+
+static int
+amlogic_com_param(struct tty *tp, struct termios *t)
+{
+
+	tp->t_ispeed = t->c_ispeed;
+	tp->t_ospeed = t->c_ospeed;
+	tp->t_cflag = t->c_cflag;
+
+	return 0;
+}
+
+static int
+amlogic_com_intr(void *priv)
+{
+	struct amlogic_com_softc *sc = priv;
+	struct tty *tp = sc->sc_tty;
+	uint32_t status, c;
+
+	for (;;) {
+		status = bus_space_read_4(sc->sc_bst, sc->sc_bsh,
+		    UART_STATUS_REG);
+		if (status & UART_STATUS_RX_EMPTY) {
+			break;
+		}
+
+		c = bus_space_read_4(sc->sc_bst, sc->sc_bsh, UART_RFIFO_REG);
+		cn_check_magic(tp->t_dev, c, amlogic_com_cnm_state);
+		tp->t_linesw->l_rint(c & 0xff, tp);
+	}
+
+	return 0;
+}
+
+#ifdef AMLOGIC_COM_INTRPOLL
+static void
+amlogic_com_intrpoll(void *priv)
+{
+	struct amlogic_com_softc *sc = priv;
+	int s;
+
+	s = splserial();
+	amlogic_com_intr(sc);
+	splx(s);
+
+	callout_schedule(&sc->sc_intrpoll_ch, AMLOGIC_COM_INTRPOLL_RATE);
+}
+#endif
Index: src/sys/arch/arm/amlogic/amlogic_comreg.h
diff -u src/sys/arch/arm/amlogic/amlogic_comreg.h:1.2 src/sys/arch/arm/amlogic/amlogic_comreg.h:1.3
--- src/sys/arch/arm/amlogic/amlogic_comreg.h:1.2	Fri Feb 27 17:35:08 2015
+++ src/sys/arch/arm/amlogic/amlogic_comreg.h	Sun Mar  1 23:39:28 2015
@@ -1,4 +1,4 @@
-/* $NetBSD: amlogic_comreg.h,v 1.2 2015/02/27 17:35:08 jmcneill Exp $ */
+/* $NetBSD: amlogic_comreg.h,v 1.3 2015/03/01 23:39:28 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015 Jared D. McNeill <[email protected]>
@@ -36,9 +36,21 @@
 #define UART_MISC_REG		0x10
 #define UART_REG5_REG		0x14
 
+#define UART_CONTROL_TX_INT_EN	__BIT(28)
+#define UART_CONTROL_RX_INT_EN	__BIT(27)
+#define UART_CONTROL_CLEAR_ERR	__BIT(24)
+#define UART_CONTROL_RX_RESET	__BIT(23)
+#define UART_CONTROL_TX_RESET	__BIT(22)
+#define UART_CONTROL_RX_EN	__BIT(13)
+#define UART_CONTROL_TX_EN	__BIT(12)
+
 #define UART_STATUS_RX_BUSY	__BIT(26)
 #define UART_STATUS_TX_BUSY	__BIT(25)
 #define UART_STATUS_TX_EMPTY	__BIT(22)
+#define UART_STATUS_TX_FULL	__BIT(21)
 #define UART_STATUS_RX_EMPTY	__BIT(20)
 
+#define UART_MISC_TX_IRQ_CNT	__BITS(15,8)
+#define UART_MISC_RX_IRQ_CNT	__BITS(7,0)
+
 #endif /* _ARM_AMLOGIC_COMREG_H */

Reply via email to