aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/altera
diff options
context:
space:
mode:
authorRobert Watson <rwatson@FreeBSD.org>2012-08-25 11:30:36 +0000
committerRobert Watson <rwatson@FreeBSD.org>2012-08-25 11:30:36 +0000
commit697a77c1c45f6b521941dd1a4e9484967221a7ed (patch)
tree4f3e8bdf7db997a5a95432389dc7e7b0057c532b /sys/dev/altera
parentd432e92a84604b18a645ca42884936428ffd2f1a (diff)
Notes
Diffstat (limited to 'sys/dev/altera')
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart.h197
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c318
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c143
-rw-r--r--sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c474
4 files changed, 1132 insertions, 0 deletions
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart.h b/sys/dev/altera/jtag_uart/altera_jtag_uart.h
new file mode 100644
index 000000000000..1ff3bcb4cfd8
--- /dev/null
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart.h
@@ -0,0 +1,197 @@
+/*-
+ * Copyright (c) 2011-2012 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_ALTERA_JTAG_UART_H_
+#define _DEV_ALTERA_JTAG_UART_H_
+
+struct altera_jtag_uart_softc {
+ device_t ajus_dev;
+ int ajus_unit;
+
+ /*
+ * Hardware resources.
+ */
+ struct resource *ajus_irq_res;
+ int ajus_irq_rid;
+ void *ajus_irq_cookie;
+ struct resource *ajus_mem_res;
+ int ajus_mem_rid;
+
+ /*
+ * TTY resources.
+ */
+ struct tty *ajus_ttyp;
+ int ajus_alt_break_state;
+
+ /*
+ * Driver resources.
+ */
+ u_int ajus_flags;
+ struct mtx *ajus_lockp;
+ struct mtx ajus_lock;
+ struct callout ajus_io_callout;
+ struct callout ajus_ac_callout;
+
+ /*
+ * One-character buffer required because it's not possible to peek at
+ * the input FIFO without reading it.
+ */
+ int ajus_buffer_valid;
+ int *ajus_buffer_validp;
+ uint8_t ajus_buffer_data;
+ uint8_t *ajus_buffer_datap;
+ int ajus_jtag_present;
+ int *ajus_jtag_presentp;
+ u_int ajus_jtag_missed;
+ u_int *ajus_jtag_missedp;
+};
+
+#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.
+ */
+#define AJU_CONSOLE_LOCK_INIT() do { \
+ mtx_init(&aju_cons_lock, "aju_cons_lock", NULL, MTX_SPIN); \
+} while (0)
+
+#define AJU_CONSOLE_LOCK() do { \
+ if (!kdb_active) \
+ mtx_lock_spin(&aju_cons_lock); \
+} while (0)
+
+#define AJU_CONSOLE_LOCK_ASSERT() { \
+ if (!kdb_active) \
+ mtx_assert(&aju_cons_lock, MA_OWNED); \
+} while (0)
+
+#define AJU_CONSOLE_UNLOCK() do { \
+ if (!kdb_active) \
+ mtx_unlock_spin(&aju_cons_lock); \
+} while (0)
+
+#define AJU_LOCK_INIT(sc) do { \
+ mtx_init(&(sc)->ajus_lock, "aju_lock", NULL, MTX_SPIN); \
+} while (0)
+
+#define AJU_LOCK_DESTROY(sc) do { \
+ mtx_destroy(&(sc)->ajus_lock); \
+} while (0)
+
+#define AJU_LOCK(sc) do { \
+ mtx_lock_spin((sc)->ajus_lockp); \
+} while (0)
+
+#define AJU_LOCK_ASSERT(sc) do { \
+ mtx_assert((sc)->ajus_lockp, MA_OWNED); \
+} while (0)
+
+#define AJU_UNLOCK(sc) do { \
+ mtx_unlock_spin((sc)->ajus_lockp); \
+} while (0)
+
+/*
+ * When a TTY-level Altera JTAG UART instance is also the low-level console,
+ * the TTY layer borrows the console-layer lock and buffer rather than using
+ * its own.
+ */
+extern struct mtx aju_cons_lock;
+extern char aju_cons_buffer_data;
+extern int aju_cons_buffer_valid;
+extern int aju_cons_jtag_present;
+extern u_int aju_cons_jtag_missed;
+
+/*
+ * Base physical address of the JTAG UART in BERI.
+ */
+#define BERI_UART_BASE 0x7f000000 /* JTAG UART */
+
+/*-
+ * Routines for interacting with the BERI console JTAG UART. Programming
+ * details from the June 2011 "Embedded Peripherals User Guide" by Altera
+ * Corporation, tables 6-2 (JTAG UART Core Register Map), 6-3 (Data Register
+ * Bits), and 6-4 (Control Register Bits).
+ *
+ * Offsets of data and control registers relative to the base. Altera
+ * conventions are maintained in BERI.
+ */
+#define ALTERA_JTAG_UART_DATA_OFF 0x00000000
+#define ALTERA_JTAG_UART_CONTROL_OFF 0x00000004
+
+/*
+ * Offset 0: 'data' register -- bits 31-16 (RAVAIL), 15 (RVALID),
+ * 14-8 (Reserved), 7-0 (DATA).
+ *
+ * DATA - One byte read or written.
+ * RAVAIL - Bytes available to read (excluding the current byte).
+ * RVALID - Whether the byte in DATA is valid.
+ */
+#define ALTERA_JTAG_UART_DATA_DATA 0x000000ff
+#define ALTERA_JTAG_UART_DATA_RESERVED 0x00007f00
+#define ALTERA_JTAG_UART_DATA_RVALID 0x00008000
+#define ALTERA_JTAG_UART_DATA_RAVAIL 0xffff0000
+#define ALTERA_JTAG_UART_DATA_RAVAIL_SHIFT 16
+
+/*-
+ * Offset 1: 'control' register -- bits 31-16 (WSPACE), 15-11 (Reserved),
+ * 10 (AC), 9 (WI), 8 (RI), 7..2 (Reserved), 1 (WE), 0 (RE).
+ *
+ * RE - Enable read interrupts.
+ * WE - Enable write interrupts.
+ * RI - Read interrupt pending.
+ * WI - Write interrupt pending.
+ * AC - Activity bit; set to '1' to clear to '0'.
+ * WSPACE - Space available in the write FIFO.
+ */
+#define ALTERA_JTAG_UART_CONTROL_RE 0x00000001
+#define ALTERA_JTAG_UART_CONTROL_WE 0x00000002
+#define ALTERA_JTAG_UART_CONTROL_RESERVED0 0x000000fc
+#define ALTERA_JTAG_UART_CONTROL_RI 0x00000100
+#define ALTERA_JTAG_UART_CONTROL_WI 0x00000200
+#define ALTERA_JTAG_UART_CONTROL_AC 0x00000400
+#define ALTERA_JTAG_UART_CONTROL_RESERVED1 0x0000f800
+#define ALTERA_JTAG_UART_CONTROL_WSPACE 0xffff0000
+#define ALTERA_JTAG_UART_CONTROL_WSPACE_SHIFT 16
+
+/*
+ * Driver attachment functions for Nexus.
+ */
+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_ */
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c
new file mode 100644
index 000000000000..ee209cad1c56
--- /dev/null
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_cons.c
@@ -0,0 +1,318 @@
+/*-
+ * Copyright (c) 2011-2012 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/cons.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/reboot.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+
+#include <ddb/ddb.h>
+
+#include <dev/altera/jtag_uart/altera_jtag_uart.h>
+
+/*
+ * One-byte buffer as we can't check whether the UART is readable without
+ * actually reading from it, synchronised by a spinlock; this lock also
+ * synchronises access to the I/O ports for non-atomic sequences. These
+ * symbols are public so that the TTY layer can use them when working on an
+ * instance of the UART that is also a low-level console.
+ */
+char aju_cons_buffer_data;
+int aju_cons_buffer_valid;
+int aju_cons_jtag_present;
+u_int aju_cons_jtag_missed;
+struct mtx aju_cons_lock;
+
+/*
+ * Low-level console driver functions.
+ */
+static cn_probe_t aju_cnprobe;
+static cn_init_t aju_cninit;
+static cn_term_t aju_cnterm;
+static cn_getc_t aju_cngetc;
+static cn_putc_t aju_cnputc;
+static cn_grab_t aju_cngrab;
+static cn_ungrab_t aju_cnungrab;
+
+/*
+ * JTAG sets the ALTERA_JTAG_UART_CONTROL_AC bit whenever it accesses the
+ * FIFO. This allows us to (sort of) tell when JTAG is present, so that we
+ * can adopt lossy, rather than blocking, behaviour when JTAG isn't there.
+ * When it is present, we do full flow control. This delay is how long we
+ * wait to see if JTAG has really disappeared when finding a full buffer and
+ * no AC bit set.
+ */
+#define ALTERA_JTAG_UART_AC_POLL_DELAY 10000
+
+/*
+ * I/O routines lifted from Deimos. This is not only MIPS-specific, but also
+ * BERI-specific, as we're hard coding the the address at which we expect to
+ * find the Altera JTAG UART and using it unconditionally. We use these
+ * low-level routines so that we can perform console I/O long before newbus
+ * has initialised and devices have attached. The TTY layer of the driver
+ * knows about this, and uses the console-layer spinlock instead of the
+ * TTY-layer lock to avoid confusion between layers for the console UART.
+ *
+ * XXXRW: The only place this inter-layer behaviour breaks down is if the
+ * low-level console is used for polled read while the TTY driver is also
+ * looking for input. Probably we should also share buffers between layers.
+ */
+#define MIPS_XKPHYS_UNCACHED_BASE 0x9000000000000000
+
+typedef uint64_t paddr_t;
+typedef uint64_t vaddr_t;
+
+static inline vaddr_t
+mips_phys_to_uncached(paddr_t phys)
+{
+
+ return (phys | MIPS_XKPHYS_UNCACHED_BASE);
+}
+
+static inline uint32_t
+mips_ioread_uint32(vaddr_t vaddr)
+{
+ uint32_t v;
+
+ __asm__ __volatile__ ("lw %0, 0(%1)" : "=r" (v) : "r" (vaddr));
+ return (v);
+}
+
+static inline void
+mips_iowrite_uint32(vaddr_t vaddr, uint32_t v)
+{
+
+ __asm__ __volatile__ ("sw %0, 0(%1)" : : "r" (v), "r" (vaddr));
+}
+
+/*
+ * Little-endian versions of 32-bit I/O routines.
+ */
+static inline uint32_t
+mips_ioread_uint32le(vaddr_t vaddr)
+{
+
+ return (le32toh(mips_ioread_uint32(vaddr)));
+}
+
+static inline void
+mips_iowrite_uint32le(vaddr_t vaddr, uint32_t v)
+{
+
+ mips_iowrite_uint32(vaddr, htole32(v));
+}
+
+/*
+ * Low-level read and write register routines; the Altera UART is little
+ * endian, so we byte swap 32-bit reads and writes.
+ */
+static inline uint32_t
+aju_cons_data_read(void)
+{
+
+ return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
+ ALTERA_JTAG_UART_DATA_OFF)));
+}
+
+static inline void
+aju_cons_data_write(uint32_t v)
+{
+
+ mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
+ ALTERA_JTAG_UART_DATA_OFF), v);
+}
+
+static inline uint32_t
+aju_cons_control_read(void)
+{
+
+ return (mips_ioread_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
+ ALTERA_JTAG_UART_CONTROL_OFF)));
+}
+
+static inline void
+aju_cons_control_write(uint32_t v)
+{
+
+ mips_iowrite_uint32le(mips_phys_to_uncached(BERI_UART_BASE +
+ ALTERA_JTAG_UART_CONTROL_OFF), v);
+}
+
+/*
+ * Slightly higher-level routines aware of buffering and flow control.
+ */
+static int
+aju_cons_readable(void)
+{
+ uint32_t v;
+
+ AJU_CONSOLE_LOCK_ASSERT();
+
+ if (aju_cons_buffer_valid)
+ return (1);
+ v = aju_cons_data_read();
+ if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
+ aju_cons_buffer_valid = 1;
+ aju_cons_buffer_data = (v & ALTERA_JTAG_UART_DATA_DATA);
+ return (1);
+ }
+ return (0);
+}
+
+static void
+aju_cons_write(char ch)
+{
+ uint32_t v;
+
+ AJU_CONSOLE_LOCK_ASSERT();
+
+ /*
+ * The flow control logic here is somewhat subtle: we want to wait for
+ * write buffer space only while JTAG is present. However, we can't
+ * directly ask if JTAG is present -- just whether it's been seen
+ * since we last cleared the ALTERA_JTAG_UART_CONTROL_AC bit. As
+ * such, implement a polling loop in which we both wait for space and
+ * try to decide whether JTAG has disappeared on us. We will have to
+ * wait one complete polling delay to detect that JTAG has gone away,
+ * but otherwise shouldn't wait any further once it has gone. And we
+ * had to wait for buffer space anyway, if it was there.
+ *
+ * If JTAG is spotted, reset the TTY-layer miss counter so console-
+ * layer clearing of the bit doesn't trigger a TTY-layer
+ * disconnection.
+ *
+ * XXXRW: The polling delay may require tuning.
+ */
+ v = aju_cons_control_read();
+ if (v & ALTERA_JTAG_UART_CONTROL_AC) {
+ aju_cons_jtag_present = 1;
+ aju_cons_jtag_missed = 0;
+ v &= ~ALTERA_JTAG_UART_CONTROL_AC;
+ aju_cons_control_write(v);
+ }
+ while ((v & ALTERA_JTAG_UART_CONTROL_WSPACE) == 0) {
+ if (!aju_cons_jtag_present)
+ return;
+ DELAY(ALTERA_JTAG_UART_AC_POLL_DELAY);
+ v = aju_cons_control_read();
+ if (v & ALTERA_JTAG_UART_CONTROL_AC) {
+ aju_cons_jtag_present = 1;
+ v &= ~ALTERA_JTAG_UART_CONTROL_AC;
+ aju_cons_control_write(v);
+ } else
+ aju_cons_jtag_present = 0;
+ }
+ aju_cons_data_write(ch);
+}
+
+static char
+aju_cons_read(void)
+{
+
+ AJU_CONSOLE_LOCK_ASSERT();
+
+ while (!aju_cons_readable());
+ aju_cons_buffer_valid = 0;
+ return (aju_cons_buffer_data);
+}
+
+/*
+ * Implementation of a FreeBSD low-level, polled console driver.
+ */
+static void
+aju_cnprobe(struct consdev *cp)
+{
+
+ sprintf(cp->cn_name, "%s%d", AJU_TTYNAME, 0);
+ cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL;
+}
+
+static void
+aju_cninit(struct consdev *cp)
+{
+ uint32_t v;
+
+ AJU_CONSOLE_LOCK_INIT();
+
+ AJU_CONSOLE_LOCK();
+ v = aju_cons_control_read();
+ v &= ~ALTERA_JTAG_UART_CONTROL_AC;
+ aju_cons_control_write(v);
+ AJU_CONSOLE_UNLOCK();
+}
+
+static void
+aju_cnterm(struct consdev *cp)
+{
+
+}
+
+static int
+aju_cngetc(struct consdev *cp)
+{
+ int ret;
+
+ AJU_CONSOLE_LOCK();
+ ret = aju_cons_read();
+ AJU_CONSOLE_UNLOCK();
+ return (ret);
+}
+
+static void
+aju_cnputc(struct consdev *cp, int c)
+{
+
+ AJU_CONSOLE_LOCK();
+ aju_cons_write(c);
+ AJU_CONSOLE_UNLOCK();
+}
+
+static void
+aju_cngrab(struct consdev *cp)
+{
+
+}
+
+static void
+aju_cnungrab(struct consdev *cp)
+{
+
+}
+
+CONSOLE_DRIVER(aju);
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c
new file mode 100644
index 000000000000..25922499058a
--- /dev/null
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_nexus.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 2012 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/condvar.h>
+#include <sys/conf.h>
+#include <sys/bio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/module.h>
+#include <sys/mutex.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/taskqueue.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <geom/geom_disk.h>
+
+#include <dev/altera/jtag_uart/altera_jtag_uart.h>
+
+/*
+ * Nexus bus attachment for Altera JTAG UARTs. Appropriate for most Altera
+ * FPGA SoC-style configurations in which the IP core will be exposed to the
+ * processor via a memory-mapped Avalon bus.
+ */
+static int
+altera_jtag_uart_nexus_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Altera JTAG UART");
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+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);
+ sc->ajus_mem_rid = 0;
+ sc->ajus_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
+ &sc->ajus_mem_rid, RF_ACTIVE);
+ if (sc->ajus_mem_res == NULL) {
+ device_printf(dev, "couldn't map memory\n");
+ 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);
+ }
+ return (error);
+}
+
+static int
+altera_jtag_uart_nexus_detach(device_t dev)
+{
+ struct altera_jtag_uart_softc *sc;
+
+ sc = device_get_softc(dev);
+ KASSERT(sc->ajus_mem_res != NULL, ("%s: resources not allocated",
+ __func__));
+
+ 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);
+}
+
+static device_method_t altera_jtag_uart_nexus_methods[] = {
+ DEVMETHOD(device_probe, altera_jtag_uart_nexus_probe),
+ DEVMETHOD(device_attach, altera_jtag_uart_nexus_attach),
+ DEVMETHOD(device_detach, altera_jtag_uart_nexus_detach),
+ { 0, 0 }
+};
+
+static driver_t altera_jtag_uart_nexus_driver = {
+ "altera_jtag_uart",
+ altera_jtag_uart_nexus_methods,
+ sizeof(struct altera_jtag_uart_softc),
+};
+
+static devclass_t altera_jtag_uart_devclass;
+
+DRIVER_MODULE(altera_jtag_uart, nexus, altera_jtag_uart_nexus_driver,
+ altera_jtag_uart_devclass, 0, 0);
diff --git a/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
new file mode 100644
index 000000000000..ad5b8a41bdc5
--- /dev/null
+++ b/sys/dev/altera/jtag_uart/altera_jtag_uart_tty.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 2011-2012 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/cons.h>
+#include <sys/endian.h>
+#include <sys/kdb.h>
+#include <sys/rman.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/tty.h>
+
+#include <ddb/ddb.h>
+
+#include <machine/bus.h>
+
+#include <dev/altera/jtag_uart/altera_jtag_uart.h>
+
+/*
+ * If one of the Altera JTAG UARTs is currently the system console, register
+ * it here.
+ */
+static struct altera_jtag_uart_softc *aju_cons_sc;
+
+static tsw_outwakeup_t aju_outwakeup;
+static void aju_ac_callout(void *);
+static void aju_io_callout(void *);
+
+static struct ttydevsw aju_ttydevsw = {
+ .tsw_flags = TF_NOPREFIX,
+ .tsw_outwakeup = aju_outwakeup,
+};
+
+/*
+ * When polling for the AC bit, the number of times we have to not see it
+ * before assuming JTAG has disappeared on us. By default, one second.
+ */
+#define AJU_JTAG_MAXMISS 5
+
+/*
+ * Polling intervals for input/output and JTAG connection events.
+ */
+#define AJU_IO_POLLINTERVAL (hz/100)
+#define AJU_AC_POLLINTERVAL (hz/5)
+
+/*
+ * Low-level read and write register routines; the Altera UART is little
+ * endian, so we byte swap 32-bit reads and writes.
+ */
+static inline uint32_t
+aju_data_read(struct altera_jtag_uart_softc *sc)
+{
+
+ return (le32toh(bus_read_4(sc->ajus_mem_res,
+ ALTERA_JTAG_UART_DATA_OFF)));
+}
+
+static inline void
+aju_data_write(struct altera_jtag_uart_softc *sc, uint32_t v)
+{
+
+ bus_write_4(sc->ajus_mem_res, ALTERA_JTAG_UART_DATA_OFF, htole32(v));
+}
+
+static inline uint32_t
+aju_control_read(struct altera_jtag_uart_softc *sc)
+{
+
+ return (le32toh(bus_read_4(sc->ajus_mem_res,
+ 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.
+ */
+static inline int
+aju_writable(struct altera_jtag_uart_softc *sc)
+{
+
+ return ((aju_control_read(sc) &
+ ALTERA_JTAG_UART_CONTROL_WSPACE) != 0);
+}
+
+static inline int
+aju_readable(struct altera_jtag_uart_softc *sc)
+{
+ uint32_t v;
+
+ AJU_LOCK_ASSERT(sc);
+
+ if (*sc->ajus_buffer_validp)
+ return (1);
+ v = aju_data_read(sc);
+ if ((v & ALTERA_JTAG_UART_DATA_RVALID) != 0) {
+ *sc->ajus_buffer_validp = 1;
+ *sc->ajus_buffer_datap = (v & ALTERA_JTAG_UART_DATA_DATA);
+ return (1);
+ }
+ return (0);
+}
+
+static char
+aju_read(struct altera_jtag_uart_softc *sc)
+{
+
+ AJU_LOCK_ASSERT(sc);
+
+ while (!aju_readable(sc));
+ *sc->ajus_buffer_validp = 0;
+ return (*sc->ajus_buffer_datap);
+}
+
+/*
+ * Routines for enabling and disabling interrupts for read and write.
+ */
+static void
+aju_intr_readable_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;
+ aju_control_write(sc, v);
+}
+
+static void
+aju_intr_writable_enable(struct altera_jtag_uart_softc *sc)
+{
+ uint32_t v;
+
+ AJU_LOCK_ASSERT(sc);
+
+ v = aju_control_read(sc);
+ v |= 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);
+
+ v = aju_control_read(sc);
+ 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);
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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);
+}
+
+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);
+}
+
+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_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);
+}
+
+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);
+}
+
+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);
+}