PERFORCE change 212573 for review
Robert Watson
rwatson at FreeBSD.org
Sun Jun 10 12:09:46 UTC 2012
http://p4web.freebsd.org/@@212573?ac=10
Change 212573 by rwatson at rwatson_svr_ctsrd_mipsbuild on 2012/06/10 12:09:24
Add support for interrupt-driven operation to the Altera JTAG UART
device driver. Support for pure polled-mode operation is retained
-- if an IRQ is not available (i.e., not defined in device.hints)
then that mode will be used instead, but interrupt-driven operation
will be preferred. Interrupts are used only in the receive path,
currently; transmit-side queueing and interrupt acknowledgement is
not yet supported, so all writes continue to be synchronous from
the TTY output handler. This may be worth fixing.
Configure an IRQ for the first of three JTAG UARTs in the BERI DE-4
configuration -- the other two don't have IRQs configured, but
continue to work fine in polled mode.
Affected files ...
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart.h#3 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c#2 edit
.. //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c#3 edit
.. //depot/projects/ctsrd/beribsd/src/sys/mips/conf/BERI_DE4.hints#6 edit
Differences ...
==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart.h#3 (text+ko) ====
@@ -38,6 +38,9 @@
/*
* Hardware resources.
*/
+ struct resource *ajus_irq_res;
+ int ajus_irq_rid;
+ void *ajus_irq_cookie;
struct resource *ajus_mem_res;
int ajus_mem_rid;
@@ -50,6 +53,7 @@
/*
* Driver resources.
*/
+ u_int ajus_flags;
struct mtx *ajus_lockp;
struct mtx ajus_lock;
struct callout ajus_callout;
@@ -67,6 +71,11 @@
#define AJU_TTYNAME "ttyu"
/*
+ * Flag values for ajus_flags.
+ */
+#define ALTERA_JTAG_UART_FLAG_CONSOLE 0x00000001 /* Is console. */
+
+/*
* Because tty-level use of the I/O ports completes with low-level console
* use, spinlocks must be employed here.
*/
@@ -173,7 +182,7 @@
/*
* Driver attachment functions for Nexus.
*/
-void altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc);
+int altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc);
void altera_jtag_uart_detach(struct altera_jtag_uart_softc *sc);
#endif /* _DEV_ALTERA_JTAG_UART_H_ */
==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c#2 (text+ko) ====
@@ -69,7 +69,9 @@
altera_jtag_uart_nexus_attach(device_t dev)
{
struct altera_jtag_uart_softc *sc;
+ int error;
+ error = 0;
sc = device_get_softc(dev);
sc->ajus_dev = dev;
sc->ajus_unit = device_get_unit(dev);
@@ -78,10 +80,31 @@
&sc->ajus_mem_rid, RF_ACTIVE);
if (sc->ajus_mem_res == NULL) {
device_printf(dev, "couldn't map memory\n");
- return (ENXIO);
+ error = ENXIO;
+ goto out;
+ }
+
+ /*
+ * Interrupt support is optional -- if we can't allocate an IRQ, then
+ * we fall back on polling.
+ */
+ sc->ajus_irq_rid = 0;
+ sc->ajus_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
+ &sc->ajus_irq_rid, RF_ACTIVE | RF_SHAREABLE);
+ if (sc->ajus_irq_res == NULL)
+ device_printf(dev,
+ "IRQ unavailable; selecting polled operation\n");
+ error = altera_jtag_uart_attach(sc);
+out:
+ if (error) {
+ if (sc->ajus_irq_res != NULL)
+ bus_release_resource(dev, SYS_RES_IRQ,
+ sc->ajus_irq_rid, sc->ajus_irq_res);
+ if (sc->ajus_mem_res != NULL)
+ bus_release_resource(dev, SYS_RES_MEMORY,
+ sc->ajus_mem_rid, sc->ajus_mem_res);
}
- altera_jtag_uart_attach(sc);
- return (0);
+ return (error);
}
static int
@@ -93,7 +116,9 @@
KASSERT(sc->ajus_mem_res != NULL, ("%s: resources not allocated",
__func__));
- altera_jtag_uart_detach(sc);
+ altera_jtag_uart_detach(sc);
+ bus_release_resource(dev, SYS_RES_IRQ, sc->ajus_irq_rid,
+ sc->ajus_irq_res);
bus_release_resource(dev, SYS_RES_MEMORY, sc->ajus_mem_rid,
sc->ajus_mem_res);
return (0);
==== //depot/projects/ctsrd/beribsd/src/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c#3 (text+ko) ====
@@ -94,6 +94,14 @@
ALTERA_JTAG_UART_CONTROL_OFF)));
}
+static inline void
+aju_control_write(struct altera_jtag_uart_softc *sc, uint32_t v)
+{
+
+ bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_CONTROL_OFF,
+ htole32(v));
+}
+
/*
* Slightly higher-level routines aware of buffering and flow control.
*/
@@ -154,6 +162,8 @@
/*
* XXXRW: Would be nice not to do blocking writes to the UART here,
* rescheduling on our timer tick if work remains to be done.
+ *
+ * XXXRW: Possibly, if full, defer to interrupt context.
*/
sc = tty_softc(tp);
for (;;) {
@@ -166,14 +176,17 @@
}
}
+/*
+ * 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_timeout(void *v)
+aju_handle_input(struct altera_jtag_uart_softc *sc)
{
- struct altera_jtag_uart_softc *sc;
struct tty *tp;
int c;
- sc = v;
tp = sc->ajus_ttyp;
tty_lock(tp);
AJU_LOCK(sc);
@@ -189,36 +202,119 @@
AJU_UNLOCK(sc);
ttydisc_rint_done(tp);
tty_unlock(tp);
+}
+
+static void
+aju_timeout(void *v)
+{
+ struct altera_jtag_uart_softc *sc = v;
+
+ aju_handle_input(sc);
callout_reset(&sc->ajus_callout, aju_pollinterval, aju_timeout, sc);
}
-void
+static void
+aju_intr(void *v)
+{
+ struct altera_jtag_uart_softc *sc = v;
+
+ /*
+ * XXXRW: Just receive in the interrupt path for now; possibly we
+ * should check the control flags.
+ */
+ aju_handle_input(sc);
+}
+
+static void
+aju_intr_enable(struct altera_jtag_uart_softc *sc)
+{
+ uint32_t v;
+
+ AJU_LOCK_ASSERT(sc);
+
+ v = aju_control_read(sc);
+ v |= ALTERA_JTAG_UART_CONTROL_RE;
+ v &= ~ALTERA_JTAG_UART_CONTROL_WE;
+ aju_control_write(sc, v);
+}
+
+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);
+}
+
+int
altera_jtag_uart_attach(struct altera_jtag_uart_softc *sc)
{
struct tty *tp;
+ int error;
- tp = sc->ajus_ttyp = tty_alloc(&aju_ttydevsw, sc);
+ 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?
*/
- AJU_LOCK_INIT(sc);
if (rman_get_start(sc->ajus_mem_res) == BERI_UART_BASE) {
- tty_init_console(tp, 0);
sc->ajus_lockp = &aju_cons_lock;
sc->ajus_buffer_validp = &aju_cons_buffer_valid;
sc->ajus_buffer_datap = &aju_cons_buffer_data;
- aju_cons_sc = sc;
+ 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;
}
+
+ /*
+ * 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);
- callout_init(&sc->ajus_callout, CALLOUT_MPSAFE);
- callout_reset(&sc->ajus_callout, aju_pollinterval, aju_timeout, sc);
+
+ /*
+ * 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_enable(sc);
+ AJU_UNLOCK(sc);
+ } else {
+ callout_init(&sc->ajus_callout, CALLOUT_MPSAFE);
+ callout_reset(&sc->ajus_callout, aju_pollinterval,
+ aju_timeout, sc);
+ }
+ return (0);
}
void
@@ -226,9 +322,20 @@
{
struct tty *tp = sc->ajus_ttyp;
- if (sc == aju_cons_sc)
+ /*
+ * 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_callout);
+ if (sc->ajus_flags & ALTERA_JTAG_UART_FLAG_CONSOLE)
aju_cons_sc = NULL;
- callout_drain(&sc->ajus_callout);
tty_lock(tp);
tty_rel_gone(tp);
AJU_LOCK_DESTROY(sc);
==== //depot/projects/ctsrd/beribsd/src/sys/mips/conf/BERI_DE4.hints#6 (text+ko) ====
@@ -7,6 +7,7 @@
hint.altera_jtag_uart.0.at="nexus0"
hint.altera_jtag_uart.0.maddr=0x7f000000
hint.altera_jtag_uart.0.msize=0x40
+hint.altera_jtag_uart.0.irq=0
hint.altera_jtag_uart.1.at="nexus0"
hint.altera_jtag_uart.1.maddr=0x7f001000
More information about the p4-projects
mailing list