1257062Sloos/*- 2257062Sloos * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3257062Sloos * Copyright (c) 2013 Luiz Otavio O Souza <loos@freebsd.org> 4257062Sloos * All rights reserved. 5257062Sloos * 6257062Sloos * Redistribution and use in source and binary forms, with or without 7257062Sloos * modification, are permitted provided that the following conditions 8257062Sloos * are met: 9257062Sloos * 1. Redistributions of source code must retain the above copyright 10257062Sloos * notice, this list of conditions and the following disclaimer. 11257062Sloos * 2. Redistributions in binary form must reproduce the above copyright 12257062Sloos * notice, this list of conditions and the following disclaimer in the 13257062Sloos * documentation and/or other materials provided with the distribution. 14257062Sloos * 15257062Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16257062Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17257062Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18257062Sloos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19257062Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20257062Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21257062Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22257062Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23257062Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24257062Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25257062Sloos * SUCH DAMAGE. 26257062Sloos * 27257062Sloos */ 28257062Sloos#include <sys/cdefs.h> 29257062Sloos__FBSDID("$FreeBSD: stable/10/sys/arm/broadcom/bcm2835/bcm2835_spi.c 322724 2017-08-20 16:52:27Z marius $"); 30257062Sloos 31257062Sloos#include <sys/param.h> 32257062Sloos#include <sys/systm.h> 33257062Sloos#include <sys/bus.h> 34257062Sloos 35257062Sloos#include <sys/kernel.h> 36257062Sloos#include <sys/module.h> 37257062Sloos#include <sys/rman.h> 38257062Sloos#include <sys/lock.h> 39257062Sloos#include <sys/mutex.h> 40257062Sloos#include <sys/sysctl.h> 41257062Sloos 42257062Sloos#include <machine/bus.h> 43257062Sloos#include <machine/cpu.h> 44257062Sloos#include <machine/cpufunc.h> 45257062Sloos#include <machine/resource.h> 46257062Sloos#include <machine/fdt.h> 47257062Sloos#include <machine/intr.h> 48257062Sloos 49257062Sloos#include <dev/fdt/fdt_common.h> 50257062Sloos#include <dev/ofw/ofw_bus.h> 51257062Sloos#include <dev/ofw/ofw_bus_subr.h> 52257062Sloos 53257062Sloos#include <dev/spibus/spi.h> 54257062Sloos#include <dev/spibus/spibusvar.h> 55257062Sloos 56257062Sloos#include <arm/broadcom/bcm2835/bcm2835_gpio.h> 57257062Sloos#include <arm/broadcom/bcm2835/bcm2835_spireg.h> 58257062Sloos#include <arm/broadcom/bcm2835/bcm2835_spivar.h> 59257062Sloos 60257062Sloos#include "spibus_if.h" 61257062Sloos 62322724Smariusstatic struct ofw_compat_data compat_data[] = { 63322724Smarius {"broadcom,bcm2835-spi", 1}, 64322724Smarius {"brcm,bcm2835-spi", 1}, 65322724Smarius {NULL, 0} 66322724Smarius}; 67322724Smarius 68257062Sloosstatic void bcm_spi_intr(void *); 69257062Sloos 70257062Sloos#ifdef BCM_SPI_DEBUG 71257062Sloosstatic void 72257062Sloosbcm_spi_printr(device_t dev) 73257062Sloos{ 74257062Sloos struct bcm_spi_softc *sc; 75257062Sloos uint32_t reg; 76257062Sloos 77257062Sloos sc = device_get_softc(dev); 78257062Sloos reg = BCM_SPI_READ(sc, SPI_CS); 79257062Sloos device_printf(dev, "CS=%b\n", reg, 80257062Sloos "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL" 81257062Sloos "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN" 82257062Sloos "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1" 83257062Sloos "\30CSPOL2\31DMA_LEN\32LEN_LONG"); 84257062Sloos reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK; 85257062Sloos if (reg % 2) 86257062Sloos reg--; 87257062Sloos if (reg == 0) 88257062Sloos reg = 65536; 89257062Sloos device_printf(dev, "CLK=%uMhz/%d=%luhz\n", 90257062Sloos SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg); 91257062Sloos reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK; 92257062Sloos device_printf(dev, "DLEN=%d\n", reg); 93257062Sloos reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK; 94257062Sloos device_printf(dev, "LTOH=%d\n", reg); 95257062Sloos reg = BCM_SPI_READ(sc, SPI_DC); 96257062Sloos device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n", 97257062Sloos (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT, 98257062Sloos (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT, 99257062Sloos (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT, 100257062Sloos (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT); 101257062Sloos} 102257062Sloos#endif 103257062Sloos 104257062Sloosstatic void 105257062Sloosbcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask, 106257062Sloos uint32_t value) 107257062Sloos{ 108257062Sloos uint32_t reg; 109257062Sloos 110257062Sloos mtx_assert(&sc->sc_mtx, MA_OWNED); 111257062Sloos reg = BCM_SPI_READ(sc, off); 112257062Sloos reg &= ~mask; 113257062Sloos reg |= value; 114257062Sloos BCM_SPI_WRITE(sc, off, reg); 115257062Sloos} 116257062Sloos 117257062Sloosstatic int 118257062Sloosbcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) 119257062Sloos{ 120257062Sloos struct bcm_spi_softc *sc; 121257062Sloos uint32_t clk; 122257062Sloos int error; 123257062Sloos 124257062Sloos sc = (struct bcm_spi_softc *)arg1; 125257062Sloos 126257062Sloos BCM_SPI_LOCK(sc); 127257062Sloos clk = BCM_SPI_READ(sc, SPI_CLK); 128257062Sloos BCM_SPI_UNLOCK(sc); 129257062Sloos clk &= 0xffff; 130257062Sloos if (clk == 0) 131257062Sloos clk = 65536; 132257062Sloos clk = SPI_CORE_CLK / clk; 133257062Sloos 134257062Sloos error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); 135257062Sloos if (error != 0 || req->newptr == NULL) 136257062Sloos return (error); 137257062Sloos 138257062Sloos clk = SPI_CORE_CLK / clk; 139257062Sloos if (clk <= 1) 140257062Sloos clk = 2; 141257062Sloos else if (clk % 2) 142257062Sloos clk--; 143257062Sloos if (clk > 0xffff) 144257062Sloos clk = 0; 145257062Sloos BCM_SPI_LOCK(sc); 146257062Sloos BCM_SPI_WRITE(sc, SPI_CLK, clk); 147257062Sloos BCM_SPI_UNLOCK(sc); 148257062Sloos 149257062Sloos return (0); 150257062Sloos} 151257062Sloos 152257062Sloosstatic int 153257062Sloosbcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) 154257062Sloos{ 155257062Sloos struct bcm_spi_softc *sc; 156257062Sloos uint32_t reg; 157257062Sloos int error; 158257062Sloos 159257062Sloos sc = (struct bcm_spi_softc *)arg1; 160257062Sloos BCM_SPI_LOCK(sc); 161257062Sloos reg = BCM_SPI_READ(sc, SPI_CS); 162257062Sloos BCM_SPI_UNLOCK(sc); 163257062Sloos reg = (reg & bit) ? 1 : 0; 164257062Sloos 165257062Sloos error = sysctl_handle_int(oidp, ®, sizeof(reg), req); 166257062Sloos if (error != 0 || req->newptr == NULL) 167257062Sloos return (error); 168257062Sloos 169257062Sloos if (reg) 170257062Sloos reg = bit; 171257062Sloos BCM_SPI_LOCK(sc); 172257062Sloos bcm_spi_modifyreg(sc, SPI_CS, bit, reg); 173257062Sloos BCM_SPI_UNLOCK(sc); 174257062Sloos 175257062Sloos return (0); 176257062Sloos} 177257062Sloos 178257062Sloosstatic int 179257062Sloosbcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS) 180257062Sloos{ 181257062Sloos 182257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL)); 183257062Sloos} 184257062Sloos 185257062Sloosstatic int 186257062Sloosbcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS) 187257062Sloos{ 188257062Sloos 189257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA)); 190257062Sloos} 191257062Sloos 192257062Sloosstatic int 193257062Sloosbcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS) 194257062Sloos{ 195257062Sloos 196257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0)); 197257062Sloos} 198257062Sloos 199257062Sloosstatic int 200257062Sloosbcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) 201257062Sloos{ 202257062Sloos 203257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); 204257062Sloos} 205257062Sloos 206257062Sloosstatic void 207257062Sloosbcm_spi_sysctl_init(struct bcm_spi_softc *sc) 208257062Sloos{ 209257062Sloos struct sysctl_ctx_list *ctx; 210257062Sloos struct sysctl_oid *tree_node; 211257062Sloos struct sysctl_oid_list *tree; 212257062Sloos 213257062Sloos /* 214257062Sloos * Add system sysctl tree/handlers. 215257062Sloos */ 216257062Sloos ctx = device_get_sysctl_ctx(sc->sc_dev); 217257062Sloos tree_node = device_get_sysctl_tree(sc->sc_dev); 218257062Sloos tree = SYSCTL_CHILDREN(tree_node); 219257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", 220257062Sloos CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 221257062Sloos bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); 222257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", 223257062Sloos CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 224257062Sloos bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); 225257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", 226257062Sloos CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 227257062Sloos bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); 228257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", 229257062Sloos CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 230257062Sloos bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); 231257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", 232257062Sloos CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), 233257062Sloos bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); 234257062Sloos} 235257062Sloos 236257062Sloosstatic int 237257062Sloosbcm_spi_probe(device_t dev) 238257062Sloos{ 239257062Sloos 240266152Sian if (!ofw_bus_status_okay(dev)) 241266152Sian return (ENXIO); 242266152Sian 243322724Smarius if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 244257062Sloos return (ENXIO); 245257062Sloos 246257062Sloos device_set_desc(dev, "BCM2708/2835 SPI controller"); 247257062Sloos 248257062Sloos return (BUS_PROBE_DEFAULT); 249257062Sloos} 250257062Sloos 251257062Sloosstatic int 252257062Sloosbcm_spi_attach(device_t dev) 253257062Sloos{ 254257062Sloos struct bcm_spi_softc *sc; 255257062Sloos device_t gpio; 256257062Sloos int i, rid; 257257062Sloos 258257062Sloos if (device_get_unit(dev) != 0) { 259257062Sloos device_printf(dev, "only one SPI controller supported\n"); 260257062Sloos return (ENXIO); 261257062Sloos } 262257062Sloos 263257062Sloos sc = device_get_softc(dev); 264257062Sloos sc->sc_dev = dev; 265257062Sloos 266257062Sloos /* Configure the GPIO pins to ALT0 function to enable SPI the pins. */ 267257062Sloos gpio = devclass_get_device(devclass_find("gpio"), 0); 268257062Sloos if (!gpio) { 269257062Sloos device_printf(dev, "cannot find gpio0\n"); 270257062Sloos return (ENXIO); 271257062Sloos } 272257062Sloos for (i = 0; i < nitems(bcm_spi_pins); i++) 273257062Sloos bcm_gpio_set_alternate(gpio, bcm_spi_pins[i], BCM_GPIO_ALT0); 274257062Sloos 275257062Sloos rid = 0; 276257062Sloos sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 277257062Sloos RF_ACTIVE); 278257062Sloos if (!sc->sc_mem_res) { 279257062Sloos device_printf(dev, "cannot allocate memory window\n"); 280257062Sloos return (ENXIO); 281257062Sloos } 282257062Sloos 283257062Sloos sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 284257062Sloos sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 285257062Sloos 286257062Sloos rid = 0; 287257062Sloos sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 288257062Sloos RF_ACTIVE); 289257062Sloos if (!sc->sc_irq_res) { 290257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 291257062Sloos device_printf(dev, "cannot allocate interrupt\n"); 292257062Sloos return (ENXIO); 293257062Sloos } 294257062Sloos 295257062Sloos /* Hook up our interrupt handler. */ 296257062Sloos if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 297257062Sloos NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) { 298257062Sloos bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 299257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 300257062Sloos device_printf(dev, "cannot setup the interrupt handler\n"); 301257062Sloos return (ENXIO); 302257062Sloos } 303257062Sloos 304257062Sloos mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF); 305257062Sloos 306257062Sloos /* Add sysctl nodes. */ 307257062Sloos bcm_spi_sysctl_init(sc); 308257062Sloos 309257062Sloos#ifdef BCM_SPI_DEBUG 310257062Sloos bcm_spi_printr(dev); 311257062Sloos#endif 312257062Sloos 313257062Sloos /* 314257062Sloos * Enable the SPI controller. Clear the rx and tx FIFO. 315257062Sloos * Defaults to SPI mode 0. 316257062Sloos */ 317257062Sloos BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); 318257062Sloos 319257062Sloos /* Set the SPI clock to 500Khz. */ 320257062Sloos BCM_SPI_WRITE(sc, SPI_CLK, SPI_CORE_CLK / 500000); 321257062Sloos 322257062Sloos#ifdef BCM_SPI_DEBUG 323257062Sloos bcm_spi_printr(dev); 324257062Sloos#endif 325257062Sloos 326257062Sloos device_add_child(dev, "spibus", -1); 327257062Sloos 328257062Sloos return (bus_generic_attach(dev)); 329257062Sloos} 330257062Sloos 331257062Sloosstatic int 332257062Sloosbcm_spi_detach(device_t dev) 333257062Sloos{ 334257062Sloos struct bcm_spi_softc *sc; 335257062Sloos 336257062Sloos bus_generic_detach(dev); 337257062Sloos 338257062Sloos sc = device_get_softc(dev); 339257062Sloos mtx_destroy(&sc->sc_mtx); 340257062Sloos if (sc->sc_intrhand) 341257062Sloos bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); 342257062Sloos if (sc->sc_irq_res) 343257062Sloos bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 344257062Sloos if (sc->sc_mem_res) 345257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 346257062Sloos 347257062Sloos return (0); 348257062Sloos} 349257062Sloos 350257062Sloosstatic void 351257062Sloosbcm_spi_fill_fifo(struct bcm_spi_softc *sc) 352257062Sloos{ 353257062Sloos struct spi_command *cmd; 354257062Sloos uint32_t cs, written; 355257062Sloos uint8_t *data; 356257062Sloos 357257062Sloos cmd = sc->sc_cmd; 358257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); 359257062Sloos while (sc->sc_written < sc->sc_len && 360257062Sloos cs == (SPI_CS_TA | SPI_CS_TXD)) { 361257062Sloos data = (uint8_t *)cmd->tx_cmd; 362257062Sloos written = sc->sc_written++; 363257062Sloos if (written >= cmd->tx_cmd_sz) { 364257062Sloos data = (uint8_t *)cmd->tx_data; 365257062Sloos written -= cmd->tx_cmd_sz; 366257062Sloos } 367257062Sloos BCM_SPI_WRITE(sc, SPI_FIFO, data[written]); 368257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); 369257062Sloos } 370257062Sloos} 371257062Sloos 372257062Sloosstatic void 373257062Sloosbcm_spi_drain_fifo(struct bcm_spi_softc *sc) 374257062Sloos{ 375257062Sloos struct spi_command *cmd; 376257062Sloos uint32_t cs, read; 377257062Sloos uint8_t *data; 378257062Sloos 379257062Sloos cmd = sc->sc_cmd; 380257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; 381257062Sloos while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) { 382257062Sloos data = (uint8_t *)cmd->rx_cmd; 383257062Sloos read = sc->sc_read++; 384257062Sloos if (read >= cmd->rx_cmd_sz) { 385257062Sloos data = (uint8_t *)cmd->rx_data; 386257062Sloos read -= cmd->rx_cmd_sz; 387257062Sloos } 388257062Sloos data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff; 389257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; 390257062Sloos } 391257062Sloos} 392257062Sloos 393257062Sloosstatic void 394257062Sloosbcm_spi_intr(void *arg) 395257062Sloos{ 396257062Sloos struct bcm_spi_softc *sc; 397257062Sloos 398257062Sloos sc = (struct bcm_spi_softc *)arg; 399257062Sloos BCM_SPI_LOCK(sc); 400257062Sloos 401257062Sloos /* Filter stray interrupts. */ 402257062Sloos if ((sc->sc_flags & BCM_SPI_BUSY) == 0) { 403257062Sloos BCM_SPI_UNLOCK(sc); 404257062Sloos return; 405257062Sloos } 406257062Sloos 407257062Sloos /* TX - Fill up the FIFO. */ 408257062Sloos bcm_spi_fill_fifo(sc); 409257062Sloos 410257062Sloos /* RX - Drain the FIFO. */ 411257062Sloos bcm_spi_drain_fifo(sc); 412257062Sloos 413257062Sloos /* Check for end of transfer. */ 414257062Sloos if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { 415257062Sloos /* Disable interrupts and the SPI engine. */ 416257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 417257062Sloos SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); 418257062Sloos wakeup(sc->sc_dev); 419257062Sloos } 420257062Sloos 421257062Sloos BCM_SPI_UNLOCK(sc); 422257062Sloos} 423257062Sloos 424257062Sloosstatic int 425257062Sloosbcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 426257062Sloos{ 427257062Sloos struct bcm_spi_softc *sc; 428257062Sloos int cs, err; 429257062Sloos 430257062Sloos sc = device_get_softc(dev); 431257062Sloos 432257062Sloos KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 433257062Sloos ("TX/RX command sizes should be equal")); 434257062Sloos KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 435257062Sloos ("TX/RX data sizes should be equal")); 436257062Sloos 437322724Smarius /* Get the proper chip select for this child. */ 438322724Smarius spibus_get_cs(child, &cs); 439322724Smarius if (cs < 0 || cs > 2) { 440322724Smarius device_printf(dev, 441322724Smarius "Invalid chip select %d requested by %s\n", cs, 442322724Smarius device_get_nameunit(child)); 443322724Smarius return (EINVAL); 444322724Smarius } 445322724Smarius 446257062Sloos BCM_SPI_LOCK(sc); 447257062Sloos 448257062Sloos /* If the controller is in use wait until it is available. */ 449257062Sloos while (sc->sc_flags & BCM_SPI_BUSY) 450257062Sloos mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); 451257062Sloos 452257062Sloos /* Now we have control over SPI controller. */ 453257062Sloos sc->sc_flags = BCM_SPI_BUSY; 454257062Sloos 455257062Sloos /* Clear the FIFO. */ 456257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 457257062Sloos SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO, 458257062Sloos SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); 459257062Sloos 460257062Sloos /* Save a pointer to the SPI command. */ 461257062Sloos sc->sc_cmd = cmd; 462257062Sloos sc->sc_read = 0; 463257062Sloos sc->sc_written = 0; 464257062Sloos sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; 465257062Sloos 466257062Sloos /* 467257062Sloos * Set the CS for this transaction, enable interrupts and announce 468257062Sloos * we're ready to tx. This will kick off the first interrupt. 469257062Sloos */ 470257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 471257062Sloos SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 472257062Sloos cs | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); 473257062Sloos 474257062Sloos /* Wait for the transaction to complete. */ 475257062Sloos err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); 476257062Sloos 477257062Sloos /* Make sure the SPI engine and interrupts are disabled. */ 478257062Sloos bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); 479257062Sloos 480322724Smarius /* Release the controller and wakeup the next thread waiting for it. */ 481257062Sloos sc->sc_flags = 0; 482322724Smarius wakeup_one(dev); 483322724Smarius BCM_SPI_UNLOCK(sc); 484257062Sloos 485257062Sloos /* 486257062Sloos * Check for transfer timeout. The SPI controller doesn't 487257062Sloos * return errors. 488257062Sloos */ 489257062Sloos if (err == EWOULDBLOCK) { 490257062Sloos device_printf(sc->sc_dev, "SPI error\n"); 491257062Sloos err = EIO; 492257062Sloos } 493257062Sloos 494257062Sloos return (err); 495257062Sloos} 496257062Sloos 497257062Sloosstatic phandle_t 498257062Sloosbcm_spi_get_node(device_t bus, device_t dev) 499257062Sloos{ 500257062Sloos 501257062Sloos /* We only have one child, the SPI bus, which needs our own node. */ 502257062Sloos return (ofw_bus_get_node(bus)); 503257062Sloos} 504257062Sloos 505257062Sloosstatic device_method_t bcm_spi_methods[] = { 506257062Sloos /* Device interface */ 507257062Sloos DEVMETHOD(device_probe, bcm_spi_probe), 508257062Sloos DEVMETHOD(device_attach, bcm_spi_attach), 509257062Sloos DEVMETHOD(device_detach, bcm_spi_detach), 510257062Sloos 511257062Sloos /* SPI interface */ 512257062Sloos DEVMETHOD(spibus_transfer, bcm_spi_transfer), 513257062Sloos 514257062Sloos /* ofw_bus interface */ 515257062Sloos DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node), 516257062Sloos 517257062Sloos DEVMETHOD_END 518257062Sloos}; 519257062Sloos 520257062Sloosstatic devclass_t bcm_spi_devclass; 521257062Sloos 522257062Sloosstatic driver_t bcm_spi_driver = { 523257062Sloos "spi", 524257062Sloos bcm_spi_methods, 525257062Sloos sizeof(struct bcm_spi_softc), 526257062Sloos}; 527257062Sloos 528257062SloosDRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0); 529