From owner-p4-projects@FreeBSD.ORG Sun Jun 10 12:09:46 2012 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id AE88B1065672; Sun, 10 Jun 2012 12:09:45 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id 70D9A106566B for ; Sun, 10 Jun 2012 12:09:45 +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 577A08FC15 for ; Sun, 10 Jun 2012 12:09:45 +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 q5AC9jNj037927 for ; Sun, 10 Jun 2012 12:09:45 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 q5AC9j2F037924 for perforce@freebsd.org; Sun, 10 Jun 2012 12:09:45 GMT (envelope-from bb+lists.freebsd.perforce@cyrus.watson.org) Date: Sun, 10 Jun 2012 12:09:45 GMT Message-Id: <201206101209.q5AC9j2F037924@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 212573 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: Sun, 10 Jun 2012 12:09:46 -0000 http://p4web.freebsd.org/@@212573?ac=10 Change 212573 by rwatson@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