PERFORCE change 212573 for review

Robert Watson rwatson at FreeBSD.org
Sun Jun 10 12:09:46 UTC 2012


http://p4web.freebsd.org/@@212573?ac=10

Change 212573 by rwatson at rwatson_svr_ctsrd_mipsbuild on 2012/06/10 12:09:24

	Add support for interrupt-driven operation to the Altera JTAG UART
	device driver.  Support for pure polled-mode operation is retained
	-- if an IRQ is not available (i.e., not defined in device.hints)
	then that mode will be used instead, but interrupt-driven operation
	will be preferred.  Interrupts are used only in the receive path,
	currently; transmit-side queueing and interrupt acknowledgement is
	not yet supported, so all writes continue to be synchronous from 
	the TTY output handler.  This may be worth fixing.
	
	Configure an IRQ for the first of three JTAG UARTs in the BERI DE-4
	configuration -- the other two don't have IRQs configured, but 
	continue to work fine in polled mode.

Affected files ...

.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart.h#3 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c#2 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c#3 edit
.. //depot/projects/ctsrd/beribsd/src/sys/mips/conf/BERI_DE4.hints#6 edit

Differences ...

==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart.h#3 (text+ko) ====

@@ -38,6 +38,9 @@
 	/*
 	 * Hardware resources.
 	 */
+	struct resource		*ajus_irq_res;
+	int			 ajus_irq_rid;
+	void			*ajus_irq_cookie;
 	struct resource		*ajus_mem_res;
 	int			 ajus_mem_rid;
 
@@ -50,6 +53,7 @@
 	/*
 	 * Driver resources.
 	 */
+	u_int			 ajus_flags;
 	struct mtx		*ajus_lockp;
 	struct mtx		 ajus_lock;
 	struct callout		 ajus_callout;
@@ -67,6 +71,11 @@
 #define	AJU_TTYNAME	"ttyu"
 
 /*
+ * Flag values for ajus_flags.
+ */
+#define	ALTERA_JTAG_UART_FLAG_CONSOLE	0x00000001	/* Is console. */
+
+/*
  * Because tty-level use of the I/O ports completes with low-level console
  * use, spinlocks must be employed here.
  */
@@ -173,7 +182,7 @@
 /*
  * Driver attachment functions for Nexus.
  */
-void	altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc);
+int	altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc);
 void	altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc);
 
 #endif /* _DEV_ALTERA_JTAG_UART_H_ */

==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c#2 (text+ko) ====

@@ -69,7 +69,9 @@
 altera_jtag_uart_nexus_attach(device_t dev)
 {
 	struct altera_jtag_uart_softc *sc;
+	int error;
 
+	error = 0;
 	sc = device_get_softc(dev);
 	sc->ajus_dev = dev;
 	sc->ajus_unit = device_get_unit(dev);
@@ -78,10 +80,31 @@
 	    &sc->ajus_mem_rid, RF_ACTIVE);
 	if (sc->ajus_mem_res == NULL) {
 		device_printf(dev, "couldn't map memory\n");
-		return (ENXIO);
+		error = ENXIO;
+		goto out;
+	}
+
+	/*
+	 * Interrupt support is optional -- if we can't allocate an IRQ, then
+	 * we fall back on polling.
+	 */
+	sc->ajus_irq_rid = 0;
+	sc->ajus_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+	    &sc->ajus_irq_rid, RF_ACTIVE | RF_SHAREABLE);
+	if (sc->ajus_irq_res == NULL)
+		device_printf(dev,
+		    "IRQ unavailable; selecting polled operation\n");
+	error = altera_jtag_uart_attach(sc);
+out:
+	if (error) {
+		if (sc->ajus_irq_res != NULL)
+			bus_release_resource(dev, SYS_RES_IRQ,
+			    sc->ajus_irq_rid, sc->ajus_irq_res);
+		if (sc->ajus_mem_res != NULL)
+			bus_release_resource(dev, SYS_RES_MEMORY,
+			    sc->ajus_mem_rid, sc->ajus_mem_res);
 	}
