From owner-p4-projects@FreeBSD.ORG Fri Jul 6 00:52:07 2012 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 5AD75106564A; Fri, 6 Jul 2012 00:52:06 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 122291065670 for ; Fri, 6 Jul 2012 00:52:06 +0000 (UTC) (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Received: from skunkworks.freebsd.org (skunkworks.freebsd.org [IPv6:2001:4f8:fff6::2d]) by mx1.freebsd.org (Postfix) with ESMTP id ECE498FC0A for ; Fri, 6 Jul 2012 00:52:05 +0000 (UTC) Received: from skunkworks.freebsd.org (localhost [127.0.0.1]) by skunkworks.freebsd.org (8.14.4/8.14.4) with ESMTP id q660q5OO029159 for ; Fri, 6 Jul 2012 00:52:05 GMT (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Received: (from perforce@localhost) by skunkworks.freebsd.org (8.14.4/8.14.4/Submit) id q660q5jB029155 for perforce@freebsd.org; Fri, 6 Jul 2012 00:52:05 GMT (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Date: Fri, 6 Jul 2012 00:52:05 GMT Message-Id: <201207060052.q660q5jB029155@skunkworks.freebsd.org> X-Authentication-Warning: skunkworks.freebsd.org: perforce set sender to bb+lists.freebsd.perforce@cyrus.watson.org using -f From: Robert Watson To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 213956 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 06 Jul 2012 00:52:07 -0000 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);