Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 6 Jul 2012 00:52:05 GMT
From:      Robert Watson <rwatson@FreeBSD.org>
To:        Perforce Change Reviews <perforce@freebsd.org>
Subject:   PERFORCE change 213956 for review
Message-ID:  <201207060052.q660q5jB029155@skunkworks.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://p4web.freebsd.org/@@213956?ac=10

Change 213956 by rwatson@rwatson_svr_ctsrd_mipsbuild on 2012/07/06 00:52:03

	At the TTY layer of the Altera JTAG UART driver, poll intermittently
	for JTAG activity in order to disable flow-control based output.
	The low-level console and TTY layers share state so that they can
	use a combined notion of JTAG detection.
	
	This implementation slightly unsatisfying: we don't want to wait to
	see that JTAG is still there on every write, as that involves
	clearing the AC bit and then waiting a while to see if it comes
	back.  However, there's also no way to flush writes stuck in the
	write buffer once JTAG is disconnected.  As a result, when the JTAG
	console is restored, you see 512 bytes of earlier output.  However,
	the good news is that leaving JTAG unplugged at boot not only
	doesn't block the kernel boot on low-level console output, but also
	no longer blocks the userspace boot on high-level console output,
	if JTAG is unplugged.

Affected files ...

.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart.h#4 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c#5 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c#6 edit

Differences ...

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

@@ -56,7 +56,8 @@
 	u_int			 ajus_flags;
 	struct mtx		*ajus_lockp;
 	struct mtx		 ajus_lock;
-	struct callout		 ajus_callout;
+	struct callout		 ajus_io_callout;
+	struct callout		 ajus_ac_callout;
 
 	/*
 	 * One-character buffer required because it's not possible to peek at
@@ -66,6 +67,10 @@
 	int			*ajus_buffer_validp;
 	uint8_t			 ajus_buffer_data;
 	uint8_t			*ajus_buffer_datap;
+	int			 ajus_jtag_present;
+	int			*ajus_jtag_presentp;
+	u_int			 ajus_jtag_missed;
+	u_int			*ajus_jtag_missedp;
 };
 
 #define	AJU_TTYNAME	"ttyu"
@@ -126,6 +131,8 @@
 extern struct mtx	aju_cons_lock;
 extern char  		aju_cons_buffer_data;
 extern int		aju_cons_buffer_valid;
+extern int		aju_cons_jtag_present;
+extern u_int		aju_cons_jtag_missed;
 
 /*
  * Base physical address of the JTAG UART in BERI.

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

@@ -56,6 +56,7 @@
 char		aju_cons_buffer_data;
 int		aju_cons_buffer_valid;
 int		aju_cons_jtag_present;
+u_int		aju_cons_jtag_missed;
 struct mtx	aju_cons_lock;
 
 /*
@@ -73,7 +74,9 @@
  * JTAG sets the ALTERA_JTAG_UART_CONTROL_AC bit whenever it accesses the
  * FIFO.  This allows us to (sort of) tell when JTAG is present, so that we
  * can adopt lossy, rather than blocking, behaviour when JTAG isn't there.
- * When it is present, we do full flow control.
+ * When it is present, we do full flow control.  This delay is how long we
+ * wait to see if JTAG has really disappeared when finding a full buffer and
+ * no AC bit set.
  */
 #define	ALTERA_JTAG_UART_AC_POLL_DELAY	10000
 
@@ -210,11 +213,16 @@
 	 * but otherwise shouldn't wait any further once it has gone.  And we
 	 * had to wait for buffer space anyway, if it was there.
 	 *
+	 * If JTAG is spotted, reset the TTY-layer miss counter so console-
+	 * layer clearing of the bit doesn't trigger a TTY-layer
+	 * disconnection.
+	 *
 	 * XXXRW: The polling delay may require tuning.
 	 */
 	v = aju_cons_control_read();
 	if (v & ALTERA_JTAG_UART_CONTROL_AC) {
 		aju_cons_jtag_present = 1;
+		aju_cons_jtag_missed = 0;
 		v &= ~ALTERA_JTAG_UART_CONTROL_AC;
 		aju_cons_control_write(v);
 	}

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

@@ -55,7 +55,8 @@
 static struct altera_jtag_uart_softc	*aju_cons_sc;
 
 static tsw_outwakeup_t	aju_outwakeup;
-static void		aju_timeout(void *);
+static void		aju_ac_callout(void *);
+static void		aju_io_callout(void *);
 
 static struct ttydevsw aju_ttydevsw = {
 	.tsw_flags	= TF_NOPREFIX,
@@ -63,9 +64,16 @@
 };
 
 /*
- * Poll for new input every (aju_pollinterval) ticks.
+ * When polling for the AC bit, the number of times we have to not see it
+ * before assuming JTAG has disappeared on us.  By default, one second.
+ */
+#define	AJU_JTAG_MAXMISS		5
+
+/*
+ * Polling intervals for input/output and JTAG connection events.
  */
