diff options
-rw-r--r-- | sys/arm/conf/ZEDBOARD | 98 | ||||
-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 | ||||
-rw-r--r-- | sys/boot/fdt/dts/zedboard.dts | 215 | ||||
-rw-r--r-- | sys/dev/cadence/if_cgem.c | 1378 | ||||
-rw-r--r-- | sys/dev/cadence/if_cgem_hw.h | 381 | ||||
-rw-r--r-- | sys/dev/sdhci/sdhci_fdt.c | 300 |
19 files changed, 5510 insertions, 0 deletions
diff --git a/sys/arm/conf/ZEDBOARD b/sys/arm/conf/ZEDBOARD new file mode 100644 index 0000000..1008125 --- /dev/null +++ b/sys/arm/conf/ZEDBOARD @@ -0,0 +1,98 @@ +# ZEDBOARD -- Custom configuration for the Xilinx Zynq-7000 based +# ZedBoard (www.zedboard.org) +# +# For more information on this file, please read the handbook section on +# Kernel Configuration Files: +# +# http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html +# +# The handbook is also available locally in /usr/share/doc/handbook +# if you've installed the doc distribution, otherwise always see the +# FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the +# latest information. +# +# An exhaustive list of options and more detailed explanations of the +# device lines is also present in the ../../conf/NOTES and NOTES files. +# If you are in doubt as to the purpose or necessity of a line, check first +# in NOTES. +# +# $FreeBSD$ + +ident ZEDBOARD + +include "../xilinx/zedboard/std.zedboard" + +makeoptions MODULES_OVERRIDE="" +makeoptions WITHOUT_MODULES="ahc" + +options SCHED_4BSD #4BSD scheduler +options INET #InterNETworking +options INET6 #IPv6 communications protocols +options FFS #Berkeley Fast Filesystem +options SOFTUPDATES #Enable FFS soft updates support +options UFS_ACL #Support for access control lists +options UFS_DIRHASH #Improve performance on big directories +# options ROOTDEVNAME=\"ufs:mmcsd0s2a\" + +options NFSCL #Network Filesystem Client +# options NFSSD #Network Filesystem Server +# options NFSLOCKD #Network Lock Manager +# options NFS_ROOT #NFS usable as /, requires NFSCL +# options BOOTP_NFSROOT +# options BOOTP + +options MSDOSFS #MSDOS Filesystem +options CD9660 #ISO 9660 Filesystem +options PROCFS #Process filesystem (requires PSEUDOFS) +options PSEUDOFS #Pseudo-filesystem framework +options SCSI_DELAY=5000 #Delay (in ms) before probing SCSI +options KTRACE #ktrace(1) support +options SYSVSHM #SYSV-style shared memory +options SYSVMSG #SYSV-style message queues +options SYSVSEM #SYSV-style semaphores +options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions +options FREEBSD_BOOT_LOADER + +# Debugging +makeoptions DEBUG=-g +options DDB +options KDB +# options BREAK_TO_DEBUGGER + +# options INVARIANTS #Enable calls of extra sanity checking +# options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS +# options WITNESS #Enable checks to detect deadlocks and cycles +# options WITNESS_SKIPSPIN #Don't run witness on spinlocks for speed + +device loop +device random +device ether +device if_cgem # Zynq-7000 gig ethernet device +device mii +device pty +device uart +device gpio + +device md +device mmc # mmc/sd bus +device mmcsd # mmc/sd flash cards +device sdhci # generic sdhci +device bpf # Berkeley packet filter + +# USB support +device usb +options USB_DEBUG +#options USB_REQ_DEBUG +#options USB_VERBOSE +device ehci +device umass +device scbus # SCSI bus (required for SCSI) +device da # Direct Access (disks) +device axe # USB-Ethernet + + +# Flattened Device Tree +options FDT +# options FDT_DTB_STATIC +# makeoptions FDT_DTS_FILE=zedboard.dts + 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_ */ diff --git a/sys/boot/fdt/dts/zedboard.dts b/sys/boot/fdt/dts/zedboard.dts new file mode 100644 index 0000000..a95a2f7 --- /dev/null +++ b/sys/boot/fdt/dts/zedboard.dts @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2012 The 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +/dts-v1/; + +/ { + model = "zedboard"; + compatible = "digilent,zedboard"; + #address-cells = <1>; + #size-cells = <1>; + interrupt-parent = <&GIC>; + + // cpus { + // #address-cells = <1>; + // #size-cells = <0>; + // cpu@0 { + // device-type = "cpu"; + // model = "ARM Cortex-A9"; + // }; + // }; + + memory { + // First megabyte isn't accessible by all interconnect masters. + device_type = "memory"; + reg = <0x100000 0x1ff00000>; /* 511MB RAM at 0x100000 */ + }; + + // Zynq PS System registers. + // + ps7sys@f8000000 { + device_type = "soc"; + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xf8000000 0xf10000>; + + // SLCR block + slcr: slcr@7000 { + compatible = "xlnx,zy7_slcr"; + reg = <0x0 0x1000>; + }; + + // Interrupt controller + GIC: gic { + compatible = "arm,gic"; + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + reg = <0xf01000 0x1000>, // distributer registers + <0xf00100 0x0100>; // CPU if registers + }; + + // L2 cache controller + pl310@f02000 { + compatible = "arm,pl310"; + reg = <0xf02000 0x1000>; + interrupts = <34>; + interrupt-parent = <&GIC>; + }; + + // Device Config + devcfg: devcfg@7000 { + compatible = "xlnx,zy7_devcfg"; + reg = <0x7000 0x1000>; + interrupts = <40>; + interrupt-parent = <&GIC>; + }; + + // triple timer counters0,1 + ttc0: ttc@1000 { + compatible = "xlnx,ttc"; + reg = <0x1000 0x1000>; + }; + ttc1: ttc@2000 { + compatible = "xlnx,ttc"; + reg = <0x2000 0x1000>; + }; + + // ARM Cortex A9 TWD Timer + timer@f00600 { + compatible = "arm,mpcore-timers"; + clock-frequency = <333333333>; // 333Mhz + #address-cells = <1>; + #size-cells = <0>; + reg = <0xf00200 0x100>, // Global Timer Regs + <0xf00600 0x20>; // Private Timer Regs + interrupts = < 27 29 >; + interrupt-parent = <&GIC>; + }; + + // system watch-dog timer + swdt@5000 { + device_type = "watchdog"; + compatible = "xlnx,zy7_wdt"; + reg = <0x5000 0x1000>; + interrupts = <41>; + interrupt-parent = <&GIC>; + }; + + scuwdt@f00620 { + device_type = "watchdog"; + compatible = "arm,mpcore_wdt"; + reg = <0xf00620 0x20>; + interrupts = <30>; + interrupt-parent = <&GIC>; + reset = <1>; + }; + }; // pssys@f8000000 + + // Zynq PS I/O Peripheral registers. + // + ps7io@e0000000 { + device_type = "soc"; + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0xe0000000 0x300000>; + + // uart0: uart@0000 { + // device_type = "serial"; + // compatible = "cadence,uart"; + // reg = <0x0000 0x1000>; + // interrupts = <59>; + // interrupt-parent = <&GIC>; + // clock-frequency = <50000000>; + // }; + + uart1: uart@1000 { + device_type = "serial"; + compatible = "cadence,uart"; + reg = <0x1000 0x1000>; + interrupts = <82>; + interrupt-parent = <&GIC>; + clock-frequency = <50000000>; + current-speed = <115200>; + }; + + gpio: gpio@a000 { + compatible = "xlnx,zy7_gpio"; + reg = <0xa000 0x1000>; + interrupts = <52>; + interrupt-parent = <&GIC>; + }; + + // GigE + eth0: eth@b000 { + // device_type = "network"; + + compatible = "cadence,gem"; + reg = <0xb000 0x1000>; + interrupts = <54 55>; + interrupt-parent = <&GIC>; + }; + + // SDIO + sdhci0: sdhci@100000 { + compatible = "xlnx,zy7_sdhci"; + reg = <0x100000 0x1000>; + interrupts = <56>; + interrupt-parent = <&GIC>; + clock-frequency = <50000000>; + }; + + // QSPI + qspi0: qspi@d000 { + compatible = "xlnx,zy7_qspi"; + reg = <0xd000 0x1000>; + interrupts = <51>; + interrupt-parent = <&GIC>; + spi-clock = <50000000>; + ref-clock = <190476000>; + }; + + // USB + ehci0: ehci@2000 { + compatible = "xlnx,zy7_ehci"; + reg = <0x2000 0x1000>; + interrupts = <53>; + interrupt-parent = <&GIC>; + phy_vbus_ext; + }; + + }; // ps7io@e0000000 + + chosen { + stdin = &uart1; + stdout = &uart1; + }; +}; + diff --git a/sys/dev/cadence/if_cgem.c b/sys/dev/cadence/if_cgem.c new file mode 100644 index 0000000..77c92b5 --- /dev/null +++ b/sys/dev/cadence/if_cgem.c @@ -0,0 +1,1378 @@ +/*- + * 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: + * 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 network interface driver for Cadence GEM Gigabit Ethernet + * interface such as the one used in Xilinx Zynq-7000 SoC. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. GEM is covered in Ch. 16 + * and register definitions are in appendix B.18. + */ + +#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 <sys/mbuf.h> +#include <sys/module.h> +#include <sys/rman.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> + +#include <machine/bus.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_mib.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#include <net/bpf.h> +#include <net/bpfdesc.h> + +#include <dev/fdt/fdt_common.h> +#include <dev/ofw/ofw_bus.h> +#include <dev/ofw/ofw_bus_subr.h> + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/cadence/if_cgem_hw.h> + +#include "miibus_if.h" + +#define IF_CGEM_NAME "cgem" + +#define CGEM_NUM_RX_DESCS 256 /* size of receive descriptor ring */ +#define CGEM_NUM_TX_DESCS 256 /* size of transmit descriptor ring */ + +#define MAX_DESC_RING_SIZE (MAX(CGEM_NUM_RX_DESCS*sizeof(struct cgem_rx_desc),\ + CGEM_NUM_TX_DESCS*sizeof(struct cgem_tx_desc))) + + +/* Default for sysctl rxbufs. Must be < CGEM_NUM_RX_DESCS of course. */ +#define DEFAULT_NUM_RX_BUFS 64 /* number of receive bufs to queue. */ + +#define TX_MAX_DMA_SEGS 4 /* maximum segs in a tx mbuf dma */ + +#define CGEM_CKSUM_ASSIST (CSUM_IP | CSUM_TCP | CSUM_UDP | \ + CSUM_TCP_IPV6 | CSUM_UDP_IPV6) + +struct cgem_softc { + struct ifnet *ifp; + struct mtx sc_mtx; + device_t dev; + device_t miibus; + int if_old_flags; + struct resource *mem_res; + struct resource *irq_res; + void *intrhand; + struct callout tick_ch; + uint32_t net_ctl_shadow; + u_char eaddr[6]; + + bus_dma_tag_t desc_dma_tag; + bus_dma_tag_t mbuf_dma_tag; + + /* receive descriptor ring */ + struct cgem_rx_desc *rxring; + bus_addr_t rxring_physaddr; + struct mbuf *rxring_m[CGEM_NUM_RX_DESCS]; + bus_dmamap_t rxring_m_dmamap[CGEM_NUM_RX_DESCS]; + int rxring_hd_ptr; /* where to put rcv bufs */ + int rxring_tl_ptr; /* where to get receives */ + int rxring_queued; /* how many rcv bufs queued */ + bus_dmamap_t rxring_dma_map; + int rxbufs; /* tunable number rcv bufs */ + int rxoverruns; /* rx ring overruns */ + + /* transmit descriptor ring */ + struct cgem_tx_desc *txring; + bus_addr_t txring_physaddr; + struct mbuf *txring_m[CGEM_NUM_TX_DESCS]; + bus_dmamap_t txring_m_dmamap[CGEM_NUM_TX_DESCS]; + int txring_hd_ptr; /* where to put next xmits */ + int txring_tl_ptr; /* next xmit mbuf to free */ + int txring_queued; /* num xmits segs queued */ + bus_dmamap_t txring_dma_map; +}; + +#define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) +#define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) +#define BARRIER(sc, off, len, flags) \ + (bus_barrier((sc)->mem_res, (off), (len), (flags)) + +#define CGEM_LOCK(sc) mtx_lock(&(sc)->sc_mtx) +#define CGEM_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) +#define CGEM_LOCK_INIT(sc) \ + mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ + MTX_NETWORK_LOCK, MTX_DEF) +#define CGEM_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) +#define CGEM_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) + +static devclass_t cgem_devclass; + +static int cgem_probe(device_t dev); +static int cgem_attach(device_t dev); +static int cgem_detach(device_t dev); +static void cgem_tick(void *); +static void cgem_intr(void *); + +static void +cgem_get_mac(struct cgem_softc *sc, u_char eaddr[]) +{ + int i; + uint32_t rnd; + + /* See if boot loader gave us a MAC address already. */ + for (i = 0; i < 4; i++) { + uint32_t low = RD4(sc, CGEM_SPEC_ADDR_LOW(i)); + uint32_t high = RD4(sc, CGEM_SPEC_ADDR_HI(i)) & 0xffff; + if (low != 0 || high != 0) { + eaddr[0] = low & 0xff; + eaddr[1] = (low >> 8) & 0xff; + eaddr[2] = (low >> 16) & 0xff; + eaddr[3] = (low >> 24) & 0xff; + eaddr[4] = high & 0xff; + eaddr[5] = (high >> 8) & 0xff; + break; + } + } + + /* No MAC from boot loader? Assign a random one. */ + if (i == 4) { + rnd = arc4random(); + + eaddr[0] = 'b'; + eaddr[1] = 's'; + eaddr[2] = 'd'; + eaddr[3] = (rnd >> 16) & 0xff; + eaddr[4] = (rnd >> 8) & 0xff; + eaddr[5] = rnd & 0xff; + + device_printf(sc->dev, "no mac address found, assigning " + "random: %02x:%02x:%02x:%02x:%02x:%02x\n", + eaddr[0], eaddr[1], eaddr[2], + eaddr[3], eaddr[4], eaddr[5]); + + WR4(sc, CGEM_SPEC_ADDR_LOW(0), (eaddr[3] << 24) | + (eaddr[2] << 16) | (eaddr[1] << 8) | eaddr[0]); + WR4(sc, CGEM_SPEC_ADDR_HI(0), (eaddr[5] << 8) | eaddr[4]); + } +} + +/* cgem_mac_hash(): map 48-bit address to a 6-bit hash. + * The 6-bit hash corresponds to a bit in a 64-bit hash + * register. Setting that bit in the hash register enables + * reception of all frames with a destination address that hashes + * to that 6-bit value. + * + * The hash function is described in sec. 16.2.3 in the Zynq-7000 Tech + * Reference Manual. Bits 0-5 in the hash are the exclusive-or of + * every sixth bit in the destination address. + */ +static int +cgem_mac_hash(u_char eaddr[]) +{ + int hash; + int i, j; + + hash = 0; + for (i = 0; i < 6; i++) + for (j = i; j < 48; j += 6) + if ((eaddr[j >> 3] & (1 << (j & 7))) != 0) + hash ^= (1 << i); + + return hash; +} + +/* After any change in rx flags or multi-cast addresses, set up + * hash registers and net config register bits. + */ +static void +cgem_rx_filter(struct cgem_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + struct ifmultiaddr *ifma; + int index; + uint32_t hash_hi, hash_lo; + uint32_t net_cfg; + + hash_hi = 0; + hash_lo = 0; + + net_cfg = RD4(sc, CGEM_NET_CFG); + + net_cfg &= ~(CGEM_NET_CFG_MULTI_HASH_EN | + CGEM_NET_CFG_NO_BCAST | + CGEM_NET_CFG_COPY_ALL); + + if ((ifp->if_flags & IFF_PROMISC) != 0) + net_cfg |= CGEM_NET_CFG_COPY_ALL; + else { + if ((ifp->if_flags & IFF_BROADCAST) == 0) + net_cfg |= CGEM_NET_CFG_NO_BCAST; + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + hash_hi = 0xffffffff; + hash_lo = 0xffffffff; + } else { + if_maddr_rlock(ifp); + TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { + if (ifma->ifma_addr->sa_family != AF_LINK) + continue; + index = cgem_mac_hash( + LLADDR((struct sockaddr_dl *) + ifma->ifma_addr)); + if (index > 31) + hash_hi |= (1<<(index-32)); + else + hash_lo |= (1<<index); + } + if_maddr_runlock(ifp); + } + + if (hash_hi != 0 || hash_lo != 0) + net_cfg |= CGEM_NET_CFG_MULTI_HASH_EN; + } + + WR4(sc, CGEM_HASH_TOP, hash_hi); + WR4(sc, CGEM_HASH_BOT, hash_lo); + WR4(sc, CGEM_NET_CFG, net_cfg); +} + +/* For bus_dmamap_load() callback. */ +static void +cgem_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) +{ + + if (nsegs != 1 || error != 0) + return; + *(bus_addr_t *)arg = segs[0].ds_addr; +} + +/* Create DMA'able descriptor rings. */ +static int +cgem_setup_descs(struct cgem_softc *sc) +{ + int i, err; + + sc->txring = NULL; + sc->rxring = NULL; + + /* Allocate non-cached DMA space for RX and TX descriptors. + */ + err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + MAX_DESC_RING_SIZE, + 1, + MAX_DESC_RING_SIZE, + 0, + busdma_lock_mutex, + &sc->sc_mtx, + &sc->desc_dma_tag); + if (err) + return (err); + + /* Set up a bus_dma_tag for mbufs. */ + err = bus_dma_tag_create(bus_get_dma_tag(sc->dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, + BUS_SPACE_MAXADDR, + NULL, NULL, + MCLBYTES, + TX_MAX_DMA_SEGS, + MCLBYTES, + 0, + busdma_lock_mutex, + &sc->sc_mtx, + &sc->mbuf_dma_tag); + if (err) + return (err); + + /* Allocate DMA memory in non-cacheable space. */ + err = bus_dmamem_alloc(sc->desc_dma_tag, + (void **)&sc->rxring, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, + &sc->rxring_dma_map); + if (err) + return (err); + + /* Load descriptor DMA memory. */ + err = bus_dmamap_load(sc->desc_dma_tag, sc->rxring_dma_map, + (void *)sc->rxring, + CGEM_NUM_RX_DESCS*sizeof(struct cgem_rx_desc), + cgem_getaddr, &sc->rxring_physaddr, + BUS_DMA_NOWAIT); + if (err) + return (err); + + /* Initialize RX descriptors. */ + for (i = 0; i < CGEM_NUM_RX_DESCS; i++) { + sc->rxring[i].addr = CGEM_RXDESC_OWN; + sc->rxring[i].ctl = 0; + sc->rxring_m[i] = NULL; + err = bus_dmamap_create(sc->mbuf_dma_tag, 0, + &sc->rxring_m_dmamap[i]); + if (err) + return (err); + } + sc->rxring[CGEM_NUM_RX_DESCS - 1].addr |= CGEM_RXDESC_WRAP; + + sc->rxring_hd_ptr = 0; + sc->rxring_tl_ptr = 0; + sc->rxring_queued = 0; + + /* Allocate DMA memory for TX descriptors in non-cacheable space. */ + err = bus_dmamem_alloc(sc->desc_dma_tag, + (void **)&sc->txring, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT, + &sc->txring_dma_map); + if (err) + return (err); + + /* Load TX descriptor DMA memory. */ + err = bus_dmamap_load(sc->desc_dma_tag, sc->txring_dma_map, + (void *)sc->txring, + CGEM_NUM_TX_DESCS*sizeof(struct cgem_tx_desc), + cgem_getaddr, &sc->txring_physaddr, + BUS_DMA_NOWAIT); + if (err) + return (err); + + /* Initialize TX descriptor ring. */ + for (i = 0; i < CGEM_NUM_TX_DESCS; i++) { + sc->txring[i].addr = 0; + sc->txring[i].ctl = CGEM_TXDESC_USED; + sc->txring_m[i] = NULL; + err = bus_dmamap_create(sc->mbuf_dma_tag, 0, + &sc->txring_m_dmamap[i]); + if (err) + return (err); + } + sc->txring[CGEM_NUM_TX_DESCS - 1].ctl |= CGEM_TXDESC_WRAP; + + sc->txring_hd_ptr = 0; + sc->txring_tl_ptr = 0; + sc->txring_queued = 0; + + return (0); +} + +/* Fill receive descriptor ring with mbufs. */ +static void +cgem_fill_rqueue(struct cgem_softc *sc) +{ + struct mbuf *m = NULL; + bus_dma_segment_t segs[TX_MAX_DMA_SEGS]; + int nsegs; + + CGEM_ASSERT_LOCKED(sc); + + while (sc->rxring_queued < sc->rxbufs) { + /* Get a cluster mbuf. */ + m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); + if (m == NULL) + break; + + m->m_len = MCLBYTES; + m->m_pkthdr.len = MCLBYTES; + m->m_pkthdr.rcvif = sc->ifp; + + /* Load map and plug in physical address. */ + if (bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[sc->rxring_hd_ptr], m, + segs, &nsegs, BUS_DMA_NOWAIT)) { + /* XXX: warn? */ + m_free(m); + break; + } + sc->rxring_m[sc->rxring_hd_ptr] = m; + + /* Sync cache with receive buffer. */ + bus_dmamap_sync(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[sc->rxring_hd_ptr], + BUS_DMASYNC_PREREAD); + + /* Write rx descriptor and increment head pointer. */ + sc->rxring[sc->rxring_hd_ptr].ctl = 0; + if (sc->rxring_hd_ptr == CGEM_NUM_RX_DESCS - 1) { + sc->rxring[sc->rxring_hd_ptr].addr = segs[0].ds_addr | + CGEM_RXDESC_WRAP; + sc->rxring_hd_ptr = 0; + } else + sc->rxring[sc->rxring_hd_ptr++].addr = segs[0].ds_addr; + + sc->rxring_queued++; + } +} + +/* Pull received packets off of receive descriptor ring. */ +static void +cgem_recv(struct cgem_softc *sc) +{ + struct ifnet *ifp = sc->ifp; + struct mbuf *m; + uint32_t ctl; + + CGEM_ASSERT_LOCKED(sc); + + /* Pick up all packets in which the OWN bit is set. */ + while (sc->rxring_queued > 0 && + (sc->rxring[sc->rxring_tl_ptr].addr & CGEM_RXDESC_OWN) != 0) { + + ctl = sc->rxring[sc->rxring_tl_ptr].ctl; + + /* Grab filled mbuf. */ + m = sc->rxring_m[sc->rxring_tl_ptr]; + sc->rxring_m[sc->rxring_tl_ptr] = NULL; + + /* Sync cache with receive buffer. */ + bus_dmamap_sync(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[sc->rxring_tl_ptr], + BUS_DMASYNC_POSTREAD); + + /* Unload dmamap. */ + bus_dmamap_unload(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[sc->rxring_tl_ptr]); + + /* Increment tail pointer. */ + if (++sc->rxring_tl_ptr == CGEM_NUM_RX_DESCS) + sc->rxring_tl_ptr = 0; + sc->rxring_queued--; + + /* Check FCS and make sure entire packet landed in one mbuf + * cluster (which is much bigger than the largest ethernet + * packet). + */ + if ((ctl & CGEM_RXDESC_BAD_FCS) != 0 || + (ctl & (CGEM_RXDESC_SOF | CGEM_RXDESC_EOF)) != + (CGEM_RXDESC_SOF | CGEM_RXDESC_EOF)) { + /* discard. */ + m_free(m); + ifp->if_ierrors++; + continue; + } + + /* Hand it off to upper layers. */ + m->m_data += ETHER_ALIGN; + m->m_len = (ctl & CGEM_RXDESC_LENGTH_MASK); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len; + + /* Are we using hardware checksumming? Check the + * status in the receive descriptor. + */ + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) { + /* TCP or UDP checks out, IP checks out too. */ + if ((ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == + CGEM_RXDESC_CKSUM_STAT_TCP_GOOD || + (ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == + CGEM_RXDESC_CKSUM_STAT_UDP_GOOD) { + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID | + CSUM_DATA_VALID | CSUM_PSEUDO_HDR; + m->m_pkthdr.csum_data = 0xffff; + } else if ((ctl & CGEM_RXDESC_CKSUM_STAT_MASK) == + CGEM_RXDESC_CKSUM_STAT_IP_GOOD) { + /* Only IP checks out. */ + m->m_pkthdr.csum_flags |= + CSUM_IP_CHECKED | CSUM_IP_VALID; + m->m_pkthdr.csum_data = 0xffff; + } + } + + ifp->if_ipackets++; + CGEM_UNLOCK(sc); + (*ifp->if_input)(ifp, m); + CGEM_LOCK(sc); + } +} + +/* Find completed transmits and free their mbufs. */ +static void +cgem_clean_tx(struct cgem_softc *sc) +{ + struct mbuf *m; + uint32_t ctl; + + CGEM_ASSERT_LOCKED(sc); + + /* free up finished transmits. */ + while (sc->txring_queued > 0 && + ((ctl = sc->txring[sc->txring_tl_ptr].ctl) & + CGEM_TXDESC_USED) != 0) { + + /* Sync cache. nop? */ + bus_dmamap_sync(sc->mbuf_dma_tag, + sc->txring_m_dmamap[sc->txring_tl_ptr], + BUS_DMASYNC_POSTWRITE); + + /* Unload DMA map. */ + bus_dmamap_unload(sc->mbuf_dma_tag, + sc->txring_m_dmamap[sc->txring_tl_ptr]); + + /* Free up the mbuf. */ + m = sc->txring_m[sc->txring_tl_ptr]; + sc->txring_m[sc->txring_tl_ptr] = NULL; + m_freem(m); + + /* Check the status. */ + if ((ctl & CGEM_TXDESC_AHB_ERR) != 0) { + /* Serious bus error. log to console. */ + device_printf(sc->dev, "cgem_clean_tx: Whoa! " + "AHB error, addr=0x%x\n", + sc->txring[sc->txring_tl_ptr].addr); + } else if ((ctl & (CGEM_TXDESC_RETRY_ERR | + CGEM_TXDESC_LATE_COLL)) != 0) { + sc->ifp->if_oerrors++; + } else + sc->ifp->if_opackets++; + + /* If the packet spanned more than one tx descriptor, + * skip descriptors until we find the end so that only + * start-of-frame descriptors are processed. + */ + while ((ctl & CGEM_TXDESC_LAST_BUF) == 0) { + if ((ctl & CGEM_TXDESC_WRAP) != 0) + sc->txring_tl_ptr = 0; + else + sc->txring_tl_ptr++; + sc->txring_queued--; + + ctl = sc->txring[sc->txring_tl_ptr].ctl; + + sc->txring[sc->txring_tl_ptr].ctl = + ctl | CGEM_TXDESC_USED; + } + + /* Next descriptor. */ + if ((ctl & CGEM_TXDESC_WRAP) != 0) + sc->txring_tl_ptr = 0; + else + sc->txring_tl_ptr++; + sc->txring_queued--; + } +} + +/* Start transmits. */ +static void +cgem_start_locked(struct ifnet *ifp) +{ + struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; + struct mbuf *m; + bus_dma_segment_t segs[TX_MAX_DMA_SEGS]; + uint32_t ctl; + int i, nsegs, wrap, err; + + CGEM_ASSERT_LOCKED(sc); + + if ((ifp->if_drv_flags & IFF_DRV_OACTIVE) != 0) + return; + + for (;;) { + /* Check that there is room in the descriptor ring. */ + if (sc->txring_queued >= CGEM_NUM_TX_DESCS - + TX_MAX_DMA_SEGS - 1) { + + /* Try to make room. */ + cgem_clean_tx(sc); + + /* Still no room? */ + if (sc->txring_queued >= CGEM_NUM_TX_DESCS - + TX_MAX_DMA_SEGS - 1) { + ifp->if_drv_flags |= IFF_DRV_OACTIVE; + break; + } + } + + /* Grab next transmit packet. */ + IFQ_DRV_DEQUEUE(&ifp->if_snd, m); + if (m == NULL) + break; + + /* Load DMA map. */ + err = bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, + sc->txring_m_dmamap[sc->txring_hd_ptr], + m, segs, &nsegs, BUS_DMA_NOWAIT); + if (err == EFBIG) { + /* Too many segments! defrag and try again. */ + struct mbuf *m2 = m_defrag(m, M_NOWAIT); + + if (m2 == NULL) { + m_freem(m); + continue; + } + m = m2; + err = bus_dmamap_load_mbuf_sg(sc->mbuf_dma_tag, + sc->txring_m_dmamap[sc->txring_hd_ptr], + m, segs, &nsegs, BUS_DMA_NOWAIT); + } + if (err) { + /* Give up. */ + m_freem(m); + continue; + } + sc->txring_m[sc->txring_hd_ptr] = m; + + /* Sync tx buffer with cache. */ + bus_dmamap_sync(sc->mbuf_dma_tag, + sc->txring_m_dmamap[sc->txring_hd_ptr], + BUS_DMASYNC_PREWRITE); + + /* Set wrap flag if next packet might run off end of ring. */ + wrap = sc->txring_hd_ptr + nsegs + TX_MAX_DMA_SEGS >= + CGEM_NUM_TX_DESCS; + + /* Fill in the TX descriptors back to front so that USED + * bit in first descriptor is cleared last. + */ + for (i = nsegs - 1; i >= 0; i--) { + /* Descriptor address. */ + sc->txring[sc->txring_hd_ptr + i].addr = + segs[i].ds_addr; + + /* Descriptor control word. */ + ctl = segs[i].ds_len; + if (i == nsegs - 1) { + ctl |= CGEM_TXDESC_LAST_BUF; + if (wrap) + ctl |= CGEM_TXDESC_WRAP; + } + sc->txring[sc->txring_hd_ptr + i].ctl = ctl; + + if (i != 0) + sc->txring_m[sc->txring_hd_ptr + i] = NULL; + } + + if (wrap) + sc->txring_hd_ptr = 0; + else + sc->txring_hd_ptr += nsegs; + sc->txring_queued += nsegs; + + /* Kick the transmitter. */ + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow | + CGEM_NET_CTRL_START_TX); + } + +} + +static void +cgem_start(struct ifnet *ifp) +{ + struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; + + CGEM_LOCK(sc); + cgem_start_locked(ifp); + CGEM_UNLOCK(sc); +} + +/* Respond to changes in media. */ +static void +cgem_media_update(struct cgem_softc *sc, int active) +{ + uint32_t net_cfg; + + CGEM_ASSERT_LOCKED(sc); + + /* Update hardware to reflect phy status. */ + net_cfg = RD4(sc, CGEM_NET_CFG); + net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN | + CGEM_NET_CFG_FULL_DUPLEX); + + if (IFM_SUBTYPE(active) == IFM_1000_T) + net_cfg |= (CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN); + else if (IFM_SUBTYPE(active) == IFM_100_TX) + net_cfg |= CGEM_NET_CFG_SPEED100; + + if ((active & IFM_FDX) != 0) + net_cfg |= CGEM_NET_CFG_FULL_DUPLEX; + WR4(sc, CGEM_NET_CFG, net_cfg); +} + +static void +cgem_tick(void *arg) +{ + struct cgem_softc *sc = (struct cgem_softc *)arg; + struct mii_data *mii; + int active; + + CGEM_ASSERT_LOCKED(sc); + + /* Poll the phy. */ + if (sc->miibus != NULL) { + mii = device_get_softc(sc->miibus); + active = mii->mii_media_active; + mii_tick(mii); + if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) == + (IFM_ACTIVE | IFM_AVALID) && + active != mii->mii_media_active) + cgem_media_update(sc, mii->mii_media_active); + } + + /* Next callout in one second. */ + callout_reset(&sc->tick_ch, hz, cgem_tick, sc); +} + +/* Interrupt handler. */ +static void +cgem_intr(void *arg) +{ + struct cgem_softc *sc = (struct cgem_softc *)arg; + uint32_t istatus; + + CGEM_LOCK(sc); + + if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) { + CGEM_UNLOCK(sc); + return; + } + + istatus = RD4(sc, CGEM_INTR_STAT); + WR4(sc, CGEM_INTR_STAT, istatus & + (CGEM_INTR_RX_COMPLETE | CGEM_INTR_TX_USED_READ | + CGEM_INTR_RX_OVERRUN | CGEM_INTR_HRESP_NOT_OK)); + + /* Hresp not ok. Something very bad with DMA. Try to clear. */ + if ((istatus & CGEM_INTR_HRESP_NOT_OK) != 0) { + printf("cgem_intr: hresp not okay! rx_status=0x%x\n", + RD4(sc, CGEM_RX_STAT)); + WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_HRESP_NOT_OK); + } + + /* Transmitter has idled. Free up any spent transmit buffers. */ + if ((istatus & CGEM_INTR_TX_USED_READ) != 0) + cgem_clean_tx(sc); + + /* Packets received or overflow. */ + if ((istatus & (CGEM_INTR_RX_COMPLETE | CGEM_INTR_RX_OVERRUN)) != 0) { + cgem_recv(sc); + cgem_fill_rqueue(sc); + if ((istatus & CGEM_INTR_RX_OVERRUN) != 0) { + /* Clear rx status register. */ + sc->rxoverruns++; + WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_ALL); + } + } + + CGEM_UNLOCK(sc); +} + +/* Reset hardware. */ +static void +cgem_reset(struct cgem_softc *sc) +{ + + CGEM_ASSERT_LOCKED(sc); + + WR4(sc, CGEM_NET_CTRL, 0); + WR4(sc, CGEM_NET_CFG, 0); + WR4(sc, CGEM_NET_CTRL, CGEM_NET_CTRL_CLR_STAT_REGS); + WR4(sc, CGEM_TX_STAT, CGEM_TX_STAT_ALL); + WR4(sc, CGEM_RX_STAT, CGEM_RX_STAT_ALL); + WR4(sc, CGEM_INTR_DIS, CGEM_INTR_ALL); + WR4(sc, CGEM_HASH_BOT, 0); + WR4(sc, CGEM_HASH_TOP, 0); + WR4(sc, CGEM_TX_QBAR, 0); /* manual says do this. */ + WR4(sc, CGEM_RX_QBAR, 0); + + /* Get management port running even if interface is down. */ + WR4(sc, CGEM_NET_CFG, + CGEM_NET_CFG_DBUS_WIDTH_32 | + CGEM_NET_CFG_MDC_CLK_DIV_64); + + sc->net_ctl_shadow = CGEM_NET_CTRL_MGMT_PORT_EN; + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); +} + +/* Bring up the hardware. */ +static void +cgem_config(struct cgem_softc *sc) +{ + uint32_t net_cfg; + uint32_t dma_cfg; + + CGEM_ASSERT_LOCKED(sc); + + /* Program Net Config Register. */ + net_cfg = CGEM_NET_CFG_DBUS_WIDTH_32 | + CGEM_NET_CFG_MDC_CLK_DIV_64 | + CGEM_NET_CFG_FCS_REMOVE | + CGEM_NET_CFG_RX_BUF_OFFSET(ETHER_ALIGN) | + CGEM_NET_CFG_GIGE_EN | + CGEM_NET_CFG_FULL_DUPLEX | + CGEM_NET_CFG_SPEED100; + + /* Enable receive checksum offloading? */ + if ((sc->ifp->if_capenable & IFCAP_RXCSUM) != 0) + net_cfg |= CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN; + + WR4(sc, CGEM_NET_CFG, net_cfg); + + /* Program DMA Config Register. */ + dma_cfg = CGEM_DMA_CFG_RX_BUF_SIZE(MCLBYTES) | + CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_8K | + CGEM_DMA_CFG_TX_PKTBUF_MEMSZ_SEL | + CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_16; + + /* Enable transmit checksum offloading? */ + if ((sc->ifp->if_capenable & IFCAP_TXCSUM) != 0) + dma_cfg |= CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN; + + WR4(sc, CGEM_DMA_CFG, dma_cfg); + + /* Write the rx and tx descriptor ring addresses to the QBAR regs. */ + WR4(sc, CGEM_RX_QBAR, (uint32_t) sc->rxring_physaddr); + WR4(sc, CGEM_TX_QBAR, (uint32_t) sc->txring_physaddr); + + /* Enable rx and tx. */ + sc->net_ctl_shadow |= (CGEM_NET_CTRL_TX_EN | CGEM_NET_CTRL_RX_EN); + WR4(sc, CGEM_NET_CTRL, sc->net_ctl_shadow); + + /* Set up interrupts. */ + WR4(sc, CGEM_INTR_EN, + CGEM_INTR_RX_COMPLETE | CGEM_INTR_TX_USED_READ | + CGEM_INTR_RX_OVERRUN | CGEM_INTR_HRESP_NOT_OK); +} + +/* Turn on interface and load up receive ring with buffers. */ +static void +cgem_init_locked(struct cgem_softc *sc) +{ + struct mii_data *mii; + + CGEM_ASSERT_LOCKED(sc); + + if ((sc->ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) + return; + + cgem_config(sc); + cgem_fill_rqueue(sc); + + sc->ifp->if_drv_flags |= IFF_DRV_RUNNING; + sc->ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; + + mii = device_get_softc(sc->miibus); + mii_pollstat(mii); + cgem_media_update(sc, mii->mii_media_active); + cgem_start_locked(sc->ifp); + + callout_reset(&sc->tick_ch, hz, cgem_tick, sc); +} + +static void +cgem_init(void *arg) +{ + struct cgem_softc *sc = (struct cgem_softc *)arg; + + CGEM_LOCK(sc); + cgem_init_locked(sc); + CGEM_UNLOCK(sc); +} + +/* Turn off interface. Free up any buffers in transmit or receive queues. */ +static void +cgem_stop(struct cgem_softc *sc) +{ + int i; + + CGEM_ASSERT_LOCKED(sc); + + callout_stop(&sc->tick_ch); + + /* Shut down hardware. */ + cgem_reset(sc); + + /* Clear out transmit queue. */ + for (i = 0; i < CGEM_NUM_TX_DESCS; i++) { + sc->txring[i].ctl = CGEM_TXDESC_USED; + sc->txring[i].addr = 0; + if (sc->txring_m[i]) { + bus_dmamap_unload(sc->mbuf_dma_tag, + sc->txring_m_dmamap[i]); + m_freem(sc->txring_m[i]); + sc->txring_m[i] = NULL; + } + } + sc->txring[CGEM_NUM_TX_DESCS - 1].ctl |= CGEM_TXDESC_WRAP; + + sc->txring_hd_ptr = 0; + sc->txring_tl_ptr = 0; + sc->txring_queued = 0; + + /* Clear out receive queue. */ + for (i = 0; i < CGEM_NUM_RX_DESCS; i++) { + sc->rxring[i].addr = CGEM_RXDESC_OWN; + sc->rxring[i].ctl = 0; + if (sc->rxring_m[i]) { + /* Unload dmamap. */ + bus_dmamap_unload(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[sc->rxring_tl_ptr]); + + m_freem(sc->rxring_m[i]); + sc->rxring_m[i] = NULL; + } + } + sc->rxring[CGEM_NUM_RX_DESCS - 1].addr |= CGEM_RXDESC_WRAP; + + sc->rxring_hd_ptr = 0; + sc->rxring_tl_ptr = 0; + sc->rxring_queued = 0; +} + + +static int +cgem_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct cgem_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct mii_data *mii; + int error = 0, mask; + + switch (cmd) { + case SIOCSIFFLAGS: + CGEM_LOCK(sc); + if ((ifp->if_flags & IFF_UP) != 0) { + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + if (((ifp->if_flags ^ sc->if_old_flags) & + (IFF_PROMISC | IFF_ALLMULTI)) != 0) { + cgem_rx_filter(sc); + } + } else { + cgem_init_locked(sc); + } + } else if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + ifp->if_drv_flags &= ~IFF_DRV_RUNNING; + cgem_stop(sc); + } + sc->if_old_flags = ifp->if_flags; + CGEM_UNLOCK(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + /* Set up multi-cast filters. */ + if ((ifp->if_drv_flags & IFF_DRV_RUNNING) != 0) { + CGEM_LOCK(sc); + cgem_rx_filter(sc); + CGEM_UNLOCK(sc); + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + mii = device_get_softc(sc->miibus); + error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); + break; + + case SIOCSIFCAP: + CGEM_LOCK(sc); + mask = ifp->if_capenable ^ ifr->ifr_reqcap; + + if ((mask & IFCAP_TXCSUM) != 0) { + if ((ifr->ifr_reqcap & IFCAP_TXCSUM) != 0) { + /* Turn on TX checksumming. */ + ifp->if_capenable |= (IFCAP_TXCSUM | + IFCAP_TXCSUM_IPV6); + ifp->if_hwassist |= CGEM_CKSUM_ASSIST; + + WR4(sc, CGEM_DMA_CFG, + RD4(sc, CGEM_DMA_CFG) | + CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN); + } else { + /* Turn off TX checksumming. */ + ifp->if_capenable &= ~(IFCAP_TXCSUM | + IFCAP_TXCSUM_IPV6); + ifp->if_hwassist &= ~CGEM_CKSUM_ASSIST; + + WR4(sc, CGEM_DMA_CFG, + RD4(sc, CGEM_DMA_CFG) & + ~CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN); + } + } + if ((mask & IFCAP_RXCSUM) != 0) { + if ((ifr->ifr_reqcap & IFCAP_RXCSUM) != 0) { + /* Turn on RX checksumming. */ + ifp->if_capenable |= (IFCAP_RXCSUM | + IFCAP_RXCSUM_IPV6); + WR4(sc, CGEM_NET_CFG, + RD4(sc, CGEM_NET_CFG) | + CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN); + } else { + /* Turn off RX checksumming. */ + ifp->if_capenable &= ~(IFCAP_RXCSUM | + IFCAP_RXCSUM_IPV6); + WR4(sc, CGEM_NET_CFG, + RD4(sc, CGEM_NET_CFG) & + ~CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN); + } + } + + CGEM_UNLOCK(sc); + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + return (error); +} + +/* MII bus support routines. + */ +static void +cgem_child_detached(device_t dev, device_t child) +{ + struct cgem_softc *sc = device_get_softc(dev); + if (child == sc->miibus) + sc->miibus = NULL; +} + +static int +cgem_ifmedia_upd(struct ifnet *ifp) +{ + struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->miibus); + CGEM_LOCK(sc); + mii_mediachg(mii); + CGEM_UNLOCK(sc); + return (0); +} + +static void +cgem_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc; + struct mii_data *mii; + + mii = device_get_softc(sc->miibus); + CGEM_LOCK(sc); + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; + CGEM_UNLOCK(sc); +} + +static int +cgem_miibus_readreg(device_t dev, int phy, int reg) +{ + struct cgem_softc *sc = device_get_softc(dev); + int tries, val; + + WR4(sc, CGEM_PHY_MAINT, + CGEM_PHY_MAINT_CLAUSE_22 | CGEM_PHY_MAINT_MUST_10 | + CGEM_PHY_MAINT_OP_READ | + (phy << CGEM_PHY_MAINT_PHY_ADDR_SHIFT) | + (reg << CGEM_PHY_MAINT_REG_ADDR_SHIFT)); + + /* Wait for completion. */ + tries=0; + while ((RD4(sc, CGEM_NET_STAT) & CGEM_NET_STAT_PHY_MGMT_IDLE) == 0) { + DELAY(5); + if (++tries > 200) { + device_printf(dev, "phy read timeout: %d\n", reg); + return (-1); + } + } + + val = RD4(sc, CGEM_PHY_MAINT) & CGEM_PHY_MAINT_DATA_MASK; + + return (val); +} + +static int +cgem_miibus_writereg(device_t dev, int phy, int reg, int data) +{ + struct cgem_softc *sc = device_get_softc(dev); + int tries; + + WR4(sc, CGEM_PHY_MAINT, + CGEM_PHY_MAINT_CLAUSE_22 | CGEM_PHY_MAINT_MUST_10 | + CGEM_PHY_MAINT_OP_WRITE | + (phy << CGEM_PHY_MAINT_PHY_ADDR_SHIFT) | + (reg << CGEM_PHY_MAINT_REG_ADDR_SHIFT) | + (data & CGEM_PHY_MAINT_DATA_MASK)); + + /* Wait for completion. */ + tries = 0; + while ((RD4(sc, CGEM_NET_STAT) & CGEM_NET_STAT_PHY_MGMT_IDLE) == 0) { + DELAY(5); + if (++tries > 200) { + device_printf(dev, "phy write timeout: %d\n", reg); + return (-1); + } + } + + return (0); +} + + +static int +cgem_probe(device_t dev) +{ + + if (!ofw_bus_is_compatible(dev, "cadence,gem")) + return (ENXIO); + + device_set_desc(dev, "Cadence CGEM Gigabit Ethernet Interface"); + return (0); +} + +static int +cgem_attach(device_t dev) +{ + struct cgem_softc *sc = device_get_softc(dev); + struct ifnet *ifp = NULL; + int rid, err; + u_char eaddr[ETHER_ADDR_LEN]; + + sc->dev = dev; + CGEM_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); + } + + /* Get IRQ resource. */ + rid = 0; + sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_ACTIVE); + if (sc->irq_res == NULL) { + device_printf(dev, "could not allocate interrupt resource.\n"); + cgem_detach(dev); + return (ENOMEM); + } + + ifp = sc->ifp = if_alloc(IFT_ETHER); + if (ifp == NULL) { + device_printf(dev, "could not allocate ifnet structure\n"); + cgem_detach(dev); + return (ENOMEM); + } + + CGEM_LOCK(sc); + + /* Reset hardware. */ + cgem_reset(sc); + + /* Attach phy to mii bus. */ + err = mii_attach(dev, &sc->miibus, ifp, + cgem_ifmedia_upd, cgem_ifmedia_sts, + BMSR_DEFCAPMASK, MII_PHY_ANY, MII_OFFSET_ANY, 0); + if (err) { + CGEM_UNLOCK(sc); + device_printf(dev, "attaching PHYs failed\n"); + cgem_detach(dev); + return (err); + } + + /* Set up TX and RX descriptor area. */ + err = cgem_setup_descs(sc); + if (err) { + CGEM_UNLOCK(sc); + device_printf(dev, "could not set up dma mem for descs.\n"); + cgem_detach(dev); + return (ENOMEM); + } + + /* Get a MAC address. */ + cgem_get_mac(sc, eaddr); + + /* Start ticks. */ + callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); + + /* Set up ifnet structure. */ + ifp->if_softc = sc; + if_initname(ifp, IF_CGEM_NAME, device_get_unit(dev)); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_start = cgem_start; + ifp->if_ioctl = cgem_ioctl; + ifp->if_init = cgem_init; + ifp->if_capabilities |= IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6; + /* XXX: disable hw checksumming for now. */ + ifp->if_hwassist = 0; + ifp->if_capenable = ifp->if_capabilities & + ~(IFCAP_HWCSUM | IFCAP_HWCSUM_IPV6); + IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); + ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN; + IFQ_SET_READY(&ifp->if_snd); + + sc->if_old_flags = ifp->if_flags; + sc->rxbufs = DEFAULT_NUM_RX_BUFS; + + ether_ifattach(ifp, eaddr); + + err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE | + INTR_EXCL, NULL, cgem_intr, sc, &sc->intrhand); + if (err) { + CGEM_UNLOCK(sc); + device_printf(dev, "could not set interrupt handler.\n"); + ether_ifdetach(ifp); + cgem_detach(dev); + return (err); + } + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "rxbufs", CTLFLAG_RW, + &sc->rxbufs, 0, + "Number receive buffers to provide"); + + SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "_rxoverruns", CTLFLAG_RD, + &sc->rxoverruns, 0, + "Receive ring overrun events"); + + CGEM_UNLOCK(sc); + + return (0); +} + +static int +cgem_detach(device_t dev) +{ + struct cgem_softc *sc = device_get_softc(dev); + int i; + + if (sc == NULL) + return (ENODEV); + + if (device_is_attached(dev)) { + CGEM_LOCK(sc); + cgem_stop(sc); + CGEM_UNLOCK(sc); + callout_drain(&sc->tick_ch); + sc->ifp->if_flags &= ~IFF_UP; + ether_ifdetach(sc->ifp); + } + + if (sc->miibus != NULL) { + device_delete_child(dev, sc->miibus); + sc->miibus = NULL; + } + + /* Release resrouces. */ + if (sc->mem_res != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res), sc->mem_res); + sc->mem_res = NULL; + } + if (sc->irq_res != NULL) { + if (sc->intrhand) + bus_teardown_intr(dev, sc->irq_res, sc->intrhand); + bus_release_resource(dev, SYS_RES_IRQ, + rman_get_rid(sc->irq_res), sc->irq_res); + sc->irq_res = NULL; + } + + /* Release DMA resources. */ + if (sc->rxring_dma_map != NULL) { + bus_dmamem_free(sc->desc_dma_tag, sc->rxring, + sc->rxring_dma_map); + sc->rxring_dma_map = NULL; + for (i = 0; i < CGEM_NUM_RX_DESCS; i++) + if (sc->rxring_m_dmamap[i] != NULL) { + bus_dmamap_destroy(sc->mbuf_dma_tag, + sc->rxring_m_dmamap[i]); + sc->rxring_m_dmamap[i] = NULL; + } + } + if (sc->txring_dma_map != NULL) { + bus_dmamem_free(sc->desc_dma_tag, sc->txring, + sc->txring_dma_map); + sc->txring_dma_map = NULL; + for (i = 0; i < CGEM_NUM_TX_DESCS; i++) + if (sc->txring_m_dmamap[i] != NULL) { + bus_dmamap_destroy(sc->mbuf_dma_tag, + sc->txring_m_dmamap[i]); + sc->txring_m_dmamap[i] = NULL; + } + } + if (sc->desc_dma_tag != NULL) { + bus_dma_tag_destroy(sc->desc_dma_tag); + sc->desc_dma_tag = NULL; + } + if (sc->mbuf_dma_tag != NULL) { + bus_dma_tag_destroy(sc->mbuf_dma_tag); + sc->mbuf_dma_tag = NULL; + } + + bus_generic_detach(dev); + + CGEM_LOCK_DESTROY(sc); + + return (0); +} + +static device_method_t cgem_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, cgem_probe), + DEVMETHOD(device_attach, cgem_attach), + DEVMETHOD(device_detach, cgem_detach), + + /* Bus interface */ + DEVMETHOD(bus_child_detached, cgem_child_detached), + + /* MII interface */ + DEVMETHOD(miibus_readreg, cgem_miibus_readreg), + DEVMETHOD(miibus_writereg, cgem_miibus_writereg), + + DEVMETHOD_END +}; + +static driver_t cgem_driver = { + "cgem", + cgem_methods, + sizeof(struct cgem_softc), +}; + +DRIVER_MODULE(cgem, simplebus, cgem_driver, cgem_devclass, NULL, NULL); +DRIVER_MODULE(miibus, cgem, miibus_driver, miibus_devclass, NULL, NULL); +MODULE_DEPEND(cgem, miibus, 1, 1, 1); +MODULE_DEPEND(cgem, ether, 1, 1, 1); diff --git a/sys/dev/cadence/if_cgem_hw.h b/sys/dev/cadence/if_cgem_hw.h new file mode 100644 index 0000000..1ec4e79 --- /dev/null +++ b/sys/dev/cadence/if_cgem_hw.h @@ -0,0 +1,381 @@ +/*- + * 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: + * 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. + */ + +/* $FreeBSD$ */ + +/* + * Hardware and register defines for Cadence GEM Gigabit Ethernet + * controller such as the one used in Zynq-7000 SoC. + * + * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. + * (v1.4) November 16, 2012. Xilinx doc UG585. GEM is covered in Ch. 16 + * and register definitions are in appendix B.18. + */ + +#ifndef _IF_CGEM_HW_H_ +#define _IF_CGEM_HW_H_ + +/* Cadence GEM hardware register definitions. */ +#define CGEM_NET_CTRL 0x000 /* Network Control */ +#define CGEM_NET_CTRL_FLUSH_DPRAM_PKT (1<<18) +#define CGEM_NET_CTRL_TX_PFC_PRI_PAUSE_FRAME (1<<17) +#define CGEM_NET_CTRL_EN_PFC_PRI_PAUSE_RX (1<<16) +#define CGEM_NET_CTRL_STORE_RX_TSTAMP (1<<15) +#define CGEM_NET_CTRL_TX_ZEROQ_PAUSE_FRAME (1<<12) +#define CGEM_NET_CTRL_TX_PAUSE_FRAME (1<<11) +#define CGEM_NET_CTRL_TX_HALT (1<<10) +#define CGEM_NET_CTRL_START_TX (1<<9) +#define CGEM_NET_CTRL_BACK_PRESSURE (1<<8) +#define CGEM_NET_CTRL_WREN_STAT_REGS (1<<7) +#define CGEM_NET_CTRL_INCR_STAT_REGS (1<<6) +#define CGEM_NET_CTRL_CLR_STAT_REGS (1<<5) +#define CGEM_NET_CTRL_MGMT_PORT_EN (1<<4) +#define CGEM_NET_CTRL_TX_EN (1<<3) +#define CGEM_NET_CTRL_RX_EN (1<<2) +#define CGEM_NET_CTRL_LOOP_LOCAL (1<<1) + +#define CGEM_NET_CFG 0x004 /* Netowrk Configuration */ +#define CGEM_NET_CFG_UNIDIR_EN (1<<31) +#define CGEM_NET_CFG_IGNORE_IPG_RX_ER (1<<30) +#define CGEM_NET_CFG_RX_BAD_PREAMBLE (1<<29) +#define CGEM_NET_CFG_IPG_STRETCH_EN (1<<28) +#define CGEM_NET_CFG_SGMII_EN (1<<27) +#define CGEM_NET_CFG_IGNORE_RX_FCS (1<<26) +#define CGEM_NET_CFG_RX_HD_WHILE_TX (1<<25) +#define CGEM_NET_CFG_RX_CHKSUM_OFFLD_EN (1<<24) +#define CGEM_NET_CFG_DIS_CP_PAUSE_FRAME (1<<23) +#define CGEM_NET_CFG_DBUS_WIDTH_32 (0<<21) +#define CGEM_NET_CFG_DBUS_WIDTH_64 (1<<21) +#define CGEM_NET_CFG_DBUS_WIDTH_128 (2<<21) +#define CGEM_NET_CFG_DBUS_WIDTH_MASK (3<<21) +#define CGEM_NET_CFG_MDC_CLK_DIV_8 (0<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_16 (1<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_32 (2<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_48 (3<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_64 (4<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_96 (5<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_128 (6<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_224 (7<<18) +#define CGEM_NET_CFG_MDC_CLK_DIV_MASK (7<<18) +#define CGEM_NET_CFG_FCS_REMOVE (1<<17) +#define CGEM_NET_CFG_LEN_ERR_FRAME_DISC (1<<16) +#define CGEM_NET_CFG_RX_BUF_OFFSET_SHFT 14 +#define CGEM_NET_CFG_RX_BUF_OFFSET_MASK (3<<14) +#define CGEM_NET_CFG_RX_BUF_OFFSET(n) ((n)<<14) +#define CGEM_NET_CFG_PAUSE_EN (1<<13) +#define CGEM_NET_CFG_RETRY_TEST (1<<12) +#define CGEM_NET_CFG_PCS_SEL (1<<11) +#define CGEM_NET_CFG_GIGE_EN (1<<10) +#define CGEM_NET_CFG_EXT_ADDR_MATCH_EN (1<<9) +#define CGEM_NET_CFG_UNI_HASH_EN (1<<7) +#define CGEM_NET_CFG_MULTI_HASH_EN (1<<6) +#define CGEM_NET_CFG_NO_BCAST (1<<5) +#define CGEM_NET_CFG_COPY_ALL (1<<4) +#define CGEM_NET_CFG_DISC_NON_VLAN (1<<2) +#define CGEM_NET_CFG_FULL_DUPLEX (1<<1) +#define CGEM_NET_CFG_SPEED100 (1<<0) + +#define CGEM_NET_STAT 0x008 /* Network Status */ +#define CGEM_NET_STAT_PFC_PRI_PAUSE_NEG (1<<6) +#define CGEM_NET_STAT_PCS_AUTONEG_PAUSE_TX_RES (1<<5) +#define CGEM_NET_STAT_PCS_AUTONEG_PAUSE_RX_RES (1<<4) +#define CGEM_NET_STAT_PCS_AUTONEG_DUP_RES (1<<3) +#define CGEM_NET_STAT_PHY_MGMT_IDLE (1<<2) +#define CGEM_NET_STAT_MDIO_IN_PIN_STATUS (1<<1) +#define CGEM_NET_STAT_PCS_LINK_STATE (1<<0) + +#define CGEM_USER_IO 0x00C /* User I/O */ + +#define CGEM_DMA_CFG 0x010 /* DMA Config */ +#define CGEM_DMA_CFG_DISC_WHEN_NO_AHB (1<<24) +#define CGEM_DMA_CFG_RX_BUF_SIZE_SHIFT 16 +#define CGEM_DMA_CFG_RX_BUF_SIZE_MASK (0xff<<16) +#define CGEM_DMA_CFG_RX_BUF_SIZE(sz) ((((sz) + 63) / 64) << 16) +#define CGEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN (1<<11) +#define CGEM_DMA_CFG_TX_PKTBUF_MEMSZ_SEL (1<<10) +#define CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_1K (0<<8) +#define CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_2K (1<<8) +#define CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_4K (2<<8) +#define CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_8K (3<<8) +#define CGEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL_MASK (3<<8) +#define CGEM_DMA_CFG_AHB_ENDIAN_SWAP_PKT_EN (1<<7) +#define CGEM_DMA_CFG_AHB_ENDIAN_SWAP_MGMT_EN (1<<6) +#define CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_1 (1<<0) +#define CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_4 (4<<0) +#define CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_8 (8<<0) +#define CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_16 (16<<0) +#define CGEM_DMA_CFG_AHB_FIXED_BURST_LEN_MASK (0x1f<<0) + +#define CGEM_TX_STAT 0x014 /* Transmit Status */ +#define CGEM_TX_STAT_HRESP_NOT_OK (1<<8) +#define CGEM_TX_STAT_LATE_COLL (1<<7) +#define CGEM_TX_STAT_UNDERRUN (1<<6) +#define CGEM_TX_STAT_COMPLETE (1<<5) +#define CGEM_TX_STAT_CORRUPT_AHB_ERR (1<<4) +#define CGEM_TX_STAT_GO (1<<3) +#define CGEM_TX_STAT_RETRY_LIMIT_EXC (1<<2) +#define CGEM_TX_STAT_COLLISION (1<<1) +#define CGEM_TX_STAT_USED_BIT_READ (1<<0) +#define CGEM_TX_STAT_ALL 0x1ff + +#define CGEM_RX_QBAR 0x018 /* Receive Buf Q Base Addr */ +#define CGEM_TX_QBAR 0x01C /* Transmit Buf Q Base Addr */ + +#define CGEM_RX_STAT 0x020 /* Receive Status */ +#define CGEM_RX_STAT_HRESP_NOT_OK (1<<3) +#define CGEM_RX_STAT_OVERRUN (1<<2) +#define CGEM_RX_STAT_FRAME_RECD (1<<1) +#define CGEM_RX_STAT_BUF_NOT_AVAIL (1<<0) +#define CGEM_RX_STAT_ALL 0xf + +#define CGEM_INTR_STAT 0x024 /* Interrupt Status */ +#define CGEM_INTR_EN 0x028 /* Interrupt Enable */ +#define CGEM_INTR_DIS 0x02C /* Interrupt Disable */ +#define CGEM_INTR_MASK 0x030 /* Interrupt Mask */ +#define CGEM_INTR_TSU_SEC_INCR (1<<26) +#define CGEM_INTR_PDELAY_RESP_TX (1<<25) +#define CGEM_INTR_PDELAY_REQ_TX (1<<24) +#define CGEM_INTR_PDELAY_RESP_RX (1<<23) +#define CGEM_INTR_PDELAY_REQ_RX (1<<22) +#define CGEM_INTR_SYNX_TX (1<<21) +#define CGEM_INTR_DELAY_REQ_TX (1<<20) +#define CGEM_INTR_SYNC_RX (1<<19) +#define CGEM_INTR_DELAY_REQ_RX (1<<18) +#define CGEM_INTR_PARTNER_PG_RX (1<<17) +#define CGEM_INTR_AUTONEG_COMPL (1<<16) +#define CGEM_INTR_EXT_INTR (1<<15) +#define CGEM_INTR_PAUSE_TX (1<<14) +#define CGEM_INTR_PAUSE_ZERO (1<<13) +#define CGEM_INTR_PAUSE_NONZEROQ_RX (1<<12) +#define CGEM_INTR_HRESP_NOT_OK (1<<11) +#define CGEM_INTR_RX_OVERRUN (1<<10) +#define CGEM_INTR_LINK_CHNG (1<<9) +#define CGEM_INTR_TX_COMPLETE (1<<7) +#define CGEM_INTR_TX_CORRUPT_AHB_ERR (1<<6) +#define CGEM_INTR_RETRY_EX_LATE_COLLISION (1<<5) +#define CGEM_INTR_TX_USED_READ (1<<3) +#define CGEM_INTR_RX_USED_READ (1<<2) +#define CGEM_INTR_RX_COMPLETE (1<<1) +#define CGEM_INTR_MGMT_SENT (1<<0) +#define CGEM_INTR_ALL 0x7FFFEFF + +#define CGEM_PHY_MAINT 0x034 /* PHY Maintenenace */ +#define CGEM_PHY_MAINT_CLAUSE_22 (1<<30) +#define CGEM_PHY_MAINT_OP_SHIFT 28 +#define CGEM_PHY_MAINT_OP_MASK (3<<28) +#define CGEM_PHY_MAINT_OP_READ (2<<28) +#define CGEM_PHY_MAINT_OP_WRITE (1<<28) +#define CGEM_PHY_MAINT_PHY_ADDR_SHIFT 23 +#define CGEM_PHY_MAINT_PHY_ADDR_MASK (0x1f<<23) +#define CGEM_PHY_MAINT_REG_ADDR_SHIFT 18 +#define CGEM_PHY_MAINT_REG_ADDR_MASK (0x1f<<18) +#define CGEM_PHY_MAINT_MUST_10 (2<<16) +#define CGEM_PHY_MAINT_DATA_MASK 0xffff + +#define CGEM_RX_PAUSEQ 0x038 /* Received Pause Quantum */ +#define CGEM_TX_PAUSEQ 0x03C /* Transmit Puase Quantum */ + +#define CGEM_HASH_BOT 0x080 /* Hash Reg Bottom [31:0] */ +#define CGEM_HASH_TOP 0x084 /* Hash Reg Top [63:32] */ +#define CGEM_SPEC_ADDR_LOW(n) (0x088+(n)*8) /* Specific Addr low */ +#define CGEM_SPEC_ADDR_HI(n) (0x08C+(n)*8) /* Specific Addr hi */ + +#define CGEM_TYPE_ID_MATCH1 0x0A8 /* Type ID Match 1 */ +#define CGEM_TYPE_ID_MATCH_COPY_EN (1<<31) +#define CGEM_TYPE_ID_MATCH2 0x0AC /* Type ID Match 2 */ +#define CGEM_TYPE_ID_MATCH3 0x0B0 /* Type ID Match 3 */ +#define CGEM_TYPE_ID_MATCH4 0x0B4 /* Type ID Match 4 */ + +#define CGEM_WAKE_ON_LAN 0x0B8 /* Wake on LAN Register */ +#define CGEM_WOL_MULTI_HASH_EN (1<<19) +#define CGEM_WOL_SPEC_ADDR1_EN (1<<18) +#define CGEM_WOL_ARP_REQ_EN (1<<17) +#define CGEM_WOL_MAGIC_PKT_EN (1<<16) +#define CGEM_WOL_ARP_REQ_IP_ADDR_MASK 0xffff + +#define CGEM_IPG_STRETCH /* IPG Stretch Register */ + +#define CGEM_STACKED_VLAN 0x0C0 /* Stacked VLAN Register */ +#define CGEM_STACKED_VLAN_EN (1<<31) + +#define CGEM_TX_PFC_PAUSE 0x0C4 /* Transmit PFC Pause Reg */ +#define CGEM_TX_PFC_PAUSEQ_SEL_SHIFT 8 +#define CGEM_TX_PFC_PAUSEQ_SEL_MASK (0xff<<8) +#define CGEM_TX_PFC_PAUSE_PRI_EN_VEC_VAL_MASK 0xff + +#define CGEM_SPEC_ADDR1_MASK_BOT 0x0C8 /* Specific Addr Mask1 [31:0]*/ +#define CGEM_SPEC_ADDR1_MASK_TOP 0x0CC /* Specific Addr Mask1[47:32]*/ +#define CGEM_MODULE_ID 0x0FC /* Module ID */ +#define CGEM_OCTETS_TX_BOT 0x100 /* Octets xmitted [31:0] */ +#define CGEM_OCTETS_TX_TOP 0x104 /* Octets xmitted [47:32] */ +#define CGEM_FRAMES_TX 0x108 /* Frames xmitted */ +#define CGEM_BCAST_FRAMES_TX 0x10C /* Broadcast Frames xmitted */ +#define CGEM_MULTI_FRAMES_TX 0x110 /* Multicast Frames xmitted */ +#define CGEM_PAUSE_FRAMES_TX 0x114 /* Pause Frames xmitted */ +#define CGEM_FRAMES_64B_TX 0x118 /* 64-Byte Frames xmitted */ +#define CGEM_FRAMES_65_127B_TX 0x11C /* 65-127 Byte Frames xmitted*/ +#define CGEM_FRAMES_128_255B_TX 0x120 /* 128-255 Byte Frames xmit */ +#define CGEM_FRAMES_256_511B_TX 0x124 /* 256-511 Byte Frames xmit */ +#define CGEM_FRAMES_512_1023B_TX 0x128 /* 512-1023 Byte frames xmit */ +#define CGEM_FRAMES_1024_1518B_TX 0x12C /* 1024-1518 Byte frames xmit*/ +#define CGEM_TX_UNDERRUNS 0x134 /* Transmit Under-runs */ +#define CGEM_SINGLE_COLL_FRAMES 0x138 /* Single-Collision Frames */ +#define CGEM_MULTI_COLL_FRAMES 0x13C /* Multi-Collision Frames */ +#define CGEM_EXCESSIVE_COLL_FRAMES 0x140 /* Excessive Collision Frames*/ +#define CGEM_LATE_COLL 0x144 /* Late Collisions */ +#define CGEM_DEFERRED_TX_FRAMES 0x148 /* Deferred Transmit Frames */ +#define CGEM_CARRIER_SENSE_ERRS 0x14C /* Carrier Sense Errors */ +#define CGEM_OCTETS_RX_BOT 0x150 /* Octets Received [31:0] */ +#define CGEM_OCTETS_RX_TOP 0x154 /* Octets Received [47:32] */ +#define CGEM_FRAMES_RX 0x158 /* Frames Received */ +#define CGEM_BCAST_FRAMES_RX 0x15C /* Broadcast Frames Received */ +#define CGEM_MULTI_FRAMES_RX 0x160 /* Multicast Frames Received */ +#define CGEM_PAUSE_FRAMES_RX 0x164 /* Pause Frames Reeived */ +#define CGEM_FRAMES_64B_RX 0x168 /* 64-Byte Frames Received */ +#define CGEM_FRAMES_65_127B_RX 0x16C /* 65-127 Byte Frames Rx'd */ +#define CGEM_FRAMES_128_255B_RX 0x170 /* 128-255 Byte Frames Rx'd */ +#define CGEM_FRAMES_256_511B_RX 0x174 /* 256-511 Byte Frames Rx'd */ +#define CGEM_FRAMES_512_1023B_RX 0x178 /* 512-1023 Byte Frames Rx'd */ +#define CGEM_FRAMES_1024_1518B_RX 0x17C /* 1024-1518 Byte Frames Rx'd*/ +#define CGEM_UNDERSZ_RX 0x180 /* Undersize Frames Rx'd */ +#define CGEM_OVERSZ_RX 0x184 /* Oversize Frames Rx'd */ +#define CGEM_JABBERS_RX 0x18C /* Jabbers received */ +#define CGEM_FCS_ERRS 0x190 /* Frame Check Sequence Errs */ +#define CGEM_LENGTH_FIELD_ERRS 0x194 /* Length Firled Frame Errs */ +#define CGEM_RX_SYMBOL_ERRS 0x198 /* Receive Symbol Errs */ +#define CGEM_ALIGN_ERRS 0x19C /* Alignment Errors */ +#define CGEM_RX_RESOURCE_ERRS 0x1A0 /* Receive Resoure Errors */ +#define CGEM_RX_OVERRUN_ERRS 0x1A4 /* Receive Overrun Errors */ +#define CGEM_IP_HDR_CKSUM_ERRS 0x1A8 /* IP Hdr Checksum Errors */ +#define CGEM_TCP_CKSUM_ERRS 0x1AC /* TCP Checksum Errors */ +#define CGEM_UDP_CKSUM_ERRS 0x1B0 /* UDP Checksum Errors */ +#define CGEM_TIMER_STROBE_S 0x1C8 /* 1588 timer sync strobe s */ +#define CGEM_TIMER_STROBE_NS 0x1CC /* timer sync strobe ns */ +#define CGEM_TIMER_S 0x1D0 /* 1588 timer seconds */ +#define CGEM_TIMER_NS 0x1D4 /* 1588 timer ns */ +#define CGEM_ADJUST 0x1D8 /* 1588 timer adjust */ +#define CGEM_INCR 0x1DC /* 1588 timer increment */ +#define CGEM_PTP_TX_S 0x1E0 /* PTP Event Frame xmit secs */ +#define CGEM_PTP_TX_NS 0x1E4 /* PTP Event Frame xmit ns */ +#define CGEM_PTP_RX_S 0x1E8 /* PTP Event Frame rcv'd s */ +#define CGEM_PTP_RX_NS 0x1EC /* PTP Event Frame rcv'd ns */ +#define CGEM_PTP_PEER_TX_S 0x1F0 /* PTP Peer Event xmit s */ +#define CGEM_PTP_PEER_TX_NS 0x1F4 /* PTP Peer Event xmit ns */ +#define CGEM_PTP_PEER_RX_S 0x1F8 /* PTP Peer Event rcv'd s */ +#define CGEM_PTP_PEER_RX_NS 0x1FC /* PTP Peer Event rcv'd ns */ + +#define CGEM_DESIGN_CFG2 0x284 /* Design Configuration 2 */ +#define CGEM_DESIGN_CFG2_TX_PBUF_ADDR_SHIFT 26 +#define CGEM_DESIGN_CFG2_TX_PBUF_ADDR_MASK (0xf<<26) +#define CGEM_DESIGN_CFG2_RX_PBUF_ADDR_SHIFT 22 +#define CGEM_DESIGN_CFG2_RX_PBUF_ADDR_MASK (0xf<<22) +#define CGEM_DESIGN_CFG2_TX_PKT_BUF (1<<21) +#define CGEM_DESIGN_CFG2_RX_PKT_BUF (1<<20) +#define CGEM_DESIGN_CFG2_HPROT_VAL_SHIFT 16 +#define CGEM_DESIGN_CFG2_HPROT_VAL_MASK (0xf<<16) +#define CGEM_DESIGN_CFG2_JUMBO_MAX_LEN_MASK 0xffff + +#define CGEM_DESIGN_CFG3 0x288 /* Design Configuration 3 */ +#define CGEM_DESIGN_CFG3_RX_BASE2_FIFO_SZ_MASK (0xffff<<16) +#define CGEM_DESIGN_CFG3_RX_BASE2_FIFO_SZ_SHIFT 16 +#define CGEM_DESIGN_CFG3_RX_FIFO_SIZE_MASK 0xffff + +#define CGEM_DESIGN_CFG4 0x28C /* Design Configuration 4 */ +#define CGEM_DESIGN_CFG4_TX_BASE2_FIFO_SZ_SHIFT 16 +#define CGEM_DESIGN_CFG4_TX_BASE2_FIFO_SZ_MASK (0xffff<<16) +#define CGEM_DESIGN_CFG4_TX_FIFO_SIZE_MASK 0xffff + +#define CGEM_DESIGN_CFG5 0x290 /* Design Configuration 5 */ +#define CGEM_DESIGN_CFG5_TSU_CLK (1<<28) +#define CGEM_DESIGN_CFG5_RX_BUF_LEN_DEF_SHIFT 20 +#define CGEM_DESIGN_CFG5_RX_BUF_LEN_DEF_MASK (0xff<<20) +#define CGEM_DESIGN_CFG5_TX_PBUF_SIZE_DEF (1<<19) +#define CGEM_DESIGN_CFG5_RX_PBUF_SIZE_DEF_SHIFT 17 +#define CGEM_DESIGN_CFG5_RX_PBUF_SIZE_DEF_MASK (3<<17) +#define CGEM_DESIGN_CFG5_ENDIAN_SWAP_DEF_SHIFT 15 +#define CGEM_DESIGN_CFG5_ENDIAN_SWAP_DEF_MASK (3<<15) +#define CGEM_DESIGN_CFG5_MDC_CLOCK_DIV_SHIFT 12 +#define CGEM_DESIGN_CFG5_MDC_CLOCK_DIV_MASK (7<<12) +#define CGEM_DESIGN_CFG5_DMA_BUS_WIDTH_SHIFT 10 +#define CGEM_DESIGN_CFG5_DMA_BUS_WIDTH_MASK (3<<10) +#define CGEM_DESIGN_CFG5_PHY_IDENT (1<<9) +#define CGEM_DESIGN_CFG5_TSU (1<<8) +#define CGEM_DESIGN_CFG5_TX_FIFO_CNT_WIDTH_SHIFT 4 +#define CGEM_DESIGN_CFG5_TX_FIFO_CNT_WIDTH_MASK (0xf<<4) +#define CGEM_DESIGN_CFG5_RX_FIFO_CNT_WIDTH_MASK 0xf + +/* Transmit Descriptors */ +struct cgem_tx_desc { + uint32_t addr; + uint32_t ctl; +#define CGEM_TXDESC_USED (1<<31) /* done transmitting */ +#define CGEM_TXDESC_WRAP (1<<30) /* end of descr ring */ +#define CGEM_TXDESC_RETRY_ERR (1<<29) +#define CGEM_TXDESC_AHB_ERR (1<<27) +#define CGEM_TXDESC_LATE_COLL (1<<26) +#define CGEM_TXDESC_CKSUM_GEN_STAT_MASK (7<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_VLAN_HDR_ERR (1<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_SNAP_HDR_ERR (2<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_IP_HDR_ERR (3<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_UNKNOWN_TYPE (4<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_UNSUPP_FRAG (5<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_NOT_TCPUDP (6<<20) +#define CGEM_TXDESC_CKSUM_GEN_STAT_SHORT_PKT (7<<20) +#define CGEM_TXDESC_NO_CRC_APPENDED (1<<16) +#define CGEM_TXDESC_LAST_BUF (1<<15) /* last buf in frame */ +#define CGEM_TXDESC_LENGTH_MASK 0x3fff +}; + +struct cgem_rx_desc { + uint32_t addr; +#define CGEM_RXDESC_WRAP (1<<1) /* goes in addr! */ +#define CGEM_RXDESC_OWN (1<<0) /* buf filled */ + uint32_t ctl; +#define CGEM_RXDESC_BCAST (1<<31) /* all 1's broadcast */ +#define CGEM_RXDESC_MULTI_MATCH (1<<30) /* mutlicast match */ +#define CGEM_RXDESC_UNICAST_MATCH (1<<29) +#define CGEM_RXDESC_EXTERNAL_MATCH (1<<28) /* ext addr match */ +#define CGEM_RXDESC_SPEC_MATCH_SHIFT 25 +#define CGEM_RXDESC_SPEC_MATCH_MASK (3<<25) +#define CGEM_RXDESC_TYPE_ID_MATCH_SHIFT 22 +#define CGEM_RXDESC_TYPE_ID_MATCH_MASK (3<<22) +#define CGEM_RXDESC_CKSUM_STAT_MASK (3<<22) /* same field above */ +#define CGEM_RXDESC_CKSUM_STAT_NONE (0<<22) +#define CGEM_RXDESC_CKSUM_STAT_IP_GOOD (1<<22) +#define CGEM_RXDESC_CKSUM_STAT_TCP_GOOD (2<<22) /* and ip good */ +#define CGEM_RXDESC_CKSUM_STAT_UDP_GOOD (3<<22) /* and ip good */ +#define CGEM_RXDESC_VLAN_DETECTED (1<<21) +#define CGEM_RXDESC_PRIO_DETECTED (1<<20) +#define CGEM_RXDESC_VLAN_PRIO_SHIFT 17 +#define CGEM_RXDESC_VLAN_PRIO_MASK (7<<17) +#define CGEM_RXDESC_CFI (1<<16) +#define CGEM_RXDESC_EOF (1<<15) /* end of frame */ +#define CGEM_RXDESC_SOF (1<<14) /* start of frame */ +#define CGEM_RXDESC_BAD_FCS (1<<13) +#define CGEM_RXDESC_LENGTH_MASK 0x1fff +}; + +#endif /* _IF_CGEM_HW_H_ */ diff --git a/sys/dev/sdhci/sdhci_fdt.c b/sys/dev/sdhci/sdhci_fdt.c new file mode 100644 index 0000000..e92b158 --- /dev/null +++ b/sys/dev/sdhci/sdhci_fdt.c @@ -0,0 +1,300 @@ +/*- + * Copyright (c) 2012 Thomas Skibo + * Copyright (c) 2008 Alexander Motin <mav@FreeBSD.org> + * 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. + */ + +/* Generic driver to attach sdhci controllers on simplebus. + * Derived mainly from sdhci_pci.c + */ + +#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/resource.h> +#include <sys/rman.h> +#include <sys/sysctl.h> +#include <sys/taskqueue.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/mmc/bridge.h> +#include <dev/mmc/mmcreg.h> +#include <dev/mmc/mmcbrvar.h> +#include <dev/sdhci/sdhci.h> + +#include "mmcbr_if.h" +#include "sdhci_if.h" + +#define MAX_SLOTS 6 + +struct sdhci_fdt_softc { + device_t dev; /* Controller device */ + u_int quirks; /* Chip specific quirks */ + u_int caps; /* If we override SDHCI_CAPABILITIES */ + struct resource *irq_res; /* IRQ resource */ + void *intrhand; /* Interrupt handle */ + + int num_slots; /* Number of slots on this controller*/ + struct sdhci_slot slots[MAX_SLOTS]; + struct resource *mem_res[MAX_SLOTS]; /* Memory resource */ +}; + +static uint8_t +sdhci_fdt_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + return (bus_read_1(sc->mem_res[slot->num], off)); +} + +static void +sdhci_fdt_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint8_t val) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + bus_write_1(sc->mem_res[slot->num], off, val); +} + +static uint16_t +sdhci_fdt_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + return (bus_read_2(sc->mem_res[slot->num], off)); +} + +static void +sdhci_fdt_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint16_t val) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + bus_write_2(sc->mem_res[slot->num], off, val); +} + +static uint32_t +sdhci_fdt_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + return (bus_read_4(sc->mem_res[slot->num], off)); +} + +static void +sdhci_fdt_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, + uint32_t val) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + bus_write_4(sc->mem_res[slot->num], off, val); +} + +static void +sdhci_fdt_read_multi_4(device_t dev, struct sdhci_slot *slot, + bus_size_t off, uint32_t *data, bus_size_t count) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + bus_read_multi_4(sc->mem_res[slot->num], off, data, count); +} + +static void +sdhci_fdt_write_multi_4(device_t dev, struct sdhci_slot *slot, + bus_size_t off, uint32_t *data, bus_size_t count) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + bus_write_multi_4(sc->mem_res[slot->num], off, data, count); +} + +static void +sdhci_fdt_intr(void *arg) +{ + struct sdhci_fdt_softc *sc = (struct sdhci_fdt_softc *)arg; + int i; + + for (i = 0; i < sc->num_slots; i++) { + struct sdhci_slot *slot = &sc->slots[i]; + sdhci_generic_intr(slot); + } +} + +static int +sdhci_fdt_probe(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + phandle_t node; + pcell_t cid; + + sc->quirks = 0; + sc->num_slots = 1; + + if (ofw_bus_is_compatible(dev, "sdhci_generic")) { + device_set_desc(dev, "generic fdt SDHCI controller"); + } else if (ofw_bus_is_compatible(dev, "xlnx,zy7_sdhci")) { + sc->quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; + device_set_desc(dev, "Zynq-7000 generic fdt SDHCI controller"); + } else + return (ENXIO); + + node = ofw_bus_get_node(dev); + + /* Allow dts to patch quirks and slots. */ + if ((OF_getprop(node, "quirks", &cid, sizeof(cid))) > 0) + sc->quirks = fdt32_to_cpu(cid); + if ((OF_getprop(node, "num-slots", &cid, sizeof(cid))) > 0) + sc->num_slots = fdt32_to_cpu(cid); + + return (0); +} + +static int +sdhci_fdt_attach(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + int err, slots, rid, i; + + sc->dev = dev; + + /* 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, "Can't allocate IRQ\n"); + return (ENOMEM); + } + + /* Scan all slots. */ + slots = sc->num_slots; /* number of slots determined in probe(). */ + sc->num_slots = 0; + for (i = 0; i < slots; i++) { + struct sdhci_slot *slot = &sc->slots[sc->num_slots]; + + /* Allocate memory. */ + rid = 0; + sc->mem_res[i] = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (sc->mem_res[i] == NULL) { + device_printf(dev, "Can't allocate memory for " + "slot %d\n", i); + continue; + } + + slot->quirks = sc->quirks; + slot->caps = sc->caps; + + if (sdhci_init_slot(dev, slot, i) != 0) + continue; + + sc->num_slots++; + } + device_printf(dev, "%d slot(s) allocated\n", sc->num_slots); + + /* Activate the interrupt */ + err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, + NULL, sdhci_fdt_intr, sc, &sc->intrhand); + if (err) { + device_printf(dev, "Cannot setup IRQ\n"); + return (err); + } + + /* Process cards detection. */ + for (i = 0; i < sc->num_slots; i++) { + struct sdhci_slot *slot = &sc->slots[i]; + sdhci_start_slot(slot); + } + + return (0); +} + +static int +sdhci_fdt_detach(device_t dev) +{ + struct sdhci_fdt_softc *sc = device_get_softc(dev); + int i; + + bus_generic_detach(dev); + bus_teardown_intr(dev, sc->irq_res, sc->intrhand); + bus_release_resource(dev, SYS_RES_IRQ, rman_get_rid(sc->irq_res), + sc->irq_res); + + for (i = 0; i < sc->num_slots; i++) { + struct sdhci_slot *slot = &sc->slots[i]; + + sdhci_cleanup_slot(slot); + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(sc->mem_res[i]), + sc->mem_res[i]); + } + + return (0); +} + +static device_method_t sdhci_fdt_methods[] = { + /* device_if */ + DEVMETHOD(device_probe, sdhci_fdt_probe), + DEVMETHOD(device_attach, sdhci_fdt_attach), + DEVMETHOD(device_detach, sdhci_fdt_detach), + + /* Bus interface */ + DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), + DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), + + /* mmcbr_if */ + DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), + DEVMETHOD(mmcbr_request, sdhci_generic_request), + DEVMETHOD(mmcbr_get_ro, sdhci_generic_get_ro), + DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), + DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), + + /* SDHCI registers accessors */ + DEVMETHOD(sdhci_read_1, sdhci_fdt_read_1), + DEVMETHOD(sdhci_read_2, sdhci_fdt_read_2), + DEVMETHOD(sdhci_read_4, sdhci_fdt_read_4), + DEVMETHOD(sdhci_read_multi_4, sdhci_fdt_read_multi_4), + DEVMETHOD(sdhci_write_1, sdhci_fdt_write_1), + DEVMETHOD(sdhci_write_2, sdhci_fdt_write_2), + DEVMETHOD(sdhci_write_4, sdhci_fdt_write_4), + DEVMETHOD(sdhci_write_multi_4, sdhci_fdt_write_multi_4), + + DEVMETHOD_END +}; + +static driver_t sdhci_fdt_driver = { + "sdhci_fdt", + sdhci_fdt_methods, + sizeof(struct sdhci_fdt_softc), +}; +static devclass_t sdhci_fdt_devclass; + +DRIVER_MODULE(sdhci_fdt, simplebus, sdhci_fdt_driver, sdhci_fdt_devclass, 0,0); +MODULE_DEPEND(sdhci_fdt, sdhci, 1, 1, 1); |