/* * 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_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); }
static void aju_io_callout(void *arg) { struct altera_jtag_uart_softc *sc = arg; struct tty *tp = sc->ajus_ttyp; tty_lock(tp); AJU_LOCK(sc); /* * It would be convenient if we could share code with aju_intr() here * by testing the control register for ALTERA_JTAG_UART_CONTROL_RI and * ALTERA_JTAG_UART_CONTROL_WI. Unfortunately, it's not clear that * this is supported, so do all the work to poll for both input and * output. */ aju_handle_input(sc, tp); aju_handle_output(sc, tp); /* * Reschedule next poll attempt. There's some argument that we should * do adaptive polling based on the expectation of I/O: is something * pending in the output buffer, or have we recently had input, but we * don't. */ callout_reset(&sc->ajus_io_callout, AJU_IO_POLLINTERVAL, aju_io_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); 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_outwakeup(struct tty *tp) { struct altera_jtag_uart_softc *sc = tty_softc(tp); tty_lock_assert(tp, MA_OWNED); AJU_LOCK(sc); aju_handle_output(sc, tp); AJU_UNLOCK(sc); }
/* * The actual work of checking for, and handling, available reads. This is * used in both polled and interrupt-driven modes, as JTAG UARTs may be hooked * up with, or without, IRQs allocated. */ static void aju_handle_input(struct altera_jtag_uart_softc *sc, struct tty *tp) { int c; tty_lock_assert(tp, MA_OWNED); AJU_LOCK_ASSERT(sc); while (aju_readable(sc)) { c = aju_read(sc); AJU_UNLOCK(sc); #ifdef KDB if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) kdb_alt_break(c, &sc->ajus_alt_break_state); #endif ttydisc_rint(tp, c, 0); AJU_LOCK(sc); } AJU_UNLOCK(sc); ttydisc_rint_done(tp); AJU_LOCK(sc); }
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); }
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); }
void altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc) { struct tty *tp = sc->ajus_ttyp; /* * If we're using interrupts, disable and release the interrupt * handler now. Otherwise drain the polling timeout. */ if (sc->ajus_irq_res != NULL) { AJU_LOCK(sc); aju_intr_disable(sc); AJU_UNLOCK(sc); bus_teardown_intr(sc->ajus_dev, sc->ajus_irq_res, sc->ajus_irq_cookie); } else 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); tty_rel_gone(tp); AJU_LOCK_DESTROY(sc); }
int altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc) { struct tty *tp; int error; AJU_LOCK_INIT(sc); /* * XXXRW: Currently, we detect the console solely based on it using a * reserved address, and borrow console-level locks and buffer if so. * Is there a better way? */ if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) { 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; } /* * Disable interrupts regardless of whether or not we plan to use * them. We will register an interrupt handler now if they will be * used, but not re-enable intil later once the remainder of the tty * layer is properly initialised, as we're not ready for input yet. */ AJU_LOCK(sc); aju_intr_disable(sc); AJU_UNLOCK(sc); if (sc->ajus_irq_res != NULL) { error = bus_setup_intr(sc->ajus_dev, sc->ajus_irq_res, INTR_ENTROPY | INTR_TYPE_TTY | INTR_MPSAFE, NULL, aju_intr, sc, &sc->ajus_irq_cookie); if (error) { device_printf(sc->ajus_dev, "could not activate interrupt\n"); AJU_LOCK_DESTROY(sc); return (error); } } tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc); if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE) { aju_cons_sc = sc; tty_init_console(tp, 0); } tty_makedev(tp, NULL, "%s%d", AJU_TTYNAME, sc->ajus_unit); /* * If we will be using interrupts, enable them now; otherwise, start * polling. From this point onwards, input can arrive. */ if (sc->ajus_irq_res != NULL) { AJU_LOCK(sc); aju_intr_readable_enable(sc); AJU_UNLOCK(sc); } else { 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); }