-static u_int		aju_pollinterval = 1;
+#define	AJU_IO_POLLINTERVAL		(hz/100)
+#define	AJU_AC_POLLINTERVAL		(hz/5)
 
 /*
  * Low-level read and write register routines; the Altera UART is little
@@ -232,6 +240,7 @@
 static void
 aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp)
 {
+	uint32_t v;
 	uint8_t ch;
 
 	tty_lock_assert(tp, MA_OWNED);
@@ -240,14 +249,31 @@
 	AJU_UNLOCK(sc);
 	while (ttydisc_getc_poll(tp) != 0) {
 		AJU_LOCK(sc);
-		if (aju_writable(sc)) {
+		v = aju_control_read(sc);
+		if ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) != 0) {
 			AJU_UNLOCK(sc);
 			if (ttydisc_getc(tp, &ch, sizeof(ch)) != sizeof(ch))
 				panic("%s: ttydisc_getc", __func__);
 			AJU_LOCK(sc);
 			aju_data_write(sc, ch);
 		} else {
-			aju_intr_writable_enable(sc);
+			/*
+			 * If JTAG is not present, then we will drop this
+			 * character instead of perhaps scheduling an
+			 * interrupt to let us know when there is buffer
+			 * space.  Otherwise we might get a write interrupt
+			 * later even though we aren't interested in sending
+			 * anymore.  Loop to drain TTY-layer buffer.
+			 */
+			if (*sc->ajus_jtag_presentp == 0) {
+				if (ttydisc_getc(tp, &ch, sizeof(ch)) !=
+				    sizeof(ch))
+					panic("%s: ttydisc_getc 2", __func__);
+				AJU_UNLOCK(sc);
+				continue;
+			}
+			if (sc->ajus_irq_res != NULL)
+				aju_intr_writable_enable(sc);
 			return;
 		}
 		AJU_UNLOCK(sc);
@@ -269,7 +295,7 @@
 }
 
 static void
-aju_timeout(void *arg)
+aju_io_callout(void *arg)
 {
 	struct altera_jtag_uart_softc *sc = arg;
 	struct tty *tp = sc->ajus_ttyp;
@@ -293,7 +319,39 @@
 	 * pending in the output buffer, or have we recently had input, but we
 	 * don't.
 	 */
-	callout_reset(&sc->ajus_callout, aju_pollinterval, aju_timeout, sc);
+	callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
+	    aju_io_callout, sc);
+	AJU_UNLOCK(sc);
+	tty_unlock(tp);
+}
+
+static void
+aju_ac_callout(void *arg)
+{
+	struct altera_jtag_uart_softc *sc = arg;
+	struct tty *tp = sc->ajus_ttyp;
+	uint32_t v;
+
+	tty_lock(tp);
+	AJU_LOCK(sc);
+	v = aju_control_read(sc);
+	if (v & ALTERA_JTAG_UART_CONTROL_AC) {
+		v &= ~ALTERA_JTAG_UART_CONTROL_AC;
+		aju_control_write(sc, v);
+		if (*sc->ajus_jtag_presentp == 0) {
+			*sc->ajus_jtag_missedp = 0;
+			*sc->ajus_jtag_presentp = 1;
+			aju_handle_output(sc, tp);
+		}
+	} else if (*sc->ajus_jtag_presentp != 0) {
+		(*sc->ajus_jtag_missedp)++;
+		if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) {
+			*sc->ajus_jtag_presentp = 0;
+			aju_handle_output(sc, tp);
+		}
+	}
+	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
+	    aju_ac_callout, sc);
 	AJU_UNLOCK(sc);
 	tty_unlock(tp);
 }
@@ -333,11 +391,15 @@
 		sc->ajus_lockp = &aju_cons_lock;
 		sc->ajus_buffer_validp = &aju_cons_buffer_valid;
 		sc->ajus_buffer_datap = &aju_cons_buffer_data;
+		sc->ajus_jtag_presentp = &aju_cons_jtag_present;
+		sc->ajus_jtag_missedp = &aju_cons_jtag_missed;
 		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;
+		sc->ajus_jtag_presentp = &sc->ajus_jtag_present;
+		sc->ajus_jtag_missedp = &sc->ajus_jtag_missed;
 	}
 
 	/*
@@ -376,10 +438,13 @@
 		aju_intr_readable_enable(sc);
 		AJU_UNLOCK(sc);
 	} else {
-		callout_init(&sc->ajus_callout, CALLOUT_MPSAFE);
-		callout_reset(&sc->ajus_callout, aju_pollinterval,
-		    aju_timeout, sc);
+		callout_init(&sc->ajus_io_callout, CALLOUT_MPSAFE);
+		callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL,
+		    aju_io_callout, sc);
 	}
+	callout_init(&sc->ajus_ac_callout, CALLOUT_MPSAFE);
+	callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
+	    aju_ac_callout, sc);
 	return (0);
 }
 
@@ -399,7 +464,8 @@
 		bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res,
 		    sc->ajus_irq_cookie);
 	} else
-		callout_drain(&sc->ajus_callout);
+		callout_drain(&sc->ajus_io_callout);
+	callout_drain(&sc->ajus_ac_callout);
 	if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
 		aju_cons_sc = NULL;
 	tty_lock(tp);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201207060052.q660q5jB029155>