1*2de9b4d3SEmmanuel Vadot /*- 2*2de9b4d3SEmmanuel Vadot * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*2de9b4d3SEmmanuel Vadot * 4*2de9b4d3SEmmanuel Vadot * Copyright (c) 2018 Thomas Skibo <thomasskibo@yahoo.com> 5*2de9b4d3SEmmanuel Vadot * All rights reserved. 6*2de9b4d3SEmmanuel Vadot * 7*2de9b4d3SEmmanuel Vadot * Redistribution and use in source and binary forms, with or without 8*2de9b4d3SEmmanuel Vadot * modification, are permitted provided that the following conditions 9*2de9b4d3SEmmanuel Vadot * are met: 10*2de9b4d3SEmmanuel Vadot * 1. Redistributions of source code must retain the above copyright 11*2de9b4d3SEmmanuel Vadot * notice, this list of conditions and the following disclaimer. 12*2de9b4d3SEmmanuel Vadot * 2. Redistributions in binary form must reproduce the above copyright 13*2de9b4d3SEmmanuel Vadot * notice, this list of conditions and the following disclaimer in the 14*2de9b4d3SEmmanuel Vadot * documentation and/or other materials provided with the distribution. 15*2de9b4d3SEmmanuel Vadot * 16*2de9b4d3SEmmanuel Vadot * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17*2de9b4d3SEmmanuel Vadot * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18*2de9b4d3SEmmanuel Vadot * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19*2de9b4d3SEmmanuel Vadot * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20*2de9b4d3SEmmanuel Vadot * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21*2de9b4d3SEmmanuel Vadot * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22*2de9b4d3SEmmanuel Vadot * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23*2de9b4d3SEmmanuel Vadot * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24*2de9b4d3SEmmanuel Vadot * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25*2de9b4d3SEmmanuel Vadot * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26*2de9b4d3SEmmanuel Vadot * SUCH DAMAGE. 27*2de9b4d3SEmmanuel Vadot */ 28*2de9b4d3SEmmanuel Vadot 29*2de9b4d3SEmmanuel Vadot #include <sys/cdefs.h> 30*2de9b4d3SEmmanuel Vadot __FBSDID("$FreeBSD$"); 31*2de9b4d3SEmmanuel Vadot 32*2de9b4d3SEmmanuel Vadot /* 33*2de9b4d3SEmmanuel Vadot * This is a driver for the Quad-SPI Flash Controller in the Xilinx 34*2de9b4d3SEmmanuel Vadot * Zynq-7000 SoC. 35*2de9b4d3SEmmanuel Vadot */ 36*2de9b4d3SEmmanuel Vadot 37*2de9b4d3SEmmanuel Vadot #include <sys/param.h> 38*2de9b4d3SEmmanuel Vadot #include <sys/systm.h> 39*2de9b4d3SEmmanuel Vadot #include <sys/conf.h> 40*2de9b4d3SEmmanuel Vadot #include <sys/kernel.h> 41*2de9b4d3SEmmanuel Vadot #include <sys/module.h> 42*2de9b4d3SEmmanuel Vadot #include <sys/sysctl.h> 43*2de9b4d3SEmmanuel Vadot #include <sys/lock.h> 44*2de9b4d3SEmmanuel Vadot #include <sys/mutex.h> 45*2de9b4d3SEmmanuel Vadot #include <sys/resource.h> 46*2de9b4d3SEmmanuel Vadot #include <sys/rman.h> 47*2de9b4d3SEmmanuel Vadot #include <sys/uio.h> 48*2de9b4d3SEmmanuel Vadot 49*2de9b4d3SEmmanuel Vadot #include <machine/bus.h> 50*2de9b4d3SEmmanuel Vadot #include <machine/resource.h> 51*2de9b4d3SEmmanuel Vadot #include <machine/stdarg.h> 52*2de9b4d3SEmmanuel Vadot 53*2de9b4d3SEmmanuel Vadot #include <dev/fdt/fdt_common.h> 54*2de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus.h> 55*2de9b4d3SEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h> 56*2de9b4d3SEmmanuel Vadot 57*2de9b4d3SEmmanuel Vadot #include <dev/spibus/spi.h> 58*2de9b4d3SEmmanuel Vadot #include <dev/spibus/spibusvar.h> 59*2de9b4d3SEmmanuel Vadot 60*2de9b4d3SEmmanuel Vadot #include <dev/flash/mx25lreg.h> 61*2de9b4d3SEmmanuel Vadot 62*2de9b4d3SEmmanuel Vadot #include "spibus_if.h" 63*2de9b4d3SEmmanuel Vadot 64*2de9b4d3SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 65*2de9b4d3SEmmanuel Vadot {"xlnx,zy7_qspi", 1}, 66*2de9b4d3SEmmanuel Vadot {"xlnx,zynq-qspi-1.0", 1}, 67*2de9b4d3SEmmanuel Vadot {NULL, 0} 68*2de9b4d3SEmmanuel Vadot }; 69*2de9b4d3SEmmanuel Vadot 70*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc { 71*2de9b4d3SEmmanuel Vadot device_t dev; 72*2de9b4d3SEmmanuel Vadot device_t child; 73*2de9b4d3SEmmanuel Vadot struct mtx sc_mtx; 74*2de9b4d3SEmmanuel Vadot struct resource *mem_res; 75*2de9b4d3SEmmanuel Vadot struct resource *irq_res; 76*2de9b4d3SEmmanuel Vadot void *intrhandle; 77*2de9b4d3SEmmanuel Vadot 78*2de9b4d3SEmmanuel Vadot uint32_t cfg_reg_shadow; 79*2de9b4d3SEmmanuel Vadot uint32_t lqspi_cfg_shadow; 80*2de9b4d3SEmmanuel Vadot uint32_t spi_clock; 81*2de9b4d3SEmmanuel Vadot uint32_t ref_clock; 82*2de9b4d3SEmmanuel Vadot unsigned int spi_clk_real_freq; 83*2de9b4d3SEmmanuel Vadot unsigned int rx_overflows; 84*2de9b4d3SEmmanuel Vadot unsigned int tx_underflows; 85*2de9b4d3SEmmanuel Vadot unsigned int interrupts; 86*2de9b4d3SEmmanuel Vadot unsigned int stray_ints; 87*2de9b4d3SEmmanuel Vadot struct spi_command *cmd; 88*2de9b4d3SEmmanuel Vadot int tx_bytes; /* tx_cmd_sz + tx_data_sz */ 89*2de9b4d3SEmmanuel Vadot int tx_bytes_sent; 90*2de9b4d3SEmmanuel Vadot int rx_bytes; /* rx_cmd_sz + rx_data_sz */ 91*2de9b4d3SEmmanuel Vadot int rx_bytes_rcvd; 92*2de9b4d3SEmmanuel Vadot int busy; 93*2de9b4d3SEmmanuel Vadot int is_dual; 94*2de9b4d3SEmmanuel Vadot int is_stacked; 95*2de9b4d3SEmmanuel Vadot int is_dio; 96*2de9b4d3SEmmanuel Vadot }; 97*2de9b4d3SEmmanuel Vadot 98*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DEFAULT_SPI_CLOCK 50000000 99*2de9b4d3SEmmanuel Vadot 100*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 101*2de9b4d3SEmmanuel Vadot #define QSPI_SC_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 102*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_INIT(sc) \ 103*2de9b4d3SEmmanuel Vadot mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), NULL, MTX_DEF) 104*2de9b4d3SEmmanuel Vadot #define QSPI_SC_LOCK_DESTROY(sc) mtx_destroy(&(sc)->sc_mtx) 105*2de9b4d3SEmmanuel Vadot #define QSPI_SC_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED) 106*2de9b4d3SEmmanuel Vadot 107*2de9b4d3SEmmanuel Vadot #define RD4(sc, off) (bus_read_4((sc)->mem_res, (off))) 108*2de9b4d3SEmmanuel Vadot #define WR4(sc, off, val) (bus_write_4((sc)->mem_res, (off), (val))) 109*2de9b4d3SEmmanuel Vadot 110*2de9b4d3SEmmanuel Vadot /* 111*2de9b4d3SEmmanuel Vadot * QSPI device registers. 112*2de9b4d3SEmmanuel Vadot * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 113*2de9b4d3SEmmanuel Vadot * (v1.12.2) July 1, 2018. Xilinx doc UG585. 114*2de9b4d3SEmmanuel Vadot */ 115*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_REG 0x0000 116*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_IFMODE (1U << 31) 117*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_ENDIAN (1 << 26) 118*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_HOLDB_DR (1 << 19) 119*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_RSVD1 (1 << 17) /* must be 1 */ 120*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_MANSTRT (1 << 16) 121*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_MANSTRTEN (1 << 15) 122*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_SSFORCE (1 << 14) 123*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_PCS (1 << 10) 124*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_REF_CLK (1 << 8) 125*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_FIFO_WIDTH_MASK (3 << 6) 126*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_FIFO_WIDTH32 (3 << 6) 127*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_MASK (7 << 3) 128*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_BAUD_RATE_DIV_SHIFT 3 129*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_BAUD_RATE_DIV(x) ((x) << 3) /* divide by 2<<x */ 130*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_CLK_PH (1 << 2) /* clock phase */ 131*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_CLK_POL (1 << 1) /* clock polarity */ 132*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_CONFIG_MODE_SEL (1 << 0) /* master enable */ 133*2de9b4d3SEmmanuel Vadot 134*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_STAT_REG 0x0004 135*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_EN_REG 0x0008 136*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_DIS_REG 0x000c 137*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_MASK_REG 0x0010 138*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW (1 << 6) 139*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_RX_FIFO_FULL (1 << 5) 140*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY (1 << 4) 141*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_TX_FIFO_FULL (1 << 3) 142*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_TX_FIFO_NOT_FULL (1 << 2) 143*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_INTR_RX_OVERFLOW (1 << 0) 144*2de9b4d3SEmmanuel Vadot 145*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_EN_REG 0x0014 146*2de9b4d3SEmmanuel Vadot #define ZY7_SPI_ENABLE 1 147*2de9b4d3SEmmanuel Vadot 148*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_REG 0x0018 149*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_NSS_MASK (0xffU << 24) 150*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_NSS_SHIFT 24 151*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_NSS(x) ((x) << 24) 152*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_BTWN_MASK (0xff << 16) 153*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_BTWN_SHIFT 16 154*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_BTWN(x) ((x) << 16) 155*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_AFTER_MASK (0xff << 8) 156*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_AFTER_SHIFT 8 157*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_AFTER(x) ((x) << 8) 158*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_INIT_MASK 0xff 159*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_INIT_SHIFT 0 160*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_DELAY_INIT(x) (x) 161*2de9b4d3SEmmanuel Vadot 162*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD0_REG 0x001c 163*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_DATA_REG 0x0020 164*2de9b4d3SEmmanuel Vadot 165*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_SLV_IDLE_CT_REG 0x0024 166*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_SLV_IDLE_CT_MASK 0xff 167*2de9b4d3SEmmanuel Vadot 168*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TX_THRESH_REG 0x0028 169*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_RX_THRESH_REG 0x002c 170*2de9b4d3SEmmanuel Vadot 171*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_GPIO_REG 0x0030 172*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_GPIO_WP_N 1 173*2de9b4d3SEmmanuel Vadot 174*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_REG 0x0038 175*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_SEL (1 << 8) 176*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_LPBK_PH (1 << 7) 177*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK (1 << 5) 178*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_MASK (3 << 3) 179*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY1_SHIFT 3 180*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY1(x) ((x) << 3) 181*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_MASK 7 182*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY0_SHIFT 0 183*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LPBK_DLY_ADJ_DLY0(x) (x) 184*2de9b4d3SEmmanuel Vadot 185*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD1_REG 0x0080 186*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD2_REG 0x0084 187*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_TXD3_REG 0x0088 188*2de9b4d3SEmmanuel Vadot 189*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_REG 0x00a0 190*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_LINEAR (1U << 31) 191*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_TWO_MEM (1 << 30) 192*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_SEP_BUS (1 << 29) 193*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_U_PAGE (1 << 28) 194*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_MODE_EN (1 << 25) 195*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_MODE_ON (1 << 24) 196*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_MODE_BITS_MASK (0xff << 16) 197*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_MODE_BITS_SHIFT 16 198*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_MODE_BITS(x) ((x) << 16) 199*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_MASK (7 << 8) 200*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES_SHIFT 8 201*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_DUMMY_BYTES(x) ((x) << 8) 202*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK 0xff 203*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_INST_CODE_SHIFT 0 204*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_CFG_INST_CODE(x) (x) 205*2de9b4d3SEmmanuel Vadot 206*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_STS_REG 0x00a4 207*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_STS_D_FSM_ERR (1 << 2) 208*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_LQSPI_STS_WR_RECVD (1 << 1) 209*2de9b4d3SEmmanuel Vadot 210*2de9b4d3SEmmanuel Vadot #define ZY7_QSPI_MOD_ID_REG 0x00fc 211*2de9b4d3SEmmanuel Vadot 212*2de9b4d3SEmmanuel Vadot static int zy7_qspi_detach(device_t); 213*2de9b4d3SEmmanuel Vadot 214*2de9b4d3SEmmanuel Vadot /* Fill hardware fifo with command and data bytes. */ 215*2de9b4d3SEmmanuel Vadot static void 216*2de9b4d3SEmmanuel Vadot zy7_qspi_write_fifo(struct zy7_qspi_softc *sc, int nbytes) 217*2de9b4d3SEmmanuel Vadot { 218*2de9b4d3SEmmanuel Vadot int n, nvalid; 219*2de9b4d3SEmmanuel Vadot uint32_t data; 220*2de9b4d3SEmmanuel Vadot 221*2de9b4d3SEmmanuel Vadot while (nbytes > 0) { 222*2de9b4d3SEmmanuel Vadot nvalid = MIN(4, nbytes); 223*2de9b4d3SEmmanuel Vadot data = 0xffffffff; 224*2de9b4d3SEmmanuel Vadot 225*2de9b4d3SEmmanuel Vadot /* 226*2de9b4d3SEmmanuel Vadot * A hardware bug forces us to wait until the tx fifo is 227*2de9b4d3SEmmanuel Vadot * empty before writing partial words. We'll come back 228*2de9b4d3SEmmanuel Vadot * next tx interrupt. 229*2de9b4d3SEmmanuel Vadot */ 230*2de9b4d3SEmmanuel Vadot if (nvalid < 4 && (RD4(sc, ZY7_QSPI_INTR_STAT_REG) & 231*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) == 0) 232*2de9b4d3SEmmanuel Vadot return; 233*2de9b4d3SEmmanuel Vadot 234*2de9b4d3SEmmanuel Vadot if (sc->tx_bytes_sent < sc->cmd->tx_cmd_sz) { 235*2de9b4d3SEmmanuel Vadot /* Writing command. */ 236*2de9b4d3SEmmanuel Vadot n = MIN(nvalid, sc->cmd->tx_cmd_sz - 237*2de9b4d3SEmmanuel Vadot sc->tx_bytes_sent); 238*2de9b4d3SEmmanuel Vadot memcpy(&data, (uint8_t *)sc->cmd->tx_cmd + 239*2de9b4d3SEmmanuel Vadot sc->tx_bytes_sent, n); 240*2de9b4d3SEmmanuel Vadot 241*2de9b4d3SEmmanuel Vadot if (nvalid > n) { 242*2de9b4d3SEmmanuel Vadot /* Writing start of data. */ 243*2de9b4d3SEmmanuel Vadot memcpy((uint8_t *)&data + n, 244*2de9b4d3SEmmanuel Vadot sc->cmd->tx_data, nvalid - n); 245*2de9b4d3SEmmanuel Vadot } 246*2de9b4d3SEmmanuel Vadot } else 247*2de9b4d3SEmmanuel Vadot /* Writing data. */ 248*2de9b4d3SEmmanuel Vadot memcpy(&data, (uint8_t *)sc->cmd->tx_data + 249*2de9b4d3SEmmanuel Vadot (sc->tx_bytes_sent - sc->cmd->tx_cmd_sz), nvalid); 250*2de9b4d3SEmmanuel Vadot 251*2de9b4d3SEmmanuel Vadot switch (nvalid) { 252*2de9b4d3SEmmanuel Vadot case 1: 253*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_TXD1_REG, data); 254*2de9b4d3SEmmanuel Vadot break; 255*2de9b4d3SEmmanuel Vadot case 2: 256*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_TXD2_REG, data); 257*2de9b4d3SEmmanuel Vadot break; 258*2de9b4d3SEmmanuel Vadot case 3: 259*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_TXD3_REG, data); 260*2de9b4d3SEmmanuel Vadot break; 261*2de9b4d3SEmmanuel Vadot case 4: 262*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_TXD0_REG, data); 263*2de9b4d3SEmmanuel Vadot break; 264*2de9b4d3SEmmanuel Vadot } 265*2de9b4d3SEmmanuel Vadot 266*2de9b4d3SEmmanuel Vadot sc->tx_bytes_sent += nvalid; 267*2de9b4d3SEmmanuel Vadot nbytes -= nvalid; 268*2de9b4d3SEmmanuel Vadot } 269*2de9b4d3SEmmanuel Vadot } 270*2de9b4d3SEmmanuel Vadot 271*2de9b4d3SEmmanuel Vadot 272*2de9b4d3SEmmanuel Vadot /* Read hardware fifo data into command response and data buffers. */ 273*2de9b4d3SEmmanuel Vadot static void 274*2de9b4d3SEmmanuel Vadot zy7_qspi_read_fifo(struct zy7_qspi_softc *sc) 275*2de9b4d3SEmmanuel Vadot { 276*2de9b4d3SEmmanuel Vadot int n, nbytes; 277*2de9b4d3SEmmanuel Vadot uint32_t data; 278*2de9b4d3SEmmanuel Vadot 279*2de9b4d3SEmmanuel Vadot do { 280*2de9b4d3SEmmanuel Vadot data = RD4(sc, ZY7_QSPI_RX_DATA_REG); 281*2de9b4d3SEmmanuel Vadot nbytes = MIN(4, sc->rx_bytes - sc->rx_bytes_rcvd); 282*2de9b4d3SEmmanuel Vadot 283*2de9b4d3SEmmanuel Vadot /* 284*2de9b4d3SEmmanuel Vadot * Last word in non-word-multiple transfer is packed 285*2de9b4d3SEmmanuel Vadot * non-intuitively. 286*2de9b4d3SEmmanuel Vadot */ 287*2de9b4d3SEmmanuel Vadot if (nbytes < 4) 288*2de9b4d3SEmmanuel Vadot data >>= 8 * (4 - nbytes); 289*2de9b4d3SEmmanuel Vadot 290*2de9b4d3SEmmanuel Vadot if (sc->rx_bytes_rcvd < sc->cmd->rx_cmd_sz) { 291*2de9b4d3SEmmanuel Vadot /* Reading command. */ 292*2de9b4d3SEmmanuel Vadot n = MIN(nbytes, sc->cmd->rx_cmd_sz - 293*2de9b4d3SEmmanuel Vadot sc->rx_bytes_rcvd); 294*2de9b4d3SEmmanuel Vadot memcpy((uint8_t *)sc->cmd->rx_cmd + sc->rx_bytes_rcvd, 295*2de9b4d3SEmmanuel Vadot &data, n); 296*2de9b4d3SEmmanuel Vadot sc->rx_bytes_rcvd += n; 297*2de9b4d3SEmmanuel Vadot nbytes -= n; 298*2de9b4d3SEmmanuel Vadot data >>= 8 * n; 299*2de9b4d3SEmmanuel Vadot } 300*2de9b4d3SEmmanuel Vadot 301*2de9b4d3SEmmanuel Vadot if (nbytes > 0) { 302*2de9b4d3SEmmanuel Vadot /* Reading data. */ 303*2de9b4d3SEmmanuel Vadot memcpy((uint8_t *)sc->cmd->rx_data + 304*2de9b4d3SEmmanuel Vadot (sc->rx_bytes_rcvd - sc->cmd->rx_cmd_sz), 305*2de9b4d3SEmmanuel Vadot &data, nbytes); 306*2de9b4d3SEmmanuel Vadot sc->rx_bytes_rcvd += nbytes; 307*2de9b4d3SEmmanuel Vadot } 308*2de9b4d3SEmmanuel Vadot 309*2de9b4d3SEmmanuel Vadot } while (sc->rx_bytes_rcvd < sc->rx_bytes && 310*2de9b4d3SEmmanuel Vadot (RD4(sc, ZY7_QSPI_INTR_STAT_REG) & 311*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0); 312*2de9b4d3SEmmanuel Vadot } 313*2de9b4d3SEmmanuel Vadot 314*2de9b4d3SEmmanuel Vadot /* End a transfer early by draining rx fifo and disabling interrupts. */ 315*2de9b4d3SEmmanuel Vadot static void 316*2de9b4d3SEmmanuel Vadot zy7_qspi_abort_transfer(struct zy7_qspi_softc *sc) 317*2de9b4d3SEmmanuel Vadot { 318*2de9b4d3SEmmanuel Vadot /* Drain receive fifo. */ 319*2de9b4d3SEmmanuel Vadot while ((RD4(sc, ZY7_QSPI_INTR_STAT_REG) & 320*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) 321*2de9b4d3SEmmanuel Vadot (void)RD4(sc, ZY7_QSPI_RX_DATA_REG); 322*2de9b4d3SEmmanuel Vadot 323*2de9b4d3SEmmanuel Vadot /* Shut down interrupts. */ 324*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_DIS_REG, 325*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_OVERFLOW | 326*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY | 327*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_TX_FIFO_NOT_FULL); 328*2de9b4d3SEmmanuel Vadot } 329*2de9b4d3SEmmanuel Vadot 330*2de9b4d3SEmmanuel Vadot 331*2de9b4d3SEmmanuel Vadot static void 332*2de9b4d3SEmmanuel Vadot zy7_qspi_intr(void *arg) 333*2de9b4d3SEmmanuel Vadot { 334*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc *sc = (struct zy7_qspi_softc *)arg; 335*2de9b4d3SEmmanuel Vadot uint32_t istatus; 336*2de9b4d3SEmmanuel Vadot 337*2de9b4d3SEmmanuel Vadot QSPI_SC_LOCK(sc); 338*2de9b4d3SEmmanuel Vadot 339*2de9b4d3SEmmanuel Vadot sc->interrupts++; 340*2de9b4d3SEmmanuel Vadot 341*2de9b4d3SEmmanuel Vadot istatus = RD4(sc, ZY7_QSPI_INTR_STAT_REG); 342*2de9b4d3SEmmanuel Vadot 343*2de9b4d3SEmmanuel Vadot /* Stray interrupts can happen if a transfer gets interrupted. */ 344*2de9b4d3SEmmanuel Vadot if (!sc->busy) { 345*2de9b4d3SEmmanuel Vadot sc->stray_ints++; 346*2de9b4d3SEmmanuel Vadot QSPI_SC_UNLOCK(sc); 347*2de9b4d3SEmmanuel Vadot return; 348*2de9b4d3SEmmanuel Vadot } 349*2de9b4d3SEmmanuel Vadot 350*2de9b4d3SEmmanuel Vadot if ((istatus & ZY7_QSPI_INTR_RX_OVERFLOW) != 0) { 351*2de9b4d3SEmmanuel Vadot device_printf(sc->dev, "rx fifo overflow!\n"); 352*2de9b4d3SEmmanuel Vadot sc->rx_overflows++; 353*2de9b4d3SEmmanuel Vadot 354*2de9b4d3SEmmanuel Vadot /* Clear status bit. */ 355*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_STAT_REG, 356*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_OVERFLOW); 357*2de9b4d3SEmmanuel Vadot } 358*2de9b4d3SEmmanuel Vadot 359*2de9b4d3SEmmanuel Vadot /* Empty receive fifo before any more transmit data is sent. */ 360*2de9b4d3SEmmanuel Vadot if (sc->rx_bytes_rcvd < sc->rx_bytes && 361*2de9b4d3SEmmanuel Vadot (istatus & ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY) != 0) { 362*2de9b4d3SEmmanuel Vadot zy7_qspi_read_fifo(sc); 363*2de9b4d3SEmmanuel Vadot if (sc->rx_bytes_rcvd == sc->rx_bytes) 364*2de9b4d3SEmmanuel Vadot /* Disable receive interrupts. */ 365*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_DIS_REG, 366*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY | 367*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_OVERFLOW); 368*2de9b4d3SEmmanuel Vadot } 369*2de9b4d3SEmmanuel Vadot 370*2de9b4d3SEmmanuel Vadot /* 371*2de9b4d3SEmmanuel Vadot * Transmit underflows aren't really a bug because a hardware 372*2de9b4d3SEmmanuel Vadot * bug forces us to allow the tx fifo to go empty between full 373*2de9b4d3SEmmanuel Vadot * and partial fifo writes. Why bother counting? 374*2de9b4d3SEmmanuel Vadot */ 375*2de9b4d3SEmmanuel Vadot if ((istatus & ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW) != 0) { 376*2de9b4d3SEmmanuel Vadot sc->tx_underflows++; 377*2de9b4d3SEmmanuel Vadot 378*2de9b4d3SEmmanuel Vadot /* Clear status bit. */ 379*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_STAT_REG, 380*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_TX_FIFO_UNDERFLOW); 381*2de9b4d3SEmmanuel Vadot } 382*2de9b4d3SEmmanuel Vadot 383*2de9b4d3SEmmanuel Vadot /* Fill transmit fifo. */ 384*2de9b4d3SEmmanuel Vadot if (sc->tx_bytes_sent < sc->tx_bytes && 385*2de9b4d3SEmmanuel Vadot (istatus & ZY7_QSPI_INTR_TX_FIFO_NOT_FULL) != 0) { 386*2de9b4d3SEmmanuel Vadot zy7_qspi_write_fifo(sc, MIN(240, sc->tx_bytes - 387*2de9b4d3SEmmanuel Vadot sc->tx_bytes_sent)); 388*2de9b4d3SEmmanuel Vadot 389*2de9b4d3SEmmanuel Vadot if (sc->tx_bytes_sent == sc->tx_bytes) { 390*2de9b4d3SEmmanuel Vadot /* 391*2de9b4d3SEmmanuel Vadot * Disable transmit FIFO interrupt, enable receive 392*2de9b4d3SEmmanuel Vadot * FIFO interrupt. 393*2de9b4d3SEmmanuel Vadot */ 394*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_DIS_REG, 395*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_TX_FIFO_NOT_FULL); 396*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_EN_REG, 397*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_FIFO_NOT_EMPTY); 398*2de9b4d3SEmmanuel Vadot } 399*2de9b4d3SEmmanuel Vadot } 400*2de9b4d3SEmmanuel Vadot 401*2de9b4d3SEmmanuel Vadot /* Finished with transfer? */ 402*2de9b4d3SEmmanuel Vadot if (sc->tx_bytes_sent == sc->tx_bytes && 403*2de9b4d3SEmmanuel Vadot sc->rx_bytes_rcvd == sc->rx_bytes) { 404*2de9b4d3SEmmanuel Vadot 405*2de9b4d3SEmmanuel Vadot /* De-assert CS. */ 406*2de9b4d3SEmmanuel Vadot sc->cfg_reg_shadow |= ZY7_QSPI_CONFIG_PCS; 407*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow); 408*2de9b4d3SEmmanuel Vadot 409*2de9b4d3SEmmanuel Vadot wakeup(sc->dev); 410*2de9b4d3SEmmanuel Vadot } 411*2de9b4d3SEmmanuel Vadot 412*2de9b4d3SEmmanuel Vadot QSPI_SC_UNLOCK(sc); 413*2de9b4d3SEmmanuel Vadot } 414*2de9b4d3SEmmanuel Vadot 415*2de9b4d3SEmmanuel Vadot /* Initialize hardware. */ 416*2de9b4d3SEmmanuel Vadot static int 417*2de9b4d3SEmmanuel Vadot zy7_qspi_init_hw(struct zy7_qspi_softc *sc) 418*2de9b4d3SEmmanuel Vadot { 419*2de9b4d3SEmmanuel Vadot uint32_t baud_div; 420*2de9b4d3SEmmanuel Vadot 421*2de9b4d3SEmmanuel Vadot /* Configure LQSPI Config register. Disable linear mode. */ 422*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow = RD4(sc, ZY7_QSPI_LQSPI_CFG_REG); 423*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow &= ~(ZY7_QSPI_LQSPI_CFG_LINEAR | 424*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LQSPI_CFG_TWO_MEM | 425*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LQSPI_CFG_SEP_BUS); 426*2de9b4d3SEmmanuel Vadot if (sc->is_dual) { 427*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_TWO_MEM; 428*2de9b4d3SEmmanuel Vadot if (sc->is_stacked) { 429*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow &= 430*2de9b4d3SEmmanuel Vadot ~ZY7_QSPI_LQSPI_CFG_INST_CODE_MASK; 431*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow |= 432*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LQSPI_CFG_INST_CODE(sc->is_dio ? 433*2de9b4d3SEmmanuel Vadot CMD_READ_DUAL_IO : CMD_READ_QUAD_OUTPUT); 434*2de9b4d3SEmmanuel Vadot } else 435*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_SEP_BUS; 436*2de9b4d3SEmmanuel Vadot } 437*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow); 438*2de9b4d3SEmmanuel Vadot 439*2de9b4d3SEmmanuel Vadot /* Find best clock divider. */ 440*2de9b4d3SEmmanuel Vadot baud_div = 0; 441*2de9b4d3SEmmanuel Vadot while ((sc->ref_clock >> (baud_div + 1)) > sc->spi_clock && 442*2de9b4d3SEmmanuel Vadot baud_div < 8) 443*2de9b4d3SEmmanuel Vadot baud_div++; 444*2de9b4d3SEmmanuel Vadot if (baud_div >= 8) { 445*2de9b4d3SEmmanuel Vadot device_printf(sc->dev, "cannot configure clock divider: ref=%d" 446*2de9b4d3SEmmanuel Vadot " spi=%d.\n", sc->ref_clock, sc->spi_clock); 447*2de9b4d3SEmmanuel Vadot return (EINVAL); 448*2de9b4d3SEmmanuel Vadot } 449*2de9b4d3SEmmanuel Vadot sc->spi_clk_real_freq = sc->ref_clock >> (baud_div + 1); 450*2de9b4d3SEmmanuel Vadot 451*2de9b4d3SEmmanuel Vadot /* 452*2de9b4d3SEmmanuel Vadot * If divider is 2 (the max speed), use internal loopback master 453*2de9b4d3SEmmanuel Vadot * clock for read data. (See section 12.3.1 in ref man.) 454*2de9b4d3SEmmanuel Vadot */ 455*2de9b4d3SEmmanuel Vadot if (baud_div == 0) 456*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 457*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LPBK_DLY_ADJ_USE_LPBK | 458*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LPBK_DLY_ADJ_DLY1(0) | 459*2de9b4d3SEmmanuel Vadot ZY7_QSPI_LPBK_DLY_ADJ_DLY0(0)); 460*2de9b4d3SEmmanuel Vadot else 461*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_LPBK_DLY_ADJ_REG, 0); 462*2de9b4d3SEmmanuel Vadot 463*2de9b4d3SEmmanuel Vadot /* Set up configuration register. */ 464*2de9b4d3SEmmanuel Vadot sc->cfg_reg_shadow = 465*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_IFMODE | 466*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_HOLDB_DR | 467*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_RSVD1 | 468*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_SSFORCE | 469*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_PCS | 470*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_FIFO_WIDTH32 | 471*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_BAUD_RATE_DIV(baud_div) | 472*2de9b4d3SEmmanuel Vadot ZY7_QSPI_CONFIG_MODE_SEL; 473*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow); 474*2de9b4d3SEmmanuel Vadot 475*2de9b4d3SEmmanuel Vadot /* 476*2de9b4d3SEmmanuel Vadot * Set thresholds. We must use 1 for tx threshold because there 477*2de9b4d3SEmmanuel Vadot * is no fifo empty flag and we need one to implement a bug 478*2de9b4d3SEmmanuel Vadot * workaround. 479*2de9b4d3SEmmanuel Vadot */ 480*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_TX_THRESH_REG, 1); 481*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_RX_THRESH_REG, 1); 482*2de9b4d3SEmmanuel Vadot 483*2de9b4d3SEmmanuel Vadot /* Clear and disable all interrupts. */ 484*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0); 485*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0); 486*2de9b4d3SEmmanuel Vadot 487*2de9b4d3SEmmanuel Vadot /* Enable SPI. */ 488*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_EN_REG, ZY7_SPI_ENABLE); 489*2de9b4d3SEmmanuel Vadot 490*2de9b4d3SEmmanuel Vadot return (0); 491*2de9b4d3SEmmanuel Vadot } 492*2de9b4d3SEmmanuel Vadot 493*2de9b4d3SEmmanuel Vadot 494*2de9b4d3SEmmanuel Vadot static void 495*2de9b4d3SEmmanuel Vadot zy7_qspi_add_sysctls(device_t dev) 496*2de9b4d3SEmmanuel Vadot { 497*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc *sc = device_get_softc(dev); 498*2de9b4d3SEmmanuel Vadot struct sysctl_ctx_list *ctx; 499*2de9b4d3SEmmanuel Vadot struct sysctl_oid_list *child; 500*2de9b4d3SEmmanuel Vadot 501*2de9b4d3SEmmanuel Vadot ctx = device_get_sysctl_ctx(dev); 502*2de9b4d3SEmmanuel Vadot child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); 503*2de9b4d3SEmmanuel Vadot 504*2de9b4d3SEmmanuel Vadot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "spi_clk_real_freq", CTLFLAG_RD, 505*2de9b4d3SEmmanuel Vadot &sc->spi_clk_real_freq, 0, "SPI clock real frequency"); 506*2de9b4d3SEmmanuel Vadot 507*2de9b4d3SEmmanuel Vadot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "rx_overflows", CTLFLAG_RD, 508*2de9b4d3SEmmanuel Vadot &sc->rx_overflows, 0, "RX FIFO overflow events"); 509*2de9b4d3SEmmanuel Vadot 510*2de9b4d3SEmmanuel Vadot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "tx_underflows", CTLFLAG_RD, 511*2de9b4d3SEmmanuel Vadot &sc->tx_underflows, 0, "TX FIFO underflow events"); 512*2de9b4d3SEmmanuel Vadot 513*2de9b4d3SEmmanuel Vadot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "interrupts", CTLFLAG_RD, 514*2de9b4d3SEmmanuel Vadot &sc->interrupts, 0, "interrupt calls"); 515*2de9b4d3SEmmanuel Vadot 516*2de9b4d3SEmmanuel Vadot SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "stray_ints", CTLFLAG_RD, 517*2de9b4d3SEmmanuel Vadot &sc->stray_ints, 0, "stray interrupts"); 518*2de9b4d3SEmmanuel Vadot } 519*2de9b4d3SEmmanuel Vadot 520*2de9b4d3SEmmanuel Vadot 521*2de9b4d3SEmmanuel Vadot static int 522*2de9b4d3SEmmanuel Vadot zy7_qspi_probe(device_t dev) 523*2de9b4d3SEmmanuel Vadot { 524*2de9b4d3SEmmanuel Vadot 525*2de9b4d3SEmmanuel Vadot if (!ofw_bus_status_okay(dev)) 526*2de9b4d3SEmmanuel Vadot return (ENXIO); 527*2de9b4d3SEmmanuel Vadot 528*2de9b4d3SEmmanuel Vadot if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 529*2de9b4d3SEmmanuel Vadot return (ENXIO); 530*2de9b4d3SEmmanuel Vadot 531*2de9b4d3SEmmanuel Vadot device_set_desc(dev, "Zynq Quad-SPI Flash Controller"); 532*2de9b4d3SEmmanuel Vadot 533*2de9b4d3SEmmanuel Vadot return (BUS_PROBE_DEFAULT); 534*2de9b4d3SEmmanuel Vadot } 535*2de9b4d3SEmmanuel Vadot 536*2de9b4d3SEmmanuel Vadot 537*2de9b4d3SEmmanuel Vadot static int 538*2de9b4d3SEmmanuel Vadot zy7_qspi_attach(device_t dev) 539*2de9b4d3SEmmanuel Vadot { 540*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc *sc; 541*2de9b4d3SEmmanuel Vadot int rid, err; 542*2de9b4d3SEmmanuel Vadot phandle_t node; 543*2de9b4d3SEmmanuel Vadot pcell_t cell; 544*2de9b4d3SEmmanuel Vadot 545*2de9b4d3SEmmanuel Vadot sc = device_get_softc(dev); 546*2de9b4d3SEmmanuel Vadot sc->dev = dev; 547*2de9b4d3SEmmanuel Vadot 548*2de9b4d3SEmmanuel Vadot QSPI_SC_LOCK_INIT(sc); 549*2de9b4d3SEmmanuel Vadot 550*2de9b4d3SEmmanuel Vadot /* Get ref-clock, spi-clock, and other properties. */ 551*2de9b4d3SEmmanuel Vadot node = ofw_bus_get_node(dev); 552*2de9b4d3SEmmanuel Vadot if (OF_getprop(node, "ref-clock", &cell, sizeof(cell)) > 0) 553*2de9b4d3SEmmanuel Vadot sc->ref_clock = fdt32_to_cpu(cell); 554*2de9b4d3SEmmanuel Vadot else { 555*2de9b4d3SEmmanuel Vadot device_printf(dev, "must have ref-clock property\n"); 556*2de9b4d3SEmmanuel Vadot return (ENXIO); 557*2de9b4d3SEmmanuel Vadot } 558*2de9b4d3SEmmanuel Vadot if (OF_getprop(node, "spi-clock", &cell, sizeof(cell)) > 0) 559*2de9b4d3SEmmanuel Vadot sc->spi_clock = fdt32_to_cpu(cell); 560*2de9b4d3SEmmanuel Vadot else 561*2de9b4d3SEmmanuel Vadot sc->spi_clock = ZY7_QSPI_DEFAULT_SPI_CLOCK; 562*2de9b4d3SEmmanuel Vadot if (OF_getprop(node, "is-stacked", &cell, sizeof(cell)) > 0 && 563*2de9b4d3SEmmanuel Vadot fdt32_to_cpu(cell) != 0) { 564*2de9b4d3SEmmanuel Vadot sc->is_dual = 1; 565*2de9b4d3SEmmanuel Vadot sc->is_stacked = 1; 566*2de9b4d3SEmmanuel Vadot } else if (OF_getprop(node, "is-dual", &cell, sizeof(cell)) > 0 && 567*2de9b4d3SEmmanuel Vadot fdt32_to_cpu(cell) != 0) 568*2de9b4d3SEmmanuel Vadot sc->is_dual = 1; 569*2de9b4d3SEmmanuel Vadot if (OF_getprop(node, "is-dio", &cell, sizeof(cell)) > 0 && 570*2de9b4d3SEmmanuel Vadot fdt32_to_cpu(cell) != 0) 571*2de9b4d3SEmmanuel Vadot sc->is_dio = 1; 572*2de9b4d3SEmmanuel Vadot 573*2de9b4d3SEmmanuel Vadot /* Get memory resource. */ 574*2de9b4d3SEmmanuel Vadot rid = 0; 575*2de9b4d3SEmmanuel Vadot sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 576*2de9b4d3SEmmanuel Vadot RF_ACTIVE); 577*2de9b4d3SEmmanuel Vadot if (sc->mem_res == NULL) { 578*2de9b4d3SEmmanuel Vadot device_printf(dev, "could not allocate memory resources.\n"); 579*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(dev); 580*2de9b4d3SEmmanuel Vadot return (ENOMEM); 581*2de9b4d3SEmmanuel Vadot } 582*2de9b4d3SEmmanuel Vadot 583*2de9b4d3SEmmanuel Vadot /* Allocate IRQ. */ 584*2de9b4d3SEmmanuel Vadot rid = 0; 585*2de9b4d3SEmmanuel Vadot sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 586*2de9b4d3SEmmanuel Vadot RF_ACTIVE); 587*2de9b4d3SEmmanuel Vadot if (sc->irq_res == NULL) { 588*2de9b4d3SEmmanuel Vadot device_printf(dev, "could not allocate IRQ resource.\n"); 589*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(dev); 590*2de9b4d3SEmmanuel Vadot return (ENOMEM); 591*2de9b4d3SEmmanuel Vadot } 592*2de9b4d3SEmmanuel Vadot 593*2de9b4d3SEmmanuel Vadot /* Activate the interrupt. */ 594*2de9b4d3SEmmanuel Vadot err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 595*2de9b4d3SEmmanuel Vadot NULL, zy7_qspi_intr, sc, &sc->intrhandle); 596*2de9b4d3SEmmanuel Vadot if (err) { 597*2de9b4d3SEmmanuel Vadot device_printf(dev, "could not setup IRQ.\n"); 598*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(dev); 599*2de9b4d3SEmmanuel Vadot return (err); 600*2de9b4d3SEmmanuel Vadot } 601*2de9b4d3SEmmanuel Vadot 602*2de9b4d3SEmmanuel Vadot /* Configure the device. */ 603*2de9b4d3SEmmanuel Vadot err = zy7_qspi_init_hw(sc); 604*2de9b4d3SEmmanuel Vadot if (err) { 605*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(dev); 606*2de9b4d3SEmmanuel Vadot return (err); 607*2de9b4d3SEmmanuel Vadot } 608*2de9b4d3SEmmanuel Vadot 609*2de9b4d3SEmmanuel Vadot sc->child = device_add_child(dev, "spibus", -1); 610*2de9b4d3SEmmanuel Vadot 611*2de9b4d3SEmmanuel Vadot zy7_qspi_add_sysctls(dev); 612*2de9b4d3SEmmanuel Vadot 613*2de9b4d3SEmmanuel Vadot /* Attach spibus driver as a child later when interrupts work. */ 614*2de9b4d3SEmmanuel Vadot config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev); 615*2de9b4d3SEmmanuel Vadot 616*2de9b4d3SEmmanuel Vadot return (0); 617*2de9b4d3SEmmanuel Vadot } 618*2de9b4d3SEmmanuel Vadot 619*2de9b4d3SEmmanuel Vadot static int 620*2de9b4d3SEmmanuel Vadot zy7_qspi_detach(device_t dev) 621*2de9b4d3SEmmanuel Vadot { 622*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc *sc = device_get_softc(dev); 623*2de9b4d3SEmmanuel Vadot 624*2de9b4d3SEmmanuel Vadot if (device_is_attached(dev)) 625*2de9b4d3SEmmanuel Vadot bus_generic_detach(dev); 626*2de9b4d3SEmmanuel Vadot 627*2de9b4d3SEmmanuel Vadot /* Delete child bus. */ 628*2de9b4d3SEmmanuel Vadot if (sc->child) 629*2de9b4d3SEmmanuel Vadot device_delete_child(dev, sc->child); 630*2de9b4d3SEmmanuel Vadot 631*2de9b4d3SEmmanuel Vadot /* Disable hardware. */ 632*2de9b4d3SEmmanuel Vadot if (sc->mem_res != NULL) { 633*2de9b4d3SEmmanuel Vadot /* Disable SPI. */ 634*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_EN_REG, 0); 635*2de9b4d3SEmmanuel Vadot 636*2de9b4d3SEmmanuel Vadot /* Clear and disable all interrupts. */ 637*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_STAT_REG, ~0); 638*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_DIS_REG, ~0); 639*2de9b4d3SEmmanuel Vadot } 640*2de9b4d3SEmmanuel Vadot 641*2de9b4d3SEmmanuel Vadot /* Teardown and release interrupt. */ 642*2de9b4d3SEmmanuel Vadot if (sc->irq_res != NULL) { 643*2de9b4d3SEmmanuel Vadot if (sc->intrhandle) 644*2de9b4d3SEmmanuel Vadot bus_teardown_intr(dev, sc->irq_res, sc->intrhandle); 645*2de9b4d3SEmmanuel Vadot bus_release_resource(dev, SYS_RES_IRQ, 646*2de9b4d3SEmmanuel Vadot rman_get_rid(sc->irq_res), sc->irq_res); 647*2de9b4d3SEmmanuel Vadot } 648*2de9b4d3SEmmanuel Vadot 649*2de9b4d3SEmmanuel Vadot /* Release memory resource. */ 650*2de9b4d3SEmmanuel Vadot if (sc->mem_res != NULL) 651*2de9b4d3SEmmanuel Vadot bus_release_resource(dev, SYS_RES_MEMORY, 652*2de9b4d3SEmmanuel Vadot rman_get_rid(sc->mem_res), sc->mem_res); 653*2de9b4d3SEmmanuel Vadot 654*2de9b4d3SEmmanuel Vadot QSPI_SC_LOCK_DESTROY(sc); 655*2de9b4d3SEmmanuel Vadot 656*2de9b4d3SEmmanuel Vadot return (0); 657*2de9b4d3SEmmanuel Vadot } 658*2de9b4d3SEmmanuel Vadot 659*2de9b4d3SEmmanuel Vadot 660*2de9b4d3SEmmanuel Vadot static phandle_t 661*2de9b4d3SEmmanuel Vadot zy7_qspi_get_node(device_t bus, device_t dev) 662*2de9b4d3SEmmanuel Vadot { 663*2de9b4d3SEmmanuel Vadot 664*2de9b4d3SEmmanuel Vadot return (ofw_bus_get_node(bus)); 665*2de9b4d3SEmmanuel Vadot } 666*2de9b4d3SEmmanuel Vadot 667*2de9b4d3SEmmanuel Vadot 668*2de9b4d3SEmmanuel Vadot static int 669*2de9b4d3SEmmanuel Vadot zy7_qspi_transfer(device_t dev, device_t child, struct spi_command *cmd) 670*2de9b4d3SEmmanuel Vadot { 671*2de9b4d3SEmmanuel Vadot struct zy7_qspi_softc *sc = device_get_softc(dev); 672*2de9b4d3SEmmanuel Vadot int err = 0; 673*2de9b4d3SEmmanuel Vadot 674*2de9b4d3SEmmanuel Vadot KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 675*2de9b4d3SEmmanuel Vadot ("TX/RX command sizes should be equal")); 676*2de9b4d3SEmmanuel Vadot KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 677*2de9b4d3SEmmanuel Vadot ("TX/RX data sizes should be equal")); 678*2de9b4d3SEmmanuel Vadot 679*2de9b4d3SEmmanuel Vadot if (sc->is_dual && cmd->tx_data_sz % 2 != 0) { 680*2de9b4d3SEmmanuel Vadot device_printf(dev, "driver does not support odd byte data " 681*2de9b4d3SEmmanuel Vadot "transfers in dual mode. (sz=%d)\n", cmd->tx_data_sz); 682*2de9b4d3SEmmanuel Vadot return (EINVAL); 683*2de9b4d3SEmmanuel Vadot } 684*2de9b4d3SEmmanuel Vadot 685*2de9b4d3SEmmanuel Vadot QSPI_SC_LOCK(sc); 686*2de9b4d3SEmmanuel Vadot 687*2de9b4d3SEmmanuel Vadot /* Wait for controller available. */ 688*2de9b4d3SEmmanuel Vadot while (sc->busy != 0) { 689*2de9b4d3SEmmanuel Vadot err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi0", 0); 690*2de9b4d3SEmmanuel Vadot if (err) { 691*2de9b4d3SEmmanuel Vadot QSPI_SC_UNLOCK(sc); 692*2de9b4d3SEmmanuel Vadot return (err); 693*2de9b4d3SEmmanuel Vadot } 694*2de9b4d3SEmmanuel Vadot } 695*2de9b4d3SEmmanuel Vadot 696*2de9b4d3SEmmanuel Vadot /* Start transfer. */ 697*2de9b4d3SEmmanuel Vadot sc->busy = 1; 698*2de9b4d3SEmmanuel Vadot sc->cmd = cmd; 699*2de9b4d3SEmmanuel Vadot sc->tx_bytes = sc->cmd->tx_cmd_sz + sc->cmd->tx_data_sz; 700*2de9b4d3SEmmanuel Vadot sc->tx_bytes_sent = 0; 701*2de9b4d3SEmmanuel Vadot sc->rx_bytes = sc->cmd->rx_cmd_sz + sc->cmd->rx_data_sz; 702*2de9b4d3SEmmanuel Vadot sc->rx_bytes_rcvd = 0; 703*2de9b4d3SEmmanuel Vadot 704*2de9b4d3SEmmanuel Vadot /* Enable interrupts. zy7_qspi_intr() will handle transfer. */ 705*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_INTR_EN_REG, 706*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_TX_FIFO_NOT_FULL | 707*2de9b4d3SEmmanuel Vadot ZY7_QSPI_INTR_RX_OVERFLOW); 708*2de9b4d3SEmmanuel Vadot 709*2de9b4d3SEmmanuel Vadot #ifdef SPI_XFER_U_PAGE /* XXX: future support for stacked memories. */ 710*2de9b4d3SEmmanuel Vadot if (sc->is_stacked) { 711*2de9b4d3SEmmanuel Vadot if ((cmd->flags & SPI_XFER_U_PAGE) != 0) 712*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow |= ZY7_QSPI_LQSPI_CFG_U_PAGE; 713*2de9b4d3SEmmanuel Vadot else 714*2de9b4d3SEmmanuel Vadot sc->lqspi_cfg_shadow &= ~ZY7_QSPI_LQSPI_CFG_U_PAGE; 715*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_LQSPI_CFG_REG, sc->lqspi_cfg_shadow); 716*2de9b4d3SEmmanuel Vadot } 717*2de9b4d3SEmmanuel Vadot #endif 718*2de9b4d3SEmmanuel Vadot 719*2de9b4d3SEmmanuel Vadot /* Assert CS. */ 720*2de9b4d3SEmmanuel Vadot sc->cfg_reg_shadow &= ~ZY7_QSPI_CONFIG_PCS; 721*2de9b4d3SEmmanuel Vadot WR4(sc, ZY7_QSPI_CONFIG_REG, sc->cfg_reg_shadow); 722*2de9b4d3SEmmanuel Vadot 723*2de9b4d3SEmmanuel Vadot /* Wait for completion. */ 724*2de9b4d3SEmmanuel Vadot err = mtx_sleep(dev, &sc->sc_mtx, 0, "zqspi1", hz * 2); 725*2de9b4d3SEmmanuel Vadot if (err) 726*2de9b4d3SEmmanuel Vadot zy7_qspi_abort_transfer(sc); 727*2de9b4d3SEmmanuel Vadot 728*2de9b4d3SEmmanuel Vadot /* Release controller. */ 729*2de9b4d3SEmmanuel Vadot sc->busy = 0; 730*2de9b4d3SEmmanuel Vadot wakeup_one(dev); 731*2de9b4d3SEmmanuel Vadot 732*2de9b4d3SEmmanuel Vadot QSPI_SC_UNLOCK(sc); 733*2de9b4d3SEmmanuel Vadot 734*2de9b4d3SEmmanuel Vadot return (err); 735*2de9b4d3SEmmanuel Vadot } 736*2de9b4d3SEmmanuel Vadot 737*2de9b4d3SEmmanuel Vadot static device_method_t zy7_qspi_methods[] = { 738*2de9b4d3SEmmanuel Vadot /* Device interface */ 739*2de9b4d3SEmmanuel Vadot DEVMETHOD(device_probe, zy7_qspi_probe), 740*2de9b4d3SEmmanuel Vadot DEVMETHOD(device_attach, zy7_qspi_attach), 741*2de9b4d3SEmmanuel Vadot DEVMETHOD(device_detach, zy7_qspi_detach), 742*2de9b4d3SEmmanuel Vadot 743*2de9b4d3SEmmanuel Vadot /* SPI interface */ 744*2de9b4d3SEmmanuel Vadot DEVMETHOD(spibus_transfer, zy7_qspi_transfer), 745*2de9b4d3SEmmanuel Vadot 746*2de9b4d3SEmmanuel Vadot /* ofw_bus interface */ 747*2de9b4d3SEmmanuel Vadot DEVMETHOD(ofw_bus_get_node, zy7_qspi_get_node), 748*2de9b4d3SEmmanuel Vadot 749*2de9b4d3SEmmanuel Vadot DEVMETHOD_END 750*2de9b4d3SEmmanuel Vadot }; 751*2de9b4d3SEmmanuel Vadot 752*2de9b4d3SEmmanuel Vadot 753*2de9b4d3SEmmanuel Vadot static driver_t zy7_qspi_driver = { 754*2de9b4d3SEmmanuel Vadot "zy7_qspi", 755*2de9b4d3SEmmanuel Vadot zy7_qspi_methods, 756*2de9b4d3SEmmanuel Vadot sizeof(struct zy7_qspi_softc), 757*2de9b4d3SEmmanuel Vadot }; 758*2de9b4d3SEmmanuel Vadot static devclass_t zy7_qspi_devclass; 759*2de9b4d3SEmmanuel Vadot 760*2de9b4d3SEmmanuel Vadot DRIVER_MODULE(zy7_qspi, simplebus, zy7_qspi_driver, zy7_qspi_devclass, 0, 0); 761*2de9b4d3SEmmanuel Vadot DRIVER_MODULE(ofw_spibus, zy7_qspi, ofw_spibus_driver, ofw_spibus_devclass, 0, 0); 762*2de9b4d3SEmmanuel Vadot SIMPLEBUS_PNP_INFO(compat_data); 763*2de9b4d3SEmmanuel Vadot MODULE_DEPEND(zy7_qspi, ofw_spibus, 1, 1, 1); 764