aboutsummaryrefslogtreecommitdiff
path: root/sys/arm/xilinx
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arm/xilinx')
-rw-r--r--sys/arm/xilinx/files.zynq716
-rw-r--r--sys/arm/xilinx/std.zynq79
-rw-r--r--sys/arm/xilinx/uart_dev_cdnc.c711
-rw-r--r--sys/arm/xilinx/zy7_devcfg.c845
-rw-r--r--sys/arm/xilinx/zy7_ehci.c368
-rw-r--r--sys/arm/xilinx/zy7_gpio.c499
-rw-r--r--sys/arm/xilinx/zy7_l2cache.c48
-rw-r--r--sys/arm/xilinx/zy7_machdep.c99
-rw-r--r--sys/arm/xilinx/zy7_machdep.h36
-rw-r--r--sys/arm/xilinx/zy7_mp.c142
-rw-r--r--sys/arm/xilinx/zy7_qspi.c749
-rw-r--r--sys/arm/xilinx/zy7_reg.h71
-rw-r--r--sys/arm/xilinx/zy7_slcr.c711
-rw-r--r--sys/arm/xilinx/zy7_slcr.h327
-rw-r--r--sys/arm/xilinx/zy7_spi.c588
15 files changed, 5219 insertions, 0 deletions
diff --git a/sys/arm/xilinx/files.zynq7 b/sys/arm/xilinx/files.zynq7
new file mode 100644
index 000000000000..1c7a596a4ed8
--- /dev/null
+++ b/sys/arm/xilinx/files.zynq7
@@ -0,0 +1,16 @@
+#
+# files.zynq7
+#
+
+arm/xilinx/zy7_machdep.c standard
+arm/xilinx/zy7_l2cache.c standard
+arm/xilinx/zy7_slcr.c standard
+arm/xilinx/zy7_devcfg.c standard
+arm/xilinx/zy7_mp.c optional smp
+
+arm/xilinx/zy7_ehci.c optional ehci
+arm/xilinx/uart_dev_cdnc.c optional uart
+arm/xilinx/zy7_gpio.c optional gpio
+arm/xilinx/zy7_qspi.c optional zy7_qspi
+arm/xilinx/zy7_spi.c optional zy7_spi
+
diff --git a/sys/arm/xilinx/std.zynq7 b/sys/arm/xilinx/std.zynq7
new file mode 100644
index 000000000000..2fda167eeae5
--- /dev/null
+++ b/sys/arm/xilinx/std.zynq7
@@ -0,0 +1,9 @@
+#
+# std.zynq7 - Generic configuration for Xilinx Zynq-7000 PS.
+#
+
+cpu CPU_CORTEXA
+machine arm armv7
+makeoptions CONF_CFLAGS="-march=armv7a"
+
+files "../xilinx/files.zynq7"
diff --git a/sys/arm/xilinx/uart_dev_cdnc.c b/sys/arm/xilinx/uart_dev_cdnc.c
new file mode 100644
index 000000000000..5404bc1e27d4
--- /dev/null
+++ b/sys/arm/xilinx/uart_dev_cdnc.c
@@ -0,0 +1,711 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2005 Olivier Houchard All rights reserved.
+ * Copyright (c) 2012 Thomas Skibo All rights reserved.
+ * Copyright (c) 2005 M. Warner Losh <imp@FreeBSD.org>
+ *
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <machine/bus.h>
+
+#include <dev/uart/uart.h>
+#include <dev/uart/uart_cpu.h>
+#include <dev/uart/uart_cpu_fdt.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
+ * interrupt 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 void cdnc_uart_bus_grab(struct uart_softc *);
+static void cdnc_uart_bus_ungrab(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(uart_grab, cdnc_uart_bus_grab),
+ KOBJMETHOD(uart_ungrab, cdnc_uart_bus_ungrab),
+
+ 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);
+}
+
+static void
+cdnc_uart_bus_grab(struct uart_softc *sc)
+{
+
+ /* Enable interrupts. */
+ WR4(&sc->sc_bas, CDNC_UART_IEN_REG,
+ CDNC_UART_INT_TXOVR | CDNC_UART_INT_RXOVR |
+ CDNC_UART_INT_DMSI);
+}
+
+static void
+cdnc_uart_bus_ungrab(struct uart_softc *sc)
+{
+
+ /* Enable interrupts. */
+ WR4(&sc->sc_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);
+}
+
+static struct uart_class uart_cdnc_class = {
+ "cdnc_uart",
+ cdnc_uart_bus_methods,
+ sizeof(struct uart_softc),
+ .uc_ops = &cdnc_uart_ops,
+ .uc_range = 8
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"cadence,uart", (uintptr_t)&uart_cdnc_class},
+ {"cdns,uart-r1p12", (uintptr_t)&uart_cdnc_class},
+ {"xlnx,xuartps", (uintptr_t)&uart_cdnc_class},
+ {NULL, (uintptr_t)NULL},
+};
+UART_FDT_CLASS_AND_DEVICE(compat_data);
diff --git a/sys/arm/xilinx/zy7_devcfg.c b/sys/arm/xilinx/zy7_devcfg.c
new file mode 100644
index 000000000000..41618225a566
--- /dev/null
+++ b/sys/arm/xilinx/zy7_devcfg.c
@@ -0,0 +1,845 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/stdarg.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 <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;
+
+ struct sysctl_ctx_list sysctl_tree;
+ struct sysctl_oid *sysctl_tree_top;
+};
+
+static struct zy7_devcfg_softc *zy7_devcfg_softc_p;
+
+#define FCLK_NUM 4
+
+struct zy7_fclk_config {
+ int source;
+ int frequency;
+ int actual_frequency;
+};
+
+static struct zy7_fclk_config fclk_configs[FCLK_NUM];
+
+#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 | CTLFLAG_MPSAFE, 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 | CTLFLAG_NEEDGIANT, 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");
+
+static int zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS);
+SYSCTL_PROC(_hw_fpga, OID_AUTO, level_shifters,
+ CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT, NULL, 0,
+ zy7_devcfg_fclk_sysctl_level_shifters, "I",
+ "Enable/disable level shifters");
+
+/* 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
+
+static int
+zy7_devcfg_fclk_sysctl_source(SYSCTL_HANDLER_ARGS)
+{
+ char buf[4];
+ struct zy7_fclk_config *cfg;
+ int unit;
+ int error;
+
+ cfg = arg1;
+ unit = arg2;
+
+ switch (cfg->source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ case ZY7_PL_FCLK_SRC_IO_ALT:
+ strncpy(buf, "IO", sizeof(buf));
+ break;
+ case ZY7_PL_FCLK_SRC_DDR:
+ strncpy(buf, "DDR", sizeof(buf));
+ break;
+ case ZY7_PL_FCLK_SRC_ARM:
+ strncpy(buf, "ARM", sizeof(buf));
+ break;
+ default:
+ strncpy(buf, "???", sizeof(buf));
+ break;
+ }
+
+ error = sysctl_handle_string(oidp, buf, sizeof(buf), req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (strcasecmp(buf, "io") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_IO;
+ else if (strcasecmp(buf, "ddr") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_DDR;
+ else if (strcasecmp(buf, "arm") == 0)
+ cfg->source = ZY7_PL_FCLK_SRC_ARM;
+ else
+ return (EINVAL);
+
+ zy7_pl_fclk_set_source(unit, cfg->source);
+ if (cfg->frequency > 0)
+ cfg->actual_frequency = zy7_pl_fclk_get_freq(unit);
+
+ return (0);
+}
+
+static int
+zy7_devcfg_fclk_sysctl_freq(SYSCTL_HANDLER_ARGS)
+{
+ struct zy7_fclk_config *cfg;
+ int unit;
+ int error;
+ int freq;
+ int new_actual_freq;
+
+ cfg = arg1;
+ unit = arg2;
+
+ freq = cfg->frequency;
+
+ error = sysctl_handle_int(oidp, &freq, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (freq > 0) {
+ new_actual_freq = zy7_pl_fclk_set_freq(unit, freq);
+ if (new_actual_freq < 0)
+ return (EINVAL);
+ if (!zy7_pl_fclk_enabled(unit))
+ zy7_pl_fclk_enable(unit);
+ }
+ else {
+ zy7_pl_fclk_disable(unit);
+ new_actual_freq = 0;
+ }
+
+ cfg->frequency = freq;
+ cfg->actual_frequency = new_actual_freq;
+
+ return (0);
+}
+
+static int
+zy7_devcfg_fclk_sysctl_level_shifters(SYSCTL_HANDLER_ARGS)
+{
+ int error, enabled;
+
+ enabled = zy7_pl_level_shifters_enabled();
+
+ error = sysctl_handle_int(oidp, &enabled, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+
+ if (enabled)
+ zy7_pl_level_shifters_enable();
+ else
+ zy7_pl_level_shifters_disable();
+
+ return (0);
+}
+
+static int
+zy7_devcfg_init_fclk_sysctl(struct zy7_devcfg_softc *sc)
+{
+ struct sysctl_oid *fclk_node;
+ char fclk_num[4];
+ int i;
+
+ sysctl_ctx_init(&sc->sysctl_tree);
+ sc->sysctl_tree_top = SYSCTL_ADD_NODE(&sc->sysctl_tree,
+ SYSCTL_STATIC_CHILDREN(_hw_fpga), OID_AUTO, "fclk",
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+ if (sc->sysctl_tree_top == NULL) {
+ sysctl_ctx_free(&sc->sysctl_tree);
+ return (-1);
+ }
+
+ for (i = 0; i < FCLK_NUM; i++) {
+ snprintf(fclk_num, sizeof(fclk_num), "%d", i);
+ fclk_node = SYSCTL_ADD_NODE(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(sc->sysctl_tree_top), OID_AUTO, fclk_num,
+ CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "");
+
+ SYSCTL_ADD_INT(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "actual_freq", CTLFLAG_RD,
+ &fclk_configs[i].actual_frequency, i,
+ "Actual frequency");
+ SYSCTL_ADD_PROC(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "freq", CTLFLAG_RW | CTLTYPE_INT | CTLFLAG_NEEDGIANT,
+ &fclk_configs[i], i,
+ zy7_devcfg_fclk_sysctl_freq,
+ "I", "Configured frequency");
+ SYSCTL_ADD_PROC(&sc->sysctl_tree,
+ SYSCTL_CHILDREN(fclk_node), OID_AUTO,
+ "source", CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT,
+ &fclk_configs[i], i,
+ zy7_devcfg_fclk_sysctl_source,
+ "A", "Clock source");
+ }
+
+ return (0);
+}
+
+/* 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);
+
+ /* Clear sticky bits and set up INIT signal 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 (active low). */
+ devcfg_ctl |= ZY7_DEVCFG_CTRL_PCFG_PROG_B;
+ WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl);
+
+ /*
+ * Wait for INIT to assert. If it is already asserted, we may not get
+ * an edge interrupt so cancel it and continue.
+ */
+ if ((RD4(sc, ZY7_DEVCFG_STATUS) &
+ ZY7_DEVCFG_STATUS_PCFG_INIT) != 0) {
+ /* Already asserted. Cancel interrupt. */
+ WR4(sc, ZY7_DEVCFG_INT_MASK, ~0);
+ }
+ else {
+ /* Wait for positive edge interrupt. */
+ err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i1", hz);
+ if (err != 0)
+ return (err);
+ }
+
+ /* Reassert PROG_B (active low). */
+ devcfg_ctl &= ~ZY7_DEVCFG_CTRL_PCFG_PROG_B;
+ WR4(sc, ZY7_DEVCFG_CTRL, devcfg_ctl);
+
+ /* Wait for INIT deasserted. This happens almost instantly. */
+ 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 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 asserted indicating FPGA internal initialization
+ * is complete.
+ */
+ err = mtx_sleep(sc, &sc->sc_mtx, PCATCH, "zy7i2", 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);
+ DEVCFG_SC_UNLOCK(sc);
+ err = uiomove(dma_mem, segsz, uio);
+ DEVCFG_SC_LOCK(sc);
+ 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);
+
+ zy7_slcr_postload_pl(zy7_en_level_shifters);
+
+ 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_status_okay(dev))
+ return (ENXIO);
+
+ 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 i;
+ 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;
+
+ for (i = 0; i < FCLK_NUM; i++) {
+ fclk_configs[i].source = zy7_pl_fclk_get_source(i);
+ fclk_configs[i].actual_frequency =
+ zy7_pl_fclk_enabled(i) ? zy7_pl_fclk_get_freq(i) : 0;
+ /* Initially assume actual frequency is the configure one */
+ fclk_configs[i].frequency = fclk_configs[i].actual_frequency;
+ }
+
+ if (zy7_devcfg_init_fclk_sysctl(sc) < 0)
+ device_printf(dev, "failed to initialized sysctl tree\n");
+
+ return (0);
+}
+
+static int
+zy7_devcfg_detach(device_t dev)
+{
+ struct zy7_devcfg_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ if (sc->sysctl_tree_top != NULL) {
+ sysctl_ctx_free(&sc->sysctl_tree);
+ sc->sysctl_tree_top = NULL;
+ }
+
+ /* 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),
+};
+
+DRIVER_MODULE(zy7_devcfg, simplebus, zy7_devcfg_driver, 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 000000000000..6d84ec751e08
--- /dev/null
+++ b/sys/arm/xilinx/zy7_ehci.c
@@ -0,0 +1,368 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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/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 <sys/stdarg.h>
+
+#include <machine/bus.h>
+#include <machine/resource.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 void
+zy7_ehci_post_reset(struct ehci_softc *ehci_softc)
+{
+ uint32_t usbmode;
+
+ /* Force HOST mode */
+ usbmode = EOREAD4(ehci_softc, EHCI_USBMODE_NOLPM);
+ usbmode &= ~EHCI_UM_CM;
+ usbmode |= EHCI_UM_CM_HOST;
+ EOWRITE4(ehci_softc, EHCI_USBMODE_NOLPM, usbmode);
+}
+
+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_status_okay(dev))
+ return (ENXIO);
+
+ 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;
+ sc->sc_bus.dma_bits = 32;
+
+ /* 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", DEVICE_UNIT_ANY);
+ 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_TT | EHCI_SCFLG_NORESTERM;
+ sc->sc_vendor_post_reset = zy7_ehci_post_reset;
+ sc->sc_vendor_get_port_speed = ehci_get_port_speed_portsc;
+
+ /* 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);
+ int error;
+
+ /* during module unload there are lots of children leftover */
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ if ((sc->sc_flags & EHCI_SCFLG_DONEINIT) != 0) {
+ ehci_detach(sc);
+ sc->sc_flags &= ~EHCI_SCFLG_DONEINIT;
+ }
+
+ 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),
+};
+
+DRIVER_MODULE(zy7_ehci, simplebus, ehci_driver, NULL, NULL);
+MODULE_DEPEND(zy7_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 000000000000..2434e43bf27c
--- /dev/null
+++ b/sys/arm/xilinx/zy7_gpio.c
@@ -0,0 +1,499 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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/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/stdarg.h>
+#include <sys/gpio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/gpio/gpiobusvar.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "gpio_if.h"
+
+#define ZYNQ7_MAX_BANK 4
+#define ZYNQMP_MAX_BANK 6
+
+/* Zynq 7000 */
+#define ZYNQ7_BANK0_PIN_MIN 0
+#define ZYNQ7_BANK0_NPIN 32
+#define ZYNQ7_BANK1_PIN_MIN 32
+#define ZYNQ7_BANK1_NPIN 22
+#define ZYNQ7_BANK2_PIN_MIN 64
+#define ZYNQ7_BANK2_NPIN 32
+#define ZYNQ7_BANK3_PIN_MIN 96
+#define ZYNQ7_BANK3_NPIN 32
+#define ZYNQ7_PIN_MIO_MIN 0
+#define ZYNQ7_PIN_MIO_MAX 54
+#define ZYNQ7_PIN_EMIO_MIN 64
+#define ZYNQ7_PIN_EMIO_MAX 118
+
+/* ZynqMP */
+#define ZYNQMP_BANK0_PIN_MIN 0
+#define ZYNQMP_BANK0_NPIN 26
+#define ZYNQMP_BANK1_PIN_MIN 26
+#define ZYNQMP_BANK1_NPIN 26
+#define ZYNQMP_BANK2_PIN_MIN 52
+#define ZYNQMP_BANK2_NPIN 26
+#define ZYNQMP_BANK3_PIN_MIN 78
+#define ZYNQMP_BANK3_NPIN 32
+#define ZYNQMP_BANK4_PIN_MIN 110
+#define ZYNQMP_BANK4_NPIN 32
+#define ZYNQMP_BANK5_PIN_MIN 142
+#define ZYNQMP_BANK5_NPIN 32
+#define ZYNQMP_PIN_MIO_MIN 0
+#define ZYNQMP_PIN_MIO_MAX 77
+#define ZYNQMP_PIN_EMIO_MIN 78
+#define ZYNQMP_PIN_EMIO_MAX 174
+
+#define ZYNQ_BANK_NPIN(type, bank) (ZYNQ##type##_BANK##bank##_NPIN)
+#define ZYNQ_BANK_PIN_MIN(type, bank) (ZYNQ##type##_BANK##bank##_PIN_MIN)
+#define ZYNQ_BANK_PIN_MAX(type, bank) (ZYNQ##type##_BANK##bank##_PIN_MIN + ZYNQ##type##_BANK##bank##_NPIN - 1)
+
+#define ZYNQ_PIN_IS_MIO(type, pin) (pin >= ZYNQ##type##_PIN_MIO_MIN && \
+ pin <= ZYNQ##type##_PIN_MIO_MAX)
+#define ZYNQ_PIN_IS_EMIO(type, pin) (pin >= ZYNQ##type##_PIN_EMIO_MIN && \
+ pin <= ZYNQ##type##_PIN_EMIO_MAX)
+
+#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);
+
+enum zynq_gpio_type {
+ ZYNQ_7000 = 0,
+ ZYNQMP,
+};
+
+struct zynq_gpio_conf {
+ char *name;
+ enum zynq_gpio_type type;
+ uint32_t nbanks;
+ uint32_t maxpin;
+ uint32_t bank_min[ZYNQMP_MAX_BANK];
+ uint32_t bank_max[ZYNQMP_MAX_BANK];
+};
+
+struct zy7_gpio_softc {
+ device_t dev;
+ device_t busdev;
+ struct mtx sc_mtx;
+ struct resource *mem_res; /* Memory resource */
+ struct zynq_gpio_conf *conf;
+};
+
+static struct zynq_gpio_conf z7_gpio_conf = {
+ .name = "Zynq-7000 GPIO Controller",
+ .type = ZYNQ_7000,
+ .nbanks = ZYNQ7_MAX_BANK,
+ .maxpin = ZYNQ7_PIN_EMIO_MAX,
+ .bank_min[0] = ZYNQ_BANK_PIN_MIN(7, 0),
+ .bank_max[0] = ZYNQ_BANK_PIN_MAX(7, 0),
+ .bank_min[1] = ZYNQ_BANK_PIN_MIN(7, 1),
+ .bank_max[1] = ZYNQ_BANK_PIN_MAX(7, 1),
+ .bank_min[2] = ZYNQ_BANK_PIN_MIN(7, 2),
+ .bank_max[2] = ZYNQ_BANK_PIN_MAX(7, 2),
+ .bank_min[3] = ZYNQ_BANK_PIN_MIN(7, 3),
+ .bank_max[3] = ZYNQ_BANK_PIN_MAX(7, 3),
+};
+
+static struct zynq_gpio_conf zynqmp_gpio_conf = {
+ .name = "ZynqMP GPIO Controller",
+ .type = ZYNQMP,
+ .nbanks = ZYNQMP_MAX_BANK,
+ .maxpin = ZYNQMP_PIN_EMIO_MAX,
+ .bank_min[0] = ZYNQ_BANK_PIN_MIN(MP, 0),
+ .bank_max[0] = ZYNQ_BANK_PIN_MAX(MP, 0),
+ .bank_min[1] = ZYNQ_BANK_PIN_MIN(MP, 1),
+ .bank_max[1] = ZYNQ_BANK_PIN_MAX(MP, 1),
+ .bank_min[2] = ZYNQ_BANK_PIN_MIN(MP, 2),
+ .bank_max[2] = ZYNQ_BANK_PIN_MAX(MP, 2),
+ .bank_min[3] = ZYNQ_BANK_PIN_MIN(MP, 3),
+ .bank_max[3] = ZYNQ_BANK_PIN_MAX(MP, 3),
+ .bank_min[4] = ZYNQ_BANK_PIN_MIN(MP, 4),
+ .bank_max[4] = ZYNQ_BANK_PIN_MAX(MP, 4),
+ .bank_min[5] = ZYNQ_BANK_PIN_MIN(MP, 5),
+ .bank_max[5] = ZYNQ_BANK_PIN_MAX(MP, 5),
+};
+
+static struct ofw_compat_data compat_data[] = {
+ {"xlnx,zy7_gpio", (uintptr_t)&z7_gpio_conf},
+ {"xlnx,zynqmp-gpio-1.0", (uintptr_t)&zynqmp_gpio_conf},
+ {NULL, 0},
+};
+
+#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 device_t
+zy7_gpio_get_bus(device_t dev)
+{
+ struct zy7_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ return (sc->busdev);
+}
+
+static int
+zy7_gpio_pin_max(device_t dev, int *maxpin)
+{
+ struct zy7_gpio_softc *sc;
+
+ sc = device_get_softc(dev);
+ *maxpin = sc->conf->maxpin;
+ return (0);
+}
+
+static inline bool
+zy7_pin_valid(device_t dev, uint32_t pin)
+{
+ struct zy7_gpio_softc *sc;
+ int i;
+ bool found = false;
+
+ sc = device_get_softc(dev);
+ for (i = 0; i < sc->conf->nbanks; i++) {
+ if (pin >= sc->conf->bank_min[i] && pin <= sc->conf->bank_max[i]) {
+ found = true;
+ break;
+ }
+ }
+
+ return (found);
+}
+
+/* Get a specific pin's capabilities. */
+static int
+zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
+{
+
+ if (!zy7_pin_valid(dev, 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)
+{
+ struct zy7_gpio_softc *sc;
+ uint32_t emio_min;
+ bool is_mio;
+
+ sc = device_get_softc(dev);
+ if (!zy7_pin_valid(dev, pin))
+ return (EINVAL);
+
+ switch (sc->conf->type) {
+ case ZYNQ_7000:
+ is_mio = ZYNQ_PIN_IS_MIO(7, pin);
+ emio_min = ZYNQ7_PIN_EMIO_MIN;
+ break;
+ case ZYNQMP:
+ is_mio = ZYNQ_PIN_IS_MIO(MP, pin);
+ emio_min = ZYNQMP_PIN_EMIO_MIN;
+ break;
+ default:
+ return (EINVAL);
+ }
+ if (is_mio) {
+ snprintf(name, GPIOMAXNAME, "MIO_%d", pin);
+ name[GPIOMAXNAME - 1] = '\0';
+ } else {
+ snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - emio_min);
+ 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 (!zy7_pin_valid(dev, 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 (!zy7_pin_valid(dev, 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 (!zy7_pin_valid(dev, 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 (!zy7_pin_valid(dev, 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 (!zy7_pin_valid(dev, 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)
+{
+ struct zynq_gpio_conf *conf;
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+ if (conf == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, conf->name);
+ return (0);
+}
+
+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;
+ sc->conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+ 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);
+ }
+
+ sc->busdev = gpiobus_add_bus(dev);
+ if (sc->busdev == NULL) {
+ zy7_gpio_detach(dev);
+ return (ENOMEM);
+ }
+
+ bus_attach_children(dev);
+ return (0);
+}
+
+static int
+zy7_gpio_detach(device_t dev)
+{
+ struct zy7_gpio_softc *sc = device_get_softc(dev);
+
+ gpiobus_detach_bus(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_get_bus, zy7_gpio_get_bus),
+ 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 = {
+ "gpio",
+ zy7_gpio_methods,
+ sizeof(struct zy7_gpio_softc),
+};
+
+EARLY_DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
diff --git a/sys/arm/xilinx/zy7_l2cache.c b/sys/arm/xilinx/zy7_l2cache.c
new file mode 100644
index 000000000000..418a4d5782ce
--- /dev/null
+++ b/sys/arm/xilinx/zy7_l2cache.c
@@ -0,0 +1,48 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/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>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+
+#include "platform_pl310_if.h"
+
+void
+zynq7_pl310_init(platform_t plat, struct pl310_softc *softc)
+{
+}
diff --git a/sys/arm/xilinx/zy7_machdep.c b/sys/arm/xilinx/zy7_machdep.c
new file mode 100644
index 000000000000..723c3f3b2d73
--- /dev/null
+++ b/sys/arm/xilinx/zy7_machdep.c
@@ -0,0 +1,99 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/devmap.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/bus.h>
+#include <machine/machdep.h>
+#include <machine/platform.h>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+#include <arm/xilinx/zy7_reg.h>
+
+#include "platform_if.h"
+#include "platform_pl310_if.h"
+
+void (*zynq7_cpu_reset)(void);
+
+/*
+ * Set up static device mappings. Not strictly necessary -- simplebus will
+ * dynamically establish mappings as needed -- but doing it this way gets us
+ * nice efficient 1MB section mappings.
+ */
+static int
+zynq7_devmap_init(platform_t plat)
+{
+
+ devmap_add_entry(ZYNQ7_PSIO_HWBASE, ZYNQ7_PSIO_SIZE);
+ devmap_add_entry(ZYNQ7_PSCTL_HWBASE, ZYNQ7_PSCTL_SIZE);
+
+ return (0);
+}
+
+static void
+zynq7_do_cpu_reset(platform_t plat)
+{
+ if (zynq7_cpu_reset != NULL)
+ (*zynq7_cpu_reset)();
+
+ printf("cpu_reset: no platform cpu_reset. hanging.\n");
+ for (;;)
+ ;
+}
+
+static platform_method_t zynq7_methods[] = {
+ PLATFORMMETHOD(platform_devmap_init, zynq7_devmap_init),
+ PLATFORMMETHOD(platform_cpu_reset, zynq7_do_cpu_reset),
+
+#ifdef SMP
+ PLATFORMMETHOD(platform_mp_setmaxid, zynq7_mp_setmaxid),
+ PLATFORMMETHOD(platform_mp_start_ap, zynq7_mp_start_ap),
+#endif
+
+ PLATFORMMETHOD(platform_pl310_init, zynq7_pl310_init),
+
+ PLATFORMMETHOD_END,
+};
+
+FDT_PLATFORM_DEF(zynq7, "zynq7", 0, "xlnx,zynq-7000", 200);
diff --git a/sys/arm/xilinx/zy7_machdep.h b/sys/arm/xilinx/zy7_machdep.h
new file mode 100644
index 000000000000..9e4820d4b74a
--- /dev/null
+++ b/sys/arm/xilinx/zy7_machdep.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2017 Andrew Turner <andrew@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.
+ */
+
+#ifndef _ZY7_MACHDEP_H_
+#define _ZY7_MACHDEP_H_
+
+struct pl310_softc;
+
+void zynq7_mp_setmaxid(platform_t);
+void zynq7_mp_start_ap(platform_t);
+
+void zynq7_pl310_init(platform_t, struct pl310_softc *);
+
+#endif /* _ZY7_MACHDEP_H_ */
diff --git a/sys/arm/xilinx/zy7_mp.c b/sys/arm/xilinx/zy7_mp.c
new file mode 100644
index 000000000000..def950cadf0b
--- /dev/null
+++ b/sys/arm/xilinx/zy7_mp.c
@@ -0,0 +1,142 @@
+/*-
+ * 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 "opt_platform.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/smp.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include <machine/cpu.h>
+#include <machine/smp.h>
+#include <machine/fdt.h>
+#include <machine/intr.h>
+#include <machine/platformvar.h>
+
+#include <arm/xilinx/zy7_machdep.h>
+#include <arm/xilinx/zy7_reg.h>
+#include <arm/xilinx/zy7_slcr.h>
+
+#define ZYNQ7_CPU1_ENTRY 0xfffffff0
+
+#define SCU_CONTROL_REG 0xf8f00000
+#define SCU_CONTROL_ENABLE 1
+#define SCU_CONFIG_REG 0xf8f00004
+#define SCU_CONFIG_N_CPUS_MASK 3
+
+#define SLCR_PSS_IDCODE 0xf8000530
+
+void
+zynq7_mp_setmaxid(platform_t plat)
+{
+ bus_space_handle_t slcr_handle;
+ int device_id;
+ bus_space_handle_t scu_handle;
+
+ if (mp_ncpus != 0)
+ return;
+
+ /* Map in SLCR PSS_IDCODE register. */
+ if (bus_space_map(fdtbus_bs_tag, SLCR_PSS_IDCODE, 4, 0,
+ &slcr_handle) != 0)
+ panic("%s: Could not map SLCR IDCODE reg.\n", __func__);
+
+ device_id = bus_space_read_4(fdtbus_bs_tag, slcr_handle, 0) &
+ ZY7_SLCR_PSS_IDCODE_DEVICE_MASK;
+
+ bus_space_unmap(fdtbus_bs_tag, slcr_handle, 4);
+
+ /*
+ * Zynq XC7z0xxS single core chips indicate incorrect number of CPUs in
+ * SCU configuration register.
+ */
+ if (device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z007S ||
+ device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z012S ||
+ device_id == ZY7_SLCR_PSS_IDCODE_DEVICE_7Z014S) {
+ mp_maxid = 0;
+ mp_ncpus = 1;
+ return;
+ }
+
+ /* Map in SCU config register. */
+ if (bus_space_map(fdtbus_bs_tag, SCU_CONFIG_REG, 4, 0,
+ &scu_handle) != 0)
+ panic("zynq7_mp_setmaxid: Could not map SCU config reg.\n");
+
+ mp_maxid = bus_space_read_4(fdtbus_bs_tag, scu_handle, 0) &
+ SCU_CONFIG_N_CPUS_MASK;
+ mp_ncpus = mp_maxid + 1;
+
+ bus_space_unmap(fdtbus_bs_tag, scu_handle, 4);
+}
+
+void
+zynq7_mp_start_ap(platform_t plat)
+{
+ bus_space_handle_t scu_handle;
+ bus_space_handle_t ocm_handle;
+ uint32_t scu_ctrl;
+
+ /* Map in SCU control register. */
+ if (bus_space_map(fdtbus_bs_tag, SCU_CONTROL_REG, 4,
+ 0, &scu_handle) != 0)
+ panic("%s: Could not map SCU control reg.\n", __func__);
+
+ /* Set SCU enable bit. */
+ scu_ctrl = bus_space_read_4(fdtbus_bs_tag, scu_handle, 0);
+ scu_ctrl |= SCU_CONTROL_ENABLE;
+ bus_space_write_4(fdtbus_bs_tag, scu_handle, 0, scu_ctrl);
+
+ bus_space_unmap(fdtbus_bs_tag, scu_handle, 4);
+
+ /* Map in magic location to give entry address to CPU1. */
+ if (bus_space_map(fdtbus_bs_tag, ZYNQ7_CPU1_ENTRY, 4,
+ 0, &ocm_handle) != 0)
+ panic("%s: Could not map OCM\n", __func__);
+
+ /* Write start address for CPU1. */
+ bus_space_write_4(fdtbus_bs_tag, ocm_handle, 0,
+ pmap_kextract((vm_offset_t)mpentry));
+
+ bus_space_unmap(fdtbus_bs_tag, ocm_handle, 4);
+
+ /*
+ * The SCU is enabled above but I think the second CPU doesn't
+ * turn on filtering until after the wake-up below. I think that's why
+ * things don't work if I don't put these cache ops here. Also, the
+ * magic location, 0xfffffff0, isn't in the SCU's filtering range so it
+ * needs a write-back too.
+ */
+ dcache_wbinv_poc_all();
+
+ /* Wake up CPU1. */
+ dsb();
+ sev();
+}
diff --git a/sys/arm/xilinx/zy7_qspi.c b/sys/arm/xilinx/zy7_qspi.c
new file mode 100644
index 000000000000..42091bc78619
--- /dev/null
+++ b/sys/arm/xilinx/zy7_qspi.c
@@ -0,0 +1,749 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+/*
+ * This is a driver for the Quad-SPI Flash Controller in the Xilinx
+ * Zynq-7000 SoC.
+ */
+
+#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/stdarg.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include <dev/flash/mx25lreg.h>
+
+#include "spibus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"xlnx,zy7_qspi", 1},
+ {"xlnx,zynq-qspi-1.0", 1},
+ {NULL, 0}
+};
+
+struct zy7_qspi_softc {
+ device_t dev;
+ device_t child;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *intrhandle;
+
+ uint32_t cfg_reg_shadow;
+ uint32_t lqspi_cfg_shadow;
+ uint32_t spi_clock;
+ uint32_t ref_clock;
+ unsigned int spi_clk_real_freq;
+ unsigned int rx_overflows;
+ unsigned int tx_underflows;
+ unsigned int interrupts;
+ unsigned int stray_ints;
+ struct spi_command *cmd;
+ int tx_bytes; /* tx_cmd_sz + tx_data_sz */
+ int tx_bytes_sent;
+ int rx_bytes; /* rx_cmd_sz + rx_data_sz */
+ int rx_bytes_rcvd;
+ int busy;
+ int is_dual;
+ int is_stacked;
+ int is_dio;
+};
+
+#define ZY7_QSPI_DEFAULT_SPI_CLOCK 50000000
+
+#define QSPI_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define QSPI_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define QSPI_SC_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
+#define QSPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
+#define QSPI_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)))
+
+/*
+ * QSPI device registers.
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.12.2) July 1, 2018. Xilinx doc UG585.
+ */
+#define ZY7_QSPI_CONFIG_REG 0x0000
+#define ZY7_QSPI_CONFIG_IFMODE (1U << 31)
+#define ZY7_QSPI_CONFIG_ENDIAN (1 << 26)
+#define ZY7_QSPI_CONFIG_HOLDB_DR (1 << 19)
+#define ZY7_QSPI_CONFIG_RSVD1 (1 << 17) /* must be 1 */
+#define ZY7_QSPI_CONFIG_MANSTRT (1 << 16)
+#define ZY7_QSPI_CONFIG_MANSTRTEN (1 << 15)
+#define ZY7_QSPI_CONFIG_SSFORCE (1 << 14)
+#define ZY7_QSPI_CONFIG_PCS (1 << 10)
+#define ZY7_QSPI_CONFIG_REF_CLK (1 << 8)
+#define ZY7_QSPI_CONFIG_FIFO_WIDTH_MASK (3 << 6)
+#define ZY7_QSPI_CONFIG_FIFO_WIDTH32 (3 << 6)
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_MASK (7 << 3)
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_SHIFT 3
+#define ZY7_QSPI_CONFIG_BAUD_RATE_DIV(x) ((x) << 3) /* divide by 2<<x */
+#define ZY7_QSPI_CONFIG_CLK_PH (1 << 2) /* clock phase */
+#define ZY7_QSPI_CONFIG_CLK_POL (1 << 1) /* clock polarity */
+#define ZY7_QSPI_CONFIG_MODE_SEL (1 << 0) /* master enable */
+
+#define ZY7_QSPI_INTR_STAT_REG 0x0004
+#define ZY7_QSPI_INTR_EN_REG 0x0008
+#define ZY7_QSPI_INTR_DIS_REG 0x000c
+#define ZY7_QSPI_INTR_MASK_REG 0x0010
+#define ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW (1 << 6)
+#define ZY7_QSPI_INTR_RX_FIFO_FULL (1 << 5)
+#define ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define ZY7_QSPI_INTR_TX_FIFO_FULL (1 << 3)
+#define ZY7_QSPI_INTR_TX_FIFO_NOT_FULL (1 << 2)
+#define ZY7_QSPI_INTR_RX_OVERFLOW (1 << 0)
+
+#define ZY7_QSPI_EN_REG 0x0014
+#define ZY7_SPI_ENABLE 1
+
+#define ZY7_QSPI_DELAY_REG 0x0018
+#define ZY7_QSPI_DELAY_NSS_MASK (0xffU << 24)
+#define ZY7_QSPI_DELAY_NSS_SHIFT 24
+#define ZY7_QSPI_DELAY_NSS(x) ((x) << 24)
+#define ZY7_QSPI_DELAY_BTWN_MASK (0xff << 16)
+#define ZY7_QSPI_DELAY_BTWN_SHIFT 16
+#define ZY7_QSPI_DELAY_BTWN(x) ((x) << 16)
+#define ZY7_QSPI_DELAY_AFTER_MASK (0xff << 8)
+#define ZY7_QSPI_DELAY_AFTER_SHIFT 8
+#define ZY7_QSPI_DELAY_AFTER(x) ((x) << 8)
+#define ZY7_QSPI_DELAY_INIT_MASK 0xff
+#define ZY7_QSPI_DELAY_INIT_SHIFT 0
+#define ZY7_QSPI_DELAY_INIT(x) (x)
+
+#define ZY7_QSPI_TXD0_REG 0x001c
+#define ZY7_QSPI_RX_DATA_REG 0x0020
+
+#define ZY7_QSPI_SLV_IDLE_CT_REG 0x0024
+#define ZY7_QSPI_SLV_IDLE_CT_MASK 0xff
+
+#define ZY7_QSPI_TX_THRESH_REG 0x0028
+#define ZY7_QSPI_RX_THRESH_REG 0x002c
+
+#define ZY7_QSPI_GPIO_REG 0x0030
+#define ZY7_QSPI_GPIO_WP_N 1
+
+#define ZY7_QSPI_LPBK_DLY_ADJ_REG 0x0038
+#define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_SEL (1 << 8)
+#define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_PH (1 << 7)
+#define ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK (1 << 5)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_MASK (3 << 3)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_SHIFT 3
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY1(x) ((x) << 3)
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_MASK 7
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_SHIFT 0
+#define ZY7_QSPI_LPBK_DLY_ADJ_DLY0(x) (x)
+
+#define ZY7_QSPI_TXD1_REG 0x0080
+#define ZY7_QSPI_TXD2_REG 0x0084
+#define ZY7_QSPI_TXD3_REG 0x0088
+
+#define ZY7_QSPI_LQSPI_CFG_REG 0x00a0
+#define ZY7_QSPI_LQSPI_CFG_LINEAR (1U << 31)
+#define ZY7_QSPI_LQSPI_CFG_TWO_MEM (1 << 30)
+#define ZY7_QSPI_LQSPI_CFG_SEP_BUS (1 << 29)
+#define ZY7_QSPI_LQSPI_CFG_U_PAGE (1 << 28)
+#define ZY7_QSPI_LQSPI_CFG_MODE_EN (1 << 25)
+#define ZY7_QSPI_LQSPI_CFG_MODE_ON (1 << 24)
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS_MASK (0xff << 16)
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS_SHIFT 16
+#define ZY7_QSPI_LQSPI_CFG_MODE_BITS(x) ((x) << 16)
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_MASK (7 << 8)
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_SHIFT 8
+#define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES(x) ((x) << 8)
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK 0xff
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE_SHIFT 0
+#define ZY7_QSPI_LQSPI_CFG_INST_CODE(x) (x)
+
+#define ZY7_QSPI_LQSPI_STS_REG 0x00a4
+#define ZY7_QSPI_LQSPI_STS_D_FSM_ERR (1 << 2)
+#define ZY7_QSPI_LQSPI_STS_WR_RECVD (1 << 1)
+
+#define ZY7_QSPI_MOD_ID_REG 0x00fc
+
+static int zy7_qspi_detach(device_t);
+
+/* Fill hardware fifo with command and data bytes. */
+static void
+zy7_qspi_write_fifo(struct zy7_qspi_softc *sc, int nbytes)
+{
+ int n, nvalid;
+ uint32_t data;
+
+ while (nbytes > 0) {
+ nvalid = MIN(4, nbytes);
+ data = 0xffffffff;
+
+ /*
+ * A hardware bug forces us to wait until the tx fifo is
+ * empty before writing partial words. We'll come back
+ * next tx interrupt.
+ */
+ if (nvalid < 4 && (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) == 0)
+ return;
+
+ if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz) {
+ /* Writing command. */
+ n = MIN(nvalid, sc->cmd->tx_cmd_sz -
+ sc->tx_bytes_sent);
+ memcpy(&data, (uint8_t *)sc->cmd->tx_cmd +
+ sc->tx_bytes_sent, n);
+
+ if (nvalid > n) {
+ /* Writing start of data. */
+ memcpy((uint8_t *)&data + n,
+ sc->cmd->tx_data, nvalid - n);
+ }
+ } else
+ /* Writing data. */
+ memcpy(&data, (uint8_t *)sc->cmd->tx_data +
+ (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz), nvalid);
+
+ switch (nvalid) {
+ case 1:
+ WR4(sc, ZY7_QSPI_TXD1_REG, data);
+ break;
+ case 2:
+ WR4(sc, ZY7_QSPI_TXD2_REG, data);
+ break;
+ case 3:
+ WR4(sc, ZY7_QSPI_TXD3_REG, data);
+ break;
+ case 4:
+ WR4(sc, ZY7_QSPI_TXD0_REG, data);
+ break;
+ }
+
+ sc->tx_bytes_sent += nvalid;
+ nbytes -= nvalid;
+ }
+}
+
+/* Read hardware fifo data into command response and data buffers. */
+static void
+zy7_qspi_read_fifo(struct zy7_qspi_softc *sc)
+{
+ int n, nbytes;
+ uint32_t data;
+
+ do {
+ data = RD4(sc, ZY7_QSPI_RX_DATA_REG);
+ nbytes = MIN(4, sc->rx_bytes - sc->rx_bytes_rcvd);
+
+ /*
+ * Last word in non-word-multiple transfer is packed
+ * non-intuitively.
+ */
+ if (nbytes < 4)
+ data >>= 8 * (4 - nbytes);
+
+ if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz) {
+ /* Reading command. */
+ n = MIN(nbytes, sc->cmd->rx_cmd_sz -
+ sc->rx_bytes_rcvd);
+ memcpy((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd,
+ &data, n);
+ sc->rx_bytes_rcvd += n;
+ nbytes -= n;
+ data >>= 8 * n;
+ }
+
+ if (nbytes > 0) {
+ /* Reading data. */
+ memcpy((uint8_t *)sc->cmd->rx_data +
+ (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz),
+ &data, nbytes);
+ sc->rx_bytes_rcvd += nbytes;
+ }
+
+ } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
+}
+
+/* End a transfer early by draining rx fifo and disabling interrupts. */
+static void
+zy7_qspi_abort_transfer(struct zy7_qspi_softc *sc)
+{
+ /* Drain receive fifo. */
+ while ((RD4(sc, ZY7_QSPI_INTR_STAT_REG) &
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
+ (void)RD4(sc, ZY7_QSPI_RX_DATA_REG);
+
+ /* Shut down interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_RX_OVERFLOW |
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
+}
+
+static void
+zy7_qspi_intr(void *arg)
+{
+ struct zy7_qspi_softc *sc = (struct zy7_qspi_softc *)arg;
+ uint32_t istatus;
+
+ QSPI_SC_LOCK(sc);
+
+ sc->interrupts++;
+
+ istatus = RD4(sc, ZY7_QSPI_INTR_STAT_REG);
+
+ /* Stray interrupts can happen if a transfer gets interrupted. */
+ if (!sc->busy) {
+ sc->stray_ints++;
+ QSPI_SC_UNLOCK(sc);
+ return;
+ }
+
+ if ((istatus & ZY7_QSPI_INTR_RX_OVERFLOW) != 0) {
+ device_printf(sc->dev, "rx fifo overflow!\n");
+ sc->rx_overflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG,
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Empty receive fifo before any more transmit data is sent. */
+ if (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (istatus & ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
+ zy7_qspi_read_fifo(sc);
+ if (sc->rx_bytes_rcvd == sc->rx_bytes)
+ /* Disable receive interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+ }
+
+ /*
+ * Transmit underflows aren't really a bug because a hardware
+ * bug forces us to allow the tx fifo to go empty between full
+ * and partial fifo writes. Why bother counting?
+ */
+ if ((istatus & ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
+ sc->tx_underflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG,
+ ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW);
+ }
+
+ /* Fill transmit fifo. */
+ if (sc->tx_bytes_sent < sc->tx_bytes &&
+ (istatus & ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) != 0) {
+ zy7_qspi_write_fifo(sc, MIN(240, sc->tx_bytes -
+ sc->tx_bytes_sent));
+
+ if (sc->tx_bytes_sent == sc->tx_bytes) {
+ /*
+ * Disable transmit FIFO interrupt, enable receive
+ * FIFO interrupt.
+ */
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG,
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL);
+ WR4(sc, ZY7_QSPI_INTR_EN_REG,
+ ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY);
+ }
+ }
+
+ /* Finished with transfer? */
+ if (sc->tx_bytes_sent == sc->tx_bytes &&
+ sc->rx_bytes_rcvd == sc->rx_bytes) {
+ /* De-assert CS. */
+ sc->cfg_reg_shadow |= ZY7_QSPI_CONFIG_PCS;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ wakeup(sc->dev);
+ }
+
+ QSPI_SC_UNLOCK(sc);
+}
+
+/* Initialize hardware. */
+static int
+zy7_qspi_init_hw(struct zy7_qspi_softc *sc)
+{
+ uint32_t baud_div;
+
+ /* Configure LQSPI Config register. Disable linear mode. */
+ sc->lqspi_cfg_shadow = RD4(sc, ZY7_QSPI_LQSPI_CFG_REG);
+ sc->lqspi_cfg_shadow &= ~(ZY7_QSPI_LQSPI_CFG_LINEAR |
+ ZY7_QSPI_LQSPI_CFG_TWO_MEM |
+ ZY7_QSPI_LQSPI_CFG_SEP_BUS);
+ if (sc->is_dual) {
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_TWO_MEM;
+ if (sc->is_stacked) {
+ sc->lqspi_cfg_shadow &=
+ ~ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK;
+ sc->lqspi_cfg_shadow |=
+ ZY7_QSPI_LQSPI_CFG_INST_CODE(sc->is_dio ?
+ CMD_READ_DUAL_IO : CMD_READ_QUAD_OUTPUT);
+ } else
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_SEP_BUS;
+ }
+ WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
+
+ /* Find best clock divider. */
+ baud_div = 0;
+ while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
+ baud_div < 8)
+ baud_div++;
+ if (baud_div >= 8) {
+ device_printf(sc->dev, "cannot configure clock divider: ref=%d"
+ " spi=%d.\n", sc->ref_clock, sc->spi_clock);
+ return (EINVAL);
+ }
+ sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
+
+ /*
+ * If divider is 2 (the max speed), use internal loopback master
+ * clock for read data. (See section 12.3.1 in ref man.)
+ */
+ if (baud_div == 0)
+ WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG,
+ ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK |
+ ZY7_QSPI_LPBK_DLY_ADJ_DLY1(0) |
+ ZY7_QSPI_LPBK_DLY_ADJ_DLY0(0));
+ else
+ WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 0);
+
+ /* Set up configuration register. */
+ sc->cfg_reg_shadow =
+ ZY7_QSPI_CONFIG_IFMODE |
+ ZY7_QSPI_CONFIG_HOLDB_DR |
+ ZY7_QSPI_CONFIG_RSVD1 |
+ ZY7_QSPI_CONFIG_SSFORCE |
+ ZY7_QSPI_CONFIG_PCS |
+ ZY7_QSPI_CONFIG_FIFO_WIDTH32 |
+ ZY7_QSPI_CONFIG_BAUD_RATE_DIV(baud_div) |
+ ZY7_QSPI_CONFIG_MODE_SEL;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /*
+ * Set thresholds. We must use 1 for tx threshold because there
+ * is no fifo empty flag and we need one to implement a bug
+ * workaround.
+ */
+ WR4(sc, ZY7_QSPI_TX_THRESH_REG, 1);
+ WR4(sc, ZY7_QSPI_RX_THRESH_REG, 1);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
+
+ /* Enable SPI. */
+ WR4(sc, ZY7_QSPI_EN_REG, ZY7_SPI_ENABLE);
+
+ return (0);
+}
+
+static void
+zy7_qspi_add_sysctls(device_t dev)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
+ &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
+ &sc->rx_overflows, 0, "RX FIFO overflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
+ &sc->tx_underflows, 0, "TX FIFO underflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
+ &sc->interrupts, 0, "interrupt calls");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
+ &sc->stray_ints, 0, "stray interrupts");
+}
+
+static int
+zy7_qspi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq Quad-SPI Flash Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int
+zy7_qspi_attach(device_t dev)
+{
+ struct zy7_qspi_softc *sc;
+ int rid, err;
+ phandle_t node;
+ pcell_t cell;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ QSPI_SC_LOCK_INIT(sc);
+
+ /* Get ref-clock, spi-clock, and other properties. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
+ sc->ref_clock = fdt32_to_cpu(cell);
+ else {
+ device_printf(dev, "must have ref-clock property\n");
+ return (ENXIO);
+ }
+ if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
+ sc->spi_clock = fdt32_to_cpu(cell);
+ else
+ sc->spi_clock = ZY7_QSPI_DEFAULT_SPI_CLOCK;
+ if (OF_getprop(node, "is-stacked", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0) {
+ sc->is_dual = 1;
+ sc->is_stacked = 1;
+ } else if (OF_getprop(node, "is-dual", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0)
+ sc->is_dual = 1;
+ if (OF_getprop(node, "is-dio", &cell, sizeof(cell)) > 0 &&
+ fdt32_to_cpu(cell) != 0)
+ sc->is_dio = 1;
+
+ /* 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_qspi_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, "could not allocate IRQ resource.\n");
+ zy7_qspi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, zy7_qspi_intr, sc, &sc->intrhandle);
+ if (err) {
+ device_printf(dev, "could not setup IRQ.\n");
+ zy7_qspi_detach(dev);
+ return (err);
+ }
+
+ /* Configure the device. */
+ err = zy7_qspi_init_hw(sc);
+ if (err) {
+ zy7_qspi_detach(dev);
+ return (err);
+ }
+
+ sc->child = device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
+
+ zy7_qspi_add_sysctls(dev);
+
+ /* Attach spibus driver as a child later when interrupts work. */
+ bus_delayed_attach_children(dev);
+
+ return (0);
+}
+
+static int
+zy7_qspi_detach(device_t dev)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ /* Disable hardware. */
+ if (sc->mem_res != NULL) {
+ /* Disable SPI. */
+ WR4(sc, ZY7_QSPI_EN_REG, 0);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0);
+ }
+
+ /* 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);
+
+ QSPI_SC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+zy7_qspi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+zy7_qspi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct zy7_qspi_softc *sc = device_get_softc(dev);
+ int err = 0;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ if (sc->is_dual && cmd->tx_data_sz % 2 != 0) {
+ device_printf(dev, "driver does not support odd byte data "
+ "transfers in dual mode. (sz=%d)\n", cmd->tx_data_sz);
+ return (EINVAL);
+ }
+
+ QSPI_SC_LOCK(sc);
+
+ /* Wait for controller available. */
+ while (sc->busy != 0) {
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi0", 0);
+ if (err) {
+ QSPI_SC_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ /* Start transfer. */
+ sc->busy = 1;
+ sc->cmd = cmd;
+ sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
+ sc->tx_bytes_sent = 0;
+ sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
+ sc->rx_bytes_rcvd = 0;
+
+ /* Enable interrupts. zy7_qspi_intr() will handle transfer. */
+ WR4(sc, ZY7_QSPI_INTR_EN_REG,
+ ZY7_QSPI_INTR_TX_FIFO_NOT_FULL |
+ ZY7_QSPI_INTR_RX_OVERFLOW);
+
+#ifdef SPI_XFER_U_PAGE /* XXX: future support for stacked memories. */
+ if (sc->is_stacked) {
+ if ((cmd->flags & SPI_XFER_U_PAGE) != 0)
+ sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_U_PAGE;
+ else
+ sc->lqspi_cfg_shadow &= ~ZY7_QSPI_LQSPI_CFG_U_PAGE;
+ WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow);
+ }
+#endif
+
+ /* Assert CS. */
+ sc->cfg_reg_shadow &= ~ZY7_QSPI_CONFIG_PCS;
+ WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Wait for completion. */
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi1", hz * 2);
+ if (err)
+ zy7_qspi_abort_transfer(sc);
+
+ /* Release controller. */
+ sc->busy = 0;
+ wakeup_one(dev);
+
+ QSPI_SC_UNLOCK(sc);
+
+ return (err);
+}
+
+static device_method_t zy7_qspi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zy7_qspi_probe),
+ DEVMETHOD(device_attach, zy7_qspi_attach),
+ DEVMETHOD(device_detach, zy7_qspi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, zy7_qspi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, zy7_qspi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_qspi_driver = {
+ "zy7_qspi",
+ zy7_qspi_methods,
+ sizeof(struct zy7_qspi_softc),
+};
+
+DRIVER_MODULE(zy7_qspi, simplebus, zy7_qspi_driver, 0, 0);
+DRIVER_MODULE(ofw_spibus, zy7_qspi, ofw_spibus_driver, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
+MODULE_DEPEND(zy7_qspi, ofw_spibus, 1, 1, 1);
diff --git a/sys/arm/xilinx/zy7_reg.h b/sys/arm/xilinx/zy7_reg.h
new file mode 100644
index 000000000000..76954a1d9fd9
--- /dev/null
+++ b/sys/arm/xilinx/zy7_reg.h
@@ -0,0 +1,71 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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_HWBASE 0xE0000000
+#define ZYNQ7_PSIO_SIZE 0x00300000
+
+/* UART0 and UART1 */
+#define ZYNQ7_UART0_HWBASE (ZYNQ7_PSIO_HWBASE)
+#define ZYNQ7_UART0_SIZE 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_HWBASE 0xF8000000
+#define ZYNQ7_PSCTL_SIZE 0x01000000
+
+#define ZYNQ7_SLCR_HWBASE (ZYNQ7_PSCTL_HWBASE)
+#define ZYNQ7_SLCR_SIZE 0x1000
+
+#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 000000000000..40bcff49162b
--- /dev/null
+++ b/sys/arm/xilinx/zy7_slcr.c
@@ -0,0 +1,711 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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/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/stdarg.h>
+#include <sys/sysctl.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+#include <machine/resource.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_DEF)
+#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)))
+
+#define ZYNQ_DEFAULT_PS_CLK_FREQUENCY 33333333 /* 33.3 Mhz */
+
+SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD | CTLFLAG_MPSAFE, 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[100];
+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 int ps_clk_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency,
+ 0, "Zynq PS_CLK Frequency");
+
+static int io_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency,
+ 0, "Zynq IO PLL Frequency");
+
+static int arm_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD,
+ &arm_pll_frequency, 0, "Zynq ARM PLL Frequency");
+
+static int ddr_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD,
+ &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency");
+
+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 controlled 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);
+}
+
+/* Override cgem_set_refclk() in gigabit ethernet driver
+ * (sys/dev/cadence/if_cgem.c). This function is called to
+ * request a change in the gem's reference clock speed.
+ */
+int
+cgem_set_ref_clk(int unit, int frequency)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+
+ if (!sc)
+ return (-1);
+
+ /* Find suitable divisor pairs. Round result to nearest khz
+ * to test for match.
+ */
+ for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) {
+ div0 = (io_pll_frequency + div1 * frequency / 2) /
+ div1 / frequency;
+ if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX &&
+ ((io_pll_frequency / div0 / div1) + 500) / 1000 ==
+ (frequency + 500) / 1000)
+ break;
+ }
+
+ if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify GEM reference clock. */
+ WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL,
+ (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) |
+ (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) |
+ ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL |
+ ZY7_SLCR_GEM_CLK_CTRL_CLKACT);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+/*
+ * PL clocks management function
+ */
+int
+zy7_pl_fclk_set_source(int unit, int source)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify FPGAx source. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK);
+ reg |= (source << ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT);
+ WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_get_source(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Modify GEM reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ source = (reg & ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT;
+
+ /* ZY7_PL_FCLK_SRC_IO is actually b0x */
+ if ((source & 2) == 0)
+ source = ZY7_PL_FCLK_SRC_IO;
+
+ ZSLCR_UNLOCK(sc);
+
+ return (source);
+}
+
+int
+zy7_pl_fclk_set_freq(int unit, int frequency)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+ int base_frequency;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ source = zy7_pl_fclk_get_source(unit);
+ switch (source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ base_frequency = io_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_ARM:
+ base_frequency = arm_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_DDR:
+ base_frequency = ddr_pll_frequency;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /* Find suitable divisor pairs. Round result to nearest khz
+ * to test for match.
+ */
+ for (div1 = 1; div1 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX; div1++) {
+ div0 = (base_frequency + div1 * frequency / 2) /
+ div1 / frequency;
+ if (div0 > 0 && div0 <= ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX &&
+ ((base_frequency / div0 / div1) + 500) / 1000 ==
+ (frequency + 500) / 1000)
+ break;
+ }
+
+ if (div1 > ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ /* Modify FPGAx reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ reg &= ~(ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK |
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK);
+ reg |= (div1 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT) |
+ (div0 << ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT);
+ WR4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit), reg);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (base_frequency / div0 / div1);
+}
+
+int
+zy7_pl_fclk_get_freq(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ int div0, div1;
+ int base_frequency;
+ int frequency;
+ uint32_t reg;
+ int source;
+
+ if (!sc)
+ return (-1);
+
+ source = zy7_pl_fclk_get_source(unit);
+ switch (source) {
+ case ZY7_PL_FCLK_SRC_IO:
+ base_frequency = io_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_ARM:
+ base_frequency = arm_pll_frequency;
+ break;
+
+ case ZY7_PL_FCLK_SRC_DDR:
+ base_frequency = ddr_pll_frequency;
+ break;
+
+ default:
+ return (-1);
+ }
+
+ ZSLCR_LOCK(sc);
+
+ /* Modify FPGAx reference clock. */
+ reg = RD4(sc, ZY7_SLCR_FPGA_CLK_CTRL(unit));
+ div1 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT;
+ div0 = (reg & ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK) >>
+ ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT;
+
+ ZSLCR_UNLOCK(sc);
+
+ if (div0 == 0)
+ div0 = 1;
+
+ if (div1 == 0)
+ div1 = 1;
+
+ frequency = (base_frequency / div0 / div1);
+ /* Round to KHz */
+ frequency = (frequency + 500) / 1000;
+ frequency = frequency * 1000;
+
+ return (frequency);
+}
+
+int
+zy7_pl_fclk_enable(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0);
+ WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 0);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_disable(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+
+ /* Unlock SLCR registers. */
+ zy7_slcr_unlock(sc);
+
+ WR4(sc, ZY7_SLCR_FPGA_THR_CTRL(unit), 0);
+ WR4(sc, ZY7_SLCR_FPGA_THR_CNT(unit), 1);
+
+ /* Lock SLCR registers. */
+ zy7_slcr_lock(sc);
+
+ ZSLCR_UNLOCK(sc);
+
+ return (0);
+}
+
+int
+zy7_pl_fclk_enabled(int unit)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+ reg = RD4(sc, ZY7_SLCR_FPGA_THR_CNT(unit));
+ ZSLCR_UNLOCK(sc);
+
+ return !(reg & 1);
+}
+
+int
+zy7_pl_level_shifters_enabled(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ uint32_t reg;
+
+ if (!sc)
+ return (-1);
+
+ ZSLCR_LOCK(sc);
+ reg = RD4(sc, ZY7_SLCR_LVL_SHFTR_EN);
+ ZSLCR_UNLOCK(sc);
+
+ return (reg == ZY7_SLCR_LVL_SHFTR_EN_ALL);
+}
+
+void
+zy7_pl_level_shifters_enable(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+ zy7_slcr_unlock(sc);
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, ZY7_SLCR_LVL_SHFTR_EN_ALL);
+ zy7_slcr_lock(sc);
+ ZSLCR_UNLOCK(sc);
+}
+
+void
+zy7_pl_level_shifters_disable(void)
+{
+ struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+
+ if (!sc)
+ return;
+
+ ZSLCR_LOCK(sc);
+ zy7_slcr_unlock(sc);
+ WR4(sc, ZY7_SLCR_LVL_SHFTR_EN, 0);
+ zy7_slcr_lock(sc);
+ ZSLCR_UNLOCK(sc);
+}
+
+static int
+zy7_slcr_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ 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;
+ phandle_t node;
+ pcell_t cell;
+ uint32_t bootmode;
+ uint32_t pss_idcode;
+ uint32_t arm_pll_ctrl;
+ uint32_t ddr_pll_ctrl;
+ uint32_t io_pll_ctrl;
+ 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);
+
+ /* Derive PLL frequencies from PS_CLK. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getencprop(node, "clock-frequency", &cell, sizeof(cell)) > 0)
+ ps_clk_frequency = cell;
+ else
+ ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY;
+
+ arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL);
+ ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL);
+ io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL);
+
+ /* Determine ARM PLL frequency. */
+ if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ arm_pll_frequency = ps_clk_frequency;
+ else
+ arm_pll_frequency = ps_clk_frequency *
+ ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* Determine DDR PLL frequency. */
+ if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ ddr_pll_frequency = ps_clk_frequency;
+ else
+ ddr_pll_frequency = ps_clk_frequency *
+ ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* Determine IO PLL frequency. */
+ if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+ (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+ ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+ (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+ /* PLL is bypassed. */
+ io_pll_frequency = ps_clk_frequency;
+ else
+ io_pll_frequency = ps_clk_frequency *
+ ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+ ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+ /* 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);
+ int error;
+
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ /* 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),
+};
+
+DRIVER_MODULE(zy7_slcr, simplebus, zy7_slcr_driver, 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 000000000000..342dcd697c69
--- /dev/null
+++ b/sys/arm/xilinx/zy7_slcr.h
@@ -0,0 +1,327 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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 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.
+ */
+
+/*
+ * 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_GEM_CLK_CTRL_DIVISOR1_MASK (0x3f << 20)
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT 20
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX 0x3f
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MASK (0x3f << 8)
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT 8
+#define ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX 0x3f
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_MASK (7 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL (0 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_ARM_PLL (2 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_DDR_PLL (3 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_EMIO_CLK (4 << 4)
+#define ZY7_SLCR_GEM_CLK_CTRL_CLKACT 1
+#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_FPGA_CLK_CTRL(unit) (0x0170 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_SHIFT 20
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR1_MASK (0x3f << 20)
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_SHIFT 8
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR0_MASK (0x3f << 8)
+#define ZY7_SLCR_FPGA_CLK_CTRL_DIVISOR_MAX 0x3f
+#define ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_SHIFT 4
+#define ZY7_SLCR_FPGA_CLK_CTRL_SRCSEL_MASK (3 << 4)
+#define ZY7_SLCR_FPGA_THR_CTRL(unit) (0x0174 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_THR_CTRL_CNT_RST (1 << 1)
+#define ZY7_SLCR_FPGA_THR_CTRL_CPU_START (1 << 0)
+#define ZY7_SLCR_FPGA_THR_CNT(unit) (0x0178 + 0x10 * (unit))
+#define ZY7_SLCR_FPGA_THR_STA(unit) (0x017c + 0x10 * (unit))
+#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 (0xffU << 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 (0xfU << 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_7Z007S (0x03 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z010 (0x02 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z012S (0x1c << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z014S (0x08 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z015 (0x1b << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z020 (0x07 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z030 (0x0c << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z045 (0x11 << 12)
+#define ZY7_SLCR_PSS_IDCODE_DEVICE_7Z100 (0x16 << 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 en_level_shifters);
+extern int cgem_set_ref_clk(int unit, int frequency);
+
+/* Should be consistent with SRCSEL field of FPGAx_CLK_CTRL */
+#define ZY7_PL_FCLK_SRC_IO 0
+#define ZY7_PL_FCLK_SRC_IO_ALT 1 /* ZY7_PL_FCLK_SRC_IO is b0x */
+#define ZY7_PL_FCLK_SRC_ARM 2
+#define ZY7_PL_FCLK_SRC_DDR 3
+
+int zy7_pl_fclk_set_source(int unit, int source);
+int zy7_pl_fclk_get_source(int unit);
+int zy7_pl_fclk_set_freq(int unit, int freq);
+int zy7_pl_fclk_get_freq(int unit);
+int zy7_pl_fclk_enable(int unit);
+int zy7_pl_fclk_disable(int unit);
+int zy7_pl_fclk_enabled(int unit);
+int zy7_pl_level_shifters_enabled(void);
+void zy7_pl_level_shifters_enable(void);
+void zy7_pl_level_shifters_disable(void);
+
+#endif
+#endif /* _ZY7_SLCR_H_ */
diff --git a/sys/arm/xilinx/zy7_spi.c b/sys/arm/xilinx/zy7_spi.c
new file mode 100644
index 000000000000..636a40d729d6
--- /dev/null
+++ b/sys/arm/xilinx/zy7_spi.c
@@ -0,0 +1,588 @@
+/*-
+ * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com>
+ * 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.
+ */
+
+#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/stdarg.h>
+#include <sys/uio.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/spibus/spi.h>
+#include <dev/spibus/spibusvar.h>
+
+#include "spibus_if.h"
+
+static struct ofw_compat_data compat_data[] = {
+ {"xlnx,zy7_spi", 1},
+ {"xlnx,zynq-spi-1.0", 1},
+ {"cdns,spi-r1p6", 1},
+ {NULL, 0}
+};
+
+struct zy7_spi_softc {
+ device_t dev;
+ device_t child;
+ struct mtx sc_mtx;
+ struct resource *mem_res;
+ struct resource *irq_res;
+ void *intrhandle;
+
+ uint32_t cfg_reg_shadow;
+ uint32_t spi_clock;
+ uint32_t ref_clock;
+ unsigned int spi_clk_real_freq;
+ unsigned int rx_overflows;
+ unsigned int tx_underflows;
+ unsigned int interrupts;
+ unsigned int stray_ints;
+ struct spi_command *cmd;
+ int tx_bytes; /* tx_cmd_sz + tx_data_sz */
+ int tx_bytes_sent;
+ int rx_bytes; /* rx_cmd_sz + rx_data_sz */
+ int rx_bytes_rcvd;
+ int busy;
+};
+
+#define ZY7_SPI_DEFAULT_SPI_CLOCK 50000000
+
+#define SPI_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
+#define SPI_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
+#define SPI_SC_LOCK_INIT(sc) \
+ mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF)
+#define SPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx)
+#define SPI_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)))
+
+/*
+ * SPI device registers.
+ * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual.
+ * (v1.12.1) December 6, 2017. Xilinx doc UG585.
+ */
+#define ZY7_SPI_CONFIG_REG 0x0000
+#define ZY7_SPI_CONFIG_MODEFAIL_GEN_EN (1 << 17)
+#define ZY7_SPI_CONFIG_MAN_STRT (1 << 16)
+#define ZY7_SPI_CONFIG_MAN_STRT_EN (1 << 15)
+#define ZY7_SPI_CONFIG_MAN_CS (1 << 14)
+#define ZY7_SPI_CONFIG_CS_MASK (0xf << 10)
+#define ZY7_SPI_CONFIG_CS(x) ((0xf ^ (1 << (x))) << 10)
+#define ZY7_SPI_CONFIG_PERI_SEL (1 << 9)
+#define ZY7_SPI_CONFIG_REF_CLK (1 << 8)
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV_MASK (7 << 3)
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV_SHIFT 3
+#define ZY7_SPI_CONFIG_BAUD_RATE_DIV(x) ((x) << 3) /* divide by 2<<x */
+#define ZY7_SPI_CONFIG_CLK_PH (1 << 2) /* clock phase */
+#define ZY7_SPI_CONFIG_CLK_POL (1 << 1) /* clock polatiry */
+#define ZY7_SPI_CONFIG_MODE_SEL (1 << 0) /* master enable */
+
+#define ZY7_SPI_INTR_STAT_REG 0x0004
+#define ZY7_SPI_INTR_EN_REG 0x0008
+#define ZY7_SPI_INTR_DIS_REG 0x000c
+#define ZY7_SPI_INTR_MASK_REG 0x0010
+#define ZY7_SPI_INTR_TX_FIFO_UNDERFLOW (1 << 6)
+#define ZY7_SPI_INTR_RX_FIFO_FULL (1 << 5)
+#define ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY (1 << 4)
+#define ZY7_SPI_INTR_TX_FIFO_FULL (1 << 3)
+#define ZY7_SPI_INTR_TX_FIFO_NOT_FULL (1 << 2)
+#define ZY7_SPI_INTR_MODE_FAULT (1 << 1)
+#define ZY7_SPI_INTR_RX_OVERFLOW (1 << 0)
+
+#define ZY7_SPI_EN_REG 0x0014
+#define ZY7_SPI_ENABLE (1 << 0)
+
+#define ZY7_SPI_DELAY_CTRL_REG 0x0018
+#define ZY7_SPI_DELAY_CTRL_BTWN_MASK (0xff << 16)
+#define ZY7_SPI_DELAY_CTRL_BTWN_SHIFT 16
+#define ZY7_SPI_DELAY_CTRL_AFTER_MASK (0xff << 8)
+#define ZY7_SPI_DELAY_CTRL_AFTER_SHIFT 8
+#define ZY7_SPI_DELAY_CTRL_INIT_MASK (0xff << 0)
+#define ZY7_SPI_DELAY_CTRL_INIT_SHIFT 0
+
+#define ZY7_SPI_TX_DATA_REG 0x001c
+#define ZY7_SPI_RX_DATA_REG 0x0020
+
+#define ZY7_SPI_SLV_IDLE_COUNT_REG 0x0024
+
+#define ZY7_SPI_TX_THRESH_REG 0x0028
+#define ZY7_SPI_RX_THRESH_REG 0x002c
+
+/* Fill hardware fifo with command and data bytes. */
+static void
+zy7_spi_write_fifo(struct zy7_spi_softc *sc, int nbytes)
+{
+ uint8_t byte;
+
+ while (nbytes > 0) {
+ if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz)
+ /* Writing command. */
+ byte = *((uint8_t *)sc->cmd->tx_cmd +
+ sc->tx_bytes_sent);
+ else
+ /* Writing data. */
+ byte = *((uint8_t *)sc->cmd->tx_data +
+ (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz));
+
+ WR4(sc, ZY7_SPI_TX_DATA_REG, (uint32_t)byte);
+
+ sc->tx_bytes_sent++;
+ nbytes--;
+ }
+}
+
+/* Read hardware fifo data into command response and data buffers. */
+static void
+zy7_spi_read_fifo(struct zy7_spi_softc *sc)
+{
+ uint8_t byte;
+
+ do {
+ byte = RD4(sc, ZY7_SPI_RX_DATA_REG) & 0xff;
+
+ if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz)
+ /* Reading command. */
+ *((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd) =
+ byte;
+ else
+ /* Reading data. */
+ *((uint8_t *)sc->cmd->rx_data +
+ (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz)) =
+ byte;
+
+ sc->rx_bytes_rcvd++;
+
+ } while (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (RD4(sc, ZY7_SPI_INTR_STAT_REG) &
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0);
+}
+
+/* End a transfer early by draining rx fifo and disabling interrupts. */
+static void
+zy7_spi_abort_transfer(struct zy7_spi_softc *sc)
+{
+ /* Drain receive fifo. */
+ while ((RD4(sc, ZY7_SPI_INTR_STAT_REG) &
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0)
+ (void)RD4(sc, ZY7_SPI_RX_DATA_REG);
+
+ /* Shut down interrupts. */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_RX_OVERFLOW |
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
+}
+
+static void
+zy7_spi_intr(void *arg)
+{
+ struct zy7_spi_softc *sc = (struct zy7_spi_softc *)arg;
+ uint32_t istatus;
+
+ SPI_SC_LOCK(sc);
+
+ sc->interrupts++;
+
+ istatus = RD4(sc, ZY7_SPI_INTR_STAT_REG);
+
+ /* Stray interrupts can happen if a transfer gets interrupted. */
+ if (!sc->busy) {
+ sc->stray_ints++;
+ SPI_SC_UNLOCK(sc);
+ return;
+ }
+
+ if ((istatus & ZY7_SPI_INTR_RX_OVERFLOW) != 0) {
+ device_printf(sc->dev, "rx fifo overflow!\n");
+ sc->rx_overflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG,
+ ZY7_SPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Empty receive fifo before any more transmit data is sent. */
+ if (sc->rx_bytes_rcvd < sc->rx_bytes &&
+ (istatus & ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY) != 0) {
+ zy7_spi_read_fifo(sc);
+ if (sc->rx_bytes_rcvd == sc->rx_bytes)
+ /* Disable receive interrupts. */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY |
+ ZY7_SPI_INTR_RX_OVERFLOW);
+ }
+
+ /* Count tx underflows. They probably shouldn't happen. */
+ if ((istatus & ZY7_SPI_INTR_TX_FIFO_UNDERFLOW) != 0) {
+ sc->tx_underflows++;
+
+ /* Clear status bit. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG,
+ ZY7_SPI_INTR_TX_FIFO_UNDERFLOW);
+ }
+
+ /* Fill transmit fifo. */
+ if (sc->tx_bytes_sent < sc->tx_bytes &&
+ (istatus & ZY7_SPI_INTR_TX_FIFO_NOT_FULL) != 0) {
+ zy7_spi_write_fifo(sc, MIN(96, sc->tx_bytes -
+ sc->tx_bytes_sent));
+
+ if (sc->tx_bytes_sent == sc->tx_bytes) {
+ /* Disable transmit FIFO interrupt, enable receive
+ * FIFO interrupt.
+ */
+ WR4(sc, ZY7_SPI_INTR_DIS_REG,
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL);
+ WR4(sc, ZY7_SPI_INTR_EN_REG,
+ ZY7_SPI_INTR_RX_FIFO_NOT_EMPTY);
+ }
+ }
+
+ /* Finished with transfer? */
+ if (sc->tx_bytes_sent == sc->tx_bytes &&
+ sc->rx_bytes_rcvd == sc->rx_bytes) {
+ /* De-assert CS. */
+ sc->cfg_reg_shadow &=
+ ~(ZY7_SPI_CONFIG_CLK_PH | ZY7_SPI_CONFIG_CLK_POL);
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS_MASK;
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ wakeup(sc->dev);
+ }
+
+ SPI_SC_UNLOCK(sc);
+}
+
+/* Initialize hardware. */
+static int
+zy7_spi_init_hw(struct zy7_spi_softc *sc)
+{
+ uint32_t baud_div;
+
+ /* Find best clock divider. Divide by 2 not supported. */
+ baud_div = 1;
+ while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock &&
+ baud_div < 8)
+ baud_div++;
+ if (baud_div >= 8) {
+ device_printf(sc->dev, "cannot configure clock divider: ref=%d"
+ " spi=%d.\n", sc->ref_clock, sc->spi_clock);
+ return (EINVAL);
+ }
+ sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1);
+
+ /* Set up configuration register. */
+ sc->cfg_reg_shadow =
+ ZY7_SPI_CONFIG_MAN_CS |
+ ZY7_SPI_CONFIG_CS_MASK |
+ ZY7_SPI_CONFIG_BAUD_RATE_DIV(baud_div) |
+ ZY7_SPI_CONFIG_MODE_SEL;
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Set thresholds. */
+ WR4(sc, ZY7_SPI_TX_THRESH_REG, 32);
+ WR4(sc, ZY7_SPI_RX_THRESH_REG, 1);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
+
+ /* Enable SPI. */
+ WR4(sc, ZY7_SPI_EN_REG, ZY7_SPI_ENABLE);
+
+ return (0);
+}
+
+static void
+zy7_spi_add_sysctls(device_t dev)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *child;
+
+ ctx = device_get_sysctl_ctx(dev);
+ child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD,
+ &sc->spi_clk_real_freq, 0, "SPI clock real frequency");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD,
+ &sc->rx_overflows, 0, "RX FIFO overflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD,
+ &sc->tx_underflows, 0, "TX FIFO underflow events");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD,
+ &sc->interrupts, 0, "interrupt calls");
+
+ SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD,
+ &sc->stray_ints, 0, "stray interrupts");
+}
+
+static int
+zy7_spi_probe(device_t dev)
+{
+
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+ return (ENXIO);
+
+ device_set_desc(dev, "Zynq SPI Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+static int zy7_spi_detach(device_t);
+
+static int
+zy7_spi_attach(device_t dev)
+{
+ struct zy7_spi_softc *sc;
+ int rid, err;
+ phandle_t node;
+ pcell_t cell;
+
+ sc = device_get_softc(dev);
+ sc->dev = dev;
+
+ SPI_SC_LOCK_INIT(sc);
+
+ /* Get ref-clock and spi-clock properties. */
+ node = ofw_bus_get_node(dev);
+ if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0)
+ sc->ref_clock = fdt32_to_cpu(cell);
+ else {
+ device_printf(dev, "must have ref-clock property\n");
+ return (ENXIO);
+ }
+ if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0)
+ sc->spi_clock = fdt32_to_cpu(cell);
+ else
+ sc->spi_clock = ZY7_SPI_DEFAULT_SPI_CLOCK;
+
+ /* 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_spi_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, "could not allocate IRQ resource.\n");
+ zy7_spi_detach(dev);
+ return (ENOMEM);
+ }
+
+ /* Activate the interrupt. */
+ err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
+ NULL, zy7_spi_intr, sc, &sc->intrhandle);
+ if (err) {
+ device_printf(dev, "could not setup IRQ.\n");
+ zy7_spi_detach(dev);
+ return (err);
+ }
+
+ /* Configure the device. */
+ err = zy7_spi_init_hw(sc);
+ if (err) {
+ zy7_spi_detach(dev);
+ return (err);
+ }
+
+ sc->child = device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
+
+ zy7_spi_add_sysctls(dev);
+
+ /* Attach spibus driver as a child later when interrupts work. */
+ bus_delayed_attach_children(dev);
+
+ return (0);
+}
+
+static int
+zy7_spi_detach(device_t dev)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+ int error;
+
+ error = bus_generic_detach(dev);
+ if (error != 0)
+ return (error);
+
+ /* Disable hardware. */
+ if (sc->mem_res != NULL) {
+ /* Disable SPI. */
+ WR4(sc, ZY7_SPI_EN_REG, 0);
+
+ /* Clear and disable all interrupts. */
+ WR4(sc, ZY7_SPI_INTR_STAT_REG, ~0);
+ WR4(sc, ZY7_SPI_INTR_DIS_REG, ~0);
+ }
+
+ /* 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);
+
+ SPI_SC_LOCK_DESTROY(sc);
+
+ return (0);
+}
+
+static phandle_t
+zy7_spi_get_node(device_t bus, device_t dev)
+{
+
+ return (ofw_bus_get_node(bus));
+}
+
+static int
+zy7_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
+{
+ struct zy7_spi_softc *sc = device_get_softc(dev);
+ uint32_t cs;
+ uint32_t mode;
+ int err = 0;
+
+ KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
+ ("TX/RX command sizes should be equal"));
+ KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
+ ("TX/RX data sizes should be equal"));
+
+ /* Get chip select and mode for this child. */
+ spibus_get_cs(child, &cs);
+ cs &= ~SPIBUS_CS_HIGH;
+ if (cs > 2) {
+ device_printf(dev, "Invalid chip select %d requested by %s",
+ cs, device_get_nameunit(child));
+ return (EINVAL);
+ }
+ spibus_get_mode(child, &mode);
+
+ SPI_SC_LOCK(sc);
+
+ /* Wait for controller available. */
+ while (sc->busy != 0) {
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi0", 0);
+ if (err) {
+ SPI_SC_UNLOCK(sc);
+ return (err);
+ }
+ }
+
+ /* Start transfer. */
+ sc->busy = 1;
+ sc->cmd = cmd;
+ sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz;
+ sc->tx_bytes_sent = 0;
+ sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz;
+ sc->rx_bytes_rcvd = 0;
+
+ /* Enable interrupts. zy7_spi_intr() will handle transfer. */
+ WR4(sc, ZY7_SPI_INTR_EN_REG,
+ ZY7_SPI_INTR_TX_FIFO_NOT_FULL |
+ ZY7_SPI_INTR_RX_OVERFLOW);
+
+ /* Handle polarity and phase. */
+ if (mode == SPIBUS_MODE_CPHA || mode == SPIBUS_MODE_CPOL_CPHA)
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_PH;
+ if (mode == SPIBUS_MODE_CPOL || mode == SPIBUS_MODE_CPOL_CPHA)
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CLK_POL;
+
+ /* Assert CS. */
+ sc->cfg_reg_shadow &= ~ZY7_SPI_CONFIG_CS_MASK;
+ sc->cfg_reg_shadow |= ZY7_SPI_CONFIG_CS(cs);
+ WR4(sc, ZY7_SPI_CONFIG_REG, sc->cfg_reg_shadow);
+
+ /* Wait for completion. */
+ err = mtx_sleep(dev, &sc->sc_mtx, 0, "zspi1", hz * 2);
+ if (err)
+ zy7_spi_abort_transfer(sc);
+
+ /* Release controller. */
+ sc->busy = 0;
+ wakeup_one(dev);
+
+ SPI_SC_UNLOCK(sc);
+
+ return (err);
+}
+
+static device_method_t zy7_spi_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, zy7_spi_probe),
+ DEVMETHOD(device_attach, zy7_spi_attach),
+ DEVMETHOD(device_detach, zy7_spi_detach),
+
+ /* SPI interface */
+ DEVMETHOD(spibus_transfer, zy7_spi_transfer),
+
+ /* ofw_bus interface */
+ DEVMETHOD(ofw_bus_get_node, zy7_spi_get_node),
+
+ DEVMETHOD_END
+};
+
+static driver_t zy7_spi_driver = {
+ "zy7_spi",
+ zy7_spi_methods,
+ sizeof(struct zy7_spi_softc),
+};
+
+DRIVER_MODULE(zy7_spi, simplebus, zy7_spi_driver, 0, 0);
+DRIVER_MODULE(ofw_spibus, zy7_spi, ofw_spibus_driver, 0, 0);
+SIMPLEBUS_PNP_INFO(compat_data);
+MODULE_DEPEND(zy7_spi, ofw_spibus, 1, 1, 1);