xref: /freebsd/sys/arm/mv/mv_spi.c (revision 2e82757c)
12e82757cSLuiz Otavio O Souza /*-
22e82757cSLuiz Otavio O Souza  * Copyright (c) 2017-2018, Rubicon Communications, LLC (Netgate)
32e82757cSLuiz Otavio O Souza  * All rights reserved.
42e82757cSLuiz Otavio O Souza  *
52e82757cSLuiz Otavio O Souza  * Redistribution and use in source and binary forms, with or without
62e82757cSLuiz Otavio O Souza  * modification, are permitted provided that the following conditions
72e82757cSLuiz Otavio O Souza  * are met:
82e82757cSLuiz Otavio O Souza  * 1. Redistributions of source code must retain the above copyright
92e82757cSLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer.
102e82757cSLuiz Otavio O Souza  * 2. Redistributions in binary form must reproduce the above copyright
112e82757cSLuiz Otavio O Souza  *    notice, this list of conditions and the following disclaimer in the
122e82757cSLuiz Otavio O Souza  *    documentation and/or other materials provided with the distribution.
132e82757cSLuiz Otavio O Souza  *
142e82757cSLuiz Otavio O Souza  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
152e82757cSLuiz Otavio O Souza  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
162e82757cSLuiz Otavio O Souza  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
172e82757cSLuiz Otavio O Souza  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
182e82757cSLuiz Otavio O Souza  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
192e82757cSLuiz Otavio O Souza  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
202e82757cSLuiz Otavio O Souza  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
212e82757cSLuiz Otavio O Souza  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
222e82757cSLuiz Otavio O Souza  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
232e82757cSLuiz Otavio O Souza  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
242e82757cSLuiz Otavio O Souza  *
252e82757cSLuiz Otavio O Souza  */
262e82757cSLuiz Otavio O Souza 
272e82757cSLuiz Otavio O Souza #include <sys/cdefs.h>
282e82757cSLuiz Otavio O Souza __FBSDID("$FreeBSD$");
292e82757cSLuiz Otavio O Souza 
302e82757cSLuiz Otavio O Souza #include <sys/param.h>
312e82757cSLuiz Otavio O Souza #include <sys/systm.h>
322e82757cSLuiz Otavio O Souza #include <sys/bus.h>
332e82757cSLuiz Otavio O Souza 
342e82757cSLuiz Otavio O Souza #include <sys/kernel.h>
352e82757cSLuiz Otavio O Souza #include <sys/module.h>
362e82757cSLuiz Otavio O Souza #include <sys/rman.h>
372e82757cSLuiz Otavio O Souza 
382e82757cSLuiz Otavio O Souza #include <machine/bus.h>
392e82757cSLuiz Otavio O Souza #include <machine/resource.h>
402e82757cSLuiz Otavio O Souza #include <machine/intr.h>
412e82757cSLuiz Otavio O Souza 
422e82757cSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h>
432e82757cSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h>
442e82757cSLuiz Otavio O Souza #include <dev/spibus/spi.h>
452e82757cSLuiz Otavio O Souza #include <dev/spibus/spibusvar.h>
462e82757cSLuiz Otavio O Souza 
472e82757cSLuiz Otavio O Souza #include "spibus_if.h"
482e82757cSLuiz Otavio O Souza 
492e82757cSLuiz Otavio O Souza struct mv_spi_softc {
502e82757cSLuiz Otavio O Souza 	device_t		sc_dev;
512e82757cSLuiz Otavio O Souza 	struct mtx		sc_mtx;
522e82757cSLuiz Otavio O Souza 	struct resource		*sc_mem_res;
532e82757cSLuiz Otavio O Souza 	struct resource		*sc_irq_res;
542e82757cSLuiz Otavio O Souza 	struct spi_command	*sc_cmd;
552e82757cSLuiz Otavio O Souza 	bus_space_tag_t		sc_bst;
562e82757cSLuiz Otavio O Souza 	bus_space_handle_t	sc_bsh;
572e82757cSLuiz Otavio O Souza 	uint32_t		sc_len;
582e82757cSLuiz Otavio O Souza 	uint32_t		sc_read;
592e82757cSLuiz Otavio O Souza 	uint32_t		sc_flags;
602e82757cSLuiz Otavio O Souza 	uint32_t		sc_written;
612e82757cSLuiz Otavio O Souza 	void			*sc_intrhand;
622e82757cSLuiz Otavio O Souza };
632e82757cSLuiz Otavio O Souza 
642e82757cSLuiz Otavio O Souza #define	MV_SPI_BUSY		0x1
652e82757cSLuiz Otavio O Souza #define	MV_SPI_WRITE(_sc, _off, _val)		\
662e82757cSLuiz Otavio O Souza     bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
672e82757cSLuiz Otavio O Souza #define	MV_SPI_READ(_sc, _off)			\
682e82757cSLuiz Otavio O Souza     bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
692e82757cSLuiz Otavio O Souza #define	MV_SPI_LOCK(_sc)	mtx_lock(&(_sc)->sc_mtx)
702e82757cSLuiz Otavio O Souza #define	MV_SPI_UNLOCK(_sc)	mtx_unlock(&(_sc)->sc_mtx)
712e82757cSLuiz Otavio O Souza 
722e82757cSLuiz Otavio O Souza #define	MV_SPI_CONTROL		0
732e82757cSLuiz Otavio O Souza #define	MV_SPI_CTRL_CS_SHIFT		2
742e82757cSLuiz Otavio O Souza #define	MV_SPI_CTRL_SMEMREADY		(1 << 1)
752e82757cSLuiz Otavio O Souza #define	MV_SPI_CTRL_CS_ACTIVE		(1 << 0)
762e82757cSLuiz Otavio O Souza #define	MV_SPI_CONF		0x4
772e82757cSLuiz Otavio O Souza #define	MV_SPI_CONF_BYTELEN		(1 << 5)
782e82757cSLuiz Otavio O Souza #define	MV_SPI_DATAOUT		0x8
792e82757cSLuiz Otavio O Souza #define	MV_SPI_DATAIN		0xc
802e82757cSLuiz Otavio O Souza #define	MV_SPI_INTR_STAT	0x10
812e82757cSLuiz Otavio O Souza #define	MV_SPI_INTR_MASK	0x14
822e82757cSLuiz Otavio O Souza #define	MV_SPI_INTR_SMEMREADY		(1 << 0)
832e82757cSLuiz Otavio O Souza 
842e82757cSLuiz Otavio O Souza static struct ofw_compat_data compat_data[] = {
852e82757cSLuiz Otavio O Souza         {"marvell,armada-380-spi",	1},
862e82757cSLuiz Otavio O Souza         {NULL,                          0}
872e82757cSLuiz Otavio O Souza };
882e82757cSLuiz Otavio O Souza 
892e82757cSLuiz Otavio O Souza static void mv_spi_intr(void *);
902e82757cSLuiz Otavio O Souza 
912e82757cSLuiz Otavio O Souza static int
922e82757cSLuiz Otavio O Souza mv_spi_probe(device_t dev)
932e82757cSLuiz Otavio O Souza {
942e82757cSLuiz Otavio O Souza 
952e82757cSLuiz Otavio O Souza 	if (!ofw_bus_status_okay(dev))
962e82757cSLuiz Otavio O Souza 		return (ENXIO);
972e82757cSLuiz Otavio O Souza 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
982e82757cSLuiz Otavio O Souza 		return (ENXIO);
992e82757cSLuiz Otavio O Souza 
1002e82757cSLuiz Otavio O Souza 	device_set_desc(dev, "Marvell SPI controller");
1012e82757cSLuiz Otavio O Souza 
1022e82757cSLuiz Otavio O Souza 	return (BUS_PROBE_DEFAULT);
1032e82757cSLuiz Otavio O Souza }
1042e82757cSLuiz Otavio O Souza 
1052e82757cSLuiz Otavio O Souza static int
1062e82757cSLuiz Otavio O Souza mv_spi_attach(device_t dev)
1072e82757cSLuiz Otavio O Souza {
1082e82757cSLuiz Otavio O Souza 	struct mv_spi_softc *sc;
1092e82757cSLuiz Otavio O Souza 	int rid;
1102e82757cSLuiz Otavio O Souza 	uint32_t reg;
1112e82757cSLuiz Otavio O Souza 
1122e82757cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
1132e82757cSLuiz Otavio O Souza 	sc->sc_dev = dev;
1142e82757cSLuiz Otavio O Souza 
1152e82757cSLuiz Otavio O Souza 	rid = 0;
1162e82757cSLuiz Otavio O Souza 	sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1172e82757cSLuiz Otavio O Souza 	    RF_ACTIVE);
1182e82757cSLuiz Otavio O Souza 	if (!sc->sc_mem_res) {
1192e82757cSLuiz Otavio O Souza 		device_printf(dev, "cannot allocate memory window\n");
1202e82757cSLuiz Otavio O Souza 		return (ENXIO);
1212e82757cSLuiz Otavio O Souza 	}
1222e82757cSLuiz Otavio O Souza 
1232e82757cSLuiz Otavio O Souza 	sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
1242e82757cSLuiz Otavio O Souza 	sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
1252e82757cSLuiz Otavio O Souza 
1262e82757cSLuiz Otavio O Souza 	rid = 0;
1272e82757cSLuiz Otavio O Souza 	sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1282e82757cSLuiz Otavio O Souza 	    RF_ACTIVE);
1292e82757cSLuiz Otavio O Souza 	if (!sc->sc_irq_res) {
1302e82757cSLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1312e82757cSLuiz Otavio O Souza 		device_printf(dev, "cannot allocate interrupt\n");
1322e82757cSLuiz Otavio O Souza 		return (ENXIO);
1332e82757cSLuiz Otavio O Souza 	}
1342e82757cSLuiz Otavio O Souza 
1352e82757cSLuiz Otavio O Souza 	/* Deactivate the bus - just in case... */
1362e82757cSLuiz Otavio O Souza 	reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
1372e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
1382e82757cSLuiz Otavio O Souza 
1392e82757cSLuiz Otavio O Souza 	/* Disable the two bytes FIFO. */
1402e82757cSLuiz Otavio O Souza 	reg = MV_SPI_READ(sc, MV_SPI_CONF);
1412e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_CONF, reg & ~MV_SPI_CONF_BYTELEN);
1422e82757cSLuiz Otavio O Souza 
1432e82757cSLuiz Otavio O Souza 	/* Clear and disable interrupts. */
1442e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
1452e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
1462e82757cSLuiz Otavio O Souza 
1472e82757cSLuiz Otavio O Souza 	/* Hook up our interrupt handler. */
1482e82757cSLuiz Otavio O Souza 	if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
1492e82757cSLuiz Otavio O Souza 	    NULL, mv_spi_intr, sc, &sc->sc_intrhand)) {
1502e82757cSLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
1512e82757cSLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1522e82757cSLuiz Otavio O Souza 		device_printf(dev, "cannot setup the interrupt handler\n");
1532e82757cSLuiz Otavio O Souza 		return (ENXIO);
1542e82757cSLuiz Otavio O Souza 	}
1552e82757cSLuiz Otavio O Souza 
1562e82757cSLuiz Otavio O Souza 	mtx_init(&sc->sc_mtx, "mv_spi", NULL, MTX_DEF);
1572e82757cSLuiz Otavio O Souza 
1582e82757cSLuiz Otavio O Souza 	device_add_child(dev, "spibus", -1);
1592e82757cSLuiz Otavio O Souza 
1602e82757cSLuiz Otavio O Souza 	/* Probe and attach the spibus when interrupts are available. */
1612e82757cSLuiz Otavio O Souza 	config_intrhook_oneshot((ich_func_t)bus_generic_attach, dev);
1622e82757cSLuiz Otavio O Souza 
1632e82757cSLuiz Otavio O Souza 	return (0);
1642e82757cSLuiz Otavio O Souza }
1652e82757cSLuiz Otavio O Souza 
1662e82757cSLuiz Otavio O Souza static int
1672e82757cSLuiz Otavio O Souza mv_spi_detach(device_t dev)
1682e82757cSLuiz Otavio O Souza {
1692e82757cSLuiz Otavio O Souza 	struct mv_spi_softc *sc;
1702e82757cSLuiz Otavio O Souza 
1712e82757cSLuiz Otavio O Souza 	bus_generic_detach(dev);
1722e82757cSLuiz Otavio O Souza 
1732e82757cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
1742e82757cSLuiz Otavio O Souza 	mtx_destroy(&sc->sc_mtx);
1752e82757cSLuiz Otavio O Souza 	if (sc->sc_intrhand)
1762e82757cSLuiz Otavio O Souza 		bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
1772e82757cSLuiz Otavio O Souza 	if (sc->sc_irq_res)
1782e82757cSLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
1792e82757cSLuiz Otavio O Souza 	if (sc->sc_mem_res)
1802e82757cSLuiz Otavio O Souza 		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1812e82757cSLuiz Otavio O Souza 
1822e82757cSLuiz Otavio O Souza 	return (0);
1832e82757cSLuiz Otavio O Souza }
1842e82757cSLuiz Otavio O Souza 
1852e82757cSLuiz Otavio O Souza static __inline void
1862e82757cSLuiz Otavio O Souza mv_spi_rx_byte(struct mv_spi_softc *sc)
1872e82757cSLuiz Otavio O Souza {
1882e82757cSLuiz Otavio O Souza 	struct spi_command *cmd;
1892e82757cSLuiz Otavio O Souza 	uint32_t read;
1902e82757cSLuiz Otavio O Souza 	uint8_t *p;
1912e82757cSLuiz Otavio O Souza 
1922e82757cSLuiz Otavio O Souza 	cmd = sc->sc_cmd;
1932e82757cSLuiz Otavio O Souza 	p = (uint8_t *)cmd->rx_cmd;
1942e82757cSLuiz Otavio O Souza 	read = sc->sc_read++;
1952e82757cSLuiz Otavio O Souza 	if (read >= cmd->rx_cmd_sz) {
1962e82757cSLuiz Otavio O Souza 		p = (uint8_t *)cmd->rx_data;
1972e82757cSLuiz Otavio O Souza 		read -= cmd->rx_cmd_sz;
1982e82757cSLuiz Otavio O Souza 	}
1992e82757cSLuiz Otavio O Souza 	p[read] = MV_SPI_READ(sc, MV_SPI_DATAIN) & 0xff;
2002e82757cSLuiz Otavio O Souza }
2012e82757cSLuiz Otavio O Souza 
2022e82757cSLuiz Otavio O Souza static __inline void
2032e82757cSLuiz Otavio O Souza mv_spi_tx_byte(struct mv_spi_softc *sc)
2042e82757cSLuiz Otavio O Souza {
2052e82757cSLuiz Otavio O Souza 	struct spi_command *cmd;
2062e82757cSLuiz Otavio O Souza 	uint32_t written;
2072e82757cSLuiz Otavio O Souza 	uint8_t *p;
2082e82757cSLuiz Otavio O Souza 
2092e82757cSLuiz Otavio O Souza 	cmd = sc->sc_cmd;
2102e82757cSLuiz Otavio O Souza 	p = (uint8_t *)cmd->tx_cmd;
2112e82757cSLuiz Otavio O Souza 	written = sc->sc_written++;
2122e82757cSLuiz Otavio O Souza 	if (written >= cmd->tx_cmd_sz) {
2132e82757cSLuiz Otavio O Souza 		p = (uint8_t *)cmd->tx_data;
2142e82757cSLuiz Otavio O Souza 		written -= cmd->tx_cmd_sz;
2152e82757cSLuiz Otavio O Souza 	}
2162e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_DATAOUT, p[written]);
2172e82757cSLuiz Otavio O Souza }
2182e82757cSLuiz Otavio O Souza 
2192e82757cSLuiz Otavio O Souza static void
2202e82757cSLuiz Otavio O Souza mv_spi_intr(void *arg)
2212e82757cSLuiz Otavio O Souza {
2222e82757cSLuiz Otavio O Souza 	struct mv_spi_softc *sc;
2232e82757cSLuiz Otavio O Souza 
2242e82757cSLuiz Otavio O Souza 	sc = (struct mv_spi_softc *)arg;
2252e82757cSLuiz Otavio O Souza 	MV_SPI_LOCK(sc);
2262e82757cSLuiz Otavio O Souza 
2272e82757cSLuiz Otavio O Souza 	/* Filter stray interrupts. */
2282e82757cSLuiz Otavio O Souza 	if ((sc->sc_flags & MV_SPI_BUSY) == 0) {
2292e82757cSLuiz Otavio O Souza 		MV_SPI_UNLOCK(sc);
2302e82757cSLuiz Otavio O Souza 		return;
2312e82757cSLuiz Otavio O Souza 	}
2322e82757cSLuiz Otavio O Souza 
2332e82757cSLuiz Otavio O Souza 	/* RX */
2342e82757cSLuiz Otavio O Souza 	mv_spi_rx_byte(sc);
2352e82757cSLuiz Otavio O Souza 
2362e82757cSLuiz Otavio O Souza 	/* TX */
2372e82757cSLuiz Otavio O Souza 	mv_spi_tx_byte(sc);
2382e82757cSLuiz Otavio O Souza 
2392e82757cSLuiz Otavio O Souza 	/* Check for end of transfer. */
2402e82757cSLuiz Otavio O Souza 	if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
2412e82757cSLuiz Otavio O Souza 		wakeup(sc->sc_dev);
2422e82757cSLuiz Otavio O Souza 
2432e82757cSLuiz Otavio O Souza 	MV_SPI_UNLOCK(sc);
2442e82757cSLuiz Otavio O Souza }
2452e82757cSLuiz Otavio O Souza 
2462e82757cSLuiz Otavio O Souza static int
2472e82757cSLuiz Otavio O Souza mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
2482e82757cSLuiz Otavio O Souza {
2492e82757cSLuiz Otavio O Souza 	struct mv_spi_softc *sc;
2502e82757cSLuiz Otavio O Souza 	uint32_t cs, reg;
2512e82757cSLuiz Otavio O Souza 	int resid, timeout;
2522e82757cSLuiz Otavio O Souza 
2532e82757cSLuiz Otavio O Souza 	KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
2542e82757cSLuiz Otavio O Souza 	    ("TX/RX command sizes should be equal"));
2552e82757cSLuiz Otavio O Souza 	KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
2562e82757cSLuiz Otavio O Souza 	    ("TX/RX data sizes should be equal"));
2572e82757cSLuiz Otavio O Souza 
2582e82757cSLuiz Otavio O Souza 	/* Get the proper chip select for this child. */
2592e82757cSLuiz Otavio O Souza 	spibus_get_cs(child, &cs);
2602e82757cSLuiz Otavio O Souza 	cs &= ~SPIBUS_CS_HIGH;
2612e82757cSLuiz Otavio O Souza 
2622e82757cSLuiz Otavio O Souza 	sc = device_get_softc(dev);
2632e82757cSLuiz Otavio O Souza 	MV_SPI_LOCK(sc);
2642e82757cSLuiz Otavio O Souza 
2652e82757cSLuiz Otavio O Souza 	/* Wait until the controller is free. */
2662e82757cSLuiz Otavio O Souza 	while (sc->sc_flags & MV_SPI_BUSY)
2672e82757cSLuiz Otavio O Souza 		mtx_sleep(dev, &sc->sc_mtx, 0, "mv_spi", 0);
2682e82757cSLuiz Otavio O Souza 
2692e82757cSLuiz Otavio O Souza 	/* Now we have control over SPI controller. */
2702e82757cSLuiz Otavio O Souza 	sc->sc_flags = MV_SPI_BUSY;
2712e82757cSLuiz Otavio O Souza 
2722e82757cSLuiz Otavio O Souza 	/* Save a pointer to the SPI command. */
2732e82757cSLuiz Otavio O Souza 	sc->sc_cmd = cmd;
2742e82757cSLuiz Otavio O Souza 	sc->sc_read = 0;
2752e82757cSLuiz Otavio O Souza 	sc->sc_written = 0;
2762e82757cSLuiz Otavio O Souza 	sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
2772e82757cSLuiz Otavio O Souza 
2782e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_CONTROL, cs << MV_SPI_CTRL_CS_SHIFT);
2792e82757cSLuiz Otavio O Souza 	reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
2802e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
2812e82757cSLuiz Otavio O Souza 
2822e82757cSLuiz Otavio O Souza 	while ((resid = sc->sc_len - sc->sc_written) > 0) {
2832e82757cSLuiz Otavio O Souza 
2842e82757cSLuiz Otavio O Souza 		MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
2852e82757cSLuiz Otavio O Souza 
2862e82757cSLuiz Otavio O Souza 		/*
2872e82757cSLuiz Otavio O Souza 		 * Write to start the transmission and read the byte
2882e82757cSLuiz Otavio O Souza 		 * back when ready.
2892e82757cSLuiz Otavio O Souza 		 */
2902e82757cSLuiz Otavio O Souza 		mv_spi_tx_byte(sc);
2912e82757cSLuiz Otavio O Souza 		timeout = 1000;
2922e82757cSLuiz Otavio O Souza 		while (--timeout > 0) {
2932e82757cSLuiz Otavio O Souza 			reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
2942e82757cSLuiz Otavio O Souza 			if (reg & MV_SPI_CTRL_SMEMREADY)
2952e82757cSLuiz Otavio O Souza 				break;
2962e82757cSLuiz Otavio O Souza 			DELAY(1);
2972e82757cSLuiz Otavio O Souza 		}
2982e82757cSLuiz Otavio O Souza 		if (timeout == 0)
2992e82757cSLuiz Otavio O Souza 			break;
3002e82757cSLuiz Otavio O Souza 		mv_spi_rx_byte(sc);
3012e82757cSLuiz Otavio O Souza 	}
3022e82757cSLuiz Otavio O Souza 
3032e82757cSLuiz Otavio O Souza 	/* Stop the controller. */
3042e82757cSLuiz Otavio O Souza 	reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
3052e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
3062e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
3072e82757cSLuiz Otavio O Souza 	MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
3082e82757cSLuiz Otavio O Souza 
3092e82757cSLuiz Otavio O Souza 	/* Release the controller and wakeup the next thread waiting for it. */
3102e82757cSLuiz Otavio O Souza 	sc->sc_flags = 0;
3112e82757cSLuiz Otavio O Souza 	wakeup_one(dev);
3122e82757cSLuiz Otavio O Souza 	MV_SPI_UNLOCK(sc);
3132e82757cSLuiz Otavio O Souza 
3142e82757cSLuiz Otavio O Souza 	/*
3152e82757cSLuiz Otavio O Souza 	 * Check for transfer timeout.  The SPI controller doesn't
3162e82757cSLuiz Otavio O Souza 	 * return errors.
3172e82757cSLuiz Otavio O Souza 	 */
3182e82757cSLuiz Otavio O Souza 	return ((timeout == 0) ? EIO : 0);
3192e82757cSLuiz Otavio O Souza }
3202e82757cSLuiz Otavio O Souza 
3212e82757cSLuiz Otavio O Souza static phandle_t
3222e82757cSLuiz Otavio O Souza mv_spi_get_node(device_t bus, device_t dev)
3232e82757cSLuiz Otavio O Souza {
3242e82757cSLuiz Otavio O Souza 
3252e82757cSLuiz Otavio O Souza 	return (ofw_bus_get_node(bus));
3262e82757cSLuiz Otavio O Souza }
3272e82757cSLuiz Otavio O Souza 
3282e82757cSLuiz Otavio O Souza static device_method_t mv_spi_methods[] = {
3292e82757cSLuiz Otavio O Souza 	/* Device interface */
3302e82757cSLuiz Otavio O Souza 	DEVMETHOD(device_probe,		mv_spi_probe),
3312e82757cSLuiz Otavio O Souza 	DEVMETHOD(device_attach,	mv_spi_attach),
3322e82757cSLuiz Otavio O Souza 	DEVMETHOD(device_detach,	mv_spi_detach),
3332e82757cSLuiz Otavio O Souza 
3342e82757cSLuiz Otavio O Souza 	/* SPI interface */
3352e82757cSLuiz Otavio O Souza 	DEVMETHOD(spibus_transfer,	mv_spi_transfer),
3362e82757cSLuiz Otavio O Souza 
3372e82757cSLuiz Otavio O Souza 	/* ofw_bus interface */
3382e82757cSLuiz Otavio O Souza 	DEVMETHOD(ofw_bus_get_node,	mv_spi_get_node),
3392e82757cSLuiz Otavio O Souza 
3402e82757cSLuiz Otavio O Souza 	DEVMETHOD_END
3412e82757cSLuiz Otavio O Souza };
3422e82757cSLuiz Otavio O Souza 
3432e82757cSLuiz Otavio O Souza static devclass_t mv_spi_devclass;
3442e82757cSLuiz Otavio O Souza 
3452e82757cSLuiz Otavio O Souza static driver_t mv_spi_driver = {
3462e82757cSLuiz Otavio O Souza 	"spi",
3472e82757cSLuiz Otavio O Souza 	mv_spi_methods,
3482e82757cSLuiz Otavio O Souza 	sizeof(struct mv_spi_softc),
3492e82757cSLuiz Otavio O Souza };
3502e82757cSLuiz Otavio O Souza 
3512e82757cSLuiz Otavio O Souza DRIVER_MODULE(mv_spi, simplebus, mv_spi_driver, mv_spi_devclass, 0, 0);
352