diff options
| author | Robert Watson <rwatson@FreeBSD.org> | 2017-01-28 12:43:19 +0000 |
|---|---|---|
| committer | Robert Watson <rwatson@FreeBSD.org> | 2017-01-28 12:43:19 +0000 |
| commit | 95fadd99d5c43e5f377403f02f87d73ef4e756bb (patch) | |
| tree | f254239ff3687c767794a59a1fbed770d36327d4 /sys/dev/altera | |
| parent | 55e0d88afd2b8fdee88a56f77cc1ec21c4ae9347 (diff) | |
Notes
Diffstat (limited to 'sys/dev/altera')
| -rw-r--r-- | sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c | 17 | ||||
| -rw-r--r-- | sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c | 156 |
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); } |
