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/param.h>
282e82757cSLuiz Otavio O Souza #include <sys/systm.h>
292e82757cSLuiz Otavio O Souza #include <sys/bus.h>
302e82757cSLuiz Otavio O Souza
312e82757cSLuiz Otavio O Souza #include <sys/kernel.h>
32e2e050c8SConrad Meyer #include <sys/lock.h>
332e82757cSLuiz Otavio O Souza #include <sys/module.h>
34e2e050c8SConrad Meyer #include <sys/mutex.h>
352e82757cSLuiz Otavio O Souza #include <sys/rman.h>
362e82757cSLuiz Otavio O Souza
372e82757cSLuiz Otavio O Souza #include <machine/bus.h>
382e82757cSLuiz Otavio O Souza #include <machine/resource.h>
392e82757cSLuiz Otavio O Souza #include <machine/intr.h>
402e82757cSLuiz Otavio O Souza
412e82757cSLuiz Otavio O Souza #include <dev/ofw/ofw_bus.h>
422e82757cSLuiz Otavio O Souza #include <dev/ofw/ofw_bus_subr.h>
432e82757cSLuiz Otavio O Souza #include <dev/spibus/spi.h>
442e82757cSLuiz Otavio O Souza #include <dev/spibus/spibusvar.h>
452e82757cSLuiz Otavio O Souza
46aaf1f854SLuiz Otavio O Souza #include <arm/mv/mvvar.h>
47aaf1f854SLuiz Otavio O Souza
482e82757cSLuiz Otavio O Souza #include "spibus_if.h"
492e82757cSLuiz Otavio O Souza
502e82757cSLuiz Otavio O Souza struct mv_spi_softc {
512e82757cSLuiz Otavio O Souza device_t sc_dev;
522e82757cSLuiz Otavio O Souza struct mtx sc_mtx;
532e82757cSLuiz Otavio O Souza struct resource *sc_mem_res;
542e82757cSLuiz Otavio O Souza struct resource *sc_irq_res;
552e82757cSLuiz Otavio O Souza struct spi_command *sc_cmd;
562e82757cSLuiz Otavio O Souza bus_space_tag_t sc_bst;
572e82757cSLuiz Otavio O Souza bus_space_handle_t sc_bsh;
582e82757cSLuiz Otavio O Souza uint32_t sc_len;
592e82757cSLuiz Otavio O Souza uint32_t sc_read;
602e82757cSLuiz Otavio O Souza uint32_t sc_flags;
612e82757cSLuiz Otavio O Souza uint32_t sc_written;
622e82757cSLuiz Otavio O Souza void *sc_intrhand;
632e82757cSLuiz Otavio O Souza };
642e82757cSLuiz Otavio O Souza
652e82757cSLuiz Otavio O Souza #define MV_SPI_BUSY 0x1
662e82757cSLuiz Otavio O Souza #define MV_SPI_WRITE(_sc, _off, _val) \
672e82757cSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off), (_val))
682e82757cSLuiz Otavio O Souza #define MV_SPI_READ(_sc, _off) \
692e82757cSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, (_off))
702e82757cSLuiz Otavio O Souza #define MV_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx)
712e82757cSLuiz Otavio O Souza #define MV_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx)
722e82757cSLuiz Otavio O Souza
732e82757cSLuiz Otavio O Souza #define MV_SPI_CONTROL 0
74aaf1f854SLuiz Otavio O Souza #define MV_SPI_CTRL_CS_MASK 7
752e82757cSLuiz Otavio O Souza #define MV_SPI_CTRL_CS_SHIFT 2
762e82757cSLuiz Otavio O Souza #define MV_SPI_CTRL_SMEMREADY (1 << 1)
772e82757cSLuiz Otavio O Souza #define MV_SPI_CTRL_CS_ACTIVE (1 << 0)
782e82757cSLuiz Otavio O Souza #define MV_SPI_CONF 0x4
79aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_MODE_SHIFT 12
80aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_MODE_MASK (3 << MV_SPI_CONF_MODE_SHIFT)
812e82757cSLuiz Otavio O Souza #define MV_SPI_CONF_BYTELEN (1 << 5)
82aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_SPR_MASK 0xf
83aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_SPPR_MASK 1
84aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_SPPR_SHIFT 4
85aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_SPPRHI_MASK 3
86aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_SPPRHI_SHIFT 6
87aaf1f854SLuiz Otavio O Souza #define MV_SPI_CONF_CLOCK_MASK \
88aaf1f854SLuiz Otavio O Souza ((MV_SPI_CONF_CLOCK_SPPRHI_MASK << MV_SPI_CONF_CLOCK_SPPRHI_SHIFT) | \
89aaf1f854SLuiz Otavio O Souza (MV_SPI_CONF_CLOCK_SPPR_MASK << MV_SPI_CONF_CLOCK_SPPR_SHIFT) | \
90aaf1f854SLuiz Otavio O Souza MV_SPI_CONF_CLOCK_SPR_MASK)
912e82757cSLuiz Otavio O Souza #define MV_SPI_DATAOUT 0x8
922e82757cSLuiz Otavio O Souza #define MV_SPI_DATAIN 0xc
932e82757cSLuiz Otavio O Souza #define MV_SPI_INTR_STAT 0x10
942e82757cSLuiz Otavio O Souza #define MV_SPI_INTR_MASK 0x14
952e82757cSLuiz Otavio O Souza #define MV_SPI_INTR_SMEMREADY (1 << 0)
962e82757cSLuiz Otavio O Souza
972e82757cSLuiz Otavio O Souza static struct ofw_compat_data compat_data[] = {
982e82757cSLuiz Otavio O Souza {"marvell,armada-380-spi", 1},
992e82757cSLuiz Otavio O Souza {NULL, 0}
1002e82757cSLuiz Otavio O Souza };
1012e82757cSLuiz Otavio O Souza
1022e82757cSLuiz Otavio O Souza static void mv_spi_intr(void *);
1032e82757cSLuiz Otavio O Souza
1042e82757cSLuiz Otavio O Souza static int
mv_spi_probe(device_t dev)1052e82757cSLuiz Otavio O Souza mv_spi_probe(device_t dev)
1062e82757cSLuiz Otavio O Souza {
1072e82757cSLuiz Otavio O Souza
1082e82757cSLuiz Otavio O Souza if (!ofw_bus_status_okay(dev))
1092e82757cSLuiz Otavio O Souza return (ENXIO);
1102e82757cSLuiz Otavio O Souza if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
1112e82757cSLuiz Otavio O Souza return (ENXIO);
1122e82757cSLuiz Otavio O Souza
1132e82757cSLuiz Otavio O Souza device_set_desc(dev, "Marvell SPI controller");
1142e82757cSLuiz Otavio O Souza
1152e82757cSLuiz Otavio O Souza return (BUS_PROBE_DEFAULT);
1162e82757cSLuiz Otavio O Souza }
1172e82757cSLuiz Otavio O Souza
1182e82757cSLuiz Otavio O Souza static int
mv_spi_attach(device_t dev)1192e82757cSLuiz Otavio O Souza mv_spi_attach(device_t dev)
1202e82757cSLuiz Otavio O Souza {
1212e82757cSLuiz Otavio O Souza struct mv_spi_softc *sc;
1222e82757cSLuiz Otavio O Souza int rid;
1232e82757cSLuiz Otavio O Souza uint32_t reg;
1242e82757cSLuiz Otavio O Souza
1252e82757cSLuiz Otavio O Souza sc = device_get_softc(dev);
1262e82757cSLuiz Otavio O Souza sc->sc_dev = dev;
1272e82757cSLuiz Otavio O Souza
1282e82757cSLuiz Otavio O Souza rid = 0;
1292e82757cSLuiz Otavio O Souza sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
1302e82757cSLuiz Otavio O Souza RF_ACTIVE);
1312e82757cSLuiz Otavio O Souza if (!sc->sc_mem_res) {
1322e82757cSLuiz Otavio O Souza device_printf(dev, "cannot allocate memory window\n");
1332e82757cSLuiz Otavio O Souza return (ENXIO);
1342e82757cSLuiz Otavio O Souza }
1352e82757cSLuiz Otavio O Souza
1362e82757cSLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_mem_res);
1372e82757cSLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res);
1382e82757cSLuiz Otavio O Souza
1392e82757cSLuiz Otavio O Souza rid = 0;
1402e82757cSLuiz Otavio O Souza sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1412e82757cSLuiz Otavio O Souza RF_ACTIVE);
1422e82757cSLuiz Otavio O Souza if (!sc->sc_irq_res) {
1432e82757cSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1442e82757cSLuiz Otavio O Souza device_printf(dev, "cannot allocate interrupt\n");
1452e82757cSLuiz Otavio O Souza return (ENXIO);
1462e82757cSLuiz Otavio O Souza }
1472e82757cSLuiz Otavio O Souza
1482e82757cSLuiz Otavio O Souza /* Deactivate the bus - just in case... */
1492e82757cSLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
1502e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
1512e82757cSLuiz Otavio O Souza
1522e82757cSLuiz Otavio O Souza /* Disable the two bytes FIFO. */
1532e82757cSLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONF);
1542e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONF, reg & ~MV_SPI_CONF_BYTELEN);
1552e82757cSLuiz Otavio O Souza
1562e82757cSLuiz Otavio O Souza /* Clear and disable interrupts. */
1572e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
1582e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
1592e82757cSLuiz Otavio O Souza
1602e82757cSLuiz Otavio O Souza /* Hook up our interrupt handler. */
1612e82757cSLuiz Otavio O Souza if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
1622e82757cSLuiz Otavio O Souza NULL, mv_spi_intr, sc, &sc->sc_intrhand)) {
1632e82757cSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
1642e82757cSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1652e82757cSLuiz Otavio O Souza device_printf(dev, "cannot setup the interrupt handler\n");
1662e82757cSLuiz Otavio O Souza return (ENXIO);
1672e82757cSLuiz Otavio O Souza }
1682e82757cSLuiz Otavio O Souza
1692e82757cSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "mv_spi", NULL, MTX_DEF);
1702e82757cSLuiz Otavio O Souza
1715b56413dSWarner Losh device_add_child(dev, "spibus", DEVICE_UNIT_ANY);
1722e82757cSLuiz Otavio O Souza
1732e82757cSLuiz Otavio O Souza /* Probe and attach the spibus when interrupts are available. */
174*34f5de82SJohn Baldwin bus_delayed_attach_children(dev);
175*34f5de82SJohn Baldwin return (0);
1762e82757cSLuiz Otavio O Souza }
1772e82757cSLuiz Otavio O Souza
1782e82757cSLuiz Otavio O Souza static int
mv_spi_detach(device_t dev)1792e82757cSLuiz Otavio O Souza mv_spi_detach(device_t dev)
1802e82757cSLuiz Otavio O Souza {
1812e82757cSLuiz Otavio O Souza struct mv_spi_softc *sc;
1822e82757cSLuiz Otavio O Souza
1832e82757cSLuiz Otavio O Souza bus_generic_detach(dev);
1842e82757cSLuiz Otavio O Souza
1852e82757cSLuiz Otavio O Souza sc = device_get_softc(dev);
1862e82757cSLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx);
1872e82757cSLuiz Otavio O Souza if (sc->sc_intrhand)
1882e82757cSLuiz Otavio O Souza bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand);
1892e82757cSLuiz Otavio O Souza if (sc->sc_irq_res)
1902e82757cSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res);
1912e82757cSLuiz Otavio O Souza if (sc->sc_mem_res)
1922e82757cSLuiz Otavio O Souza bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res);
1932e82757cSLuiz Otavio O Souza
1942e82757cSLuiz Otavio O Souza return (0);
1952e82757cSLuiz Otavio O Souza }
1962e82757cSLuiz Otavio O Souza
1972e82757cSLuiz Otavio O Souza static __inline void
mv_spi_rx_byte(struct mv_spi_softc * sc)1982e82757cSLuiz Otavio O Souza mv_spi_rx_byte(struct mv_spi_softc *sc)
1992e82757cSLuiz Otavio O Souza {
2002e82757cSLuiz Otavio O Souza struct spi_command *cmd;
2012e82757cSLuiz Otavio O Souza uint32_t read;
2022e82757cSLuiz Otavio O Souza uint8_t *p;
2032e82757cSLuiz Otavio O Souza
2042e82757cSLuiz Otavio O Souza cmd = sc->sc_cmd;
2052e82757cSLuiz Otavio O Souza p = (uint8_t *)cmd->rx_cmd;
2062e82757cSLuiz Otavio O Souza read = sc->sc_read++;
2072e82757cSLuiz Otavio O Souza if (read >= cmd->rx_cmd_sz) {
2082e82757cSLuiz Otavio O Souza p = (uint8_t *)cmd->rx_data;
2092e82757cSLuiz Otavio O Souza read -= cmd->rx_cmd_sz;
2102e82757cSLuiz Otavio O Souza }
2112e82757cSLuiz Otavio O Souza p[read] = MV_SPI_READ(sc, MV_SPI_DATAIN) & 0xff;
2122e82757cSLuiz Otavio O Souza }
2132e82757cSLuiz Otavio O Souza
2142e82757cSLuiz Otavio O Souza static __inline void
mv_spi_tx_byte(struct mv_spi_softc * sc)2152e82757cSLuiz Otavio O Souza mv_spi_tx_byte(struct mv_spi_softc *sc)
2162e82757cSLuiz Otavio O Souza {
2172e82757cSLuiz Otavio O Souza struct spi_command *cmd;
2182e82757cSLuiz Otavio O Souza uint32_t written;
2192e82757cSLuiz Otavio O Souza uint8_t *p;
2202e82757cSLuiz Otavio O Souza
2212e82757cSLuiz Otavio O Souza cmd = sc->sc_cmd;
2222e82757cSLuiz Otavio O Souza p = (uint8_t *)cmd->tx_cmd;
2232e82757cSLuiz Otavio O Souza written = sc->sc_written++;
2242e82757cSLuiz Otavio O Souza if (written >= cmd->tx_cmd_sz) {
2252e82757cSLuiz Otavio O Souza p = (uint8_t *)cmd->tx_data;
2262e82757cSLuiz Otavio O Souza written -= cmd->tx_cmd_sz;
2272e82757cSLuiz Otavio O Souza }
2282e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_DATAOUT, p[written]);
2292e82757cSLuiz Otavio O Souza }
2302e82757cSLuiz Otavio O Souza
2312e82757cSLuiz Otavio O Souza static void
mv_spi_intr(void * arg)2322e82757cSLuiz Otavio O Souza mv_spi_intr(void *arg)
2332e82757cSLuiz Otavio O Souza {
2342e82757cSLuiz Otavio O Souza struct mv_spi_softc *sc;
2352e82757cSLuiz Otavio O Souza
2362e82757cSLuiz Otavio O Souza sc = (struct mv_spi_softc *)arg;
2372e82757cSLuiz Otavio O Souza MV_SPI_LOCK(sc);
2382e82757cSLuiz Otavio O Souza
2392e82757cSLuiz Otavio O Souza /* Filter stray interrupts. */
2402e82757cSLuiz Otavio O Souza if ((sc->sc_flags & MV_SPI_BUSY) == 0) {
2412e82757cSLuiz Otavio O Souza MV_SPI_UNLOCK(sc);
2422e82757cSLuiz Otavio O Souza return;
2432e82757cSLuiz Otavio O Souza }
2442e82757cSLuiz Otavio O Souza
2452e82757cSLuiz Otavio O Souza /* RX */
2462e82757cSLuiz Otavio O Souza mv_spi_rx_byte(sc);
2472e82757cSLuiz Otavio O Souza
2482e82757cSLuiz Otavio O Souza /* TX */
2492e82757cSLuiz Otavio O Souza mv_spi_tx_byte(sc);
2502e82757cSLuiz Otavio O Souza
2512e82757cSLuiz Otavio O Souza /* Check for end of transfer. */
2522e82757cSLuiz Otavio O Souza if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len)
2532e82757cSLuiz Otavio O Souza wakeup(sc->sc_dev);
2542e82757cSLuiz Otavio O Souza
2552e82757cSLuiz Otavio O Souza MV_SPI_UNLOCK(sc);
2562e82757cSLuiz Otavio O Souza }
2572e82757cSLuiz Otavio O Souza
2582e82757cSLuiz Otavio O Souza static int
mv_spi_psc_calc(uint32_t clock,uint32_t * spr,uint32_t * sppr)259aaf1f854SLuiz Otavio O Souza mv_spi_psc_calc(uint32_t clock, uint32_t *spr, uint32_t *sppr)
260aaf1f854SLuiz Otavio O Souza {
261aaf1f854SLuiz Otavio O Souza uint32_t divider, tclk;
262aaf1f854SLuiz Otavio O Souza
263aaf1f854SLuiz Otavio O Souza tclk = get_tclk_armada38x();
264aaf1f854SLuiz Otavio O Souza for (*spr = 2; *spr <= 15; (*spr)++) {
265aaf1f854SLuiz Otavio O Souza for (*sppr = 0; *sppr <= 7; (*sppr)++) {
266aaf1f854SLuiz Otavio O Souza divider = *spr * (1 << *sppr);
267aaf1f854SLuiz Otavio O Souza if (tclk / divider <= clock)
268aaf1f854SLuiz Otavio O Souza return (0);
269aaf1f854SLuiz Otavio O Souza }
270aaf1f854SLuiz Otavio O Souza }
271aaf1f854SLuiz Otavio O Souza
272aaf1f854SLuiz Otavio O Souza return (EINVAL);
273aaf1f854SLuiz Otavio O Souza }
274aaf1f854SLuiz Otavio O Souza
275aaf1f854SLuiz Otavio O Souza static int
mv_spi_transfer(device_t dev,device_t child,struct spi_command * cmd)2762e82757cSLuiz Otavio O Souza mv_spi_transfer(device_t dev, device_t child, struct spi_command *cmd)
2772e82757cSLuiz Otavio O Souza {
2782e82757cSLuiz Otavio O Souza struct mv_spi_softc *sc;
279aaf1f854SLuiz Otavio O Souza uint32_t clock, cs, mode, reg, spr, sppr;
2802e82757cSLuiz Otavio O Souza int resid, timeout;
2812e82757cSLuiz Otavio O Souza
2822e82757cSLuiz Otavio O Souza KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz,
2832e82757cSLuiz Otavio O Souza ("TX/RX command sizes should be equal"));
2842e82757cSLuiz Otavio O Souza KASSERT(cmd->tx_data_sz == cmd->rx_data_sz,
2852e82757cSLuiz Otavio O Souza ("TX/RX data sizes should be equal"));
2862e82757cSLuiz Otavio O Souza
287aaf1f854SLuiz Otavio O Souza /* Get the proper chip select, mode and clock for this transfer. */
2882e82757cSLuiz Otavio O Souza spibus_get_cs(child, &cs);
2892e82757cSLuiz Otavio O Souza cs &= ~SPIBUS_CS_HIGH;
290aaf1f854SLuiz Otavio O Souza spibus_get_mode(child, &mode);
291aaf1f854SLuiz Otavio O Souza if (mode > 3) {
292aaf1f854SLuiz Otavio O Souza device_printf(dev,
293aaf1f854SLuiz Otavio O Souza "Invalid mode %u requested by %s\n", mode,
294aaf1f854SLuiz Otavio O Souza device_get_nameunit(child));
295aaf1f854SLuiz Otavio O Souza return (EINVAL);
296aaf1f854SLuiz Otavio O Souza }
297aaf1f854SLuiz Otavio O Souza spibus_get_clock(child, &clock);
298aaf1f854SLuiz Otavio O Souza if (clock == 0 || mv_spi_psc_calc(clock, &spr, &sppr) != 0) {
299aaf1f854SLuiz Otavio O Souza device_printf(dev,
300aaf1f854SLuiz Otavio O Souza "Invalid clock %uHz requested by %s\n", clock,
301aaf1f854SLuiz Otavio O Souza device_get_nameunit(child));
302aaf1f854SLuiz Otavio O Souza return (EINVAL);
303aaf1f854SLuiz Otavio O Souza }
3042e82757cSLuiz Otavio O Souza
3052e82757cSLuiz Otavio O Souza sc = device_get_softc(dev);
3062e82757cSLuiz Otavio O Souza MV_SPI_LOCK(sc);
3072e82757cSLuiz Otavio O Souza
3082e82757cSLuiz Otavio O Souza /* Wait until the controller is free. */
3092e82757cSLuiz Otavio O Souza while (sc->sc_flags & MV_SPI_BUSY)
3102e82757cSLuiz Otavio O Souza mtx_sleep(dev, &sc->sc_mtx, 0, "mv_spi", 0);
3112e82757cSLuiz Otavio O Souza
3122e82757cSLuiz Otavio O Souza /* Now we have control over SPI controller. */
3132e82757cSLuiz Otavio O Souza sc->sc_flags = MV_SPI_BUSY;
3142e82757cSLuiz Otavio O Souza
3152e82757cSLuiz Otavio O Souza /* Save a pointer to the SPI command. */
3162e82757cSLuiz Otavio O Souza sc->sc_cmd = cmd;
3172e82757cSLuiz Otavio O Souza sc->sc_read = 0;
3182e82757cSLuiz Otavio O Souza sc->sc_written = 0;
3192e82757cSLuiz Otavio O Souza sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz;
3202e82757cSLuiz Otavio O Souza
321aaf1f854SLuiz Otavio O Souza /* Set SPI Mode and Clock. */
322aaf1f854SLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONF);
323aaf1f854SLuiz Otavio O Souza reg &= ~(MV_SPI_CONF_MODE_MASK | MV_SPI_CONF_CLOCK_MASK);
324aaf1f854SLuiz Otavio O Souza reg |= mode << MV_SPI_CONF_MODE_SHIFT;
325aaf1f854SLuiz Otavio O Souza reg |= spr & MV_SPI_CONF_CLOCK_SPR_MASK;
326aaf1f854SLuiz Otavio O Souza reg |= (sppr & MV_SPI_CONF_CLOCK_SPPR_MASK) <<
327aaf1f854SLuiz Otavio O Souza MV_SPI_CONF_CLOCK_SPPR_SHIFT;
328aaf1f854SLuiz Otavio O Souza reg |= (sppr & MV_SPI_CONF_CLOCK_SPPRHI_MASK) <<
329aaf1f854SLuiz Otavio O Souza MV_SPI_CONF_CLOCK_SPPRHI_SHIFT;
330aaf1f854SLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
331aaf1f854SLuiz Otavio O Souza
332aaf1f854SLuiz Otavio O Souza /* Set CS number and assert CS. */
333aaf1f854SLuiz Otavio O Souza reg = (cs & MV_SPI_CTRL_CS_MASK) << MV_SPI_CTRL_CS_SHIFT;
334aaf1f854SLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg);
3352e82757cSLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
3362e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg | MV_SPI_CTRL_CS_ACTIVE);
3372e82757cSLuiz Otavio O Souza
3382e82757cSLuiz Otavio O Souza while ((resid = sc->sc_len - sc->sc_written) > 0) {
3392e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
3402e82757cSLuiz Otavio O Souza
3412e82757cSLuiz Otavio O Souza /*
3422e82757cSLuiz Otavio O Souza * Write to start the transmission and read the byte
3432e82757cSLuiz Otavio O Souza * back when ready.
3442e82757cSLuiz Otavio O Souza */
3452e82757cSLuiz Otavio O Souza mv_spi_tx_byte(sc);
3462e82757cSLuiz Otavio O Souza timeout = 1000;
3472e82757cSLuiz Otavio O Souza while (--timeout > 0) {
3482e82757cSLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
3492e82757cSLuiz Otavio O Souza if (reg & MV_SPI_CTRL_SMEMREADY)
3502e82757cSLuiz Otavio O Souza break;
3512e82757cSLuiz Otavio O Souza DELAY(1);
3522e82757cSLuiz Otavio O Souza }
3532e82757cSLuiz Otavio O Souza if (timeout == 0)
3542e82757cSLuiz Otavio O Souza break;
3552e82757cSLuiz Otavio O Souza mv_spi_rx_byte(sc);
3562e82757cSLuiz Otavio O Souza }
3572e82757cSLuiz Otavio O Souza
3582e82757cSLuiz Otavio O Souza /* Stop the controller. */
3592e82757cSLuiz Otavio O Souza reg = MV_SPI_READ(sc, MV_SPI_CONTROL);
3602e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_CONTROL, reg & ~MV_SPI_CTRL_CS_ACTIVE);
3612e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_INTR_MASK, 0);
3622e82757cSLuiz Otavio O Souza MV_SPI_WRITE(sc, MV_SPI_INTR_STAT, 0);
3632e82757cSLuiz Otavio O Souza
3642e82757cSLuiz Otavio O Souza /* Release the controller and wakeup the next thread waiting for it. */
3652e82757cSLuiz Otavio O Souza sc->sc_flags = 0;
3662e82757cSLuiz Otavio O Souza wakeup_one(dev);
3672e82757cSLuiz Otavio O Souza MV_SPI_UNLOCK(sc);
3682e82757cSLuiz Otavio O Souza
3692e82757cSLuiz Otavio O Souza /*
3702e82757cSLuiz Otavio O Souza * Check for transfer timeout. The SPI controller doesn't
3712e82757cSLuiz Otavio O Souza * return errors.
3722e82757cSLuiz Otavio O Souza */
3732e82757cSLuiz Otavio O Souza return ((timeout == 0) ? EIO : 0);
3742e82757cSLuiz Otavio O Souza }
3752e82757cSLuiz Otavio O Souza
3762e82757cSLuiz Otavio O Souza static phandle_t
mv_spi_get_node(device_t bus,device_t dev)3772e82757cSLuiz Otavio O Souza mv_spi_get_node(device_t bus, device_t dev)
3782e82757cSLuiz Otavio O Souza {
3792e82757cSLuiz Otavio O Souza
3802e82757cSLuiz Otavio O Souza return (ofw_bus_get_node(bus));
3812e82757cSLuiz Otavio O Souza }
3822e82757cSLuiz Otavio O Souza
3832e82757cSLuiz Otavio O Souza static device_method_t mv_spi_methods[] = {
3842e82757cSLuiz Otavio O Souza /* Device interface */
3852e82757cSLuiz Otavio O Souza DEVMETHOD(device_probe, mv_spi_probe),
3862e82757cSLuiz Otavio O Souza DEVMETHOD(device_attach, mv_spi_attach),
3872e82757cSLuiz Otavio O Souza DEVMETHOD(device_detach, mv_spi_detach),
3882e82757cSLuiz Otavio O Souza
3892e82757cSLuiz Otavio O Souza /* SPI interface */
3902e82757cSLuiz Otavio O Souza DEVMETHOD(spibus_transfer, mv_spi_transfer),
3912e82757cSLuiz Otavio O Souza
3922e82757cSLuiz Otavio O Souza /* ofw_bus interface */
3932e82757cSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, mv_spi_get_node),
3942e82757cSLuiz Otavio O Souza
3952e82757cSLuiz Otavio O Souza DEVMETHOD_END
3962e82757cSLuiz Otavio O Souza };
3972e82757cSLuiz Otavio O Souza
3982e82757cSLuiz Otavio O Souza static driver_t mv_spi_driver = {
3992e82757cSLuiz Otavio O Souza "spi",
4002e82757cSLuiz Otavio O Souza mv_spi_methods,
4012e82757cSLuiz Otavio O Souza sizeof(struct mv_spi_softc),
4022e82757cSLuiz Otavio O Souza };
4032e82757cSLuiz Otavio O Souza
404a3b866cbSJohn Baldwin DRIVER_MODULE(mv_spi, simplebus, mv_spi_driver, 0, 0);
405