aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/altera
diff options
context:
space:
mode:
authorRobert Watson <rwatson@FreeBSD.org>2017-01-28 12:43:19 +0000
committerRobert Watson <rwatson@FreeBSD.org>2017-01-28 12:43:19 +0000
commit95fadd99d5c43e5f377403f02f87d73ef4e756bb (patch)
treef254239ff3687c767794a59a1fbed770d36327d4 /sys/dev/altera
parent55e0d88afd2b8fdee88a56f77cc1ec21c4ae9347 (diff)
Notes
Diffstat (limited to 'sys/dev/altera')
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c17
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c156
2 files changed, 127 insertions, 46 deletions
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c
index 96d20ceb21f4..07e884dde63a 100644
--- a/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c
@@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/reboot.h>
+#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/tty.h>
@@ -49,6 +50,9 @@ __FBSDID("$FreeBSD$");
devclass_t altera_jtag_uart_devclass;
+static SYSCTL_NODE(_hw, OID_AUTO, altera_jtag_uart, CTLFLAG_RW, 0,
+ "Altera JTAG UART configuration knobs");
+
/*
* One-byte buffer as we can't check whether the UART is readable without
* actually reading from it, synchronised by a spinlock; this lock also
@@ -82,6 +86,11 @@ static cn_ungrab_t aju_cnungrab;
* no AC bit set.
*/
#define ALTERA_JTAG_UART_AC_POLL_DELAY 10000
+static u_int altera_jtag_uart_ac_poll_delay =
+ ALTERA_JTAG_UART_AC_POLL_DELAY;
+SYSCTL_UINT(_hw_altera_jtag_uart, OID_AUTO, ac_poll_delay,
+ CTLFLAG_RW, &altera_jtag_uart_ac_poll_delay, 0,
+ "Maximum delay waiting for JTAG present flag when buffer is full");
/*
* I/O routines lifted from Deimos. This is not only MIPS-specific, but also
@@ -220,10 +229,10 @@ aju_cons_write(char ch)
* layer clearing of the bit doesn't trigger a TTY-layer
* disconnection.
*
- * XXXRW: The polling delay may require tuning.
- *
* XXXRW: Notice the inherent race with hardware: in clearing the
- * bit, we may race with hardware setting the same bit.
+ * bit, we may race with hardware setting the same bit. This can
+ * cause real-world reliability problems due to lost output on the
+ * console.
*/
v = aju_cons_control_read();
if (v & ALTERA_JTAG_UART_CONTROL_AC) {
@@ -235,7 +244,7 @@ aju_cons_write(char ch)
while ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
if (!aju_cons_jtag_present)
return;
- DELAY(ALTERA_JTAG_UART_AC_POLL_DELAY);
+ DELAY(altera_jtag_uart_ac_poll_delay);
v = aju_cons_control_read();
if (v & ALTERA_JTAG_UART_CONTROL_AC) {
aju_cons_jtag_present = 1;
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
index 1e9a1b89657d..9f16dba6d99e 100644
--- a/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2011-2012 Robert N. M. Watson
+ * Copyright (c) 2011-2012, 2016 Robert N. M. Watson
* All rights reserved.
*
* This software was developed by SRI International and the University of
@@ -40,10 +40,12 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/reboot.h>
+#include <sys/sysctl.h>
#include <sys/tty.h>
#include <ddb/ddb.h>
+#include <machine/atomic.h>
#include <machine/bus.h>
#include <dev/altera/jtag_uart/altera_jtag_uart.h>
@@ -65,9 +67,9 @@ static struct ttydevsw aju_ttydevsw = {
/*
* 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, two seconds.
+ * before assuming JTAG has disappeared on us. By default, four seconds.
*/
-#define AJU_JTAG_MAXMISS 10
+#define AJU_JTAG_MAXMISS 20
/*
* Polling intervals for input/output and JTAG connection events.
@@ -76,6 +78,53 @@ static struct ttydevsw aju_ttydevsw = {
#define AJU_AC_POLLINTERVAL (hz/5)
/*
+ * Statistics on JTAG removal events when sending, for debugging purposes
+ * only.
+ */
+static u_int aju_jtag_vanished;
+SYSCTL_UINT(_debug, OID_AUTO, aju_jtag_vanished, CTLFLAG_RW,
+ &aju_jtag_vanished, 0, "Number of times JTAG has vanished");
+
+static u_int aju_jtag_appeared;
+SYSCTL_UINT(_debug, OID_AUTO, aju_jtag_appeared, CTLFLAG_RW,
+ &aju_jtag_appeared, 0, "Number of times JTAG has appeared");
+
+SYSCTL_INT(_debug, OID_AUTO, aju_cons_jtag_present, CTLFLAG_RW,
+ &aju_cons_jtag_present, 0, "JTAG console present flag");
+
+SYSCTL_UINT(_debug, OID_AUTO, aju_cons_jtag_missed, CTLFLAG_RW,
+ &aju_cons_jtag_missed, 0, "JTAG console missed counter");
+
+/*
+ * Interrupt-related statistics.
+ */
+static u_int aju_intr_readable_enabled;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_readable_enabled, CTLFLAG_RW,
+ &aju_intr_readable_enabled, 0, "Number of times read interrupt enabled");
+
+static u_int aju_intr_writable_disabled;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_writable_disabled, CTLFLAG_RW,
+ &aju_intr_writable_disabled, 0,
+ "Number of times write interrupt disabled");
+
+static u_int aju_intr_writable_enabled;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_writable_enabled, CTLFLAG_RW,
+ &aju_intr_writable_enabled, 0,
+ "Number of times write interrupt enabled");
+
+static u_int aju_intr_disabled;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_disabled, CTLFLAG_RW,
+ &aju_intr_disabled, 0, "Number of times write interrupt disabled");
+
+static u_int aju_intr_read_count;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_read_count, CTLFLAG_RW,
+ &aju_intr_read_count, 0, "Number of times read interrupt fired");
+
+static u_int aju_intr_write_count;
+SYSCTL_UINT(_debug, OID_AUTO, aju_intr_write_count, CTLFLAG_RW,
+ &aju_intr_write_count, 0, "Number of times write interrupt fired");
+
+/*
* Low-level read and write register routines; the Altera UART is little
* endian, so we byte swap 32-bit reads and writes.
*/
@@ -160,6 +209,7 @@ aju_intr_readable_enable(struct altera_jtag_uart_softc *sc)
AJU_LOCK_ASSERT(sc);
+ atomic_add_int(&aju_intr_readable_enabled, 1);
v = aju_control_read(sc);
v |= ALTERA_JTAG_UART_CONTROL_RE;
aju_control_write(sc, v);
@@ -172,6 +222,7 @@ aju_intr_writable_enable(struct altera_jtag_uart_softc *sc)
AJU_LOCK_ASSERT(sc);
+ atomic_add_int(&aju_intr_writable_enabled, 1);
v = aju_control_read(sc);
v |= ALTERA_JTAG_UART_CONTROL_WE;
aju_control_write(sc, v);
@@ -184,6 +235,7 @@ aju_intr_writable_disable(struct altera_jtag_uart_softc *sc)
AJU_LOCK_ASSERT(sc);
+ atomic_add_int(&aju_intr_writable_disabled, 1);
v = aju_control_read(sc);
v &= ~ALTERA_JTAG_UART_CONTROL_WE;
aju_control_write(sc, v);
@@ -196,6 +248,7 @@ aju_intr_disable(struct altera_jtag_uart_softc *sc)
AJU_LOCK_ASSERT(sc);
+ atomic_add_int(&aju_intr_disabled, 1);
v = aju_control_read(sc);
v &= ~(ALTERA_JTAG_UART_CONTROL_RE | ALTERA_JTAG_UART_CONTROL_WE);
aju_control_write(sc, v);
@@ -249,30 +302,7 @@ aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp)
AJU_UNLOCK(sc);
while (ttydisc_getc_poll(tp) != 0) {
AJU_LOCK(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);
-
- /*
- * XXXRW: There is a slight race here in which we test
- * for writability, drop the lock, get the character
- * from the tty layer, re-acquire the lock, and then
- * write. It's possible for other code --
- * specifically, the low-level console -- to have
- * written in the mean time, which might mean that
- * there is no longer space. The BERI memory bus will
- * cause this write to block, wedging the processor
- * until space is available -- which could be a while
- * if JTAG is not attached!
- *
- * The 'easy' fix is to drop the character if WSPACE
- * has become unset. Not sure what the 'hard' fix is.
- */
- aju_data_write(sc, ch);
- } else {
+ if (*sc->ajus_jtag_presentp == 0) {
/*
* If JTAG is not present, then we will drop this
* character instead of perhaps scheduling an
@@ -281,21 +311,50 @@ aju_handle_output(struct altera_jtag_uart_softc *sc, struct tty *tp)
* 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_UNLOCK(sc);
+ if (ttydisc_getc(tp, &ch, sizeof(ch)) !=
+ sizeof(ch))
+ panic("%s: ttydisc_getc", __func__);
+ continue;
+ }
+ v = aju_control_read(sc);
+ if ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
+ if (sc->ajus_irq_res != NULL &&
+ (v & ALTERA_JTAG_UART_CONTROL_WE) == 0)
aju_intr_writable_enable(sc);
return;
}
AJU_UNLOCK(sc);
+ if (ttydisc_getc(tp, &ch, sizeof(ch)) != sizeof(ch))
+ panic("%s: ttydisc_getc 2", __func__);
+ AJU_LOCK(sc);
+
+ /*
+ * XXXRW: There is a slight race here in which we test for
+ * writability, drop the lock, get the character from the tty
+ * layer, re-acquire the lock, and then write. It's possible
+ * for other code -- specifically, the low-level console -- to
+ * have* written in the mean time, which might mean that there
+ * is no longer space. The BERI memory bus will cause this
+ * write to block, wedging the processor until space is
+ * available -- which could be a while if JTAG is not
+ * attached!
+ *
+ * The 'easy' fix is to drop the character if WSPACE has
+ * become unset. Not sure what the 'hard' fix is.
+ */
+ aju_data_write(sc, ch);
+ AJU_UNLOCK(sc);
}
AJU_LOCK(sc);
- aju_intr_writable_disable(sc);
+
+ /*
+ * If interrupts are configured, and there's no data to write, but we
+ * had previously enabled write interrupts, disable them now.
+ */
+ v = aju_control_read(sc);
+ if (sc->ajus_irq_res != NULL && (v & ALTERA_JTAG_UART_CONTROL_WE) != 0)
+ aju_intr_writable_disable(sc);
}
static void
@@ -355,16 +414,25 @@ aju_ac_callout(void *arg)
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;
+ atomic_add_int(&aju_jtag_appeared, 1);
aju_handle_output(sc, tp);
}
+
+ /* Any hit eliminates all recent misses. */
+ *sc->ajus_jtag_missedp = 0;
} else if (*sc->ajus_jtag_presentp != 0) {
- (*sc->ajus_jtag_missedp)++;
- if (*sc->ajus_jtag_missedp >= AJU_JTAG_MAXMISS) {
+ /*
+ * If we've exceeded our tolerance for misses, mark JTAG as
+ * disconnected and drain output. Otherwise, bump the miss
+ * counter.
+ */
+ if (*sc->ajus_jtag_missedp > AJU_JTAG_MAXMISS) {
*sc->ajus_jtag_presentp = 0;
+ atomic_add_int(&aju_jtag_vanished, 1);
aju_handle_output(sc, tp);
- }
+ } else
+ (*sc->ajus_jtag_missedp)++;
}
callout_reset(&sc->ajus_ac_callout, AJU_AC_POLLINTERVAL,
aju_ac_callout, sc);
@@ -382,10 +450,14 @@ aju_intr(void *arg)
tty_lock(tp);
AJU_LOCK(sc);
v = aju_control_read(sc);
- if (v & ALTERA_JTAG_UART_CONTROL_RI)
+ if (v & ALTERA_JTAG_UART_CONTROL_RI) {
+ atomic_add_int(&aju_intr_read_count, 1);
aju_handle_input(sc, tp);
- if (v & ALTERA_JTAG_UART_CONTROL_WI)
+ }
+ if (v & ALTERA_JTAG_UART_CONTROL_WI) {
+ atomic_add_int(&aju_intr_write_count, 1);
aju_handle_output(sc, tp);
+ }
AJU_UNLOCK(sc);
tty_unlock(tp);
}