-	altera_jtag_uart_attach(sc);
-	return (0);
+	return (error);
 }
 
 static int
@@ -93,7 +116,9 @@
 	KASSERT(sc->ajus_mem_res != NULL, ("%s: resources not allocated",
 	    __func__));
 
-	 altera_jtag_uart_detach(sc);
+	altera_jtag_uart_detach(sc);
+	bus_release_resource(dev, SYS_RES_IRQ, sc->ajus_irq_rid,
+	    sc->ajus_irq_res);
 	bus_release_resource(dev, SYS_RES_MEMORY, sc->ajus_mem_rid,
 	    sc->ajus_mem_res);
 	return (0);

==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c#3 (text+ko) ====

@@ -94,6 +94,14 @@
 	    ALTERA_JTAG_UART_CONTROL_OFF)));
 }
 
+static inline void
+aju_control_write(struct altera_jtag_uart_softc *sc, uint32_t v)
+{
+
+	bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_CONTROL_OFF,
+	    htole32(v));
+}
+
 /*
  * Slightly higher-level routines aware of buffering and flow control.
  */
@@ -154,6 +162,8 @@
 	/*
 	 * XXXRW: Would be nice not to do blocking writes to the UART here,
 	 * rescheduling on our timer tick if work remains to be done.
+	 *
+	 * XXXRW: Possibly, if full, defer to interrupt context.
 	 */
 	sc = tty_softc(tp);
 	for (;;) {
@@ -166,14 +176,17 @@
 	}
 }
 
+/*
+ * The actual work of checking for, and handling, available reads.  This is
+ * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked
+ * up with, or without, IRQs allocated.
+ */
 static void
-aju_timeout(void *v)
+aju_handle_input(struct altera_jtag_uart_softc *sc)
 {
-	struct altera_jtag_uart_softc *sc;
 	struct tty *tp;
 	int c;
 
-	sc = v;
 	tp = sc->ajus_ttyp;
 	tty_lock(tp);
 	AJU_LOCK(sc);
@@ -189,36 +202,119 @@
 	AJU_UNLOCK(sc);
 	ttydisc_rint_done(tp);
 	tty_unlock(tp);
+}
+
+static void
+aju_timeout(void *v)
+{
+	struct altera_jtag_uart_softc *sc = v;
+
+	aju_handle_input(sc);
 	callout_reset(&sc->ajus_callout, aju_pollinterval, aju_timeout, sc);
 }
 
-void
+static void
+aju_intr(void *v)
+{
+	struct altera_jtag_uart_softc *sc = v;
+
+	/*
+	 * XXXRW: Just receive in the interrupt path for now; possibly we
+	 * should check the control flags.
+	 */
+	aju_handle_input(sc);
+}
+
+static void
+aju_intr_enable(struct altera_jtag_uart_softc *sc)
+{
+	uint32_t v;
+
+	AJU_LOCK_ASSERT(sc);
+
+	v = aju_control_read(sc);
+	v |= ALTERA_JTAG_UART_CONTROL_RE;
+	v &= ~ALTERA_JTAG_UART_CONTROL_WE;
+	aju_control_write(sc, v);
+}
+
+static void
+aju_intr_disable(struct altera_jtag_uart_softc *sc)
+{
+	uint32_t v;
+
+	AJU_LOCK_ASSERT(sc);
+
+	v = aju_control_read(sc);
+	v &= ~(ALTERA_JTAG_UART_CONTROL_RE | ALTERA_JTAG_UART_CONTROL_WE);
+	aju_control_write(sc, v);
+}
+
+int
 altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc)
 {
 	struct tty *tp;
+	int error;
 
-	tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
+	AJU_LOCK_INIT(sc);
 
 	/*
 	 * XXXRW: Currently, we detect the console solely based on it using a
 	 * reserved address, and borrow console-level locks and buffer if so.
 	 * Is there a better way?
 	 */
-	AJU_LOCK_INIT(sc);
 	if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) {
-		tty_init_console(tp, 0);
 		sc->ajus_lockp = &aju_cons_lock;
 		sc->ajus_buffer_validp = &aju_cons_buffer_valid;
 		sc->ajus_buffer_datap = &aju_cons_buffer_data;
-		aju_cons_sc = sc;
+		sc->ajus_flags |= ALTERA_JTAG_UART_FLAG_CONSOLE;
 	} else {
 		sc->ajus_lockp = &sc->ajus_lock;
 		sc->ajus_buffer_validp = &sc->ajus_buffer_valid;
 		sc->ajus_buffer_datap = &sc->ajus_buffer_data;
 	}
