xref: /freebsd/sys/arm/freescale/vybrid/vf_spi.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
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