Exemplo n.º 1
0
/*
 * 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);
}
Exemplo n.º 2
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);
}
Exemplo n.º 3
0
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);
}
Exemplo n.º 4
0
/*
 * 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);
}
Exemplo n.º 5
0
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);
}
Exemplo n.º 6
0
/*
 * 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);
}
Exemplo n.º 7
0
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);
}
Exemplo n.º 8
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_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);
}
Exemplo n.º 9
0
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);
}
Exemplo n.º 10
0
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);
}