+
+	/*
+	 * Disable interrupts regardless of whether or not we plan to use
+	 * them.  We will register an interrupt handler now if they will be
+	 * used, but not re-enable intil later once the remainder of the tty
+	 * layer is properly initialised, as we're not ready for input yet.
+	 */
+	AJU_LOCK(sc);
+	aju_intr_disable(sc);
+	AJU_UNLOCK(sc);
+	if (sc->ajus_irq_res != NULL) {
+		error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res,
+		    INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL,
+		    aju_intr, sc, &sc->ajus_irq_cookie);
+		if (error) {
+			device_printf(sc->ajus_dev,
+			    "could not activate interrupt\n");
+			AJU_LOCK_DESTROY(sc);
+			return (error);
+		}
+	}
+	tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
+	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) {
+		aju_cons_sc = sc;
+		tty_init_console(tp, 0);
+	}
 	tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit);
-	callout_init(&sc->ajus_callout, CALLOUT_MPSAFE);
-	callout_reset(&sc->ajus_callout, aju_pollinterval, aju_timeout, sc);
+
+	/*
+	 * If we will be using interrupts, enable them now; otherwise, start
+	 * polling.  From this point onwards, input can arrive.
+	 */
+	if (sc->ajus_irq_res != NULL) {
+		AJU_LOCK(sc);
+		aju_intr_enable(sc);
+		AJU_UNLOCK(sc);
+	} else {
+		callout_init(&sc->ajus_callout, CALLOUT_MPSAFE);
+		callout_reset(&sc->ajus_callout, aju_pollinterval,
+		    aju_timeout, sc);
+	}
+	return (0);
 }
 
 void
@@ -226,9 +322,20 @@
 {
 	struct tty *tp = sc->ajus_ttyp;
 
-	if (sc == aju_cons_sc)
+	/*
+	 * If we're using interrupts, disable and release the interrupt
+	 * handler now.  Otherwise drain the polling timeout.
+	 */
+	if (sc->ajus_irq_res != NULL) {
+		AJU_LOCK(sc);
+		aju_intr_disable(sc);
+		AJU_UNLOCK(sc);
+		bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res,
+		    sc->ajus_irq_cookie);
+	} else
+		callout_drain(&sc->ajus_callout);
+	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
 		aju_cons_sc = NULL;
-	callout_drain(&sc->ajus_callout);
 	tty_lock(tp);
 	tty_rel_gone(tp);
 	AJU_LOCK_DESTROY(sc);

==== //depot/projects/ctsrd/beribsd/src/sys/mips/conf/BERI_DE4.hints#6 (text+ko) ====

@@ -7,6 +7,7 @@
 hint.altera_jtag_uart.0.at="nexus0"
 hint.altera_jtag_uart.0.maddr=0x7f000000
 hint.altera_jtag_uart.0.msize=0x40
+hint.altera_jtag_uart.0.irq=0
 
 hint.altera_jtag_uart.1.at="nexus0"
 hint.altera_jtag_uart.1.maddr=0x7f001000


More information about the p4-projects mailing list