Add altera_jtag_uart(4), a device driver for Altera's JTAG UART soft core,

which presents a UART-like interface over the Avalon bus that can be
addressed over JTAG.  This IP core proves extremely useful, allowing us to
connect trivially to the FreeBSD console over JTAG for FPGA-embedded hard
and soft cores.  As interrupts are optionally configured for this soft
core, we support both interrupt-driven and polled modes of operation,
which must be selected using device.hints.  UART instances appear in /dev
as ttyu0, ttyu1, etc.

However, it also contains a number of quirks, which make it difficult to
tell when JTAG is connected, and some buffering issues.  We work around
these as best we can, using various heuristics.

While the majority of this device driver is not only not BERI-specific,
but also not MIPS-specific, for now add its defines in the BERI files
list, as the console-level parts are aware of where the first JTAG UART
is mapped on Avalon, and contain MIPS-specific address translation, to
use before Newbus and device.hints are available.

Sponsored by:	DARPA, AFRL
This commit is contained in:
Robert Watson 2012-08-25 11:30:36 +00:00
parent d432e92a84
commit 697a77c1c4
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=239676
7 changed files with 1257 additions and 0 deletions

View File

@ -33,6 +33,7 @@ MAN= aac.4 \
ale.4 \
alpm.4 \
altera_avgen.4 \
altera_jtag_uart.4 \
altera_sdcard.4 \
altq.4 \
amdpm.4 \

View File

@ -0,0 +1,121 @@
.\"-
.\" 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.
.\"
.\" $FreeBSD$
.\"
.Dd August 18, 2012
.Dt ALTERA_JTAG_UART 4
.Os
.Sh NAME
.Nm altera_jtag_uart
.Nd driver for the Altera JTAG UART Core
.Sh SYNOPSIS
.Cd "device altera_jtag_uart"
.Pp
In
.Pa /boot/device.hints :
.Cd hint.altera_jtag_uart.0.at="nexus0"
.Cd hint.altera_jtag_uart.0.maddr=0x7f000000
.Cd hint.altera_jtag_uart.0.msize=0x40
.Cd hint.altera_jtag_uart.0.irq=0
.Cd hint.altera_jtag_uart.1.at="nexus0"
.Cd hint.altera_jtag_uart.1.maddr=0x7f001000
.Cd hint.altera_jtag_uart.1.msize=0x40
.Sh DESCRIPTION
The
.Nm
device driver provides support for the Altera JTAG UART core, which allows
multiple UART-like streams to be carried over JTAG.
.Nm
allows JTAG UART streams to be attached to both the low-level console
interface, used for direct kernel input and output, and the
.Xr tty 4
layer, to be used with
.Xr ttys 5
and
.Xr login 1 .
Sequential Altera JTAG UART devices will appear as
.Li ttyu0 ,
.Li ttyu1 ,
etc.
.Sh HARDWARE
Altera JTAG UART devices can be connected to using Altera's
.Pa nios2-terminal
program, with the instance selected using the
.Li --instance
argument on the management host.
.Nm
supports JTAG UART cores with or without interrupt lines connected; if the
.Li irq
portion of the
.Pa device.hints
entry is omitted, the driver will poll rather than configure interrupts.
.Sh SEE ALSO
.Xr login 1 ,
.Xr tty 4 ,
.Xr ttys 5
.Rs
.%T Altera Embedded Peripherals IP User Guide
.%D June 2011
.%I Altera Corporation
.%U http://www.altera.com/literature/ug/ug_embedded_ip.pdf
.Re
.Sh HISTORY
The
.Nm
device driver first appeared in
.Fx 10.0 .
.Sh AUTHORS
The
.Nm
device driver and this manual page were
developed by SRI International and the University of Cambridge Computer
Laboratory under DARPA/AFRL contract
.Pq FA8750-10-C-0237
.Pq Do CTSRD Dc ,
as part of the DARPA CRASH research programme.
This device driver was written by
.An Robert N. M. Watson .
.Sh BUGS
.Nm
must dynamically poll to detect when JTAG is present, in order to disable flow
control in the event that there is no receiving endpoint.
Otherwise, the boot may hang waiting for the JTAG client to be attached, and
user processes attached to JTAG UART devices might block indefinitely.
However, there is no way to flush the output buffer once JTAG is detected to
have disappeared; this means that a small amount of stale output data will
remain in the output buffer, being displayed by
.Li nios2-terminal
when it is connected.
Loss of JTAG will not generate a hang-up event, as that is rarely the desired
behaviour.
.Pp
.Li nios2-terminal
does not place the client-side TTY in raw mode, and so by default will not
pass all control characters through to the UART.

View File

@ -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_ */

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -1,4 +1,7 @@
# $FreeBSD$
dev/altera/jtag_uart/altera_jtag_uart_cons.c optional altera_jtag_uart
dev/altera/jtag_uart/altera_jtag_uart_tty.c optional altera_jtag_uart
dev/altera/jtag_uart/altera_jtag_uart_nexus.c optional altera_jtag_uart
mips/beri/beri_machdep.c standard
mips/mips/intr_machdep.c standard
mips/mips/tick.c standard