diff options
author | wkoszek <wkoszek@FreeBSD.org> | 2013-04-27 22:38:29 +0000 |
---|---|---|
committer | wkoszek <wkoszek@FreeBSD.org> | 2013-04-27 22:38:29 +0000 |
commit | 1497a98f71419ff66d08ad2b8c90530e65521ac2 (patch) | |
tree | bd70a5f310bc059b61bc7c71f1f51e07a04e58ae /sys/arm/xilinx | |
parent | 84376c209e05ed70bdeb154f77a6a6d030d7a66d (diff) | |
download | FreeBSD-src-1497a98f71419ff66d08ad2b8c90530e65521ac2.zip FreeBSD-src-1497a98f71419ff66d08ad2b8c90530e65521ac2.tar.gz |
Add Xilinx Zynq ARM/FPGA SoC support to FreeBSD/arm port.
Submitted by: Thomas Skibo <ThomasSkibo (at) sbcglobal.net>
Reviewed by: wkoszek, freebsd-arm@ (no objections raised)
Diffstat (limited to 'sys/arm/xilinx')
-rw-r--r-- | sys/arm/xilinx/files.zynq7 | 31 | ||||
-rw-r--r-- | sys/arm/xilinx/std.zynq7 | 23 | ||||
-rw-r--r-- | sys/arm/xilinx/uart_dev_cdnc.c | 684 | ||||
-rw-r--r-- | sys/arm/xilinx/zedboard/files.zedboard | 9 | ||||
-rw-r--r-- | sys/arm/xilinx/zedboard/std.zedboard | 8 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_bus_space.c | 113 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_devcfg.c | 650 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_ehci.c | 360 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_gpio.c | 384 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_l2cache.c | 56 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_machdep.c | 161 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_reg.h | 78 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_slcr.c | 301 | ||||
-rw-r--r-- | sys/arm/xilinx/zy7_slcr.h | 280 |
14 files changed, 3138 insertions, 0 deletions
diff --git a/sys/arm/xilinx/files.zynq7 b/sys/arm/xilinx/files.zynq7 new file mode 100644 index 0000000..1be012f --- /dev/null +++ b/sys/arm/xilinx/files.zynq7 @@ -0,0 +1,31 @@ +# +# files.zynq7 +# +# $FreeBSD$ + +kern/kern_clocksource.c standard + +arm/arm/bus_space_generic.c standard +arm/arm/bus_space_asm_generic.S standard +arm/arm/cpufunc_asm_armv5.S standard +arm/arm/cpufunc_asm_arm10.S standard +arm/arm/cpufunc_asm_arm11.S standard +arm/arm/cpufunc_asm_armv7.S standard +arm/arm/irq_dispatch.S standard + +arm/arm/gic.c standard +arm/arm/mpcore_timer.c standard +arm/arm/pl310.c standard + +arm/xilinx/zy7_machdep.c standard +arm/xilinx/zy7_l2cache.c standard +arm/xilinx/zy7_bus_space.c standard +arm/xilinx/zy7_slcr.c standard +arm/xilinx/zy7_devcfg.c standard + +dev/cadence/if_cgem.c optional if_cgem +dev/sdhci/sdhci_fdt.c optional sdhci +arm/xilinx/zy7_ehci.c optional ehci +arm/xilinx/uart_dev_cdnc.c optional uart +arm/xilinx/zy7_gpio.c optional gpio + diff --git a/sys/arm/xilinx/std.zynq7 b/sys/arm/xilinx/std.zynq7 new file mode 100644 index 0000000..76a21e2 --- /dev/null +++ b/sys/arm/xilinx/std.zynq7 @@ -0,0 +1,23 @@ +# +# std.zynq7 - Generic configuration for Xilinx Zynq-7000 PS. +# +# $FreeBSD$ + +cpu CPU_CORTEXA +machine arm armv6 + +files "../xilinx/files.zynq7" + +# Physical memory starts at 0x00000000. We assume images are loaded at +# 0x00100000, e.g. from u-boot with 'fatload mmc 0 0x100000 kernel.bin' +# +# +options PHYSADDR=0x00000000 +options KERNPHYSADDR=0x00100000 +makeoptions KERNPHYSADDR=0x00100000 +options KERNVIRTADDR=0xc0100000 # Used in ldscript.arm +makeoptions KERNVIRTADDR=0xc0100000 + +options STARTUP_PAGETABLE_ADDR=0x000f0000 +options ARM_L2_PIPT + diff --git a/sys/arm/xilinx/uart_dev_cdnc.c b/sys/arm/xilinx/uart_dev_cdnc.c new file mode 100644 index 0000000..a008853 --- /dev/null +++ b/sys/arm/xilinx/uart_dev_cdnc.c @@ -0,0 +1,684 @@ +/*- + * Copyright (c) 2005 M. Warner Losh + * Copyright (c) 2005 Olivier Houchard + * Copyright (c) 2012 Thomas Skibo + * All rights reserved. + * + * 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 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 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. + */ + +/* A driver for the Cadence AMBA UART as used by the Xilinx Zynq-7000. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. UART is covered in Ch. 19 + * and register definitions are in appendix B.33. + */ + + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include "opt_global.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/cons.h> +#include <sys/tty.h> +#include <machine/bus.h> + +#include <dev/uart/uart.h> +#include <dev/uart/uart_cpu.h> +#include <dev/uart/uart_bus.h> + +#include "uart_if.h" + +#define UART_FIFO_SIZE 64 + +#define RD4(bas, reg) \ + bus_space_read_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg))) +#define WR4(bas, reg, value) \ + bus_space_write_4((bas)->bst, (bas)->bsh, uart_regofs((bas), (reg)), \ + (value)) + +/* Register definitions for Cadence UART Controller. + */ +#define CDNC_UART_CTRL_REG 0x00 /* Control Register. */ +#define CDNC_UART_CTRL_REG_STOPBRK (1<<8) +#define CDNC_UART_CTRL_REG_STARTBRK (1<<7) +#define CDNC_UART_CTRL_REG_TORST (1<<6) +#define CDNC_UART_CTRL_REG_TX_DIS (1<<5) +#define CDNC_UART_CTRL_REG_TX_EN (1<<4) +#define CDNC_UART_CTRL_REG_RX_DIS (1<<3) +#define CDNC_UART_CTRL_REG_RX_EN (1<<2) +#define CDNC_UART_CTRL_REG_TXRST (1<<1) +#define CDNC_UART_CTRL_REG_RXRST (1<<0) + +#define CDNC_UART_MODE_REG 0x04 /* Mode Register. */ +#define CDNC_UART_MODE_REG_CHMOD_R_LOOP (3<<8) /* [9:8] - channel mode */ +#define CDNC_UART_MODE_REG_CHMOD_L_LOOP (2<<8) +#define CDNC_UART_MODE_REG_CHMOD_AUTECHO (1<<8) +#define CDNC_UART_MODE_REG_STOP2 (2<<6) /* [7:6] - stop bits */ +#define CDNC_UART_MODE_REG_PAR_NONE (4<<3) /* [5:3] - parity type */ +#define CDNC_UART_MODE_REG_PAR_MARK (3<<3) +#define CDNC_UART_MODE_REG_PAR_SPACE (2<<3) +#define CDNC_UART_MODE_REG_PAR_ODD (1<<3) +#define CDNC_UART_MODE_REG_PAR_EVEN (0<<3) +#define CDNC_UART_MODE_REG_6BIT (3<<1) /* [2:1] - character len */ +#define CDNC_UART_MODE_REG_7BIT (2<<1) +#define CDNC_UART_MODE_REG_8BIT (0<<1) +#define CDNC_UART_MODE_REG_CLKSEL (1<<0) + +#define CDNC_UART_IEN_REG 0x08 /* Interrupt registers. */ +#define CDNC_UART_IDIS_REG 0x0C +#define CDNC_UART_IMASK_REG 0x10 +#define CDNC_UART_ISTAT_REG 0x14 +#define CDNC_UART_INT_TXOVR (1<<12) +#define CDNC_UART_INT_TXNRLYFUL (1<<11) /* tx "nearly" full */ +#define CDNC_UART_INT_TXTRIG (1<<10) +#define CDNC_UART_INT_DMSI (1<<9) /* delta modem status */ +#define CDNC_UART_INT_RXTMOUT (1<<8) +#define CDNC_UART_INT_PARITY (1<<7) +#define CDNC_UART_INT_FRAMING (1<<6) +#define CDNC_UART_INT_RXOVR (1<<5) +#define CDNC_UART_INT_TXFULL (1<<4) +#define CDNC_UART_INT_TXEMPTY (1<<3) +#define CDNC_UART_INT_RXFULL (1<<2) +#define CDNC_UART_INT_RXEMPTY (1<<1) +#define CDNC_UART_INT_RXTRIG (1<<0) +#define CDNC_UART_INT_ALL 0x1FFF + +#define CDNC_UART_BAUDGEN_REG 0x18 +#define CDNC_UART_RX_TIMEO_REG 0x1C +#define CDNC_UART_RX_WATER_REG 0x20 + +#define CDNC_UART_MODEM_CTRL_REG 0x24 +#define CDNC_UART_MODEM_CTRL_REG_FCM (1<<5) /* automatic flow control */ +#define CDNC_UART_MODEM_CTRL_REG_RTS (1<<1) +#define CDNC_UART_MODEM_CTRL_REG_DTR (1<<0) + +#define CDNC_UART_MODEM_STAT_REG 0x28 +#define CDNC_UART_MODEM_STAT_REG_FCMS (1<<8) /* flow control mode (rw) */ +#define CDNC_UART_MODEM_STAT_REG_DCD (1<<7) +#define CDNC_UART_MODEM_STAT_REG_RI (1<<6) +#define CDNC_UART_MODEM_STAT_REG_DSR (1<<5) +#define CDNC_UART_MODEM_STAT_REG_CTS (1<<4) +#define CDNC_UART_MODEM_STAT_REG_DDCD (1<<3) /* change in DCD (w1tc) */ +#define CDNC_UART_MODEM_STAT_REG_TERI (1<<2) /* trail edge ring (w1tc) */ +#define CDNC_UART_MODEM_STAT_REG_DDSR (1<<1) /* change in DSR (w1tc) */ +#define CDNC_UART_MODEM_STAT_REG_DCTS (1<<0) /* change in CTS (w1tc) */ + +#define CDNC_UART_CHAN_STAT_REG 0x2C /* Channel status register. */ +#define CDNC_UART_CHAN_STAT_REG_TXNRLYFUL (1<<14) /* tx "nearly" full */ +#define CDNC_UART_CHAN_STAT_REG_TXTRIG (1<<13) +#define CDNC_UART_CHAN_STAT_REG_FDELT (1<<12) +#define CDNC_UART_CHAN_STAT_REG_TXACTIVE (1<<11) +#define CDNC_UART_CHAN_STAT_REG_RXACTIVE (1<<10) +#define CDNC_UART_CHAN_STAT_REG_TXFULL (1<<4) +#define CDNC_UART_CHAN_STAT_REG_TXEMPTY (1<<3) +#define CDNC_UART_CHAN_STAT_REG_RXEMPTY (1<<1) +#define CDNC_UART_CHAN_STAT_REG_RXTRIG (1<<0) + +#define CDNC_UART_FIFO 0x30 /* Data FIFO (tx and rx) */ +#define CDNC_UART_BAUDDIV_REG 0x34 +#define CDNC_UART_FLOWDEL_REG 0x38 +#define CDNC_UART_TX_WATER_REG 0x44 + + +/* + * Low-level UART interface. + */ +static int cdnc_uart_probe(struct uart_bas *bas); +static void cdnc_uart_init(struct uart_bas *bas, int, int, int, int); +static void cdnc_uart_term(struct uart_bas *bas); +static void cdnc_uart_putc(struct uart_bas *bas, int); +static int cdnc_uart_rxready(struct uart_bas *bas); +static int cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx); + +extern SLIST_HEAD(uart_devinfo_list, uart_devinfo) uart_sysdevs; + +static struct uart_ops cdnc_uart_ops = { + .probe = cdnc_uart_probe, + .init = cdnc_uart_init, + .term = cdnc_uart_term, + .putc = cdnc_uart_putc, + .rxready = cdnc_uart_rxready, + .getc = cdnc_uart_getc, +}; + +#define SIGCHG(c, i, s, d) \ + if (c) { \ + i |= (i & s) ? s : s | d; \ + } else { \ + i = (i & s) ? (i & ~s) | d : i; \ + } + +static int +cdnc_uart_probe(struct uart_bas *bas) +{ + + return (0); +} + +static int +cdnc_uart_set_baud(struct uart_bas *bas, int baudrate) +{ + uint32_t baudgen, bauddiv; + uint32_t best_bauddiv, best_baudgen, best_error; + uint32_t baud_out, err; + + best_bauddiv = 0; + best_baudgen = 0; + best_error = ~0; + + /* Try all possible bauddiv values and pick best match. */ + for (bauddiv = 4; bauddiv <= 255; bauddiv++) { + baudgen = (bas->rclk + (baudrate * (bauddiv + 1)) / 2) / + (baudrate * (bauddiv + 1)); + if (baudgen < 1 || baudgen > 0xffff) + continue; + + baud_out = bas->rclk / (baudgen * (bauddiv + 1)); + err = baud_out > baudrate ? + baud_out - baudrate : baudrate - baud_out; + + if (err < best_error) { + best_error = err; + best_bauddiv = bauddiv; + best_baudgen = baudgen; + } + } + + if (best_bauddiv > 0) { + WR4(bas, CDNC_UART_BAUDDIV_REG, best_bauddiv); + WR4(bas, CDNC_UART_BAUDGEN_REG, best_baudgen); + return (0); + } else + return (-1); /* out of range */ +} + +static int +cdnc_uart_set_params(struct uart_bas *bas, int baudrate, int databits, + int stopbits, int parity) +{ + uint32_t mode_reg_value = 0; + + switch (databits) { + case 6: + mode_reg_value |= CDNC_UART_MODE_REG_6BIT; + break; + case 7: + mode_reg_value |= CDNC_UART_MODE_REG_7BIT; + break; + case 8: + default: + mode_reg_value |= CDNC_UART_MODE_REG_8BIT; + break; + } + + if (stopbits == 2) + mode_reg_value |= CDNC_UART_MODE_REG_STOP2; + + switch (parity) { + case UART_PARITY_MARK: + mode_reg_value |= CDNC_UART_MODE_REG_PAR_MARK; + break; + case UART_PARITY_SPACE: + mode_reg_value |= CDNC_UART_MODE_REG_PAR_SPACE; + break; + case UART_PARITY_ODD: + mode_reg_value |= CDNC_UART_MODE_REG_PAR_ODD; + break; + case UART_PARITY_EVEN: + mode_reg_value |= CDNC_UART_MODE_REG_PAR_EVEN; + break; + case UART_PARITY_NONE: + default: + mode_reg_value |= CDNC_UART_MODE_REG_PAR_NONE; + break; + } + + WR4(bas, CDNC_UART_MODE_REG, mode_reg_value); + + if (baudrate > 0 && cdnc_uart_set_baud(bas, baudrate) < 0) + return (EINVAL); + + return(0); +} + +static void +cdnc_uart_hw_init(struct uart_bas *bas) +{ + + /* Reset RX and TX. */ + WR4(bas, CDNC_UART_CTRL_REG, + CDNC_UART_CTRL_REG_RXRST | CDNC_UART_CTRL_REG_TXRST); + + /* Interrupts all off. */ + WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_ALL); + WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_ALL); + + /* Clear delta bits. */ + WR4(bas, CDNC_UART_MODEM_STAT_REG, + CDNC_UART_MODEM_STAT_REG_DDCD | CDNC_UART_MODEM_STAT_REG_TERI | + CDNC_UART_MODEM_STAT_REG_DDSR | CDNC_UART_MODEM_STAT_REG_DCTS); + + /* RX FIFO water level, stale timeout */ + WR4(bas, CDNC_UART_RX_WATER_REG, UART_FIFO_SIZE/2); + WR4(bas, CDNC_UART_RX_TIMEO_REG, 10); + + /* TX FIFO water level (not used.) */ + WR4(bas, CDNC_UART_TX_WATER_REG, UART_FIFO_SIZE/2); + + /* Bring RX and TX online. */ + WR4(bas, CDNC_UART_CTRL_REG, + CDNC_UART_CTRL_REG_RX_EN | CDNC_UART_CTRL_REG_TX_EN | + CDNC_UART_CTRL_REG_TORST | CDNC_UART_CTRL_REG_STOPBRK); + + /* Set DTR and RTS. */ + WR4(bas, CDNC_UART_MODEM_CTRL_REG, CDNC_UART_MODEM_CTRL_REG_DTR | + CDNC_UART_MODEM_CTRL_REG_RTS); +} + +/* + * Initialize this device for use as a console. + */ +static void +cdnc_uart_init(struct uart_bas *bas, int baudrate, int databits, int stopbits, + int parity) +{ + + /* Initialize hardware. */ + cdnc_uart_hw_init(bas); + + /* Set baudrate, parameters. */ + (void)cdnc_uart_set_params(bas, baudrate, databits, stopbits, parity); +} + +/* + * Free resources now that we're no longer the console. This appears to + * be never called, and I'm unsure quite what to do if I am called. + */ +static void +cdnc_uart_term(struct uart_bas *bas) +{ + + /* XXX */ +} + +/* + * Put a character of console output (so we do it here polling rather than + * interrutp driven). + */ +static void +cdnc_uart_putc(struct uart_bas *bas, int c) +{ + + /* Wait for room. */ + while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & + CDNC_UART_CHAN_STAT_REG_TXFULL) != 0) + ; + + WR4(bas, CDNC_UART_FIFO, c); + + while ((RD4(bas,CDNC_UART_CHAN_STAT_REG) & + CDNC_UART_CHAN_STAT_REG_TXEMPTY) == 0) + ; +} + +/* + * Check for a character available. + */ +static int +cdnc_uart_rxready(struct uart_bas *bas) +{ + + return ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & + CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0); +} + +/* + * Block waiting for a character. + */ +static int +cdnc_uart_getc(struct uart_bas *bas, struct mtx *mtx) +{ + int c; + + uart_lock(mtx); + + while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & + CDNC_UART_CHAN_STAT_REG_RXEMPTY) != 0) { + uart_unlock(mtx); + DELAY(4); + uart_lock(mtx); + } + + c = RD4(bas, CDNC_UART_FIFO); + + uart_unlock(mtx); + + c &= 0xff; + return (c); +} + +/*****************************************************************************/ +/* + * High-level UART interface. + */ + +static int cdnc_uart_bus_probe(struct uart_softc *sc); +static int cdnc_uart_bus_attach(struct uart_softc *sc); +static int cdnc_uart_bus_flush(struct uart_softc *, int); +static int cdnc_uart_bus_getsig(struct uart_softc *); +static int cdnc_uart_bus_ioctl(struct uart_softc *, int, intptr_t); +static int cdnc_uart_bus_ipend(struct uart_softc *); +static int cdnc_uart_bus_param(struct uart_softc *, int, int, int, int); +static int cdnc_uart_bus_receive(struct uart_softc *); +static int cdnc_uart_bus_setsig(struct uart_softc *, int); +static int cdnc_uart_bus_transmit(struct uart_softc *); + +static kobj_method_t cdnc_uart_bus_methods[] = { + KOBJMETHOD(uart_probe, cdnc_uart_bus_probe), + KOBJMETHOD(uart_attach, cdnc_uart_bus_attach), + KOBJMETHOD(uart_flush, cdnc_uart_bus_flush), + KOBJMETHOD(uart_getsig, cdnc_uart_bus_getsig), + KOBJMETHOD(uart_ioctl, cdnc_uart_bus_ioctl), + KOBJMETHOD(uart_ipend, cdnc_uart_bus_ipend), + KOBJMETHOD(uart_param, cdnc_uart_bus_param), + KOBJMETHOD(uart_receive, cdnc_uart_bus_receive), + KOBJMETHOD(uart_setsig, cdnc_uart_bus_setsig), + KOBJMETHOD(uart_transmit, cdnc_uart_bus_transmit), + + KOBJMETHOD_END +}; + +int +cdnc_uart_bus_probe(struct uart_softc *sc) +{ + + sc->sc_txfifosz = UART_FIFO_SIZE; + sc->sc_rxfifosz = UART_FIFO_SIZE; + sc->sc_hwiflow = 0; + sc->sc_hwoflow = 0; + + device_set_desc(sc->sc_dev, "Cadence UART"); + + return (0); +} + +static int +cdnc_uart_bus_attach(struct uart_softc *sc) +{ + struct uart_bas *bas = &sc->sc_bas; + struct uart_devinfo *di; + + if (sc->sc_sysdev != NULL) { + di = sc->sc_sysdev; + (void)cdnc_uart_set_params(bas, di->baudrate, di->databits, + di->stopbits, di->parity); + } else + cdnc_uart_hw_init(bas); + + (void)cdnc_uart_bus_getsig(sc); + + /* Enable interrupts. */ + WR4(bas, CDNC_UART_IEN_REG, + CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | + CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | + CDNC_UART_INT_DMSI); + + return (0); +} + +static int +cdnc_uart_bus_transmit(struct uart_softc *sc) +{ + int i; + struct uart_bas *bas = &sc->sc_bas; + + uart_lock(sc->sc_hwmtx); + + /* Clear sticky TXEMPTY status bit. */ + WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_TXEMPTY); + + for (i = 0; i < sc->sc_txdatasz; i++) + WR4(bas, CDNC_UART_FIFO, sc->sc_txbuf[i]); + + /* Enable TX empty interrupt. */ + WR4(bas, CDNC_UART_IEN_REG, CDNC_UART_INT_TXEMPTY); + sc->sc_txbusy = 1; + + uart_unlock(sc->sc_hwmtx); + + return (0); +} + +static int +cdnc_uart_bus_setsig(struct uart_softc *sc, int sig) +{ + struct uart_bas *bas = &sc->sc_bas; + uint32_t new, old, modem_ctrl; + + do { + old = sc->sc_hwsig; + new = old; + if (sig & SER_DDTR) { + SIGCHG(sig & SER_DTR, new, SER_DTR, SER_DDTR); + } + if (sig & SER_DRTS) { + SIGCHG(sig & SER_RTS, new, SER_RTS, SER_DRTS); + } + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + uart_lock(sc->sc_hwmtx); + modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG) & + ~(CDNC_UART_MODEM_CTRL_REG_DTR | CDNC_UART_MODEM_CTRL_REG_RTS); + if ((new & SER_DTR) != 0) + modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_DTR; + if ((new & SER_RTS) != 0) + modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; + WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); + + uart_unlock(sc->sc_hwmtx); + return (0); +} + +static int +cdnc_uart_bus_receive(struct uart_softc *sc) +{ + struct uart_bas *bas = &sc->sc_bas; + uint32_t status; + int c, c_status = 0; + + uart_lock(sc->sc_hwmtx); + + /* Check for parity or framing errors and clear the status bits. */ + status = RD4(bas, CDNC_UART_ISTAT_REG); + if ((status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)) != 0) { + WR4(bas, CDNC_UART_ISTAT_REG, + status & (CDNC_UART_INT_FRAMING | CDNC_UART_INT_PARITY)); + if ((status & CDNC_UART_INT_PARITY) != 0) + c_status |= UART_STAT_PARERR; + if ((status & CDNC_UART_INT_FRAMING) != 0) + c_status |= UART_STAT_FRAMERR; + } + + while ((RD4(bas, CDNC_UART_CHAN_STAT_REG) & + CDNC_UART_CHAN_STAT_REG_RXEMPTY) == 0) { + c = RD4(bas, CDNC_UART_FIFO) & 0xff; +#ifdef KDB + /* Detect break and drop into debugger. */ + if (c == 0 && (c_status & UART_STAT_FRAMERR) != 0 && + sc->sc_sysdev != NULL && + sc->sc_sysdev->type == UART_DEV_CONSOLE) { + kdb_break(); + WR4(bas, CDNC_UART_ISTAT_REG, CDNC_UART_INT_FRAMING); + } +#endif + uart_rx_put(sc, c | c_status); + } + + uart_unlock(sc->sc_hwmtx); + + return (0); +} + +static int +cdnc_uart_bus_param(struct uart_softc *sc, int baudrate, int databits, + int stopbits, int parity) +{ + + return (cdnc_uart_set_params(&sc->sc_bas, baudrate, + databits, stopbits, parity)); +} + +static int +cdnc_uart_bus_ipend(struct uart_softc *sc) +{ + int ipend = 0; + struct uart_bas *bas = &sc->sc_bas; + uint32_t istatus; + + uart_lock(sc->sc_hwmtx); + + istatus = RD4(bas, CDNC_UART_ISTAT_REG); + + /* Clear interrupt bits. */ + WR4(bas, CDNC_UART_ISTAT_REG, istatus & + (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT | + CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR | + CDNC_UART_INT_TXEMPTY | CDNC_UART_INT_DMSI)); + + /* Receive data. */ + if ((istatus & (CDNC_UART_INT_RXTRIG | CDNC_UART_INT_RXTMOUT)) != 0) + ipend |= SER_INT_RXREADY; + + /* Transmit fifo empty. */ + if (sc->sc_txbusy && (istatus & CDNC_UART_INT_TXEMPTY) != 0) { + /* disable txempty interrupt. */ + WR4(bas, CDNC_UART_IDIS_REG, CDNC_UART_INT_TXEMPTY); + ipend |= SER_INT_TXIDLE; + } + + /* TX Overflow. */ + if ((istatus & CDNC_UART_INT_TXOVR) != 0) + ipend |= SER_INT_OVERRUN; + + /* RX Overflow. */ + if ((istatus & CDNC_UART_INT_RXOVR) != 0) + ipend |= SER_INT_OVERRUN; + + /* Modem signal change. */ + if ((istatus & CDNC_UART_INT_DMSI) != 0) { + WR4(bas, CDNC_UART_MODEM_STAT_REG, + CDNC_UART_MODEM_STAT_REG_DDCD | + CDNC_UART_MODEM_STAT_REG_TERI | + CDNC_UART_MODEM_STAT_REG_DDSR | + CDNC_UART_MODEM_STAT_REG_DCTS); + ipend |= SER_INT_SIGCHG; + } + + uart_unlock(sc->sc_hwmtx); + return (ipend); +} + +static int +cdnc_uart_bus_flush(struct uart_softc *sc, int what) +{ + + return (0); +} + +static int +cdnc_uart_bus_getsig(struct uart_softc *sc) +{ + struct uart_bas *bas = &sc->sc_bas; + uint32_t new, old, sig; + uint8_t modem_status; + + do { + old = sc->sc_hwsig; + sig = old; + uart_lock(sc->sc_hwmtx); + modem_status = RD4(bas, CDNC_UART_MODEM_STAT_REG); + uart_unlock(sc->sc_hwmtx); + SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DSR, + sig, SER_DSR, SER_DDSR); + SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_CTS, + sig, SER_CTS, SER_DCTS); + SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_DCD, + sig, SER_DCD, SER_DDCD); + SIGCHG(modem_status & CDNC_UART_MODEM_STAT_REG_RI, + sig, SER_RI, SER_DRI); + new = sig & ~SER_MASK_DELTA; + } while (!atomic_cmpset_32(&sc->sc_hwsig, old, new)); + return (sig); +} + +static int +cdnc_uart_bus_ioctl(struct uart_softc *sc, int request, intptr_t data) +{ + struct uart_bas *bas = &sc->sc_bas; + uint32_t uart_ctrl, modem_ctrl; + int error = 0; + + uart_lock(sc->sc_hwmtx); + + switch (request) { + case UART_IOCTL_BREAK: + uart_ctrl = RD4(bas, CDNC_UART_CTRL_REG); + if (data) { + uart_ctrl |= CDNC_UART_CTRL_REG_STARTBRK; + uart_ctrl &= ~CDNC_UART_CTRL_REG_STOPBRK; + } else { + uart_ctrl |= CDNC_UART_CTRL_REG_STOPBRK; + uart_ctrl &= ~CDNC_UART_CTRL_REG_STARTBRK; + } + WR4(bas, CDNC_UART_CTRL_REG, uart_ctrl); + break; + case UART_IOCTL_IFLOW: + modem_ctrl = RD4(bas, CDNC_UART_MODEM_CTRL_REG); + if (data) + modem_ctrl |= CDNC_UART_MODEM_CTRL_REG_RTS; + else + modem_ctrl &= ~CDNC_UART_MODEM_CTRL_REG_RTS; + WR4(bas, CDNC_UART_MODEM_CTRL_REG, modem_ctrl); + break; + default: + error = EINVAL; + break; + } + + uart_unlock(sc->sc_hwmtx); + + return (error); +} + +struct uart_class uart_cdnc_class = { + "cdnc_uart", + cdnc_uart_bus_methods, + sizeof(struct uart_softc), + .uc_ops = &cdnc_uart_ops, + .uc_range = 8 +}; diff --git a/sys/arm/xilinx/zedboard/files.zedboard b/sys/arm/xilinx/zedboard/files.zedboard new file mode 100644 index 0000000..8a1af16 --- /dev/null +++ b/sys/arm/xilinx/zedboard/files.zedboard @@ -0,0 +1,9 @@ +# +# files.zedboard +# +# $FreeBSD$ + +# We'll need board specific files once we start implementing drivers +# for Zedboard PL peripherals such as HDMI, VGA, or Audio Codecs. For +# now, nothing is needed. +# diff --git a/sys/arm/xilinx/zedboard/std.zedboard b/sys/arm/xilinx/zedboard/std.zedboard new file mode 100644 index 0000000..86f04f1 --- /dev/null +++ b/sys/arm/xilinx/zedboard/std.zedboard @@ -0,0 +1,8 @@ +# +# std.zedboard +# +# $FreeBSD$ + +include "../xilinx/std.zynq7" +files "../xilinx/zedboard/files.zedboard" + diff --git a/sys/arm/xilinx/zy7_bus_space.c b/sys/arm/xilinx/zy7_bus_space.c new file mode 100644 index 0000000..4cce820 --- /dev/null +++ b/sys/arm/xilinx/zy7_bus_space.c @@ -0,0 +1,113 @@ +/*- + * Copyright (C) 2012 FreeBSD Foundation + * All rights reserved. + * + * 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. + * 3. Neither the name of MARVELL nor the names of contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY 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 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/systm.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/malloc.h> + +#include <machine/bus.h> + +/* Prototypes for all the bus_space structure functions */ +bs_protos(generic); +bs_protos(generic_armv4); + +struct bus_space _base_tag = { + /* cookie */ + .bs_cookie = (void *) 0, + + /* mapping/unmapping */ + .bs_map = generic_bs_map, + .bs_unmap = generic_bs_unmap, + .bs_subregion = generic_bs_subregion, + + /* allocation/deallocation */ + .bs_alloc = generic_bs_alloc, + .bs_free = generic_bs_free, + + /* barrier */ + .bs_barrier = generic_bs_barrier, + + /* read (single) */ + .bs_r_1 = generic_bs_r_1, + .bs_r_2 = generic_armv4_bs_r_2, + .bs_r_4 = generic_bs_r_4, + .bs_r_8 = NULL, + + /* read multiple */ + .bs_rm_1 = generic_bs_rm_1, + .bs_rm_2 = generic_armv4_bs_rm_2, + .bs_rm_4 = generic_bs_rm_4, + .bs_rm_8 = NULL, + + /* read region */ + .bs_rr_1 = generic_bs_rr_1, + .bs_rr_2 = generic_armv4_bs_rr_2, + .bs_rr_4 = generic_bs_rr_4, + .bs_rr_8 = NULL, + + /* write (single) */ + .bs_w_1 = generic_bs_w_1, + .bs_w_2 = generic_armv4_bs_w_2, + .bs_w_4 = generic_bs_w_4, + .bs_w_8 = NULL, + + /* write multiple */ + .bs_wm_1 = generic_bs_wm_1, + .bs_wm_2 = generic_armv4_bs_wm_2, + .bs_wm_4 = generic_bs_wm_4, + .bs_wm_8 = NULL, + + /* write region */ + .bs_wr_1 = generic_bs_wr_1, + .bs_wr_2 = generic_armv4_bs_wr_2, + .bs_wr_4 = generic_bs_wr_4, + .bs_wr_8 = NULL, + + /* set multiple */ + /* XXX not implemented */ + + /* set region */ + .bs_sr_1 = NULL, + .bs_sr_2 = generic_armv4_bs_sr_2, + .bs_sr_4 = generic_bs_sr_4, + .bs_sr_8 = NULL, + + /* copy */ + .bs_c_1 = NULL, + .bs_c_2 = generic_armv4_bs_c_2, + .bs_c_4 = NULL, + .bs_c_8 = NULL, +}; + +bus_space_tag_t fdtbus_bs_tag = &_base_tag; diff --git a/sys/arm/xilinx/zy7_devcfg.c b/sys/arm/xilinx/zy7_devcfg.c new file mode 100644 index 0000000..45b72de9 --- /dev/null +++ b/sys/arm/xilinx/zy7_devcfg.c @@ -0,0 +1,650 @@ +/*- + * Copyright (C) 2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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. + * + */ + +/* Zynq-7000 Devcfg driver. This allows programming the PL (FPGA) section + * of Zynq. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. PL Configuration is + * covered in section 6.4.5. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/uio.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/xilinx/zy7_slcr.h> + +struct zy7_devcfg_softc { + device_t dev; + struct mtx sc_mtx; + struct resource *mem_res; + struct resource *irq_res; + struct cdev *sc_ctl_dev; + void *intrhandle; + + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + + int is_open; +}; + +static struct zy7_devcfg_softc *zy7_devcfg_softc_p; + +#define DEVCFG_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define DEVCFG_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define DEVCFG_SC_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ + "zy7_devcfg", MTX_DEF) +#define DEVCFG_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx); +#define DEVCFG_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED); + +#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) +#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) + +SYSCTL_NODE(_hw, OID_AUTO, fpga, CTLFLAG_RD, 0, \ + "Xilinx Zynq-7000 PL (FPGA) section"); + +static int zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS); +SYSCTL_PROC(_hw_fpga, OID_AUTO, pl_done, CTLTYPE_INT | CTLFLAG_RD, NULL, 0, + zy7_devcfg_sysctl_pl_done, "I", "PL section config DONE signal"); + +static int zy7_en_level_shifters = 1; +SYSCTL_INT(_hw_fpga, OID_AUTO, en_level_shifters, CTLFLAG_RW, + &zy7_en_level_shifters, 0, + "Enable PS-PL level shifters after device config"); + +static int zy7_ps_vers = 0; +SYSCTL_INT(_hw, OID_AUTO, ps_vers, CTLFLAG_RD, &zy7_ps_vers, 0, + "Zynq-7000 PS version"); + + +/* cdev entry points. */ +static int zy7_devcfg_open(struct cdev *, int, int, struct thread *); +static int zy7_devcfg_write(struct cdev *, struct uio *, int); +static int zy7_devcfg_close(struct cdev *, int, int, struct thread *); + + +struct cdevsw zy7_devcfg_cdevsw = { + .d_version = D_VERSION, + .d_open = zy7_devcfg_open, + .d_write = zy7_devcfg_write, + .d_close = zy7_devcfg_close, + .d_name = "devcfg", +}; + +/* Devcfg block registers. */ +#define ZY7_DEVCFG_CTRL 0x0000 +#define ZY7_DEVCFG_CTRL_FORCE_RST (1<<31) +#define ZY7_DEVCFG_CTRL_PCFG_PROG_B (1<<30) +#define ZY7_DEVCFG_CTRL_PCFG_POR_CNT_4K (1<<29) +#define ZY7_DEVCFG_CTRL_PCAP_PR (1<<27) +#define ZY7_DEVCFG_CTRL_PCAP_MODE (1<<26) +#define ZY7_DEVCFG_CTRL_QTR_PCAP_RATE_EN (1<<25) +#define ZY7_DEVCFG_CTRL_MULTIBOOT_EN (1<<24) +#define ZY7_DEVCFG_CTRL_JTAG_CHAIN_DIS (1<<23) +#define ZY7_DEVCFG_CTRL_USER_MODE (1<<15) +#define ZY7_DEVCFG_CTRL_RESVD_WR11 (3<<13) /* always write 11 */ +#define ZY7_DEVCFG_CTRL_PCFG_AES_FUSE (1<<12) +#define ZY7_DEVCFG_CTRL_PCFG_AES_EN_MASK (7<<9) /* all 1's or 0's */ +#define ZY7_DEVCFG_CTRL_SEU_EN (1<<8) +#define ZY7_DEVCFG_CTRL_SEC_EN (1<<7) +#define ZY7_DEVCFG_CTRL_SPNIDEN (1<<6) +#define ZY7_DEVCFG_CTRL_SPIDEN (1<<5) +#define ZY7_DEVCFG_CTRL_NIDEN (1<<4) +#define ZY7_DEVCFG_CTRL_DBGEN (1<<3) +#define ZY7_DEVCFG_CTRL_DAP_EN_MASK (7<<0) /* all 1's to enable */ + +#define ZY7_DEVCFG_LOCK 0x004 +#define ZY7_DEVCFG_LOCK_AES_FUSE_LOCK (1<<4) +#define ZY7_DEVCFG_LOCK_AES_EN (1<<3) +#define ZY7_DEVCFG_LOCK_SEU_LOCK (1<<2) +#define ZY7_DEVCFG_LOCK_SEC_LOCK (1<<1) +#define ZY7_DEVCFG_LOCK_DBG_LOCK (1<<0) + +#define ZY7_DEVCFG_CFG 0x008 +#define ZY7_DEVCFG_CFG_RFIFO_TH_MASK (3<<10) +#define ZY7_DEVCFG_CFG_WFIFO_TH_MASK (3<<8) +#define ZY7_DEVCFG_CFG_RCLK_EDGE (1<<7) +#define ZY7_DEVCFG_CFG_WCLK_EDGE (1<<6) +#define ZY7_DEVCFG_CFG_DIS_SRC_INC (1<<5) +#define ZY7_DEVCFG_CFG_DIS_DST_INC (1<<4) + +#define ZY7_DEVCFG_INT_STATUS 0x00C +#define ZY7_DEVCFG_INT_MASK 0x010 +#define ZY7_DEVCFG_INT_PSS_GTS_USR_B (1<<31) +#define ZY7_DEVCFG_INT_PSS_FST_CFG_B (1<<30) +#define ZY7_DEVCFG_INT_PSS_GPWRDWN_B (1<<29) +#define ZY7_DEVCFG_INT_PSS_GTS_CFG_B (1<<28) +#define ZY7_DEVCFG_INT_CFG_RESET_B (1<<27) +#define ZY7_DEVCFG_INT_AXI_WTO (1<<23) /* axi write timeout */ +#define ZY7_DEVCFG_INT_AXI_WERR (1<<22) /* axi write err */ +#define ZY7_DEVCFG_INT_AXI_RTO (1<<21) /* axi read timeout */ +#define ZY7_DEVCFG_INT_AXI_RERR (1<<20) /* axi read err */ +#define ZY7_DEVCFG_INT_RX_FIFO_OV (1<<18) /* rx fifo overflow */ +#define ZY7_DEVCFG_INT_WR_FIFO_LVL (1<<17) /* wr fifo < level */ +#define ZY7_DEVCFG_INT_RD_FIFO_LVL (1<<16) /* rd fifo >= level */ +#define ZY7_DEVCFG_INT_DMA_CMD_ERR (1<<15) +#define ZY7_DEVCFG_INT_DMA_Q_OV (1<<14) +#define ZY7_DEVCFG_INT_DMA_DONE (1<<13) +#define ZY7_DEVCFG_INT_DMA_PCAP_DONE (1<<12) +#define ZY7_DEVCFG_INT_P2D_LEN_ERR (1<<11) +#define ZY7_DEVCFG_INT_PCFG_HMAC_ERR (1<<6) +#define ZY7_DEVCFG_INT_PCFG_SEU_ERR (1<<5) +#define ZY7_DEVCFG_INT_PCFG_POR_B (1<<4) +#define ZY7_DEVCFG_INT_PCFG_CFG_RST (1<<3) +#define ZY7_DEVCFG_INT_PCFG_DONE (1<<2) +#define ZY7_DEVCFG_INT_PCFG_INIT_PE (1<<1) +#define ZY7_DEVCFG_INT_PCFG_INIT_NE (1<<0) +#define ZY7_DEVCFG_INT_ERRORS 0x00f0f860 +#define ZY7_DEVCFG_INT_ALL 0xf8f7f87f + +#define ZY7_DEVCFG_STATUS 0x014 +#define ZY7_DEVCFG_STATUS_DMA_CMD_Q_F (1<<31) /* cmd queue full */ +#define ZY7_DEVCFG_STATUS_DMA_CMD_Q_E (1<<30) /* cmd queue empty */ +#define ZY7_DEVCFG_STATUS_DONE_COUNT_MASK (3<<28) +#define ZY7_DEVCFG_STATUS_DONE_COUNT_SHIFT 28 +#define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_MASK (0x1f<<20) +#define ZY7_DEVCFG_STATUS_RX_FIFO_LVL_SHIFT 20 +#define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_MASK (0x7f<<12) +#define ZY7_DEVCFG_STATUS_TX_FIFO_LVL_SHIFT 12 +#define ZY7_DEVCFG_STATUS_PSS_GTS_USR_B (1<<11) +#define ZY7_DEVCFG_STATUS_PSS_FST_CFG_B (1<<10) +#define ZY7_DEVCFG_STATUS_PSS_GPWRDWN_B (1<<9) +#define ZY7_DEVCFG_STATUS_PSS_GTS_CFG_B (1<<8) +#define ZY7_DEVCFG_STATUS_ILL_APB_ACCE (1<<6) +#define ZY7_DEVCFG_STATUS_PSS_CFG_RESET_B (1<<5) +#define ZY7_DEVCFG_STATUS_PCFG_INIT (1<<4) +#define ZY7_DEVCFG_STATUS_EFUSE_BBRAM_KEY_DIS (1<<3) +#define ZY7_DEVCFG_STATUS_EFUSE_SEC_EN (1<<2) +#define ZY7_DEVCFG_STATUS_EFUSE_JTAG_DIS (1<<1) + +#define ZY7_DEVCFG_DMA_SRC_ADDR 0x018 +#define ZY7_DEVCFG_DMA_DST_ADDR 0x01c +#define ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP 1 +#define ZY7_DEVCFG_DMA_ADDR_ILLEGAL 0xffffffff + +#define ZY7_DEVCFG_DMA_SRC_LEN 0x020 /* in 4-byte words. */ +#define ZY7_DEVCFG_DMA_SRC_LEN_MAX 0x7ffffff +#define ZY7_DEVCFG_DMA_DST_LEN 0x024 +#define ZY7_DEVCFG_ROM_SHADOW 0x028 +#define ZY7_DEVCFG_MULTIBOOT_ADDR 0x02c +#define ZY7_DEVCFG_SW_ID 0x030 +#define ZY7_DEVCFG_UNLOCK 0x034 +#define ZY7_DEVCFG_UNLOCK_MAGIC 0x757bdf0d +#define ZY7_DEVCFG_MCTRL 0x080 +#define ZY7_DEVCFG_MCTRL_PS_VERS_MASK (0xf<<28) +#define ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT 28 +#define ZY7_DEVCFG_MCTRL_PCFG_POR_B (1<<8) +#define ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK (1<<4) +#define ZY7_DEVCFG_XADCIF_CFG 0x100 +#define ZY7_DEVCFG_XADCIF_INT_STAT 0x104 +#define ZY7_DEVCFG_XADCIF_INT_MASK 0x108 +#define ZY7_DEVCFG_XADCIF_MSTS 0x10c +#define ZY7_DEVCFG_XADCIF_CMD_FIFO 0x110 +#define ZY7_DEVCFG_XADCIF_RD_FIFO 0x114 +#define ZY7_DEVCFG_XADCIF_MCTL 0x118 + + +/* Enable programming the PL through PCAP. */ +static void +zy7_devcfg_init_hw(struct zy7_devcfg_softc *sc) +{ + + DEVCFG_SC_ASSERT_LOCKED(sc); + + /* Set devcfg control register. */ + WR4(sc, ZY7_DEVCFG_CTRL, + ZY7_DEVCFG_CTRL_PCFG_PROG_B | + ZY7_DEVCFG_CTRL_PCAP_PR | + ZY7_DEVCFG_CTRL_PCAP_MODE | + ZY7_DEVCFG_CTRL_USER_MODE | + ZY7_DEVCFG_CTRL_RESVD_WR11 | + ZY7_DEVCFG_CTRL_SPNIDEN | + ZY7_DEVCFG_CTRL_SPIDEN | + ZY7_DEVCFG_CTRL_NIDEN | + ZY7_DEVCFG_CTRL_DBGEN | + ZY7_DEVCFG_CTRL_DAP_EN_MASK); + + /* Turn off internal PCAP loopback. */ + WR4(sc, ZY7_DEVCFG_MCTRL, RD4(sc, ZY7_DEVCFG_MCTRL) & + ~ZY7_DEVCFG_MCTRL_INT_PCAP_LPBK); +} + +/* Clear previous configuration of the PL by asserting PROG_B. */ +static int +zy7_devcfg_reset_pl(struct zy7_devcfg_softc *sc) +{ + uint32_t devcfg_ctl; + int tries, err; + + DEVCFG_SC_ASSERT_LOCKED(sc); + + devcfg_ctl = RD4(sc, ZY7_DEVCFG_CTRL); + + /* Deassert PROG_B (active low). */ + devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B; + WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); + + /* Wait for INIT_B deasserted (active low). */ + tries = 0; + while ((RD4(sc, ZY7_DEVCFG_STATUS) & + ZY7_DEVCFG_STATUS_PCFG_INIT) == 0) { + if (++tries >= 100) + return (EIO); + DELAY(5); + } + + /* Reassert PROG_B. */ + devcfg_ctl &= ~ZY7_DEVCFG_CTRL_PCFG_PROG_B; + WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); + + /* Wait for INIT_B asserted. */ + tries = 0; + while ((RD4(sc, ZY7_DEVCFG_STATUS) & + ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) { + if (++tries >= 100) + return (EIO); + DELAY(5); + } + + /* Clear sticky bits and set up INIT_B positive edge interrupt. */ + WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); + WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_PCFG_INIT_PE); + + /* Deassert PROG_B again. */ + devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B; + WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl); + + /* Wait for INIT_B deasserted indicating FPGA internal initialization + * is complete. This takes much longer than the previous waits for + * INIT_B transition (on the order of 700us). + */ + err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7in", hz); + if (err != 0) + return (err); + + /* Clear sticky DONE bit in interrupt status. */ + WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); + + return (0); +} + +/* Callback function for bus_dmamap_load(). */ +static void +zy7_dma_cb2(void *arg, bus_dma_segment_t *seg, int nsegs, int error) +{ + if (!error && nsegs == 1) + *(bus_addr_t *)arg = seg[0].ds_addr; +} + + +static int +zy7_devcfg_open(struct cdev *dev, int oflags, int devtype, struct thread *td) +{ + struct zy7_devcfg_softc *sc = dev->si_drv1; + int err; + + DEVCFG_SC_LOCK(sc); + if (sc->is_open) { + DEVCFG_SC_UNLOCK(sc); + return (EBUSY); + } + + sc->dma_map = NULL; + err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 4, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + PAGE_SIZE, + 1, + PAGE_SIZE, + 0, + busdma_lock_mutex, + &sc->sc_mtx, + &sc->dma_tag); + if (err) { + DEVCFG_SC_UNLOCK(sc); + return (err); + } + + sc->is_open = 1; + DEVCFG_SC_UNLOCK(sc); + return (0); +} + +static int +zy7_devcfg_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct zy7_devcfg_softc *sc = dev->si_drv1; + void *dma_mem; + bus_addr_t dma_physaddr; + int segsz, err; + + DEVCFG_SC_LOCK(sc); + + /* First write? Reset PL. */ + if (uio->uio_offset == 0 && uio->uio_resid > 0) { + zy7_devcfg_init_hw(sc); + zy7_slcr_preload_pl(); + err = zy7_devcfg_reset_pl(sc); + if (err != 0) { + DEVCFG_SC_UNLOCK(sc); + return (err); + } + } + + /* Allocate dma memory and load. */ + err = bus_dmamem_alloc(sc->dma_tag, &dma_mem, BUS_DMA_NOWAIT, + &sc->dma_map); + if (err != 0) { + DEVCFG_SC_UNLOCK(sc); + return (err); + } + err = bus_dmamap_load(sc->dma_tag, sc->dma_map, dma_mem, PAGE_SIZE, + zy7_dma_cb2, &dma_physaddr, 0); + if (err != 0) { + bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map); + DEVCFG_SC_UNLOCK(sc); + return (err); + } + + while (uio->uio_resid > 0) { + /* If DONE signal has been set, we shouldn't write anymore. */ + if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & + ZY7_DEVCFG_INT_PCFG_DONE) != 0) { + err = EIO; + break; + } + + /* uiomove the data from user buffer to our dma map. */ + segsz = MIN(PAGE_SIZE, uio->uio_resid); + err = uiomove(dma_mem, segsz, uio); + if (err != 0) + break; + + /* Flush the cache to memory. */ + bus_dmamap_sync(sc->dma_tag, sc->dma_map, + BUS_DMASYNC_PREWRITE); + + /* Program devcfg's DMA engine. The ordering of these + * register writes is critical. + */ + if (uio->uio_resid > segsz) + WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR, + (uint32_t) dma_physaddr); + else + WR4(sc, ZY7_DEVCFG_DMA_SRC_ADDR, + (uint32_t) dma_physaddr | + ZY7_DEVCFG_DMA_ADDR_WAIT_PCAP); + WR4(sc, ZY7_DEVCFG_DMA_DST_ADDR, ZY7_DEVCFG_DMA_ADDR_ILLEGAL); + WR4(sc, ZY7_DEVCFG_DMA_SRC_LEN, (segsz+3)/4); + WR4(sc, ZY7_DEVCFG_DMA_DST_LEN, 0); + + /* Now clear done bit and set up DMA done interrupt. */ + WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); + WR4(sc, ZY7_DEVCFG_INT_MASK, ~ZY7_DEVCFG_INT_DMA_DONE); + + /* Wait for DMA done interrupt. */ + err = mtx_sleep(sc->dma_map, &sc->sc_mtx, PCATCH, + "zy7dma", hz); + if (err != 0) + break; + + bus_dmamap_sync(sc->dma_tag, sc->dma_map, + BUS_DMASYNC_POSTWRITE); + + /* Check DONE signal. */ + if ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & + ZY7_DEVCFG_INT_PCFG_DONE) != 0) + zy7_slcr_postload_pl(zy7_en_level_shifters); + } + + bus_dmamap_unload(sc->dma_tag, sc->dma_map); + bus_dmamem_free(sc->dma_tag, dma_mem, sc->dma_map); + DEVCFG_SC_UNLOCK(sc); + return (err); +} + +static int +zy7_devcfg_close(struct cdev *dev, int fflag, int devtype, struct thread *td) +{ + struct zy7_devcfg_softc *sc = dev->si_drv1; + + DEVCFG_SC_LOCK(sc); + sc->is_open = 0; + bus_dma_tag_destroy(sc->dma_tag); + DEVCFG_SC_UNLOCK(sc); + + return (0); +} + + +static void +zy7_devcfg_intr(void *arg) +{ + struct zy7_devcfg_softc *sc = (struct zy7_devcfg_softc *)arg; + uint32_t istatus, imask; + + DEVCFG_SC_LOCK(sc); + + istatus = RD4(sc, ZY7_DEVCFG_INT_STATUS); + imask = ~RD4(sc, ZY7_DEVCFG_INT_MASK); + + /* Turn interrupt off. */ + WR4(sc, ZY7_DEVCFG_INT_MASK, ~0); + + if ((istatus & imask) == 0) { + DEVCFG_SC_UNLOCK(sc); + return; + } + + /* DMA done? */ + if ((istatus & ZY7_DEVCFG_INT_DMA_DONE) != 0) + wakeup(sc->dma_map); + + /* INIT_B positive edge? */ + if ((istatus & ZY7_DEVCFG_INT_PCFG_INIT_PE) != 0) + wakeup(sc); + + DEVCFG_SC_UNLOCK(sc); +} + +/* zy7_devcfg_sysctl_pl_done() returns status of the PL_DONE signal. + */ +static int +zy7_devcfg_sysctl_pl_done(SYSCTL_HANDLER_ARGS) +{ + struct zy7_devcfg_softc *sc = zy7_devcfg_softc_p; + int pl_done = 0; + + if (sc) { + DEVCFG_SC_LOCK(sc); + + /* PCFG_DONE bit is sticky. Clear it before checking it. */ + WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_PCFG_DONE); + pl_done = ((RD4(sc, ZY7_DEVCFG_INT_STATUS) & + ZY7_DEVCFG_INT_PCFG_DONE) != 0); + + DEVCFG_SC_UNLOCK(sc); + } + return (sysctl_handle_int(oidp, &pl_done, 0, req)); +} + +static int +zy7_devcfg_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "xlnx,zy7_devcfg")) + return (ENXIO); + + device_set_desc(dev, "Zynq devcfg block"); + return (0); +} + +static int zy7_devcfg_detach(device_t dev); + +static int +zy7_devcfg_attach(device_t dev) +{ + struct zy7_devcfg_softc *sc = device_get_softc(dev); + int rid, err; + + /* Allow only one attach. */ + if (zy7_devcfg_softc_p != NULL) + return (ENXIO); + + sc->dev = dev; + + DEVCFG_SC_LOCK_INIT(sc); + + /* Get memory resource. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "could not allocate memory resources.\n"); + zy7_devcfg_detach(dev); + return (ENOMEM); + } + + /* Allocate IRQ. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "cannot allocate IRQ\n"); + zy7_devcfg_detach(dev); + return (ENOMEM); + } + + /* Activate the interrupt. */ + err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, zy7_devcfg_intr, sc, &sc->intrhandle); + if (err) { + device_printf(dev, "cannot setup IRQ\n"); + zy7_devcfg_detach(dev); + return (err); + } + + /* Create /dev/devcfg */ + sc->sc_ctl_dev = make_dev(&zy7_devcfg_cdevsw, 0, + UID_ROOT, GID_WHEEL, 0600, "devcfg"); + if (sc->sc_ctl_dev == NULL) { + device_printf(dev, "failed to create /dev/devcfg"); + zy7_devcfg_detach(dev); + return (ENXIO); + } + sc->sc_ctl_dev->si_drv1 = sc; + + zy7_devcfg_softc_p = sc; + + /* Unlock devcfg registers. */ + WR4(sc, ZY7_DEVCFG_UNLOCK, ZY7_DEVCFG_UNLOCK_MAGIC); + + /* Make sure interrupts are completely disabled. */ + WR4(sc, ZY7_DEVCFG_INT_STATUS, ZY7_DEVCFG_INT_ALL); + WR4(sc, ZY7_DEVCFG_INT_MASK, 0xffffffff); + + /* Get PS_VERS for SYSCTL. */ + zy7_ps_vers = (RD4(sc, ZY7_DEVCFG_MCTRL) & + ZY7_DEVCFG_MCTRL_PS_VERS_MASK) >> + ZY7_DEVCFG_MCTRL_PS_VERS_SHIFT; + + return (0); +} + +static int +zy7_devcfg_detach(device_t dev) +{ + struct zy7_devcfg_softc *sc = device_get_softc(dev); + + if (device_is_attached(dev)) + bus_generic_detach(dev); + + /* Get rid of /dev/devcfg0. */ + if (sc->sc_ctl_dev != NULL) + destroy_dev(sc->sc_ctl_dev); + + /* Teardown and release interrupt. */ + if (sc->irq_res != NULL) { + if (sc->intrhandle) + bus_teardown_intr(dev, sc->irq_res, sc->intrhandle); + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->irq_res), sc->irq_res); + } + + /* Release memory resource. */ + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), sc->mem_res); + + zy7_devcfg_softc_p = NULL; + + DEVCFG_SC_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t zy7_devcfg_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, zy7_devcfg_probe), + DEVMETHOD(device_attach, zy7_devcfg_attach), + DEVMETHOD(device_detach, zy7_devcfg_detach), + + DEVMETHOD_END +}; + +static driver_t zy7_devcfg_driver = { + "zy7_devcfg", + zy7_devcfg_methods, + sizeof(struct zy7_devcfg_softc), +}; +static devclass_t zy7_devcfg_devclass; + +DRIVER_MODULE(zy7_devcfg, simplebus, zy7_devcfg_driver, zy7_devcfg_devclass, \ + 0, 0); +MODULE_DEPEND(zy7_devcfg, zy7_slcr, 1, 1, 1); diff --git a/sys/arm/xilinx/zy7_ehci.c b/sys/arm/xilinx/zy7_ehci.c new file mode 100644 index 0000000..2ccb93a --- /dev/null +++ b/sys/arm/xilinx/zy7_ehci.c @@ -0,0 +1,360 @@ +/*- + * Copyright (C) 2012-2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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. + * + */ + +/* A host-controller driver for Zynq-7000's USB OTG controller. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. Ch. 15 covers the USB + * controller and register definitions are in appendix B.34. + */ + + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/lock.h> +#include <sys/module.h> +#include <sys/mutex.h> +#include <sys/condvar.h> +#include <sys/resource.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> + +#include <dev/usb/usb_core.h> +#include <dev/usb/usb_busdma.h> +#include <dev/usb/usb_process.h> +#include <dev/usb/usb_util.h> + +#include <dev/usb/usb_controller.h> +#include <dev/usb/usb_bus.h> +#include <dev/usb/controller/ehci.h> +#include <dev/usb/controller/ehcireg.h> + + +/* Register definitions. */ +#define ZY7_USB_ID 0x0000 +#define ZY7_USB_HWGENERAL 0x0004 +#define ZY7_USB_HWHOST 0x0008 +#define ZY7_USB_HWDEVICE 0x000c +#define ZY7_USB_HWTXBUF 0x0010 +#define ZY7_USB_HWRXBUF 0x0014 +#define ZY7_USB_GPTIMER0LD 0x0080 +#define ZY7_USB_GPTIMER0CTRL 0x0084 +#define ZY7_USB_GPTIMER1LD 0x0088 +#define ZY7_USB_GPTIMER1CTRL 0x008c +#define ZY7_USB_SBUSCFG 0x0090 +#define ZY7_USB_CAPLENGTH_HCIVERSION 0x0100 +#define ZY7_USB_HCSPARAMS 0x0104 +#define ZY7_USB_HCCPARAMS 0x0108 +#define ZY7_USB_DCIVERSION 0x0120 +#define ZY7_USB_DCCPARAMS 0x0124 +#define ZY7_USB_USBCMD 0x0140 +#define ZY7_USB_USBSTS 0x0144 +#define ZY7_USB_USBINTR 0x0148 +#define ZY7_USB_FRINDEX 0x014c +#define ZY7_USB_PERIODICLISTBASE_DEICEADDR 0x0154 +#define ZY7_USB_ASYNCLISTADDR_ENDPOINTLISTADDR 0x0158 +#define ZY7_USB_TTCTRL 0x015c +#define ZY7_USB_BURSTSIZE 0x0160 +#define ZY7_USB_TXFILLTUNING 0x0164 +#define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT 16 +#define ZY7_USB_TXFILLTUNING_TXFIFOTHRES_MASK (0x3f<<16) +#define ZY7_USB_TXTFILLTUNING 0x0168 +#define ZY7_USB_IC_USB 0x016c +#define ZY7_USB_ULPI_VIEWPORT 0x0170 +#define ZY7_USB_ULPI_VIEWPORT_WU (1<<31) +#define ZY7_USB_ULPI_VIEWPORT_RUN (1<<30) +#define ZY7_USB_ULPI_VIEWPORT_RW (1<<29) +#define ZY7_USB_ULPI_VIEWPORT_SS (1<<27) +#define ZY7_USB_ULPI_VIEWPORT_PORT_MASK (7<<24) +#define ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT 24 +#define ZY7_USB_ULPI_VIEWPORT_ADDR_MASK (0xff<<16) +#define ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT 16 +#define ZY7_USB_ULPI_VIEWPORT_DATARD_MASK (0xff<<8) +#define ZY7_USB_ULPI_VIEWPORT_DATARD_SHIFT 8 +#define ZY7_USB_ULPI_VIEWPORT_DATAWR_MASK (0xff<<0) +#define ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT 0 +#define ZY7_USB_ENDPTNAK 0x0178 +#define ZY7_USB_ENDPTNAKEN 0x017c +#define ZY7_USB_CONFIGFLAG 0x0180 +#define ZY7_USB_PORTSC(n) (0x0180+4*(n)) +#define ZY7_USB_PORTSC_PTS_MASK (3<<30) +#define ZY7_USB_PORTSC_PTS_SHIFT 30 +#define ZY7_USB_PORTSC_PTS_UTMI (0<<30) +#define ZY7_USB_PORTSC_PTS_ULPI (2<<30) +#define ZY7_USB_PORTSC_PTS_SERIAL (3<<30) +#define ZY7_USB_PORTSC_PTW (1<<28) +#define ZY7_USB_PORTSC_PTS2 (1<<25) +#define ZY7_USB_OTGSC 0x01a4 +#define ZY7_USB_USBMODE 0x01a8 +#define ZY7_USB_ENDPTSETUPSTAT 0x01ac +#define ZY7_USB_ENDPTPRIME 0x01b0 +#define ZY7_USB_ENDPTFLUSH 0x01b4 +#define ZY7_USB_ENDPTSTAT 0x01b8 +#define ZY7_USB_ENDPTCOMPLETE 0x01bc +#define ZY7_USB_ENDPTCTRL(n) (0x01c0+4*(n)) + +#define EHCI_REG_OFFSET ZY7_USB_CAPLENGTH_HCIVERSION +#define EHCI_REG_SIZE 0x100 + +static int +zy7_phy_config(device_t dev, bus_space_tag_t io_tag, bus_space_handle_t bsh) +{ + phandle_t node; + char buf[64]; + uint32_t portsc; + int tries; + + node = ofw_bus_get_node(dev); + + if (OF_getprop(node, "phy_type", buf, sizeof(buf)) > 0) { + portsc = bus_space_read_4(io_tag, bsh, ZY7_USB_PORTSC(1)); + portsc &= ~(ZY7_USB_PORTSC_PTS_MASK | ZY7_USB_PORTSC_PTW | + ZY7_USB_PORTSC_PTS2); + + if (strcmp(buf,"ulpi") == 0) + portsc |= ZY7_USB_PORTSC_PTS_ULPI; + else if (strcmp(buf,"utmi") == 0) + portsc |= ZY7_USB_PORTSC_PTS_UTMI; + else if (strcmp(buf,"utmi-wide") == 0) + portsc |= (ZY7_USB_PORTSC_PTS_UTMI | + ZY7_USB_PORTSC_PTW); + else if (strcmp(buf, "serial") == 0) + portsc |= ZY7_USB_PORTSC_PTS_SERIAL; + + bus_space_write_4(io_tag, bsh, ZY7_USB_PORTSC(1), portsc); + } + + if (OF_getprop(node, "phy_vbus_ext", buf, sizeof(buf)) >= 0) { + + /* Tell PHY that VBUS is supplied externally. */ + bus_space_write_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT, + ZY7_USB_ULPI_VIEWPORT_RUN | + ZY7_USB_ULPI_VIEWPORT_RW | + (0 << ZY7_USB_ULPI_VIEWPORT_PORT_SHIFT) | + (0x0b << ZY7_USB_ULPI_VIEWPORT_ADDR_SHIFT) | + (0x60 << ZY7_USB_ULPI_VIEWPORT_DATAWR_SHIFT) + ); + + tries = 100; + while ((bus_space_read_4(io_tag, bsh, ZY7_USB_ULPI_VIEWPORT) & + ZY7_USB_ULPI_VIEWPORT_RUN) != 0) { + if (--tries < 0) + return (-1); + DELAY(1); + } + } + + return (0); +} + +static int +zy7_ehci_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "xlnx,zy7_ehci")) + return (ENXIO); + + device_set_desc(dev, "Zynq-7000 EHCI USB 2.0 controller"); + return (0); +} + +static int zy7_ehci_detach(device_t dev); + +static int +zy7_ehci_attach(device_t dev) +{ + ehci_softc_t *sc = device_get_softc(dev); + bus_space_handle_t bsh; + int err, rid; + + /* initialize some bus fields */ + sc->sc_bus.parent = dev; + sc->sc_bus.devices = sc->sc_devices; + sc->sc_bus.devices_max = EHCI_MAX_DEVICES; + + /* get all DMA memory */ + if (usb_bus_mem_alloc_all(&sc->sc_bus, + USB_GET_DMA_TAG(dev), &ehci_iterate_hw_softc)) + return (ENOMEM); + + /* Allocate memory. */ + rid = 0; + sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->sc_io_res == NULL) { + device_printf(dev, "Can't allocate memory"); + zy7_ehci_detach(dev); + return (ENOMEM); + } + + sc->sc_io_tag = rman_get_bustag(sc->sc_io_res); + bsh = rman_get_bushandle(sc->sc_io_res); + sc->sc_io_size = EHCI_REG_SIZE; + + if (bus_space_subregion(sc->sc_io_tag, bsh, EHCI_REG_OFFSET, + sc->sc_io_size, &sc->sc_io_hdl) != 0) + panic("%s: unable to subregion USB host registers", + device_get_name(dev)); + + /* Allocate IRQ. */ + rid = 0; + sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->sc_irq_res == NULL) { + device_printf(dev, "Can't allocate IRQ\n"); + zy7_ehci_detach(dev); + return (ENOMEM); + } + + /* Add USB device */ + sc->sc_bus.bdev = device_add_child(dev, "usbus", -1); + if (!sc->sc_bus.bdev) { + device_printf(dev, "Could not add USB device\n"); + zy7_ehci_detach(dev); + return (ENXIO); + } + device_set_ivars(sc->sc_bus.bdev, &sc->sc_bus); + device_set_desc(sc->sc_bus.bdev, "Zynq-7000 ehci USB 2.0 controller"); + + strcpy(sc->sc_vendor, "Xilinx"); /* or IP vendor? */ + + /* Activate the interrupt */ + err = bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, + NULL, (driver_intr_t *)ehci_interrupt, sc, + &sc->sc_intr_hdl); + if (err) { + device_printf(dev, "Cannot setup IRQ\n"); + zy7_ehci_detach(dev); + return (err); + } + + /* Customization. */ + sc->sc_flags |= EHCI_SCFLG_SETMODE | EHCI_SCFLG_TT | + EHCI_SCFLG_NORESTERM; + + /* Modify FIFO burst threshold from 2 to 8. */ + bus_space_write_4(sc->sc_io_tag, bsh, + ZY7_USB_TXFILLTUNING, + 8 << ZY7_USB_TXFILLTUNING_TXFIFOTHRES_SHFT); + + /* Handle PHY options. */ + if (zy7_phy_config(dev, sc->sc_io_tag, bsh) < 0) { + device_printf(dev, "Cannot config phy!\n"); + zy7_ehci_detach(dev); + return (EIO); + } + + /* Init ehci. */ + err = ehci_init(sc); + if (!err) { + sc->sc_flags |= EHCI_SCFLG_DONEINIT; + err = device_probe_and_attach(sc->sc_bus.bdev); + } + if (err) { + device_printf(dev, "USB init failed err=%d\n", err); + zy7_ehci_detach(dev); + return (err); + } + + return (0); +} + +static int +zy7_ehci_detach(device_t dev) +{ + ehci_softc_t *sc = device_get_softc(dev); + + sc->sc_flags &= ~EHCI_SCFLG_DONEINIT; + + if (device_is_attached(dev)) + bus_generic_detach(dev); + + if (sc->sc_irq_res && sc->sc_intr_hdl) + /* call ehci_detach() after ehci_init() called after + * successful bus_setup_intr(). + */ + ehci_detach(sc); + if (sc->sc_bus.bdev) { + device_detach(sc->sc_bus.bdev); + device_delete_child(dev, sc->sc_bus.bdev); + } + if (sc->sc_irq_res) { + if (sc->sc_intr_hdl != NULL) + bus_teardown_intr(dev, sc->sc_irq_res, + sc->sc_intr_hdl); + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->sc_irq_res), sc->sc_irq_res); + } + + if (sc->sc_io_res) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->sc_io_res), sc->sc_io_res); + usb_bus_mem_free_all(&sc->sc_bus, &ehci_iterate_hw_softc); + + return (0); +} + +static device_method_t ehci_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, zy7_ehci_probe), + DEVMETHOD(device_attach, zy7_ehci_attach), + DEVMETHOD(device_detach, zy7_ehci_detach), + DEVMETHOD(device_suspend, bus_generic_suspend), + DEVMETHOD(device_resume, bus_generic_resume), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* Bus interface */ + DEVMETHOD(bus_print_child, bus_generic_print_child), + + DEVMETHOD_END +}; + +static driver_t ehci_driver = { + "ehci", + ehci_methods, + sizeof(struct ehci_softc), +}; +static devclass_t ehci_devclass; + +DRIVER_MODULE(ehci, simplebus, ehci_driver, ehci_devclass, NULL, NULL); +MODULE_DEPEND(ehci, usb, 1, 1, 1); diff --git a/sys/arm/xilinx/zy7_gpio.c b/sys/arm/xilinx/zy7_gpio.c new file mode 100644 index 0000000..5b6bb27 --- /dev/null +++ b/sys/arm/xilinx/zy7_gpio.c @@ -0,0 +1,384 @@ +/*- + * Copyright (C) 2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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. + * + */ + +/* A GPIO driver for Xilinx Zynq-7000. + * + * The GPIO peripheral on Zynq allows controlling 114 general purpose I/Os. + * + * Pins 53-0 are sent to the MIO. Any MIO pins not used by a PS peripheral are + * available as a GPIO pin. Pins 64-127 are sent to the PL (FPGA) section of + * Zynq as EMIO signals. + * + * The hardware provides a way to use IOs as interrupt sources but the + * gpio framework doesn't seem to have hooks for this. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. GPIO is covered in + * chater 14. Register definitions are in appendix B.19. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/rman.h> +#include <sys/gpio.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include "gpio_if.h" + +#define NUMBANKS 4 +#define MAXPIN (32*NUMBANKS) + +#define MIO_PIN 0 /* pins 0-53 go to MIO */ +#define NUM_MIO_PINS 54 +#define EMIO_PIN 64 /* pins 64-127 go to PL */ +#define NUM_EMIO_PINS 64 + +#define VALID_PIN(u) (((u) >= MIO_PIN && (u) < MIO_PIN + NUM_MIO_PINS) || \ + ((u) >= EMIO_PIN && (u) < EMIO_PIN + NUM_EMIO_PINS)) + +#define ZGPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define ZGPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define ZGPIO_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ + "gpio", MTX_DEF) +#define ZGPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); + +struct zy7_gpio_softc { + device_t dev; + struct mtx sc_mtx; + struct resource *mem_res; /* Memory resource */ +}; + +#define WR4(sc, off, val) bus_write_4((sc)->mem_res, (off), (val)) +#define RD4(sc, off) bus_read_4((sc)->mem_res, (off)) + + +/* Xilinx Zynq-7000 GPIO register definitions: + */ +#define ZY7_GPIO_MASK_DATA_LSW(b) (0x0000+8*(b)) /* maskable wr lo */ +#define ZY7_GPIO_MASK_DATA_MSW(b) (0x0004+8*(b)) /* maskable wr hi */ +#define ZY7_GPIO_DATA(b) (0x0040+4*(b)) /* in/out data */ +#define ZY7_GPIO_DATA_RO(b) (0x0060+4*(b)) /* input data */ + +#define ZY7_GPIO_DIRM(b) (0x0204+0x40*(b)) /* direction mode */ +#define ZY7_GPIO_OEN(b) (0x0208+0x40*(b)) /* output enable */ +#define ZY7_GPIO_INT_MASK(b) (0x020c+0x40*(b)) /* int mask */ +#define ZY7_GPIO_INT_EN(b) (0x0210+0x40*(b)) /* int enable */ +#define ZY7_GPIO_INT_DIS(b) (0x0214+0x40*(b)) /* int disable */ +#define ZY7_GPIO_INT_STAT(b) (0x0218+0x40*(b)) /* int status */ +#define ZY7_GPIO_INT_TYPE(b) (0x021c+0x40*(b)) /* int type */ +#define ZY7_GPIO_INT_POLARITY(b) (0x0220+0x40*(b)) /* int polarity */ +#define ZY7_GPIO_INT_ANY(b) (0x0224+0x40*(b)) /* any edge */ + + +static int +zy7_gpio_pin_max(device_t dev, int *maxpin) +{ + + *maxpin = MAXPIN; + return (0); +} + +/* Get a specific pin's capabilities. */ +static int +zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) +{ + + if (!VALID_PIN(pin)) + return (EINVAL); + + *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE); + + return (0); +} + +/* Get a specific pin's name. */ +static int +zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name) +{ + + if (!VALID_PIN(pin)) + return (EINVAL); + + if (pin < NUM_MIO_PINS) { + snprintf(name, GPIOMAXNAME, "MIO_%d", pin); + name[GPIOMAXNAME - 1] = '\0'; + } else { + snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - EMIO_PIN); + name[GPIOMAXNAME - 1] = '\0'; + } + + return (0); +} + +/* Get a specific pin's current in/out/tri state. */ +static int +zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + if (!VALID_PIN(pin)) + return (EINVAL); + + ZGPIO_LOCK(sc); + + if ((RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & (1 << (pin & 31))) != 0) { + /* output */ + if ((RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & (1 << (pin & 31))) == 0) + *flags = (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE); + else + *flags = GPIO_PIN_OUTPUT; + } else + /* input */ + *flags = GPIO_PIN_INPUT; + + ZGPIO_UNLOCK(sc); + + return (0); +} + +/* Set a specific pin's in/out/tri state. */ +static int +zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + if (!VALID_PIN(pin)) + return (EINVAL); + + ZGPIO_LOCK(sc); + + if ((flags & GPIO_PIN_OUTPUT) != 0) { + /* Output. Set or reset OEN too. */ + WR4(sc, ZY7_GPIO_DIRM(pin >> 5), + RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) | (1 << (pin & 31))); + + if ((flags & GPIO_PIN_TRISTATE) != 0) + WR4(sc, ZY7_GPIO_OEN(pin >> 5), + RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & + ~(1 << (pin & 31))); + else + WR4(sc, ZY7_GPIO_OEN(pin >> 5), + RD4(sc, ZY7_GPIO_OEN(pin >> 5)) | + (1 << (pin & 31))); + } else { + /* Input. Turn off OEN. */ + WR4(sc, ZY7_GPIO_DIRM(pin >> 5), + RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & ~(1 << (pin & 31))); + WR4(sc, ZY7_GPIO_OEN(pin >> 5), + RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & ~(1 << (pin & 31))); + } + + ZGPIO_UNLOCK(sc); + + return (0); +} + +/* Set a specific output pin's value. */ +static int +zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + if (!VALID_PIN(pin) || value > 1) + return (EINVAL); + + /* Fancy register tricks allow atomic set or reset. */ + if ((pin & 16) != 0) + WR4(sc, ZY7_GPIO_MASK_DATA_MSW(pin >> 5), + (0xffff0000 ^ (0x10000 << (pin & 15))) | + (value << (pin & 15))); + else + WR4(sc, ZY7_GPIO_MASK_DATA_LSW(pin >> 5), + (0xffff0000 ^ (0x10000 << (pin & 15))) | + (value << (pin & 15))); + + return (0); +} + +/* Get a specific pin's input value. */ +static int +zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + if (!VALID_PIN(pin)) + return (EINVAL); + + *value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1; + + return (0); +} + +/* Toggle a pin's output value. */ +static int +zy7_gpio_pin_toggle(device_t dev, uint32_t pin) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + if (!VALID_PIN(pin)) + return (EINVAL); + + ZGPIO_LOCK(sc); + + WR4(sc, ZY7_GPIO_DATA(pin >> 5), + RD4(sc, ZY7_GPIO_DATA(pin >> 5)) ^ (1 << (pin & 31))); + + ZGPIO_UNLOCK(sc); + + return (0); +} + +static int +zy7_gpio_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "xlnx,zy7_gpio")) + return (ENXIO); + + device_set_desc(dev, "Zynq-7000 GPIO driver"); + return (0); +} + +static void +zy7_gpio_hw_reset(struct zy7_gpio_softc *sc) +{ + int i; + + for (i = 0; i < NUMBANKS; i++) { + WR4(sc, ZY7_GPIO_DATA(i), 0); + WR4(sc, ZY7_GPIO_DIRM(i), 0); + WR4(sc, ZY7_GPIO_OEN(i), 0); + WR4(sc, ZY7_GPIO_INT_DIS(i), 0xffffffff); + WR4(sc, ZY7_GPIO_INT_POLARITY(i), 0); + WR4(sc, ZY7_GPIO_INT_TYPE(i), + i == 1 ? 0x003fffff : 0xffffffff); + WR4(sc, ZY7_GPIO_INT_ANY(i), 0); + WR4(sc, ZY7_GPIO_INT_STAT(i), 0xffffffff); + } +} + +static int zy7_gpio_detach(device_t dev); + +static int +zy7_gpio_attach(device_t dev) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + int rid; + + sc->dev = dev; + + ZGPIO_LOCK_INIT(sc); + + /* Allocate memory. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, + SYS_RES_MEMORY, &rid, RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "Can't allocate memory for device"); + zy7_gpio_detach(dev); + return (ENOMEM); + } + + /* Completely reset. */ + zy7_gpio_hw_reset(sc); + + device_add_child(dev, "gpioc", device_get_unit(dev)); + device_add_child(dev, "gpiobus", device_get_unit(dev)); + + return (bus_generic_attach(dev)); +} + +static int +zy7_gpio_detach(device_t dev) +{ + struct zy7_gpio_softc *sc = device_get_softc(dev); + + bus_generic_detach(dev); + + if (sc->mem_res != NULL) { + /* Release memory resource. */ + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), sc->mem_res); + } + + ZGPIO_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t zy7_gpio_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, zy7_gpio_probe), + DEVMETHOD(device_attach, zy7_gpio_attach), + DEVMETHOD(device_detach, zy7_gpio_detach), + + /* GPIO protocol */ + DEVMETHOD(gpio_pin_max, zy7_gpio_pin_max), + DEVMETHOD(gpio_pin_getname, zy7_gpio_pin_getname), + DEVMETHOD(gpio_pin_getflags, zy7_gpio_pin_getflags), + DEVMETHOD(gpio_pin_getcaps, zy7_gpio_pin_getcaps), + DEVMETHOD(gpio_pin_setflags, zy7_gpio_pin_setflags), + DEVMETHOD(gpio_pin_get, zy7_gpio_pin_get), + DEVMETHOD(gpio_pin_set, zy7_gpio_pin_set), + DEVMETHOD(gpio_pin_toggle, zy7_gpio_pin_toggle), + + DEVMETHOD_END +}; + +static driver_t zy7_gpio_driver = { + "zy7_gpio", + zy7_gpio_methods, + sizeof(struct zy7_gpio_softc), +}; +static devclass_t zy7_gpio_devclass; + +extern devclass_t gpiobus_devclass, gpioc_devclass; +extern driver_t gpiobus_driver, gpioc_driver; + +DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, zy7_gpio_devclass, \ + NULL, NULL); +DRIVER_MODULE(gpiobus, zy7_gpio, gpiobus_driver, gpiobus_devclass, 0, 0); +DRIVER_MODULE(gpioc, zy7_gpio, gpioc_driver, gpioc_devclass, 0, 0); diff --git a/sys/arm/xilinx/zy7_l2cache.c b/sys/arm/xilinx/zy7_l2cache.c new file mode 100644 index 0000000..f1ab181 --- /dev/null +++ b/sys/arm/xilinx/zy7_l2cache.c @@ -0,0 +1,56 @@ +/*- + * Copyright (C) 2013, Thomas Skibo. + * All rights reserved. + * + * 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 ``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 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/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/lock.h> +#include <sys/mutex.h> + +#include <machine/bus.h> +#include <machine/pl310.h> + +void +platform_pl310_init(struct pl310_softc *softc) +{ +} + +void +platform_pl310_write_ctrl(struct pl310_softc *sc, uint32_t val) +{ + + pl310_write4(sc, PL310_CTRL, val); +} + +void +platform_pl310_write_debug(struct pl310_softc *sc, uint32_t val) +{ + + pl310_write4(sc, PL310_DEBUG_CTRL, val); +} diff --git a/sys/arm/xilinx/zy7_machdep.c b/sys/arm/xilinx/zy7_machdep.c new file mode 100644 index 0000000..c50796d --- /dev/null +++ b/sys/arm/xilinx/zy7_machdep.c @@ -0,0 +1,161 @@ +/*- + * Copyright (C) 2012-2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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. + * + */ + +/* Machine dependent code for Xilinx Zynq-7000 Soc. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. + */ + +#include "opt_global.h" + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#define _ARM32_BUS_DMA_PRIVATE +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/bus.h> + +#include <vm/vm.h> +#include <vm/pmap.h> + +#include <dev/fdt/fdt_common.h> + +#include <machine/bus.h> +#include <machine/pmap.h> +#include <machine/frame.h> +#include <machine/machdep.h> + +#include <arm/xilinx/zy7_reg.h> + +void (*zynq7_cpu_reset)(void); + +vm_offset_t +initarm_lastaddr(void) +{ + + return (ZYNQ7_PSIO_VBASE - ARM_NOCACHE_KVA_SIZE); +} + +void +initarm_gpio_init(void) +{ +} + +void +initarm_late_init(void) +{ +} + +#define FDT_DEVMAP_SIZE 3 +static struct pmap_devmap fdt_devmap[FDT_DEVMAP_SIZE]; + +/* + * Construct pmap_devmap[] with DT-derived config data. + */ +int +platform_devmap_init(void) +{ + int i = 0; + + fdt_devmap[i].pd_va = ZYNQ7_PSIO_VBASE; + fdt_devmap[i].pd_pa = ZYNQ7_PSIO_HWBASE; + fdt_devmap[i].pd_size = ZYNQ7_PSIO_SIZE; + fdt_devmap[i].pd_prot = VM_PROT_READ | VM_PROT_WRITE; + fdt_devmap[i].pd_cache = PTE_DEVICE; + i++; + + fdt_devmap[i].pd_va = ZYNQ7_PSCTL_VBASE; + fdt_devmap[i].pd_pa = ZYNQ7_PSCTL_HWBASE; + fdt_devmap[i].pd_size = ZYNQ7_PSCTL_SIZE; + fdt_devmap[i].pd_prot = VM_PROT_READ | VM_PROT_WRITE; + fdt_devmap[i].pd_cache = PTE_DEVICE; + i++; + + /* end of table */ + fdt_devmap[i].pd_va = 0; + fdt_devmap[i].pd_pa = 0; + fdt_devmap[i].pd_size = 0; + fdt_devmap[i].pd_prot = 0; + fdt_devmap[i].pd_cache = 0; + + pmap_devmap_bootstrap_table = &fdt_devmap[0]; + return (0); +} + + +struct fdt_fixup_entry fdt_fixup_table[] = { + { NULL, NULL } +}; + +static int +fdt_gic_decode_ic(phandle_t node, pcell_t *intr, int *interrupt, int *trig, + int *pol) +{ + + if (!fdt_is_compatible(node, "arm,gic")) + return (ENXIO); + + *interrupt = fdt32_to_cpu(intr[0]); + *trig = INTR_TRIGGER_CONFORM; + *pol = INTR_POLARITY_CONFORM; + + return (0); +} + +fdt_pic_decode_t fdt_pic_table[] = { + &fdt_gic_decode_ic, + NULL +}; + + +struct arm32_dma_range * +bus_dma_get_range(void) +{ + + return (NULL); +} + +int +bus_dma_get_range_nb(void) +{ + + return (0); +} + +void +cpu_reset() +{ + if (zynq7_cpu_reset != NULL) + (*zynq7_cpu_reset)(); + + printf("cpu_reset: no platform cpu_reset. hanging.\n"); + for (;;) + ; +} diff --git a/sys/arm/xilinx/zy7_reg.h b/sys/arm/xilinx/zy7_reg.h new file mode 100644 index 0000000..b30c86d --- /dev/null +++ b/sys/arm/xilinx/zy7_reg.h @@ -0,0 +1,78 @@ +/*- + * Copyright (C) 2012-2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 Thomas Skibo 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$ */ + +/* Address regions of Zynq-7000. + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. + */ + +#ifndef _ZY7_REG_H_ +#define _ZY7_REG_H_ + +/* PL AXI buses: General Purpose Port #0, M_AXI_GP0. */ +#define ZYNQ7_PLGP0_HWBASE 0x40000000 +#define ZYNQ7_PLGP0_SIZE 0x40000000 + +/* PL AXI buses: General Purpose Port #1, M_AXI_GP1. */ +#define ZYNQ7_PLGP1_HWBASE 0x80000000 +#define ZYNQ7_PLGP1_SIZE 0x40000000 + +/* I/O Peripheral registers. */ +#define ZYNQ7_PSIO_VBASE 0xE0000000 +#define ZYNQ7_PSIO_HWBASE 0xE0000000 +#define ZYNQ7_PSIO_SIZE 0x00300000 + +/* UART0 and UART1 */ +#define ZYNQ7_UART0_VBASE (ZYNQ7_PSIO_VBASE) +#define ZYNQ7_UART0_HWBASE (ZYNQ7_PSIO_HWBASE) +#define ZYNQ7_UART0_SIZE 0x1000 + +#define ZYNQ7_UART1_VBASE (ZYNQ7_PSIO_VBASE+0x1000) +#define ZYNQ7_UART1_HWBASE (ZYNQ7_PSIO_HWBASE+0x1000) +#define ZYNQ7_UART1_SIZE 0x1000 + + +/* SMC Memories not mapped for now. */ +#define ZYNQ7_SMC_HWBASE 0xE1000000 +#define ZYNQ7_SMC_SIZE 0x05000000 + +/* SLCR, PS system, and CPU private registers combined in this region. */ +#define ZYNQ7_PSCTL_VBASE 0xF8000000 +#define ZYNQ7_PSCTL_HWBASE 0xF8000000 +#define ZYNQ7_PSCTL_SIZE 0x01000000 + +#define ZYNQ7_SLCR_VBASE (ZYNQ7_PSCTL_VBASE) +#define ZYNQ7_SLCR_HWBASE (ZYNQ7_PSCTL_HWBASE) +#define ZYNQ7_SLCR_SIZE 0x1000 + +#define ZYNQ7_DEVCFG_VBASE (ZYNQ7_PSCTL_VBASE+0x7000) +#define ZYNQ7_DEVCFG_HWBASE (ZYNQ7_PSCTL_HWBASE+0x7000) +#define ZYNQ7_DEVCFG_SIZE 0x1000 + +#endif /* _ZY7_REG_H_ */ diff --git a/sys/arm/xilinx/zy7_slcr.c b/sys/arm/xilinx/zy7_slcr.c new file mode 100644 index 0000000..048b768 --- /dev/null +++ b/sys/arm/xilinx/zy7_slcr.c @@ -0,0 +1,301 @@ +/*- + * Copyright (C) 2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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. + * + */ + +/* Zynq-700 SLCR driver. Provides hooks for cpu_reset and PL control stuff. + * In the future, maybe MIO control, clock control, etc. could go here. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/resource.h> +#include <sys/sysctl.h> +#include <sys/rman.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <machine/stdarg.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <arm/xilinx/zy7_slcr.h> + +struct zy7_slcr_softc { + device_t dev; + struct mtx sc_mtx; + struct resource *mem_res; +}; + +static struct zy7_slcr_softc *zy7_slcr_softc_p; +extern void (*zynq7_cpu_reset); + +#define ZSLCR_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define ZSLCR_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define ZSLCR_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ + "zy7_slcr", MTX_SPIN) +#define ZSLCR_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); + +#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) +#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) + + +SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000"); + +static char zynq_bootmode[64]; +SYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0, + "Zynq boot mode"); + +static char zynq_pssid[80]; +SYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0, + "Zynq PSS IDCODE"); + +static uint32_t zynq_reboot_status; +SYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status, + 0, "Zynq REBOOT_STATUS register"); + +static void +zy7_slcr_unlock(struct zy7_slcr_softc *sc) +{ + + /* Unlock SLCR with magic number. */ + WR4(sc, ZY7_SLCR_UNLOCK, ZY7_SLCR_UNLOCK_MAGIC); +} + +static void +zy7_slcr_lock(struct zy7_slcr_softc *sc) +{ + + /* Lock SLCR with magic number. */ + WR4(sc, ZY7_SLCR_LOCK, ZY7_SLCR_LOCK_MAGIC); +} + + +static void +zy7_slcr_cpu_reset(void) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + /* This has something to do with a work-around so the fsbl will load + * the bitstream after soft-reboot. It's very important. + */ + WR4(sc, ZY7_SLCR_REBOOT_STAT, + RD4(sc, ZY7_SLCR_REBOOT_STAT) & 0xf0ffffff); + + /* Soft reset */ + WR4(sc, ZY7_SLCR_PSS_RST_CTRL, ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET); + + for (;;) + ; +} + +/* Assert PL resets and disable level shifters in preparation of programming + * the PL (FPGA) section. Called from zy7_devcfg.c. + */ +void +zy7_slcr_preload_pl(void) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return; + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + /* Assert top level output resets. */ + WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, ZY7_SLCR_FPGA_RST_CTRL_RST_ALL); + + /* Disable all level shifters. */ + WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); +} + +/* After PL configuration, enable level shifters and deassert top-level + * PL resets. Called from zy7_devcfg.c. Optionally, the level shifters + * can be left disabled but that's rare of an FPGA application. That option + * is controled by a sysctl in the devcfg driver. + */ +void +zy7_slcr_postload_pl(int en_level_shifters) +{ + struct zy7_slcr_softc *sc = zy7_slcr_softc_p; + + if (!sc) + return; + + ZSLCR_LOCK(sc); + + /* Unlock SLCR registers. */ + zy7_slcr_unlock(sc); + + if (en_level_shifters) + /* Enable level shifters. */ + WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL); + + /* Deassert top level output resets. */ + WR4(sc, ZY7_SLCR_FPGA_RST_CTRL, 0); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + ZSLCR_UNLOCK(sc); +} + +static int +zy7_slcr_probe(device_t dev) +{ + if (!ofw_bus_is_compatible(dev, "xlnx,zy7_slcr")) + return (ENXIO); + + device_set_desc(dev, "Zynq-7000 slcr block"); + return (0); +} + +static int +zy7_slcr_attach(device_t dev) +{ + struct zy7_slcr_softc *sc = device_get_softc(dev); + int rid; + uint32_t bootmode; + uint32_t pss_idcode; + static char *bootdev_names[] = { + "JTAG", "Quad-SPI", "NOR", "(3?)", + "NAND", "SD Card", "(6?)", "(7?)" + }; + + /* Allow only one attach. */ + if (zy7_slcr_softc_p != NULL) + return (ENXIO); + + sc->dev = dev; + + ZSLCR_LOCK_INIT(sc); + + /* Get memory resource. */ + rid = 0; + sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, + RF_ACTIVE); + if (sc->mem_res == NULL) { + device_printf(dev, "could not allocate memory resources.\n"); + return (ENOMEM); + } + + /* Hook up cpu_reset. */ + zy7_slcr_softc_p = sc; + zynq7_cpu_reset = zy7_slcr_cpu_reset; + + /* Read info and set sysctls. */ + bootmode = RD4(sc, ZY7_SLCR_BOOT_MODE); + snprintf(zynq_bootmode, sizeof(zynq_bootmode), + "0x%x: boot device: %s", bootmode, + bootdev_names[bootmode & ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK]); + + pss_idcode = RD4(sc, ZY7_SLCR_PSS_IDCODE); + snprintf(zynq_pssid, sizeof(zynq_pssid), + "0x%x: manufacturer: 0x%x device: 0x%x " + "family: 0x%x sub-family: 0x%x rev: 0x%x", + pss_idcode, + (pss_idcode & ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK) >> + ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT, + (pss_idcode & ZY7_SLCR_PSS_IDCODE_DEVICE_MASK) >> + ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT, + (pss_idcode & ZY7_SLCR_PSS_IDCODE_FAMILY_MASK) >> + ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT, + (pss_idcode & ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK) >> + ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT, + (pss_idcode & ZY7_SLCR_PSS_IDCODE_REVISION_MASK) >> + ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT); + + zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT); + + /* Lock SLCR registers. */ + zy7_slcr_lock(sc); + + return (0); +} + +static int +zy7_slcr_detach(device_t dev) +{ + struct zy7_slcr_softc *sc = device_get_softc(dev); + + bus_generic_detach(dev); + + /* Release memory resource. */ + if (sc->mem_res != NULL) + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), sc->mem_res); + + zy7_slcr_softc_p = NULL; + zynq7_cpu_reset = NULL; + + ZSLCR_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t zy7_slcr_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, zy7_slcr_probe), + DEVMETHOD(device_attach, zy7_slcr_attach), + DEVMETHOD(device_detach, zy7_slcr_detach), + + DEVMETHOD_END +}; + +static driver_t zy7_slcr_driver = { + "zy7_slcr", + zy7_slcr_methods, + sizeof(struct zy7_slcr_softc), +}; +static devclass_t zy7_slcr_devclass; + +DRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, zy7_slcr_devclass, 0, 0); +MODULE_VERSION(zy7_slcr, 1); diff --git a/sys/arm/xilinx/zy7_slcr.h b/sys/arm/xilinx/zy7_slcr.h new file mode 100644 index 0000000..ae6d03b --- /dev/null +++ b/sys/arm/xilinx/zy7_slcr.h @@ -0,0 +1,280 @@ +/*- + * Copyright (C) 2013, Thomas Skibo. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * The names of contributors may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 AUTHORS 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$ */ + +/* Defines for Zynq-7000 SLCR registers. + * + * Most of these registers are initialized by the First Stage Boot + * Loader and are not modified by the kernel. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. SLCR register definitions + * are in appendix B.28. + */ + + +#ifndef _ZY7_SLCR_H_ +#define _ZY7_SLCR_H_ + +#define ZY7_SCLR_SCL 0x0000 +#define ZY7_SLCR_LOCK 0x0004 +#define ZY7_SLCR_LOCK_MAGIC 0x767b +#define ZY7_SLCR_UNLOCK 0x0008 +#define ZY7_SLCR_UNLOCK_MAGIC 0xdf0d +#define ZY7_SLCR_LOCKSTA 0x000c + +/* PLL controls. */ +#define ZY7_SLCR_ARM_PLL_CTRL 0x0100 +#define ZY7_SLCR_DDR_PLL_CTRL 0x0104 +#define ZY7_SLCR_IO_PLL_CTRL 0x0108 +#define ZY7_SLCR_PLL_CTRL_RESET (1<<0) +#define ZY7_SLCR_PLL_CTRL_PWRDWN (1<<1) +#define ZY7_SLCR_PLL_CTRL_BYPASS_QUAL (1<<3) +#define ZY7_SLCR_PLL_CTRL_BYPASS_FORCE (1<<4) +#define ZY7_SLCR_PLL_CTRL_FDIV_SHIFT 12 +#define ZY7_SLCR_PLL_CTRL_FDIV_MASK (0x7f<<12) +#define ZY7_SLCR_PLL_STATUS 0x010c +#define ZY7_SLCR_PLL_STAT_ARM_PLL_LOCK (1<<0) +#define ZY7_SLCR_PLL_STAT_DDR_PLL_LOCK (1<<1) +#define ZY7_SLCR_PLL_STAT_IO_PLL_LOCK (1<<2) +#define ZY7_SLCR_PLL_STAT_ARM_PLL_STABLE (1<<3) +#define ZY7_SLCR_PLL_STAT_DDR_PLL_STABLE (1<<4) +#define ZY7_SLCR_PLL_STAT_IO_PLL_STABLE (1<<5) +#define ZY7_SLCR_ARM_PLL_CFG 0x0110 +#define ZY7_SLCR_DDR_PLL_CFG 0x0114 +#define ZY7_SLCR_IO_PLL_CFG 0x0118 +#define ZY7_SLCR_PLL_CFG_RES_SHIFT 4 +#define ZY7_SLCR_PLL_CFG_RES_MASK (0xf<<4) +#define ZY7_SLCR_PLL_CFG_PLL_CP_SHIFT 8 +#define ZY7_SLCR_PLL_CFG_PLL_CP_MASK (0xf<<8) +#define ZY7_SLCR_PLL_CFG_LOCK_CNT_SHIFT 12 +#define ZY7_SLCR_PLL_CFG_LOCK_CNT_MASK (0x3ff<<12) + +/* Clock controls. */ +#define ZY7_SLCR_ARM_CLK_CTRL 0x0120 +#define ZY7_SLCR_ARM_CLK_CTRL_CPU_PERI_CLKACT (1<<28) +#define ZY7_SLCR_ARM_CLK_CTRL_CPU_1XCLKACT (1<<27) +#define ZY7_SLCR_ARM_CLK_CTRL_CPU_2XCLKACT (1<<26) +#define ZY7_SLCR_ARM_CLK_CTRL_CPU_3OR2XCLKACT (1<<25) +#define ZY7_SLCR_ARM_CLK_CTRL_CPU_6OR4XCLKACT (1<<24) +#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_MASK (3<<4) +#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_ARM_PLL (0<<4) +#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_DDR_PLL (2<<4) +#define ZY7_SLCR_ARM_CLK_CTRL_SRCSEL_IO_PLL (3<<4) +#define ZY7_SLCR_ARM_CLK_CTRL_DIVISOR_SHIFT 8 +#define ZY7_SLCR_ARM_CLK_CTRL_DIVISOR_MASK (0x3f<<8) +#define ZY7_SLCR_DDR_CLK_CTRL 0x0124 +#define ZY7_SLCR_DDR_CLK_CTRL_2XCLK_DIV_SHIFT 26 +#define ZY7_SLCR_DDR_CLK_CTRL_2XCLK_DIV_MASK (0x3f<<26) +#define ZY7_SLCR_DDR_CLK_CTRL_3XCLK_DIV_SHIFT 20 +#define ZY7_SLCR_DDR_CLK_CTRL_3XCLK_DIV_MASK (0x3f<<20) +#define ZY7_SLCR_DDR_CLK_CTRL_2XCLKACT (1<<1) +#define ZY7_SLCR_DDR_CLK_CTRL_3XCLKACT (1<<0) +#define ZY7_SLCR_DCI_CLK_CTRL 0x0128 +#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR1_SHIFT 20 +#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR1_MASK (0x3f<<20) +#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR0_SHIFT 8 +#define ZY7_SLCR_DCI_CLK_CTRL_DIVISOR0_MASK (0x3f<<8) +#define ZY7_SLCR_DCI_CLK_CTRL_CLKACT (1<<0) +#define ZY7_SLCR_APER_CLK_CTRL 0x012c /* amba periph clk ctrl */ +#define ZY7_SLCR_APER_CLK_CTRL_SMC_CPU_1XCLKACT (1<<24) +#define ZY7_SLCR_APER_CLK_CTRL_LQSPI_CPU_1XCLKACT (1<<23) +#define ZY7_SLCR_APER_CLK_CTRL_GPIO_CPU_1XCLKACT (1<<22) +#define ZY7_SLCR_APER_CLK_CTRL_UART1_CPU_1XCLKACT (1<<21) +#define ZY7_SLCR_APER_CLK_CTRL_UART0_CPU_1XCLKACT (1<<20) +#define ZY7_SLCR_APER_CLK_CTRL_I2C1_CPU_1XCLKACT (1<<19) +#define ZY7_SLCR_APER_CLK_CTRL_I2C0_CPU_1XCLKACT (1<<18) +#define ZY7_SLCR_APER_CLK_CTRL_CAN1_CPU_1XCLKACT (1<<17) +#define ZY7_SLCR_APER_CLK_CTRL_CAN0_CPU_1XCLKACT (1<<16) +#define ZY7_SLCR_APER_CLK_CTRL_SPI1_CPU_1XCLKACT (1<<15) +#define ZY7_SLCR_APER_CLK_CTRL_SPI0_CPU_1XCLKACT (1<<14) +#define ZY7_SLCR_APER_CLK_CTRL_SDI1_CPU_1XCLKACT (1<<11) +#define ZY7_SLCR_APER_CLK_CTRL_SDI0_CPU_1XCLKACT (1<<10) +#define ZY7_SLCR_APER_CLK_CTRL_GEM1_CPU_1XCLKACT (1<<7) +#define ZY7_SLCR_APER_CLK_CTRL_GEM0_CPU_1XCLKACT (1<<6) +#define ZY7_SLCR_APER_CLK_CTRL_USB1_CPU_1XCLKACT (1<<3) +#define ZY7_SLCR_APER_CLK_CTRL_USB0_CPU_1XCLKACT (1<<2) +#define ZY7_SLCR_APER_CLK_CTRL_DMA_CPU_1XCLKACT (1<<0) +#define ZY7_SLCR_USB0_CLK_CTRL 0x0130 +#define ZY7_SLCR_USB1_CLK_CTRL 0x0134 +#define ZY7_SLCR_GEM0_RCLK_CTRL 0x0138 +#define ZY7_SLCR_GEM1_RCLK_CTRL 0x013c +#define ZY7_SLCR_GEM0_CLK_CTRL 0x0140 +#define ZY7_SLCR_GEM1_CLK_CTRL 0x0144 +#define ZY7_SLCR_SMC_CLK_CTRL 0x0148 +#define ZY7_SLCR_LQSPI_CLK_CTRL 0x014c +#define ZY7_SLCR_SDIO_CLK_CTRL 0x0150 +#define ZY7_SLCR_UART_CLK_CTRL 0x0154 +#define ZY7_SLCR_SPI_CLK_CTRL 0x0158 +#define ZY7_SLCR_CAN_CLK_CTRL 0x015c +#define ZY7_SLCR_CAN_MIOCLK_CTRL 0x0160 +#define ZY7_SLCR_DBG_CLK_CTRL 0x0164 +#define ZY7_SLCR_PCAP_CLK_CTRL 0x0168 +#define ZY7_SLCR_TOPSW_CLK_CTRL 0x016c /* central intercnn clk ctrl */ +#define ZY7_SLCR_FPGA0_CLK_CTRL 0x0170 +#define ZY7_SLCR_FPGA1_CLK_CTRL 0x0180 +#define ZY7_SLCR_FPGA2_CLK_CTRL 0x0190 +#define ZY7_SLCR_FPGA3_CLK_CTRL 0x01a0 +#define ZY7_SLCR_CLK_621_TRUE 0x01c4 /* cpu clock ratio mode */ + +/* Reset controls. */ +#define ZY7_SLCR_PSS_RST_CTRL 0x0200 +#define ZY7_SLCR_PSS_RST_CTRL_SOFT_RESET (1<<0) +#define ZY7_SLCR_DDR_RST_CTRL 0x0204 +#define ZY7_SLCR_TOPSW_RST_CTRL 0x0208 +#define ZY7_SLCR_DMAC_RST_CTRL 0x020c +#define ZY7_SLCR_USB_RST_CTRL 0x0210 +#define ZY7_SLCR_GEM_RST_CTRL 0x0214 +#define ZY7_SLCR_SDIO_RST_CTRL 0x0218 +#define ZY7_SLCR_SPI_RST_CTRL 0x021c +#define ZY7_SLCR_CAN_RST_CTRL 0x0220 +#define ZY7_SLCR_I2C_RST_CTRL 0x0224 +#define ZY7_SLCR_UART_RST_CTRL 0x0228 +#define ZY7_SLCR_GPIO_RST_CTRL 0x022c +#define ZY7_SLCR_LQSPI_RST_CTRL 0x0230 +#define ZY7_SLCR_SMC_RST_CTRL 0x0234 +#define ZY7_SLCR_OCM_RST_CTRL 0x0238 +#define ZY7_SLCR_DEVCI_RST_CTRL 0x023c +#define ZY7_SLCR_FPGA_RST_CTRL 0x0240 +#define ZY7_SLCR_FPGA_RST_CTRL_FPGA3_OUT_RST (1<<3) +#define ZY7_SLCR_FPGA_RST_CTRL_FPGA2_OUT_RST (1<<2) +#define ZY7_SLCR_FPGA_RST_CTRL_FPGA1_OUT_RST (1<<1) +#define ZY7_SLCR_FPGA_RST_CTRL_FPGA0_OUT_RST (1<<0) +#define ZY7_SLCR_FPGA_RST_CTRL_RST_ALL 0xf +#define ZY7_SLCR_A9_CPU_RST_CTRL 0x0244 +#define ZY7_SLCR_RS_AWDT_CTRL 0x024c + +#define ZY7_SLCR_REBOOT_STAT 0x0258 +#define ZY7_SLCR_REBOOT_STAT_STATE_MASK (0xff<<24) +#define ZY7_SLCR_REBOOT_STAT_POR (1<<22) +#define ZY7_SLCR_REBOOT_STAT_SRST_B (1<<21) +#define ZY7_SLCR_REBOOT_STAT_DBG_RST (1<<20) +#define ZY7_SLCR_REBOOT_STAT_SLC_RST (1<<19) +#define ZY7_SLCR_REBOOT_STAT_AWDT1_RST (1<<18) +#define ZY7_SLCR_REBOOT_STAT_AWDT0_RST (1<<17) +#define ZY7_SLCR_REBOOT_STAT_SWDT_RST (1<<16) +#define ZY7_SLCR_REBOOT_STAT_BOOTROM_ERR_CODE_MASK (0xffff) +#define ZY7_SLCR_BOOT_MODE 0x025c +#define ZY7_SLCR_BOOT_MODE_PLL_BYPASS (1<<4) +#define ZY7_SLCR_BOOT_MODE_JTAG_INDEP (1<<3) +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_MASK 7 +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_JTAG 0 +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_QUAD_SPI 1 +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_NOR 2 +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_NAND 4 +#define ZY7_SLCR_BOOT_MODE_BOOTDEV_SD_CARD 5 +#define ZY7_SLCR_APU_CTRL 0x0300 +#define ZY7_SLCR_WDT_CLK_SEL 0x0304 + +#define ZY7_SLCR_PSS_IDCODE 0x0530 +#define ZY7_SLCR_PSS_IDCODE_REVISION_MASK (0xf<<28) +#define ZY7_SLCR_PSS_IDCODE_REVISION_SHIFT 28 +#define ZY7_SLCR_PSS_IDCODE_FAMILY_MASK (0x7f<<21) +#define ZY7_SLCR_PSS_IDCODE_FAMILY_SHIFT 21 +#define ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_MASK (0xf<<17) +#define ZY7_SLCR_PSS_IDCODE_SUB_FAMILY_SHIFT 17 +#define ZY7_SLCR_PSS_IDCODE_DEVICE_MASK (0x1f<<12) +#define ZY7_SLCR_PSS_IDCODE_DEVICE_SHIFT 12 +#define ZY7_SLCR_PSS_IDCODE_MNFR_ID_MASK (0x7ff<<1) +#define ZY7_SLCR_PSS_IDCODE_MNFR_ID_SHIFT 1 + +#define ZY7_SLCR_DDR_URGENT 0x0600 +#define ZY7_SLCR_DDR_CAL_START 0x060c +#define ZY7_SLCR_DDR_REF_START 0x0614 +#define ZY7_SLCR_DDR_CMD_STA 0x0618 +#define ZY7_SLCR_DDR_URGENT_SEL 0x061c +#define ZY7_SLCR_DDR_DFI_STATUS 0x0620 + +/* MIO Pin controls */ +#define ZY7_SLCR_MIO_PIN(n) (0x0700+(n)*4) /* 0-53 */ +#define ZY7_SLCR_MIO_PIN_RCVR_DIS (1<<13) +#define ZY7_SLCR_MIO_PIN_PULLUP_EN (1<<12) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_MASK (7<<9) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVTTL (0<<9) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS18 (1<<9) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS25 (2<<9) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_LVCMOS33 (3<<9) +#define ZY7_SLCR_MIO_PIN_IO_TYPE_HSTL (4<<9) +#define ZY7_SLCR_MIO_PIN_L2_SEL_MASK (3<<3) +#define ZY7_SLCR_MIO_PIN_L2_SEL_L3_MUX (0<<3) +#define ZY7_SLCR_MIO_PIN_L2_SEL_SRAM_NOR_CS0 (1<<3) +#define ZY7_SLCR_MIO_PIN_L2_SEL_NAND_CS (2<<3) +#define ZY7_SLCR_MIO_PIN_L2_SEL_SDIO0_PC (3<<3) +#define ZY7_SLCR_MIO_PIN_L1_SEL (1<<2) +#define ZY7_SLCR_MIO_PIN_L0_SEL (1<<1) +#define ZY7_SLCR_MIO_PIN_TRI_EN (1<<0) + +#define ZY7_SLCR_MIO_LOOPBACK 0x0804 +#define ZY7_SLCR_MIO_LOOPBACK_I2C0_I2C1 (1<<3) +#define ZY7_SLCR_MIO_LOOPBACK_CAN0_CAN1 (1<<2) +#define ZY7_SLCR_MIO_LOOPBACK_UA0_UA1 (1<<1) +#define ZY7_SLCR_MIO_LOOPBACK_SPI0_SPI1 (1<<0) +#define ZY7_SLCR_MIO_MST_TRI0 0x080c +#define ZY7_SLCR_MIO_MST_TRI1 0x0810 +#define ZY7_SLCR_SD0_WP_CD_SEL 0x0830 +#define ZY7_SLCR_SD1_WP_CD_SEL 0x0834 + +/* PS-PL level shifter control. */ +#define ZY7_SLCR_LVL_SHFTR_EN 0x900 +#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_IN_EN_0 (1<<3) /* PL to PS */ +#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_OUT_EN_0 (1<<2) /* PS to PL */ +#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_IN_EN_1 (1<<1) /* PL to PS */ +#define ZY7_SLCR_LVL_SHFTR_EN_USER_LVL_OUT_EN_1 (1<<0) /* PS to PL */ +#define ZY7_SLCR_LVL_SHFTR_EN_ALL 0xf + +#define ZY7_SLCR_OCM_CFG 0x0910 + +#define ZY7_SLCR_GPIOB_CTRL 0x0b00 +#define ZY7_SLCR_GPIOB_CFG_CMOS18 0x0b04 +#define ZY7_SLCR_GPIOB_CFG_CMOS25 0x0b08 +#define ZY7_SLCR_GPIOB_CFG_CMOS33 0x0b0c +#define ZY7_SLCR_GPIOB_CFG_LVTTL 0x0b10 +#define ZY7_SLCR_GPIOB_CFG_HSTL 0x0b14 +#define ZY7_SLCR_GPIOB_DRVR_BIAS_CTRL 0x0b18 + +#define ZY7_SLCR_DDRIOB_ADDR0 0x0b40 +#define ZY7_SLCR_DDRIOB_ADDR1 0x0b44 +#define ZY7_SLCR_DDRIOB_DATA0 0x0b48 +#define ZY7_SLCR_DDRIOB_DATA1 0x0b4c +#define ZY7_SLCR_DDRIOB_DIFF0 0x0b50 +#define ZY7_SLCR_DDRIOB_DIFF1 0x0b54 +#define ZY7_SLCR_DDRIOB_CLK 0x0b58 +#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_ADDR 0x0b5c +#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_DATA 0x0b60 +#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_DIFF 0x0b64 +#define ZY7_SLCR_DDRIOB_DRIVE_SLEW_CLK 0x0b68 +#define ZY7_SLCR_DDRIOB_DDR_CTRL 0x0b6c +#define ZY7_SLCR_DDRIOB_DCI_CTRL 0x0b70 +#define ZY7_SLCR_DDRIOB_DCI_STATUS 0x0b74 + +#ifdef _KERNEL +extern void zy7_slcr_preload_pl(void); +extern void zy7_slcr_postload_pl(int); +#endif +#endif /* _ZY7_SLCR_H_ */ |