/* * Slightly higher-level routines aware of buffering and flow control. */ static inline int aju_writable(struct altera_jtag_uart_softc *sc) { return ((aju_control_read(sc) & ALTERA_JTAG_UART_CONTROL_WSPACE) != 0); }
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); }
/* * Send output to the UART until either there's none left to send, or we run * out of room and need to await an interrupt so that we can start sending * again. * * XXXRW: It would be nice to query WSPACE at the beginning and write to the * FIFO in bugger chunks. */ 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); AJU_LOCK_ASSERT(sc); 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 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); } AJU_LOCK(sc); aju_intr_writable_disable(sc); }
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); }
static void aju_intr_writable_disable(struct altera_jtag_uart_softc *sc) { uint32_t v; 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); }
static void aju_intr(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_RI) aju_handle_input(sc, tp); if (v & ALTERA_JTAG_UART_CONTROL_WI) aju_handle_output(sc, tp); AJU_UNLOCK(sc); tty_unlock(tp); }
/* * Send output to the UART until either there's none left to send, or we run * out of room and need to await an interrupt so that we can start sending * again. * * XXXRW: It would be nice to query WSPACE at the beginning and write to the * FIFO in bugger chunks. */ 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); AJU_LOCK_ASSERT(sc); 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); aju_data_write(sc, ch); } else { /* * 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); } AJU_LOCK(sc); aju_intr_writable_disable(sc); }
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_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) { /* * 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); AJU_UNLOCK(sc); tty_unlock(tp); }