12aaaabd4SRuslan Bukin /*-
22aaaabd4SRuslan Bukin * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
32aaaabd4SRuslan Bukin * All rights reserved.
42aaaabd4SRuslan Bukin *
52aaaabd4SRuslan Bukin * Redistribution and use in source and binary forms, with or without
62aaaabd4SRuslan Bukin * modification, are permitted provided that the following conditions
72aaaabd4SRuslan Bukin * are met:
82aaaabd4SRuslan Bukin * 1. Redistributions of source code must retain the above copyright
92aaaabd4SRuslan Bukin * notice, this list of conditions and the following disclaimer.
102aaaabd4SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright
112aaaabd4SRuslan Bukin * notice, this list of conditions and the following disclaimer in the
122aaaabd4SRuslan Bukin * documentation and/or other materials provided with the distribution.
132aaaabd4SRuslan Bukin *
142aaaabd4SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
152aaaabd4SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
162aaaabd4SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
172aaaabd4SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
182aaaabd4SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
192aaaabd4SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
202aaaabd4SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
212aaaabd4SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
222aaaabd4SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
232aaaabd4SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
242aaaabd4SRuslan Bukin * SUCH DAMAGE.
252aaaabd4SRuslan Bukin */
262aaaabd4SRuslan Bukin
272aaaabd4SRuslan Bukin /*
282aaaabd4SRuslan Bukin * Vybrid Family Serial Peripheral Interface (SPI)
292aaaabd4SRuslan Bukin * Chapter 47, Vybrid Reference Manual, Rev. 5, 07/2013
302aaaabd4SRuslan Bukin */
312aaaabd4SRuslan Bukin
322aaaabd4SRuslan Bukin #include <sys/param.h>
332aaaabd4SRuslan Bukin #include <sys/systm.h>
342aaaabd4SRuslan Bukin #include <sys/bus.h>
352aaaabd4SRuslan Bukin #include <sys/kernel.h>
362aaaabd4SRuslan Bukin #include <sys/module.h>
372aaaabd4SRuslan Bukin #include <sys/malloc.h>
382aaaabd4SRuslan Bukin #include <sys/rman.h>
392aaaabd4SRuslan Bukin #include <sys/timeet.h>
402aaaabd4SRuslan Bukin #include <sys/timetc.h>
412aaaabd4SRuslan Bukin #include <sys/watchdog.h>
422aaaabd4SRuslan Bukin
432aaaabd4SRuslan Bukin #include <dev/spibus/spi.h>
442aaaabd4SRuslan Bukin #include <dev/spibus/spibusvar.h>
452aaaabd4SRuslan Bukin
462aaaabd4SRuslan Bukin #include "spibus_if.h"
472aaaabd4SRuslan Bukin
482aaaabd4SRuslan Bukin #include <dev/ofw/openfirm.h>
492aaaabd4SRuslan Bukin #include <dev/ofw/ofw_bus.h>
502aaaabd4SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
512aaaabd4SRuslan Bukin
522aaaabd4SRuslan Bukin #include <machine/bus.h>
532aaaabd4SRuslan Bukin #include <machine/cpu.h>
542aaaabd4SRuslan Bukin #include <machine/intr.h>
552aaaabd4SRuslan Bukin
562aaaabd4SRuslan Bukin #include <arm/freescale/vybrid/vf_common.h>
572aaaabd4SRuslan Bukin
582aaaabd4SRuslan Bukin #define SPI_FIFO_SIZE 4
592aaaabd4SRuslan Bukin
602aaaabd4SRuslan Bukin #define SPI_MCR 0x00 /* Module Configuration */
612aaaabd4SRuslan Bukin #define MCR_MSTR (1 << 31) /* Master/Slave Mode Select */
622aaaabd4SRuslan Bukin #define MCR_CONT_SCKE (1 << 30) /* Continuous SCK Enable */
632aaaabd4SRuslan Bukin #define MCR_FRZ (1 << 27) /* Freeze */
642aaaabd4SRuslan Bukin #define MCR_PCSIS_S 16 /* Peripheral Chip Select */
652aaaabd4SRuslan Bukin #define MCR_PCSIS_M 0x3f
662aaaabd4SRuslan Bukin #define MCR_MDIS (1 << 14) /* Module Disable */
672aaaabd4SRuslan Bukin #define MCR_CLR_TXF (1 << 11) /* Clear TX FIFO */
682aaaabd4SRuslan Bukin #define MCR_CLR_RXF (1 << 10) /* Clear RX FIFO */
692aaaabd4SRuslan Bukin #define MCR_HALT (1 << 0) /* Starts and stops SPI transfers */
702aaaabd4SRuslan Bukin #define SPI_TCR 0x08 /* Transfer Count */
712aaaabd4SRuslan Bukin #define SPI_CTAR0 0x0C /* Clock and Transfer Attributes */
722aaaabd4SRuslan Bukin #define SPI_CTAR0_SLAVE 0x0C /* Clock and Transfer Attributes */
732aaaabd4SRuslan Bukin #define SPI_CTAR1 0x10 /* Clock and Transfer Attributes */
742aaaabd4SRuslan Bukin #define SPI_CTAR2 0x14 /* Clock and Transfer Attributes */
752aaaabd4SRuslan Bukin #define SPI_CTAR3 0x18 /* Clock and Transfer Attributes */
762aaaabd4SRuslan Bukin #define CTAR_FMSZ_M 0xf
772aaaabd4SRuslan Bukin #define CTAR_FMSZ_S 27 /* Frame Size */
782aaaabd4SRuslan Bukin #define CTAR_FMSZ_8 0x7 /* 8 bits */
792aaaabd4SRuslan Bukin #define CTAR_CPOL (1 << 26) /* Clock Polarity */
802aaaabd4SRuslan Bukin #define CTAR_CPHA (1 << 25) /* Clock Phase */
812aaaabd4SRuslan Bukin #define CTAR_LSBFE (1 << 24) /* Less significant bit first */
822aaaabd4SRuslan Bukin #define CTAR_PCSSCK_M 0x3
832aaaabd4SRuslan Bukin #define CTAR_PCSSCK_S 22 /* PCS to SCK Delay Prescaler */
842aaaabd4SRuslan Bukin #define CTAR_PBR_M 0x3
852aaaabd4SRuslan Bukin #define CTAR_PBR_S 16 /* Baud Rate Prescaler */
862aaaabd4SRuslan Bukin #define CTAR_PBR_7 0x3 /* Divide by 7 */
872aaaabd4SRuslan Bukin #define CTAR_CSSCK_M 0xf
882aaaabd4SRuslan Bukin #define CTAR_CSSCK_S 12 /* PCS to SCK Delay Scaler */
892aaaabd4SRuslan Bukin #define CTAR_BR_M 0xf
902aaaabd4SRuslan Bukin #define CTAR_BR_S 0 /* Baud Rate Scaler */
912aaaabd4SRuslan Bukin #define SPI_SR 0x2C /* Status Register */
922aaaabd4SRuslan Bukin #define SR_TCF (1 << 31) /* Transfer Complete Flag */
932aaaabd4SRuslan Bukin #define SR_EOQF (1 << 28) /* End of Queue Flag */
942aaaabd4SRuslan Bukin #define SR_TFFF (1 << 25) /* Transmit FIFO Fill Flag */
952aaaabd4SRuslan Bukin #define SR_RFDF (1 << 17) /* Receive FIFO Drain Flag */
962aaaabd4SRuslan Bukin #define SPI_RSER 0x30 /* DMA/Interrupt Select */
972aaaabd4SRuslan Bukin #define RSER_EOQF_RE (1 << 28) /* Finished Request Enable */
982aaaabd4SRuslan Bukin #define SPI_PUSHR 0x34 /* PUSH TX FIFO In Master Mode */
992aaaabd4SRuslan Bukin #define PUSHR_CONT (1 << 31) /* Continuous Peripheral CS */
1002aaaabd4SRuslan Bukin #define PUSHR_EOQ (1 << 27) /* End Of Queue */
1012aaaabd4SRuslan Bukin #define PUSHR_CTCNT (1 << 26) /* Clear Transfer Counter */
1022aaaabd4SRuslan Bukin #define PUSHR_PCS_M 0x3f
1032aaaabd4SRuslan Bukin #define PUSHR_PCS_S 16 /* Select PCS signals */
1042aaaabd4SRuslan Bukin
1052aaaabd4SRuslan Bukin #define SPI_PUSHR_SLAVE 0x34 /* PUSH TX FIFO Register In Slave Mode */
1062aaaabd4SRuslan Bukin #define SPI_POPR 0x38 /* POP RX FIFO Register */
1072aaaabd4SRuslan Bukin #define SPI_TXFR0 0x3C /* Transmit FIFO Registers */
1082aaaabd4SRuslan Bukin #define SPI_TXFR1 0x40
1092aaaabd4SRuslan Bukin #define SPI_TXFR2 0x44
1102aaaabd4SRuslan Bukin #define SPI_TXFR3 0x48
1112aaaabd4SRuslan Bukin #define SPI_RXFR0 0x7C /* Receive FIFO Registers */
1122aaaabd4SRuslan Bukin #define SPI_RXFR1 0x80
1132aaaabd4SRuslan Bukin #define SPI_RXFR2 0x84
1142aaaabd4SRuslan Bukin #define SPI_RXFR3 0x88
1152aaaabd4SRuslan Bukin
1162aaaabd4SRuslan Bukin struct spi_softc {
1172aaaabd4SRuslan Bukin struct resource *res[2];
1182aaaabd4SRuslan Bukin bus_space_tag_t bst;
1192aaaabd4SRuslan Bukin bus_space_handle_t bsh;
1202aaaabd4SRuslan Bukin void *ih;
1212aaaabd4SRuslan Bukin };
1222aaaabd4SRuslan Bukin
1232aaaabd4SRuslan Bukin static struct resource_spec spi_spec[] = {
1242aaaabd4SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE },
1252aaaabd4SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE },
1262aaaabd4SRuslan Bukin { -1, 0 }
1272aaaabd4SRuslan Bukin };
1282aaaabd4SRuslan Bukin
1292aaaabd4SRuslan Bukin static int
spi_probe(device_t dev)1302aaaabd4SRuslan Bukin spi_probe(device_t dev)
1312aaaabd4SRuslan Bukin {
1322aaaabd4SRuslan Bukin
1332aaaabd4SRuslan Bukin if (!ofw_bus_status_okay(dev))
1342aaaabd4SRuslan Bukin return (ENXIO);
1352aaaabd4SRuslan Bukin
1362aaaabd4SRuslan Bukin if (!ofw_bus_is_compatible(dev, "fsl,mvf600-spi"))
1372aaaabd4SRuslan Bukin return (ENXIO);
1382aaaabd4SRuslan Bukin
1392aaaabd4SRuslan Bukin device_set_desc(dev, "Vybrid Family Serial Peripheral Interface");
1402aaaabd4SRuslan Bukin return (BUS_PROBE_DEFAULT);
1412aaaabd4SRuslan Bukin }
1422aaaabd4SRuslan Bukin
1432aaaabd4SRuslan Bukin static int
spi_attach(device_t dev)1442aaaabd4SRuslan Bukin spi_attach(device_t dev)
1452aaaabd4SRuslan Bukin {
1462aaaabd4SRuslan Bukin struct spi_softc *sc;
1472aaaabd4SRuslan Bukin uint32_t reg;
1482aaaabd4SRuslan Bukin
1492aaaabd4SRuslan Bukin sc = device_get_softc(dev);
1502aaaabd4SRuslan Bukin
1512aaaabd4SRuslan Bukin if (bus_alloc_resources(dev, spi_spec, sc->res)) {
1522aaaabd4SRuslan Bukin device_printf(dev, "could not allocate resources\n");
1532aaaabd4SRuslan Bukin return (ENXIO);
1542aaaabd4SRuslan Bukin }
1552aaaabd4SRuslan Bukin
1562aaaabd4SRuslan Bukin /* Memory interface */
1572aaaabd4SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]);
1582aaaabd4SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]);
1592aaaabd4SRuslan Bukin
1602aaaabd4SRuslan Bukin reg = READ4(sc, SPI_MCR);
1612aaaabd4SRuslan Bukin reg |= MCR_MSTR;
1622aaaabd4SRuslan Bukin reg &= ~(MCR_CONT_SCKE | MCR_MDIS | MCR_FRZ);
1632aaaabd4SRuslan Bukin reg &= ~(MCR_PCSIS_M << MCR_PCSIS_S);
1642aaaabd4SRuslan Bukin reg |= (MCR_PCSIS_M << MCR_PCSIS_S); /* PCS Active low */
1652aaaabd4SRuslan Bukin reg |= (MCR_CLR_TXF | MCR_CLR_RXF);
1662aaaabd4SRuslan Bukin WRITE4(sc, SPI_MCR, reg);
1672aaaabd4SRuslan Bukin
1682aaaabd4SRuslan Bukin reg = READ4(sc, SPI_RSER);
1692aaaabd4SRuslan Bukin reg |= RSER_EOQF_RE;
1702aaaabd4SRuslan Bukin WRITE4(sc, SPI_RSER, reg);
1712aaaabd4SRuslan Bukin
1722aaaabd4SRuslan Bukin reg = READ4(sc, SPI_MCR);
1732aaaabd4SRuslan Bukin reg &= ~MCR_HALT;
1742aaaabd4SRuslan Bukin WRITE4(sc, SPI_MCR, reg);
1752aaaabd4SRuslan Bukin
1762aaaabd4SRuslan Bukin reg = READ4(sc, SPI_CTAR0);
1772aaaabd4SRuslan Bukin reg &= ~(CTAR_FMSZ_M << CTAR_FMSZ_S);
1782aaaabd4SRuslan Bukin reg |= (CTAR_FMSZ_8 << CTAR_FMSZ_S);
1792aaaabd4SRuslan Bukin /*
1802aaaabd4SRuslan Bukin * TODO: calculate BR
1812aaaabd4SRuslan Bukin * SCK baud rate = ( fsys / PBR ) * (1 + DBR) / BR
1822aaaabd4SRuslan Bukin *
1832aaaabd4SRuslan Bukin * reg &= ~(CTAR_BR_M << CTAR_BR_S);
1842aaaabd4SRuslan Bukin */
1852aaaabd4SRuslan Bukin reg &= ~CTAR_CPOL; /* Polarity */
1862aaaabd4SRuslan Bukin reg |= CTAR_CPHA;
1872aaaabd4SRuslan Bukin /*
1882aaaabd4SRuslan Bukin * Set LSB (Less significant bit first)
1892aaaabd4SRuslan Bukin * must be used for some applications, e.g. some LCDs
1902aaaabd4SRuslan Bukin */
1912aaaabd4SRuslan Bukin reg |= CTAR_LSBFE;
1922aaaabd4SRuslan Bukin WRITE4(sc, SPI_CTAR0, reg);
1932aaaabd4SRuslan Bukin
1942aaaabd4SRuslan Bukin reg = READ4(sc, SPI_CTAR0);
1952aaaabd4SRuslan Bukin reg &= ~(CTAR_PBR_M << CTAR_PBR_S);
1962aaaabd4SRuslan Bukin reg |= (CTAR_PBR_7 << CTAR_PBR_S);
1972aaaabd4SRuslan Bukin WRITE4(sc, SPI_CTAR0, reg);
1982aaaabd4SRuslan Bukin
1992aaaabd4SRuslan Bukin device_add_child(dev, "spibus", 0);
2002aaaabd4SRuslan Bukin return (bus_generic_attach(dev));
2012aaaabd4SRuslan Bukin }
2022aaaabd4SRuslan Bukin
2032aaaabd4SRuslan Bukin static int
spi_txrx(struct spi_softc * sc,uint8_t * out_buf,uint8_t * in_buf,int bufsz,int cs)2042aaaabd4SRuslan Bukin spi_txrx(struct spi_softc *sc, uint8_t *out_buf,
2052aaaabd4SRuslan Bukin uint8_t *in_buf, int bufsz, int cs)
2062aaaabd4SRuslan Bukin {
2072aaaabd4SRuslan Bukin uint32_t reg, wreg;
2082aaaabd4SRuslan Bukin uint32_t txcnt;
2092aaaabd4SRuslan Bukin uint32_t i;
2102aaaabd4SRuslan Bukin
2112aaaabd4SRuslan Bukin txcnt = 0;
2122aaaabd4SRuslan Bukin
2132aaaabd4SRuslan Bukin for (i = 0; i < bufsz; i++) {
2142aaaabd4SRuslan Bukin txcnt++;
2152aaaabd4SRuslan Bukin wreg = out_buf[i];
2162aaaabd4SRuslan Bukin wreg |= PUSHR_CONT;
2172aaaabd4SRuslan Bukin wreg |= (cs << PUSHR_PCS_S);
2182aaaabd4SRuslan Bukin if (i == 0)
2192aaaabd4SRuslan Bukin wreg |= PUSHR_CTCNT;
2202aaaabd4SRuslan Bukin if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE)
2212aaaabd4SRuslan Bukin wreg |= PUSHR_EOQ;
2222aaaabd4SRuslan Bukin WRITE4(sc, SPI_PUSHR, wreg);
2232aaaabd4SRuslan Bukin
2242aaaabd4SRuslan Bukin if (i == (bufsz - 1) || txcnt == SPI_FIFO_SIZE) {
2252aaaabd4SRuslan Bukin txcnt = 0;
2262aaaabd4SRuslan Bukin
2272aaaabd4SRuslan Bukin /* Wait last entry in a queue to be transmitted */
2282aaaabd4SRuslan Bukin while((READ4(sc, SPI_SR) & SR_EOQF) == 0)
2292aaaabd4SRuslan Bukin continue;
2302aaaabd4SRuslan Bukin
2312aaaabd4SRuslan Bukin reg = READ4(sc, SPI_SR);
2322aaaabd4SRuslan Bukin reg |= (SR_TCF | SR_EOQF);
2332aaaabd4SRuslan Bukin WRITE4(sc, SPI_SR, reg);
2342aaaabd4SRuslan Bukin }
2352aaaabd4SRuslan Bukin
2362aaaabd4SRuslan Bukin /* Wait until RX FIFO is empty */
2372aaaabd4SRuslan Bukin while((READ4(sc, SPI_SR) & SR_RFDF) == 0)
2382aaaabd4SRuslan Bukin continue;
2392aaaabd4SRuslan Bukin
2402aaaabd4SRuslan Bukin in_buf[i] = READ1(sc, SPI_POPR);
2412aaaabd4SRuslan Bukin }
2422aaaabd4SRuslan Bukin
2432aaaabd4SRuslan Bukin return (0);
2442aaaabd4SRuslan Bukin }
2452aaaabd4SRuslan Bukin
2462aaaabd4SRuslan Bukin static int
spi_transfer(device_t dev,device_t child,struct spi_command * cmd)2472aaaabd4SRuslan Bukin spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
2482aaaabd4SRuslan Bukin {
2492aaaabd4SRuslan Bukin struct spi_softc *sc;
2502aaaabd4SRuslan Bukin uint32_t cs;
2512aaaabd4SRuslan Bukin
2522aaaabd4SRuslan Bukin sc = device_get_softc(dev);
2532aaaabd4SRuslan Bukin
2542aaaabd4SRuslan Bukin KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
2552aaaabd4SRuslan Bukin ("%s: TX/RX command sizes should be equal", __func__));
2562aaaabd4SRuslan Bukin KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
2572aaaabd4SRuslan Bukin ("%s: TX/RX data sizes should be equal", __func__));
2582aaaabd4SRuslan Bukin
2592aaaabd4SRuslan Bukin /* get the proper chip select */
2602aaaabd4SRuslan Bukin spibus_get_cs(child, &cs);
2612aaaabd4SRuslan Bukin
2627073d12cSEmmanuel Vadot cs &= ~SPIBUS_CS_HIGH;
2637073d12cSEmmanuel Vadot
2642aaaabd4SRuslan Bukin /* Command */
2652aaaabd4SRuslan Bukin spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs);
2662aaaabd4SRuslan Bukin
2672aaaabd4SRuslan Bukin /* Data */
2682aaaabd4SRuslan Bukin spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs);
2692aaaabd4SRuslan Bukin
2702aaaabd4SRuslan Bukin return (0);
2712aaaabd4SRuslan Bukin }
2722aaaabd4SRuslan Bukin
2732aaaabd4SRuslan Bukin static device_method_t spi_methods[] = {
2742aaaabd4SRuslan Bukin /* Device interface */
2752aaaabd4SRuslan Bukin DEVMETHOD(device_probe, spi_probe),
2762aaaabd4SRuslan Bukin DEVMETHOD(device_attach, spi_attach),
2772aaaabd4SRuslan Bukin /* SPI interface */
2782aaaabd4SRuslan Bukin DEVMETHOD(spibus_transfer, spi_transfer),
2792aaaabd4SRuslan Bukin { 0, 0 }
2802aaaabd4SRuslan Bukin };
2812aaaabd4SRuslan Bukin
2822aaaabd4SRuslan Bukin static driver_t spi_driver = {
2832aaaabd4SRuslan Bukin "spi",
2842aaaabd4SRuslan Bukin spi_methods,
2852aaaabd4SRuslan Bukin sizeof(struct spi_softc),
2862aaaabd4SRuslan Bukin };
2872aaaabd4SRuslan Bukin
288*ea538dabSJohn Baldwin DRIVER_MODULE(spi, simplebus, spi_driver, 0, 0);
289