1*e72dd8d7SRuslan Bukin /*- 2*e72dd8d7SRuslan Bukin * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com> 3*e72dd8d7SRuslan Bukin * All rights reserved. 4*e72dd8d7SRuslan Bukin * 5*e72dd8d7SRuslan Bukin * Portions of this software were developed by SRI International and the 6*e72dd8d7SRuslan Bukin * University of Cambridge Computer Laboratory under DARPA/AFRL contract 7*e72dd8d7SRuslan Bukin * FA8750-10-C-0237 ("CTSRD"), as part of the DARPA CRASH research programme. 8*e72dd8d7SRuslan Bukin * 9*e72dd8d7SRuslan Bukin * Portions of this software were developed by the University of Cambridge 10*e72dd8d7SRuslan Bukin * Computer Laboratory as part of the CTSRD Project, with support from the 11*e72dd8d7SRuslan Bukin * UK Higher Education Innovation Fund (HEIF). 12*e72dd8d7SRuslan Bukin * 13*e72dd8d7SRuslan Bukin * Redistribution and use in source and binary forms, with or without 14*e72dd8d7SRuslan Bukin * modification, are permitted provided that the following conditions 15*e72dd8d7SRuslan Bukin * are met: 16*e72dd8d7SRuslan Bukin * 1. Redistributions of source code must retain the above copyright 17*e72dd8d7SRuslan Bukin * notice, this list of conditions and the following disclaimer. 18*e72dd8d7SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright 19*e72dd8d7SRuslan Bukin * notice, this list of conditions and the following disclaimer in the 20*e72dd8d7SRuslan Bukin * documentation and/or other materials provided with the distribution. 21*e72dd8d7SRuslan Bukin * 22*e72dd8d7SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23*e72dd8d7SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24*e72dd8d7SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25*e72dd8d7SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26*e72dd8d7SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27*e72dd8d7SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28*e72dd8d7SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29*e72dd8d7SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30*e72dd8d7SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31*e72dd8d7SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32*e72dd8d7SRuslan Bukin * SUCH DAMAGE. 33*e72dd8d7SRuslan Bukin */ 34*e72dd8d7SRuslan Bukin 35*e72dd8d7SRuslan Bukin /* 36*e72dd8d7SRuslan Bukin * Xilinx AXI_QUAD_SPI 37*e72dd8d7SRuslan Bukin */ 38*e72dd8d7SRuslan Bukin 39*e72dd8d7SRuslan Bukin #include <sys/cdefs.h> 40*e72dd8d7SRuslan Bukin __FBSDID("$FreeBSD$"); 41*e72dd8d7SRuslan Bukin 42*e72dd8d7SRuslan Bukin #include <sys/param.h> 43*e72dd8d7SRuslan Bukin #include <sys/systm.h> 44*e72dd8d7SRuslan Bukin #include <sys/bus.h> 45*e72dd8d7SRuslan Bukin #include <sys/kernel.h> 46*e72dd8d7SRuslan Bukin #include <sys/module.h> 47*e72dd8d7SRuslan Bukin #include <sys/malloc.h> 48*e72dd8d7SRuslan Bukin #include <sys/rman.h> 49*e72dd8d7SRuslan Bukin #include <sys/timeet.h> 50*e72dd8d7SRuslan Bukin #include <sys/timetc.h> 51*e72dd8d7SRuslan Bukin #include <sys/watchdog.h> 52*e72dd8d7SRuslan Bukin 53*e72dd8d7SRuslan Bukin #include <dev/spibus/spi.h> 54*e72dd8d7SRuslan Bukin #include <dev/spibus/spibusvar.h> 55*e72dd8d7SRuslan Bukin 56*e72dd8d7SRuslan Bukin #include "spibus_if.h" 57*e72dd8d7SRuslan Bukin 58*e72dd8d7SRuslan Bukin #include <dev/fdt/fdt_common.h> 59*e72dd8d7SRuslan Bukin #include <dev/ofw/openfirm.h> 60*e72dd8d7SRuslan Bukin #include <dev/ofw/ofw_bus.h> 61*e72dd8d7SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h> 62*e72dd8d7SRuslan Bukin 63*e72dd8d7SRuslan Bukin #include <machine/bus.h> 64*e72dd8d7SRuslan Bukin #include <machine/cpu.h> 65*e72dd8d7SRuslan Bukin #include <machine/intr.h> 66*e72dd8d7SRuslan Bukin 67*e72dd8d7SRuslan Bukin #define READ4(_sc, _reg) \ 68*e72dd8d7SRuslan Bukin bus_space_read_4(_sc->bst, _sc->bsh, _reg) 69*e72dd8d7SRuslan Bukin #define WRITE4(_sc, _reg, _val) \ 70*e72dd8d7SRuslan Bukin bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) 71*e72dd8d7SRuslan Bukin 72*e72dd8d7SRuslan Bukin #define SPI_SRR 0x40 /* Software reset register */ 73*e72dd8d7SRuslan Bukin #define SRR_RESET 0x0A /* The only reset value */ 74*e72dd8d7SRuslan Bukin #define SPI_CR 0x60 /* Control register */ 75*e72dd8d7SRuslan Bukin #define CR_LSB_FIRST (1 << 9) /* LSB first */ 76*e72dd8d7SRuslan Bukin #define CR_MASTER_TI (1 << 8) /* Master Transaction Inhibit */ 77*e72dd8d7SRuslan Bukin #define CR_MSS (1 << 7) /* Manual Slave Select */ 78*e72dd8d7SRuslan Bukin #define CR_RST_RX (1 << 6) /* RX FIFO Reset */ 79*e72dd8d7SRuslan Bukin #define CR_RST_TX (1 << 5) /* TX FIFO Reset */ 80*e72dd8d7SRuslan Bukin #define CR_CPHA (1 << 4) /* Clock phase */ 81*e72dd8d7SRuslan Bukin #define CR_CPOL (1 << 3) /* Clock polarity */ 82*e72dd8d7SRuslan Bukin #define CR_MASTER (1 << 2) /* Master (SPI master mode) */ 83*e72dd8d7SRuslan Bukin #define CR_SPE (1 << 1) /* SPI system enable */ 84*e72dd8d7SRuslan Bukin #define CR_LOOP (1 << 0) /* Local loopback mode */ 85*e72dd8d7SRuslan Bukin #define SPI_SR 0x64 /* Status register */ 86*e72dd8d7SRuslan Bukin #define SR_TX_FULL (1 << 3) /* Transmit full */ 87*e72dd8d7SRuslan Bukin #define SR_TX_EMPTY (1 << 2) /* Transmit empty */ 88*e72dd8d7SRuslan Bukin #define SR_RX_FULL (1 << 1) /* Receive full */ 89*e72dd8d7SRuslan Bukin #define SR_RX_EMPTY (1 << 0) /* Receive empty */ 90*e72dd8d7SRuslan Bukin #define SPI_DTR 0x68 /* Data transmit register */ 91*e72dd8d7SRuslan Bukin #define SPI_DRR 0x6C /* Data receive register */ 92*e72dd8d7SRuslan Bukin #define SPI_SSR 0x70 /* Slave select register */ 93*e72dd8d7SRuslan Bukin #define SPI_TFOR 0x74 /* Transmit FIFO Occupancy Register */ 94*e72dd8d7SRuslan Bukin #define SPI_RFOR 0x78 /* Receive FIFO Occupancy Register */ 95*e72dd8d7SRuslan Bukin #define SPI_DGIER 0x1C /* Device global interrupt enable register */ 96*e72dd8d7SRuslan Bukin #define SPI_IPISR 0x20 /* IP interrupt status register */ 97*e72dd8d7SRuslan Bukin #define SPI_IPIER 0x28 /* IP interrupt enable register */ 98*e72dd8d7SRuslan Bukin 99*e72dd8d7SRuslan Bukin struct spi_softc { 100*e72dd8d7SRuslan Bukin struct resource *res[1]; 101*e72dd8d7SRuslan Bukin bus_space_tag_t bst; 102*e72dd8d7SRuslan Bukin bus_space_handle_t bsh; 103*e72dd8d7SRuslan Bukin void *ih; 104*e72dd8d7SRuslan Bukin }; 105*e72dd8d7SRuslan Bukin 106*e72dd8d7SRuslan Bukin static struct resource_spec spi_spec[] = { 107*e72dd8d7SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE }, 108*e72dd8d7SRuslan Bukin { -1, 0 } 109*e72dd8d7SRuslan Bukin }; 110*e72dd8d7SRuslan Bukin 111*e72dd8d7SRuslan Bukin static int 112*e72dd8d7SRuslan Bukin spi_probe(device_t dev) 113*e72dd8d7SRuslan Bukin { 114*e72dd8d7SRuslan Bukin 115*e72dd8d7SRuslan Bukin if (!ofw_bus_status_okay(dev)) 116*e72dd8d7SRuslan Bukin return (ENXIO); 117*e72dd8d7SRuslan Bukin 118*e72dd8d7SRuslan Bukin if (!ofw_bus_is_compatible(dev, "xlnx,xps-spi-3.2")) 119*e72dd8d7SRuslan Bukin return (ENXIO); 120*e72dd8d7SRuslan Bukin 121*e72dd8d7SRuslan Bukin device_set_desc(dev, "Xilinx Quad SPI"); 122*e72dd8d7SRuslan Bukin return (BUS_PROBE_DEFAULT); 123*e72dd8d7SRuslan Bukin } 124*e72dd8d7SRuslan Bukin 125*e72dd8d7SRuslan Bukin static int 126*e72dd8d7SRuslan Bukin spi_chip_select(device_t dev, device_t child) 127*e72dd8d7SRuslan Bukin { 128*e72dd8d7SRuslan Bukin struct spi_softc *sc; 129*e72dd8d7SRuslan Bukin uint32_t cs; 130*e72dd8d7SRuslan Bukin 131*e72dd8d7SRuslan Bukin sc = device_get_softc(dev); 132*e72dd8d7SRuslan Bukin 133*e72dd8d7SRuslan Bukin spibus_get_cs(child, &cs); 134*e72dd8d7SRuslan Bukin 135*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_SSR, ~(1 << cs)); 136*e72dd8d7SRuslan Bukin 137*e72dd8d7SRuslan Bukin return (0); 138*e72dd8d7SRuslan Bukin } 139*e72dd8d7SRuslan Bukin 140*e72dd8d7SRuslan Bukin static int 141*e72dd8d7SRuslan Bukin spi_chip_deselect(device_t dev, device_t child) 142*e72dd8d7SRuslan Bukin { 143*e72dd8d7SRuslan Bukin struct spi_softc *sc; 144*e72dd8d7SRuslan Bukin 145*e72dd8d7SRuslan Bukin sc = device_get_softc(dev); 146*e72dd8d7SRuslan Bukin 147*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_SSR, ~0); 148*e72dd8d7SRuslan Bukin 149*e72dd8d7SRuslan Bukin return (0); 150*e72dd8d7SRuslan Bukin } 151*e72dd8d7SRuslan Bukin 152*e72dd8d7SRuslan Bukin static int 153*e72dd8d7SRuslan Bukin spi_attach(device_t dev) 154*e72dd8d7SRuslan Bukin { 155*e72dd8d7SRuslan Bukin struct spi_softc *sc; 156*e72dd8d7SRuslan Bukin uint32_t reg; 157*e72dd8d7SRuslan Bukin 158*e72dd8d7SRuslan Bukin sc = device_get_softc(dev); 159*e72dd8d7SRuslan Bukin 160*e72dd8d7SRuslan Bukin if (bus_alloc_resources(dev, spi_spec, sc->res)) { 161*e72dd8d7SRuslan Bukin device_printf(dev, "could not allocate resources\n"); 162*e72dd8d7SRuslan Bukin return (ENXIO); 163*e72dd8d7SRuslan Bukin } 164*e72dd8d7SRuslan Bukin 165*e72dd8d7SRuslan Bukin /* Memory interface */ 166*e72dd8d7SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]); 167*e72dd8d7SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]); 168*e72dd8d7SRuslan Bukin 169*e72dd8d7SRuslan Bukin /* Reset */ 170*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_SRR, SRR_RESET); 171*e72dd8d7SRuslan Bukin 172*e72dd8d7SRuslan Bukin DELAY(1000); 173*e72dd8d7SRuslan Bukin 174*e72dd8d7SRuslan Bukin reg = (CR_MASTER | CR_MSS | CR_RST_RX | CR_RST_TX); 175*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_CR, reg); 176*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_DGIER, 0); /* Disable interrupts */ 177*e72dd8d7SRuslan Bukin 178*e72dd8d7SRuslan Bukin reg = (CR_MASTER | CR_MSS | CR_SPE); 179*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_CR, reg); 180*e72dd8d7SRuslan Bukin 181*e72dd8d7SRuslan Bukin device_add_child(dev, "spibus", 0); 182*e72dd8d7SRuslan Bukin return (bus_generic_attach(dev)); 183*e72dd8d7SRuslan Bukin } 184*e72dd8d7SRuslan Bukin 185*e72dd8d7SRuslan Bukin static int 186*e72dd8d7SRuslan Bukin spi_txrx(struct spi_softc *sc, uint8_t *out_buf, 187*e72dd8d7SRuslan Bukin uint8_t *in_buf, int bufsz, int cs) 188*e72dd8d7SRuslan Bukin { 189*e72dd8d7SRuslan Bukin uint32_t data; 190*e72dd8d7SRuslan Bukin uint32_t i; 191*e72dd8d7SRuslan Bukin 192*e72dd8d7SRuslan Bukin for (i = 0; i < bufsz; i++) { 193*e72dd8d7SRuslan Bukin WRITE4(sc, SPI_DTR, out_buf[i]); 194*e72dd8d7SRuslan Bukin 195*e72dd8d7SRuslan Bukin while(!(READ4(sc, SPI_SR) & SR_TX_EMPTY)) 196*e72dd8d7SRuslan Bukin continue; 197*e72dd8d7SRuslan Bukin 198*e72dd8d7SRuslan Bukin data = READ4(sc, SPI_DRR); 199*e72dd8d7SRuslan Bukin if (in_buf) 200*e72dd8d7SRuslan Bukin in_buf[i] = (data & 0xff); 201*e72dd8d7SRuslan Bukin } 202*e72dd8d7SRuslan Bukin 203*e72dd8d7SRuslan Bukin return (0); 204*e72dd8d7SRuslan Bukin } 205*e72dd8d7SRuslan Bukin 206*e72dd8d7SRuslan Bukin static int 207*e72dd8d7SRuslan Bukin spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 208*e72dd8d7SRuslan Bukin { 209*e72dd8d7SRuslan Bukin struct spi_softc *sc; 210*e72dd8d7SRuslan Bukin uint32_t cs; 211*e72dd8d7SRuslan Bukin 212*e72dd8d7SRuslan Bukin sc = device_get_softc(dev); 213*e72dd8d7SRuslan Bukin 214*e72dd8d7SRuslan Bukin KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 215*e72dd8d7SRuslan Bukin ("%s: TX/RX command sizes should be equal", __func__)); 216*e72dd8d7SRuslan Bukin KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 217*e72dd8d7SRuslan Bukin ("%s: TX/RX data sizes should be equal", __func__)); 218*e72dd8d7SRuslan Bukin 219*e72dd8d7SRuslan Bukin /* get the proper chip select */ 220*e72dd8d7SRuslan Bukin spibus_get_cs(child, &cs); 221*e72dd8d7SRuslan Bukin 222*e72dd8d7SRuslan Bukin /* Command */ 223*e72dd8d7SRuslan Bukin spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); 224*e72dd8d7SRuslan Bukin 225*e72dd8d7SRuslan Bukin /* Data */ 226*e72dd8d7SRuslan Bukin spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs); 227*e72dd8d7SRuslan Bukin 228*e72dd8d7SRuslan Bukin return (0); 229*e72dd8d7SRuslan Bukin } 230*e72dd8d7SRuslan Bukin 231*e72dd8d7SRuslan Bukin static device_method_t spi_methods[] = { 232*e72dd8d7SRuslan Bukin /* Device interface */ 233*e72dd8d7SRuslan Bukin DEVMETHOD(device_probe, spi_probe), 234*e72dd8d7SRuslan Bukin DEVMETHOD(device_attach, spi_attach), 235*e72dd8d7SRuslan Bukin 236*e72dd8d7SRuslan Bukin /* SPI interface */ 237*e72dd8d7SRuslan Bukin DEVMETHOD(spibus_transfer, spi_transfer), 238*e72dd8d7SRuslan Bukin DEVMETHOD(spibus_chip_select, spi_chip_select), 239*e72dd8d7SRuslan Bukin DEVMETHOD(spibus_chip_deselect, spi_chip_deselect), 240*e72dd8d7SRuslan Bukin 241*e72dd8d7SRuslan Bukin DEVMETHOD_END 242*e72dd8d7SRuslan Bukin }; 243*e72dd8d7SRuslan Bukin 244*e72dd8d7SRuslan Bukin static driver_t spi_driver = { 245*e72dd8d7SRuslan Bukin "spi", 246*e72dd8d7SRuslan Bukin spi_methods, 247*e72dd8d7SRuslan Bukin sizeof(struct spi_softc), 248*e72dd8d7SRuslan Bukin }; 249*e72dd8d7SRuslan Bukin 250*e72dd8d7SRuslan Bukin static devclass_t spi_devclass; 251*e72dd8d7SRuslan Bukin 252*e72dd8d7SRuslan Bukin DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0); 253