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