1*8cfe2a7aSLuiz Otavio O Souza /*- 2*8cfe2a7aSLuiz Otavio O Souza * Copyright (c) 2018, 2019 Rubicon Communications, LLC (Netgate) 3*8cfe2a7aSLuiz Otavio O Souza * 4*8cfe2a7aSLuiz Otavio O Souza * Redistribution and use in source and binary forms, with or without 5*8cfe2a7aSLuiz Otavio O Souza * modification, are permitted provided that the following conditions 6*8cfe2a7aSLuiz Otavio O Souza * are met: 7*8cfe2a7aSLuiz Otavio O Souza * 1. Redistributions of source code must retain the above copyright 8*8cfe2a7aSLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer. 9*8cfe2a7aSLuiz Otavio O Souza * 2. Redistributions in binary form must reproduce the above copyright 10*8cfe2a7aSLuiz Otavio O Souza * notice, this list of conditions and the following disclaimer in the 11*8cfe2a7aSLuiz Otavio O Souza * documentation and/or other materials provided with the distribution. 12*8cfe2a7aSLuiz Otavio O Souza * 13*8cfe2a7aSLuiz Otavio O Souza * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14*8cfe2a7aSLuiz Otavio O Souza * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15*8cfe2a7aSLuiz Otavio O Souza * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16*8cfe2a7aSLuiz Otavio O Souza * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17*8cfe2a7aSLuiz Otavio O Souza * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18*8cfe2a7aSLuiz Otavio O Souza * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19*8cfe2a7aSLuiz Otavio O Souza * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20*8cfe2a7aSLuiz Otavio O Souza * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21*8cfe2a7aSLuiz Otavio O Souza * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22*8cfe2a7aSLuiz Otavio O Souza * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23*8cfe2a7aSLuiz Otavio O Souza * 24*8cfe2a7aSLuiz Otavio O Souza */ 25*8cfe2a7aSLuiz Otavio O Souza 26*8cfe2a7aSLuiz Otavio O Souza #include <sys/cdefs.h> 27*8cfe2a7aSLuiz Otavio O Souza __FBSDID("$FreeBSD$"); 28*8cfe2a7aSLuiz Otavio O Souza 29*8cfe2a7aSLuiz Otavio O Souza #include <sys/param.h> 30*8cfe2a7aSLuiz Otavio O Souza #include <sys/systm.h> 31*8cfe2a7aSLuiz Otavio O Souza #include <sys/bus.h> 32*8cfe2a7aSLuiz Otavio O Souza 33*8cfe2a7aSLuiz Otavio O Souza #include <sys/kernel.h> 34*8cfe2a7aSLuiz Otavio O Souza #include <sys/module.h> 35*8cfe2a7aSLuiz Otavio O Souza #include <sys/rman.h> 36*8cfe2a7aSLuiz Otavio O Souza 37*8cfe2a7aSLuiz Otavio O Souza #include <machine/bus.h> 38*8cfe2a7aSLuiz Otavio O Souza #include <machine/resource.h> 39*8cfe2a7aSLuiz Otavio O Souza #include <machine/intr.h> 40*8cfe2a7aSLuiz Otavio O Souza 41*8cfe2a7aSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h> 42*8cfe2a7aSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h> 43*8cfe2a7aSLuiz Otavio O Souza #include <dev/spibus/spi.h> 44*8cfe2a7aSLuiz Otavio O Souza #include <dev/spibus/spibusvar.h> 45*8cfe2a7aSLuiz Otavio O Souza 46*8cfe2a7aSLuiz Otavio O Souza #include "spibus_if.h" 47*8cfe2a7aSLuiz Otavio O Souza 48*8cfe2a7aSLuiz Otavio O Souza struct a37x0_spi_softc { 49*8cfe2a7aSLuiz Otavio O Souza device_t sc_dev; 50*8cfe2a7aSLuiz Otavio O Souza struct mtx sc_mtx; 51*8cfe2a7aSLuiz Otavio O Souza struct resource *sc_mem_res; 52*8cfe2a7aSLuiz Otavio O Souza struct resource *sc_irq_res; 53*8cfe2a7aSLuiz Otavio O Souza struct spi_command *sc_cmd; 54*8cfe2a7aSLuiz Otavio O Souza bus_space_tag_t sc_bst; 55*8cfe2a7aSLuiz Otavio O Souza bus_space_handle_t sc_bsh; 56*8cfe2a7aSLuiz Otavio O Souza uint32_t sc_len; 57*8cfe2a7aSLuiz Otavio O Souza uint32_t sc_maxfreq; 58*8cfe2a7aSLuiz Otavio O Souza uint32_t sc_read; 59*8cfe2a7aSLuiz Otavio O Souza uint32_t sc_flags; 60*8cfe2a7aSLuiz Otavio O Souza uint32_t sc_written; 61*8cfe2a7aSLuiz Otavio O Souza void *sc_intrhand; 62*8cfe2a7aSLuiz Otavio O Souza }; 63*8cfe2a7aSLuiz Otavio O Souza 64*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_WRITE(_sc, _off, _val) \ 65*8cfe2a7aSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val)) 66*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_READ(_sc, _off) \ 67*8cfe2a7aSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off)) 68*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 69*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 70*8cfe2a7aSLuiz Otavio O Souza 71*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_BUSY (1 << 0) 72*8cfe2a7aSLuiz Otavio O Souza /* 73*8cfe2a7aSLuiz Otavio O Souza * While the A3700 utils from Marvell usually sets the QSF clock to 200MHz, 74*8cfe2a7aSLuiz Otavio O Souza * there is no guarantee that it is correct without the proper clock framework 75*8cfe2a7aSLuiz Otavio O Souza * to retrieve the actual TBG and PLL settings. 76*8cfe2a7aSLuiz Otavio O Souza */ 77*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CLOCK 200000000 /* QSF Clock 200MHz */ 78*8cfe2a7aSLuiz Otavio O Souza 79*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CONTROL 0x0 80*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CS_SHIFT 16 81*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CS_MASK (0xf << A37X0_SPI_CS_SHIFT) 82*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CONF 0x4 83*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_WFIFO_THRS_SHIFT 28 84*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_RFIFO_THRS_SHIFT 24 85*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_AUTO_CS_EN (1 << 20) 86*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DMA_WR_EN (1 << 19) 87*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DMA_RD_EN (1 << 18) 88*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_FIFO_MODE (1 << 17) 89*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_SRST (1 << 16) 90*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_XFER_START (1 << 15) 91*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_XFER_STOP (1 << 14) 92*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_INSTR_PIN (1 << 13) 93*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_ADDR_PIN (1 << 12) 94*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DATA_PIN_MASK 0x3 95*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DATA_PIN_SHIFT 10 96*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_FIFO_FLUSH (1 << 9) 97*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_RW_EN (1 << 8) 98*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CLK_POL (1 << 7) 99*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_CLK_PHASE (1 << 6) 100*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_BYTE_LEN (1 << 5) 101*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_PSC_MASK 0x1f 102*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DATA_OUT 0x8 103*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_DATA_IN 0xc 104*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_INTR_STAT 0x28 105*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_INTR_MASK 0x2c 106*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_RDY (1 << 1) 107*8cfe2a7aSLuiz Otavio O Souza #define A37X0_SPI_XFER_DONE (1 << 0) 108*8cfe2a7aSLuiz Otavio O Souza 109*8cfe2a7aSLuiz Otavio O Souza static struct ofw_compat_data compat_data[] = { 110*8cfe2a7aSLuiz Otavio O Souza { "marvell,armada-3700-spi", 1 }, 111*8cfe2a7aSLuiz Otavio O Souza { NULL, 0 } 112*8cfe2a7aSLuiz Otavio O Souza }; 113*8cfe2a7aSLuiz Otavio O Souza 114*8cfe2a7aSLuiz Otavio O Souza static void a37x0_spi_intr(void *); 115*8cfe2a7aSLuiz Otavio O Souza 116*8cfe2a7aSLuiz Otavio O Souza static int 117*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_wait(struct a37x0_spi_softc *sc, int timeout, uint32_t reg, 118*8cfe2a7aSLuiz Otavio O Souza uint32_t mask) 119*8cfe2a7aSLuiz Otavio O Souza { 120*8cfe2a7aSLuiz Otavio O Souza int i; 121*8cfe2a7aSLuiz Otavio O Souza 122*8cfe2a7aSLuiz Otavio O Souza for (i = 0; i < timeout; i++) { 123*8cfe2a7aSLuiz Otavio O Souza if ((A37X0_SPI_READ(sc, reg) & mask) == 0) 124*8cfe2a7aSLuiz Otavio O Souza return (0); 125*8cfe2a7aSLuiz Otavio O Souza DELAY(100); 126*8cfe2a7aSLuiz Otavio O Souza } 127*8cfe2a7aSLuiz Otavio O Souza 128*8cfe2a7aSLuiz Otavio O Souza return (ETIMEDOUT); 129*8cfe2a7aSLuiz Otavio O Souza } 130*8cfe2a7aSLuiz Otavio O Souza 131*8cfe2a7aSLuiz Otavio O Souza static int 132*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_probe(device_t dev) 133*8cfe2a7aSLuiz Otavio O Souza { 134*8cfe2a7aSLuiz Otavio O Souza 135*8cfe2a7aSLuiz Otavio O Souza if (!ofw_bus_status_okay(dev)) 136*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 137*8cfe2a7aSLuiz Otavio O Souza if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 138*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 139*8cfe2a7aSLuiz Otavio O Souza device_set_desc(dev, "Armada 37x0 SPI controller"); 140*8cfe2a7aSLuiz Otavio O Souza 141*8cfe2a7aSLuiz Otavio O Souza return (BUS_PROBE_DEFAULT); 142*8cfe2a7aSLuiz Otavio O Souza } 143*8cfe2a7aSLuiz Otavio O Souza 144*8cfe2a7aSLuiz Otavio O Souza static int 145*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_attach(device_t dev) 146*8cfe2a7aSLuiz Otavio O Souza { 147*8cfe2a7aSLuiz Otavio O Souza int err, rid; 148*8cfe2a7aSLuiz Otavio O Souza pcell_t maxfreq; 149*8cfe2a7aSLuiz Otavio O Souza struct a37x0_spi_softc *sc; 150*8cfe2a7aSLuiz Otavio O Souza uint32_t reg; 151*8cfe2a7aSLuiz Otavio O Souza 152*8cfe2a7aSLuiz Otavio O Souza sc = device_get_softc(dev); 153*8cfe2a7aSLuiz Otavio O Souza sc->sc_dev = dev; 154*8cfe2a7aSLuiz Otavio O Souza 155*8cfe2a7aSLuiz Otavio O Souza rid = 0; 156*8cfe2a7aSLuiz Otavio O Souza sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 157*8cfe2a7aSLuiz Otavio O Souza RF_ACTIVE); 158*8cfe2a7aSLuiz Otavio O Souza if (!sc->sc_mem_res) { 159*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, "cannot allocate memory window\n"); 160*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 161*8cfe2a7aSLuiz Otavio O Souza } 162*8cfe2a7aSLuiz Otavio O Souza 163*8cfe2a7aSLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 164*8cfe2a7aSLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 165*8cfe2a7aSLuiz Otavio O Souza 166*8cfe2a7aSLuiz Otavio O Souza rid = 0; 167*8cfe2a7aSLuiz Otavio O Souza sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 168*8cfe2a7aSLuiz Otavio O Souza RF_ACTIVE); 169*8cfe2a7aSLuiz Otavio O Souza if (!sc->sc_irq_res) { 170*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 171*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, "cannot allocate interrupt\n"); 172*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 173*8cfe2a7aSLuiz Otavio O Souza } 174*8cfe2a7aSLuiz Otavio O Souza 175*8cfe2a7aSLuiz Otavio O Souza /* Make sure that no CS is asserted. */ 176*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL); 177*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, reg & ~A37X0_SPI_CS_MASK); 178*8cfe2a7aSLuiz Otavio O Souza 179*8cfe2a7aSLuiz Otavio O Souza /* Reset FIFO. */ 180*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF); 181*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg | A37X0_SPI_FIFO_FLUSH); 182*8cfe2a7aSLuiz Otavio O Souza err = a37x0_spi_wait(sc, 20, A37X0_SPI_CONF, A37X0_SPI_FIFO_FLUSH); 183*8cfe2a7aSLuiz Otavio O Souza if (err != 0) { 184*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 185*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 186*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, "cannot flush the controller fifo.\n"); 187*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 188*8cfe2a7aSLuiz Otavio O Souza } 189*8cfe2a7aSLuiz Otavio O Souza 190*8cfe2a7aSLuiz Otavio O Souza /* Reset the Controller. */ 191*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF); 192*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg | A37X0_SPI_SRST); 193*8cfe2a7aSLuiz Otavio O Souza DELAY(1000); 194*8cfe2a7aSLuiz Otavio O Souza /* Enable the single byte IO, disable FIFO. */ 195*8cfe2a7aSLuiz Otavio O Souza reg &= ~(A37X0_SPI_FIFO_MODE | A37X0_SPI_BYTE_LEN); 196*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg); 197*8cfe2a7aSLuiz Otavio O Souza 198*8cfe2a7aSLuiz Otavio O Souza /* Disable and clear interrupts. */ 199*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_MASK, 0); 200*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT); 201*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, reg); 202*8cfe2a7aSLuiz Otavio O Souza 203*8cfe2a7aSLuiz Otavio O Souza /* Hook up our interrupt handler. */ 204*8cfe2a7aSLuiz Otavio O Souza if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 205*8cfe2a7aSLuiz Otavio O Souza NULL, a37x0_spi_intr, sc, &sc->sc_intrhand)) { 206*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 207*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 208*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, "cannot setup the interrupt handler\n"); 209*8cfe2a7aSLuiz Otavio O Souza return (ENXIO); 210*8cfe2a7aSLuiz Otavio O Souza } 211*8cfe2a7aSLuiz Otavio O Souza 212*8cfe2a7aSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "a37x0_spi", NULL, MTX_DEF); 213*8cfe2a7aSLuiz Otavio O Souza 214*8cfe2a7aSLuiz Otavio O Souza /* Read the controller max-frequency. */ 215*8cfe2a7aSLuiz Otavio O Souza if (OF_getencprop(ofw_bus_get_node(dev), "spi-max-frequency", &maxfreq, 216*8cfe2a7aSLuiz Otavio O Souza sizeof(maxfreq)) == -1) 217*8cfe2a7aSLuiz Otavio O Souza maxfreq = 0; 218*8cfe2a7aSLuiz Otavio O Souza sc->sc_maxfreq = maxfreq; 219*8cfe2a7aSLuiz Otavio O Souza 220*8cfe2a7aSLuiz Otavio O Souza device_add_child(dev, "spibus", -1); 221*8cfe2a7aSLuiz Otavio O Souza 222*8cfe2a7aSLuiz Otavio O Souza /* Probe and attach the spibus when interrupts are available. */ 223*8cfe2a7aSLuiz Otavio O Souza config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev); 224*8cfe2a7aSLuiz Otavio O Souza 225*8cfe2a7aSLuiz Otavio O Souza return (0); 226*8cfe2a7aSLuiz Otavio O Souza } 227*8cfe2a7aSLuiz Otavio O Souza 228*8cfe2a7aSLuiz Otavio O Souza static int 229*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_detach(device_t dev) 230*8cfe2a7aSLuiz Otavio O Souza { 231*8cfe2a7aSLuiz Otavio O Souza struct a37x0_spi_softc *sc; 232*8cfe2a7aSLuiz Otavio O Souza 233*8cfe2a7aSLuiz Otavio O Souza bus_generic_detach(dev); 234*8cfe2a7aSLuiz Otavio O Souza sc = device_get_softc(dev); 235*8cfe2a7aSLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 236*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_intrhand) 237*8cfe2a7aSLuiz Otavio O Souza bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); 238*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_irq_res) 239*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 240*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_mem_res) 241*8cfe2a7aSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 242*8cfe2a7aSLuiz Otavio O Souza 243*8cfe2a7aSLuiz Otavio O Souza return (0); 244*8cfe2a7aSLuiz Otavio O Souza } 245*8cfe2a7aSLuiz Otavio O Souza 246*8cfe2a7aSLuiz Otavio O Souza static __inline void 247*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_rx_byte(struct a37x0_spi_softc *sc) 248*8cfe2a7aSLuiz Otavio O Souza { 249*8cfe2a7aSLuiz Otavio O Souza struct spi_command *cmd; 250*8cfe2a7aSLuiz Otavio O Souza uint32_t read; 251*8cfe2a7aSLuiz Otavio O Souza uint8_t *p; 252*8cfe2a7aSLuiz Otavio O Souza 253*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_read == sc->sc_len) 254*8cfe2a7aSLuiz Otavio O Souza return; 255*8cfe2a7aSLuiz Otavio O Souza cmd = sc->sc_cmd; 256*8cfe2a7aSLuiz Otavio O Souza p = (uint8_t *)cmd->rx_cmd; 257*8cfe2a7aSLuiz Otavio O Souza read = sc->sc_read++; 258*8cfe2a7aSLuiz Otavio O Souza if (read >= cmd->rx_cmd_sz) { 259*8cfe2a7aSLuiz Otavio O Souza p = (uint8_t *)cmd->rx_data; 260*8cfe2a7aSLuiz Otavio O Souza read -= cmd->rx_cmd_sz; 261*8cfe2a7aSLuiz Otavio O Souza } 262*8cfe2a7aSLuiz Otavio O Souza p[read] = A37X0_SPI_READ(sc, A37X0_SPI_DATA_IN) & 0xff; 263*8cfe2a7aSLuiz Otavio O Souza } 264*8cfe2a7aSLuiz Otavio O Souza 265*8cfe2a7aSLuiz Otavio O Souza static __inline void 266*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_tx_byte(struct a37x0_spi_softc *sc) 267*8cfe2a7aSLuiz Otavio O Souza { 268*8cfe2a7aSLuiz Otavio O Souza struct spi_command *cmd; 269*8cfe2a7aSLuiz Otavio O Souza uint32_t written; 270*8cfe2a7aSLuiz Otavio O Souza uint8_t *p; 271*8cfe2a7aSLuiz Otavio O Souza 272*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_written == sc->sc_len) 273*8cfe2a7aSLuiz Otavio O Souza return; 274*8cfe2a7aSLuiz Otavio O Souza cmd = sc->sc_cmd; 275*8cfe2a7aSLuiz Otavio O Souza p = (uint8_t *)cmd->tx_cmd; 276*8cfe2a7aSLuiz Otavio O Souza written = sc->sc_written++; 277*8cfe2a7aSLuiz Otavio O Souza if (written >= cmd->tx_cmd_sz) { 278*8cfe2a7aSLuiz Otavio O Souza p = (uint8_t *)cmd->tx_data; 279*8cfe2a7aSLuiz Otavio O Souza written -= cmd->tx_cmd_sz; 280*8cfe2a7aSLuiz Otavio O Souza } 281*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_DATA_OUT, p[written]); 282*8cfe2a7aSLuiz Otavio O Souza } 283*8cfe2a7aSLuiz Otavio O Souza 284*8cfe2a7aSLuiz Otavio O Souza static __inline void 285*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_clock(struct a37x0_spi_softc *sc, uint32_t clock) 286*8cfe2a7aSLuiz Otavio O Souza { 287*8cfe2a7aSLuiz Otavio O Souza uint32_t psc, reg; 288*8cfe2a7aSLuiz Otavio O Souza 289*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_maxfreq > 0 && clock > sc->sc_maxfreq) 290*8cfe2a7aSLuiz Otavio O Souza clock = sc->sc_maxfreq; 291*8cfe2a7aSLuiz Otavio O Souza psc = A37X0_SPI_CLOCK / clock; 292*8cfe2a7aSLuiz Otavio O Souza if ((A37X0_SPI_CLOCK % clock) > 0) 293*8cfe2a7aSLuiz Otavio O Souza psc++; 294*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF); 295*8cfe2a7aSLuiz Otavio O Souza reg &= ~A37X0_SPI_PSC_MASK; 296*8cfe2a7aSLuiz Otavio O Souza reg |= psc & A37X0_SPI_PSC_MASK; 297*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg); 298*8cfe2a7aSLuiz Otavio O Souza } 299*8cfe2a7aSLuiz Otavio O Souza 300*8cfe2a7aSLuiz Otavio O Souza static __inline void 301*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_pins(struct a37x0_spi_softc *sc, uint32_t npins) 302*8cfe2a7aSLuiz Otavio O Souza { 303*8cfe2a7aSLuiz Otavio O Souza uint32_t reg; 304*8cfe2a7aSLuiz Otavio O Souza 305*8cfe2a7aSLuiz Otavio O Souza /* Sets single, dual or quad SPI mode. */ 306*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF); 307*8cfe2a7aSLuiz Otavio O Souza reg &= ~(A37X0_SPI_DATA_PIN_MASK << A37X0_SPI_DATA_PIN_SHIFT); 308*8cfe2a7aSLuiz Otavio O Souza reg |= (npins / 2) << A37X0_SPI_DATA_PIN_SHIFT; 309*8cfe2a7aSLuiz Otavio O Souza reg |= A37X0_SPI_INSTR_PIN | A37X0_SPI_ADDR_PIN; 310*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg); 311*8cfe2a7aSLuiz Otavio O Souza } 312*8cfe2a7aSLuiz Otavio O Souza 313*8cfe2a7aSLuiz Otavio O Souza static __inline void 314*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_mode(struct a37x0_spi_softc *sc, uint32_t mode) 315*8cfe2a7aSLuiz Otavio O Souza { 316*8cfe2a7aSLuiz Otavio O Souza uint32_t reg; 317*8cfe2a7aSLuiz Otavio O Souza 318*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONF); 319*8cfe2a7aSLuiz Otavio O Souza switch (mode) { 320*8cfe2a7aSLuiz Otavio O Souza case 0: 321*8cfe2a7aSLuiz Otavio O Souza reg &= ~(A37X0_SPI_CLK_PHASE | A37X0_SPI_CLK_POL); 322*8cfe2a7aSLuiz Otavio O Souza break; 323*8cfe2a7aSLuiz Otavio O Souza case 1: 324*8cfe2a7aSLuiz Otavio O Souza reg &= ~A37X0_SPI_CLK_POL; 325*8cfe2a7aSLuiz Otavio O Souza reg |= A37X0_SPI_CLK_PHASE; 326*8cfe2a7aSLuiz Otavio O Souza break; 327*8cfe2a7aSLuiz Otavio O Souza case 2: 328*8cfe2a7aSLuiz Otavio O Souza reg &= ~A37X0_SPI_CLK_PHASE; 329*8cfe2a7aSLuiz Otavio O Souza reg |= A37X0_SPI_CLK_POL; 330*8cfe2a7aSLuiz Otavio O Souza break; 331*8cfe2a7aSLuiz Otavio O Souza case 3: 332*8cfe2a7aSLuiz Otavio O Souza reg |= (A37X0_SPI_CLK_PHASE | A37X0_SPI_CLK_POL); 333*8cfe2a7aSLuiz Otavio O Souza break; 334*8cfe2a7aSLuiz Otavio O Souza } 335*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONF, reg); 336*8cfe2a7aSLuiz Otavio O Souza } 337*8cfe2a7aSLuiz Otavio O Souza 338*8cfe2a7aSLuiz Otavio O Souza static void 339*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_intr(void *arg) 340*8cfe2a7aSLuiz Otavio O Souza { 341*8cfe2a7aSLuiz Otavio O Souza struct a37x0_spi_softc *sc; 342*8cfe2a7aSLuiz Otavio O Souza uint32_t status; 343*8cfe2a7aSLuiz Otavio O Souza 344*8cfe2a7aSLuiz Otavio O Souza sc = (struct a37x0_spi_softc *)arg; 345*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_LOCK(sc); 346*8cfe2a7aSLuiz Otavio O Souza 347*8cfe2a7aSLuiz Otavio O Souza /* Filter stray interrupts. */ 348*8cfe2a7aSLuiz Otavio O Souza if ((sc->sc_flags & A37X0_SPI_BUSY) == 0) { 349*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_UNLOCK(sc); 350*8cfe2a7aSLuiz Otavio O Souza return; 351*8cfe2a7aSLuiz Otavio O Souza } 352*8cfe2a7aSLuiz Otavio O Souza 353*8cfe2a7aSLuiz Otavio O Souza status = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT); 354*8cfe2a7aSLuiz Otavio O Souza if (status & A37X0_SPI_XFER_DONE) 355*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_rx_byte(sc); 356*8cfe2a7aSLuiz Otavio O Souza 357*8cfe2a7aSLuiz Otavio O Souza /* Clear the interrupt status. */ 358*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, status); 359*8cfe2a7aSLuiz Otavio O Souza 360*8cfe2a7aSLuiz Otavio O Souza /* Check for end of transfer. */ 361*8cfe2a7aSLuiz Otavio O Souza if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) 362*8cfe2a7aSLuiz Otavio O Souza wakeup(sc->sc_dev); 363*8cfe2a7aSLuiz Otavio O Souza else 364*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_tx_byte(sc); 365*8cfe2a7aSLuiz Otavio O Souza 366*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_UNLOCK(sc); 367*8cfe2a7aSLuiz Otavio O Souza } 368*8cfe2a7aSLuiz Otavio O Souza 369*8cfe2a7aSLuiz Otavio O Souza static int 370*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 371*8cfe2a7aSLuiz Otavio O Souza { 372*8cfe2a7aSLuiz Otavio O Souza int timeout; 373*8cfe2a7aSLuiz Otavio O Souza struct a37x0_spi_softc *sc; 374*8cfe2a7aSLuiz Otavio O Souza uint32_t clock, cs, mode, reg; 375*8cfe2a7aSLuiz Otavio O Souza 376*8cfe2a7aSLuiz Otavio O Souza KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 377*8cfe2a7aSLuiz Otavio O Souza ("TX/RX command sizes should be equal")); 378*8cfe2a7aSLuiz Otavio O Souza KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 379*8cfe2a7aSLuiz Otavio O Souza ("TX/RX data sizes should be equal")); 380*8cfe2a7aSLuiz Otavio O Souza 381*8cfe2a7aSLuiz Otavio O Souza /* Get the proper data for this child. */ 382*8cfe2a7aSLuiz Otavio O Souza spibus_get_cs(child, &cs); 383*8cfe2a7aSLuiz Otavio O Souza cs &= ~SPIBUS_CS_HIGH; 384*8cfe2a7aSLuiz Otavio O Souza if (cs > 3) { 385*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, 386*8cfe2a7aSLuiz Otavio O Souza "Invalid CS %d requested by %s\n", cs, 387*8cfe2a7aSLuiz Otavio O Souza device_get_nameunit(child)); 388*8cfe2a7aSLuiz Otavio O Souza return (EINVAL); 389*8cfe2a7aSLuiz Otavio O Souza } 390*8cfe2a7aSLuiz Otavio O Souza spibus_get_clock(child, &clock); 391*8cfe2a7aSLuiz Otavio O Souza if (clock == 0) { 392*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, 393*8cfe2a7aSLuiz Otavio O Souza "Invalid clock %uHz requested by %s\n", clock, 394*8cfe2a7aSLuiz Otavio O Souza device_get_nameunit(child)); 395*8cfe2a7aSLuiz Otavio O Souza return (EINVAL); 396*8cfe2a7aSLuiz Otavio O Souza } 397*8cfe2a7aSLuiz Otavio O Souza spibus_get_mode(child, &mode); 398*8cfe2a7aSLuiz Otavio O Souza if (mode > 3) { 399*8cfe2a7aSLuiz Otavio O Souza device_printf(dev, 400*8cfe2a7aSLuiz Otavio O Souza "Invalid mode %u requested by %s\n", mode, 401*8cfe2a7aSLuiz Otavio O Souza device_get_nameunit(child)); 402*8cfe2a7aSLuiz Otavio O Souza return (EINVAL); 403*8cfe2a7aSLuiz Otavio O Souza } 404*8cfe2a7aSLuiz Otavio O Souza 405*8cfe2a7aSLuiz Otavio O Souza sc = device_get_softc(dev); 406*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_LOCK(sc); 407*8cfe2a7aSLuiz Otavio O Souza 408*8cfe2a7aSLuiz Otavio O Souza /* Wait until the controller is free. */ 409*8cfe2a7aSLuiz Otavio O Souza while (sc->sc_flags & A37X0_SPI_BUSY) 410*8cfe2a7aSLuiz Otavio O Souza mtx_sleep(dev, &sc->sc_mtx, 0, "a37x0_spi", 0); 411*8cfe2a7aSLuiz Otavio O Souza 412*8cfe2a7aSLuiz Otavio O Souza /* Now we have control over SPI controller. */ 413*8cfe2a7aSLuiz Otavio O Souza sc->sc_flags = A37X0_SPI_BUSY; 414*8cfe2a7aSLuiz Otavio O Souza 415*8cfe2a7aSLuiz Otavio O Souza /* Set transfer mode and clock. */ 416*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_mode(sc, mode); 417*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_pins(sc, 1); 418*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_set_clock(sc, clock); 419*8cfe2a7aSLuiz Otavio O Souza 420*8cfe2a7aSLuiz Otavio O Souza /* Set CS. */ 421*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, 1 << (A37X0_SPI_CS_SHIFT + cs)); 422*8cfe2a7aSLuiz Otavio O Souza 423*8cfe2a7aSLuiz Otavio O Souza /* Save a pointer to the SPI command. */ 424*8cfe2a7aSLuiz Otavio O Souza sc->sc_cmd = cmd; 425*8cfe2a7aSLuiz Otavio O Souza sc->sc_read = 0; 426*8cfe2a7aSLuiz Otavio O Souza sc->sc_written = 0; 427*8cfe2a7aSLuiz Otavio O Souza sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; 428*8cfe2a7aSLuiz Otavio O Souza 429*8cfe2a7aSLuiz Otavio O Souza /* Clear interrupts. */ 430*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_INTR_STAT); 431*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_STAT, reg); 432*8cfe2a7aSLuiz Otavio O Souza 433*8cfe2a7aSLuiz Otavio O Souza while ((sc->sc_len - sc->sc_written) > 0) { 434*8cfe2a7aSLuiz Otavio O Souza /* 435*8cfe2a7aSLuiz Otavio O Souza * Write to start the transmission and read the byte 436*8cfe2a7aSLuiz Otavio O Souza * back when ready. 437*8cfe2a7aSLuiz Otavio O Souza */ 438*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_tx_byte(sc); 439*8cfe2a7aSLuiz Otavio O Souza timeout = 1000; 440*8cfe2a7aSLuiz Otavio O Souza while (--timeout > 0) { 441*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL); 442*8cfe2a7aSLuiz Otavio O Souza if (reg & A37X0_SPI_XFER_DONE) 443*8cfe2a7aSLuiz Otavio O Souza break; 444*8cfe2a7aSLuiz Otavio O Souza DELAY(1); 445*8cfe2a7aSLuiz Otavio O Souza } 446*8cfe2a7aSLuiz Otavio O Souza if (timeout == 0) 447*8cfe2a7aSLuiz Otavio O Souza break; 448*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_rx_byte(sc); 449*8cfe2a7aSLuiz Otavio O Souza } 450*8cfe2a7aSLuiz Otavio O Souza 451*8cfe2a7aSLuiz Otavio O Souza /* Stop the controller. */ 452*8cfe2a7aSLuiz Otavio O Souza reg = A37X0_SPI_READ(sc, A37X0_SPI_CONTROL); 453*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_CONTROL, reg & ~A37X0_SPI_CS_MASK); 454*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_WRITE(sc, A37X0_SPI_INTR_MASK, 0); 455*8cfe2a7aSLuiz Otavio O Souza 456*8cfe2a7aSLuiz Otavio O Souza /* Release the controller and wakeup the next thread waiting for it. */ 457*8cfe2a7aSLuiz Otavio O Souza sc->sc_flags = 0; 458*8cfe2a7aSLuiz Otavio O Souza wakeup_one(dev); 459*8cfe2a7aSLuiz Otavio O Souza A37X0_SPI_UNLOCK(sc); 460*8cfe2a7aSLuiz Otavio O Souza 461*8cfe2a7aSLuiz Otavio O Souza return ((timeout == 0) ? EIO : 0); 462*8cfe2a7aSLuiz Otavio O Souza } 463*8cfe2a7aSLuiz Otavio O Souza 464*8cfe2a7aSLuiz Otavio O Souza static phandle_t 465*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_get_node(device_t bus, device_t dev) 466*8cfe2a7aSLuiz Otavio O Souza { 467*8cfe2a7aSLuiz Otavio O Souza 468*8cfe2a7aSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 469*8cfe2a7aSLuiz Otavio O Souza } 470*8cfe2a7aSLuiz Otavio O Souza 471*8cfe2a7aSLuiz Otavio O Souza static device_method_t a37x0_spi_methods[] = { 472*8cfe2a7aSLuiz Otavio O Souza /* Device interface */ 473*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD(device_probe, a37x0_spi_probe), 474*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD(device_attach, a37x0_spi_attach), 475*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD(device_detach, a37x0_spi_detach), 476*8cfe2a7aSLuiz Otavio O Souza 477*8cfe2a7aSLuiz Otavio O Souza /* SPI interface */ 478*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD(spibus_transfer, a37x0_spi_transfer), 479*8cfe2a7aSLuiz Otavio O Souza 480*8cfe2a7aSLuiz Otavio O Souza /* ofw_bus interface */ 481*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, a37x0_spi_get_node), 482*8cfe2a7aSLuiz Otavio O Souza 483*8cfe2a7aSLuiz Otavio O Souza DEVMETHOD_END 484*8cfe2a7aSLuiz Otavio O Souza }; 485*8cfe2a7aSLuiz Otavio O Souza 486*8cfe2a7aSLuiz Otavio O Souza static devclass_t a37x0_spi_devclass; 487*8cfe2a7aSLuiz Otavio O Souza 488*8cfe2a7aSLuiz Otavio O Souza static driver_t a37x0_spi_driver = { 489*8cfe2a7aSLuiz Otavio O Souza "spi", 490*8cfe2a7aSLuiz Otavio O Souza a37x0_spi_methods, 491*8cfe2a7aSLuiz Otavio O Souza sizeof(struct a37x0_spi_softc), 492*8cfe2a7aSLuiz Otavio O Souza }; 493*8cfe2a7aSLuiz Otavio O Souza 494*8cfe2a7aSLuiz Otavio O Souza DRIVER_MODULE(a37x0_spi, simplebus, a37x0_spi_driver, a37x0_spi_devclass, 0, 0); 495