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/11/sys/arm/broadcom/bcm2835/bcm2835_spi.c 346519 2019-04-22 04:02:16Z ian $"); 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/resource.h> 44257062Sloos#include <machine/intr.h> 45257062Sloos 46257062Sloos#include <dev/fdt/fdt_common.h> 47257062Sloos#include <dev/ofw/ofw_bus.h> 48257062Sloos#include <dev/ofw/ofw_bus_subr.h> 49257062Sloos 50257062Sloos#include <dev/spibus/spi.h> 51257062Sloos#include <dev/spibus/spibusvar.h> 52257062Sloos 53257062Sloos#include <arm/broadcom/bcm2835/bcm2835_gpio.h> 54257062Sloos#include <arm/broadcom/bcm2835/bcm2835_spireg.h> 55257062Sloos#include <arm/broadcom/bcm2835/bcm2835_spivar.h> 56257062Sloos 57257062Sloos#include "spibus_if.h" 58257062Sloos 59307575Sgonzostatic struct ofw_compat_data compat_data[] = { 60307575Sgonzo {"broadcom,bcm2835-spi", 1}, 61307575Sgonzo {"brcm,bcm2835-spi", 1}, 62307575Sgonzo {NULL, 0} 63307575Sgonzo}; 64307575Sgonzo 65257062Sloosstatic void bcm_spi_intr(void *); 66257062Sloos 67257062Sloos#ifdef BCM_SPI_DEBUG 68257062Sloosstatic void 69257062Sloosbcm_spi_printr(device_t dev) 70257062Sloos{ 71257062Sloos struct bcm_spi_softc *sc; 72257062Sloos uint32_t reg; 73257062Sloos 74257062Sloos sc = device_get_softc(dev); 75257062Sloos reg = BCM_SPI_READ(sc, SPI_CS); 76257062Sloos device_printf(dev, "CS=%b\n", reg, 77257062Sloos "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL" 78257062Sloos "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN" 79257062Sloos "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1" 80257062Sloos "\30CSPOL2\31DMA_LEN\32LEN_LONG"); 81257062Sloos reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK; 82257062Sloos if (reg % 2) 83257062Sloos reg--; 84257062Sloos if (reg == 0) 85257062Sloos reg = 65536; 86257062Sloos device_printf(dev, "CLK=%uMhz/%d=%luhz\n", 87257062Sloos SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg); 88257062Sloos reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK; 89257062Sloos device_printf(dev, "DLEN=%d\n", reg); 90257062Sloos reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK; 91257062Sloos device_printf(dev, "LTOH=%d\n", reg); 92257062Sloos reg = BCM_SPI_READ(sc, SPI_DC); 93257062Sloos device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n", 94257062Sloos (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT, 95257062Sloos (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT, 96257062Sloos (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT, 97257062Sloos (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT); 98257062Sloos} 99257062Sloos#endif 100257062Sloos 101257062Sloosstatic void 102257062Sloosbcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask, 103257062Sloos uint32_t value) 104257062Sloos{ 105257062Sloos uint32_t reg; 106257062Sloos 107257062Sloos mtx_assert(&sc->sc_mtx, MA_OWNED); 108257062Sloos reg = BCM_SPI_READ(sc, off); 109257062Sloos reg &= ~mask; 110257062Sloos reg |= value; 111257062Sloos BCM_SPI_WRITE(sc, off, reg); 112257062Sloos} 113257062Sloos 114257062Sloosstatic int 115257062Sloosbcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) 116257062Sloos{ 117257062Sloos struct bcm_spi_softc *sc; 118257062Sloos uint32_t clk; 119257062Sloos int error; 120257062Sloos 121257062Sloos sc = (struct bcm_spi_softc *)arg1; 122257062Sloos 123257062Sloos BCM_SPI_LOCK(sc); 124257062Sloos clk = BCM_SPI_READ(sc, SPI_CLK); 125257062Sloos BCM_SPI_UNLOCK(sc); 126257062Sloos clk &= 0xffff; 127257062Sloos if (clk == 0) 128257062Sloos clk = 65536; 129257062Sloos clk = SPI_CORE_CLK / clk; 130257062Sloos 131257062Sloos error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); 132257062Sloos if (error != 0 || req->newptr == NULL) 133257062Sloos return (error); 134257062Sloos 135257062Sloos return (0); 136257062Sloos} 137257062Sloos 138257062Sloosstatic int 139257062Sloosbcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) 140257062Sloos{ 141257062Sloos struct bcm_spi_softc *sc; 142257062Sloos uint32_t reg; 143257062Sloos int error; 144257062Sloos 145257062Sloos sc = (struct bcm_spi_softc *)arg1; 146257062Sloos BCM_SPI_LOCK(sc); 147257062Sloos reg = BCM_SPI_READ(sc, SPI_CS); 148257062Sloos BCM_SPI_UNLOCK(sc); 149257062Sloos reg = (reg & bit) ? 1 : 0; 150257062Sloos 151257062Sloos error = sysctl_handle_int(oidp, ®, sizeof(reg), req); 152257062Sloos if (error != 0 || req->newptr == NULL) 153257062Sloos return (error); 154257062Sloos 155257062Sloos return (0); 156257062Sloos} 157257062Sloos 158257062Sloosstatic int 159257062Sloosbcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS) 160257062Sloos{ 161257062Sloos 162257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL)); 163257062Sloos} 164257062Sloos 165257062Sloosstatic int 166257062Sloosbcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS) 167257062Sloos{ 168257062Sloos 169257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA)); 170257062Sloos} 171257062Sloos 172257062Sloosstatic int 173257062Sloosbcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS) 174257062Sloos{ 175257062Sloos 176257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0)); 177257062Sloos} 178257062Sloos 179257062Sloosstatic int 180257062Sloosbcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) 181257062Sloos{ 182257062Sloos 183257062Sloos return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); 184257062Sloos} 185257062Sloos 186346519Sianstatic int 187346519Sianbcm_spi_cspol2_proc(SYSCTL_HANDLER_ARGS) 188346519Sian{ 189346519Sian 190346519Sian return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL2)); 191346519Sian} 192346519Sian 193257062Sloosstatic void 194257062Sloosbcm_spi_sysctl_init(struct bcm_spi_softc *sc) 195257062Sloos{ 196257062Sloos struct sysctl_ctx_list *ctx; 197257062Sloos struct sysctl_oid *tree_node; 198257062Sloos struct sysctl_oid_list *tree; 199257062Sloos 200257062Sloos /* 201257062Sloos * Add system sysctl tree/handlers. 202257062Sloos */ 203257062Sloos ctx = device_get_sysctl_ctx(sc->sc_dev); 204257062Sloos tree_node = device_get_sysctl_tree(sc->sc_dev); 205257062Sloos tree = SYSCTL_CHILDREN(tree_node); 206257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", 207346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 208257062Sloos bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); 209257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", 210346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 211257062Sloos bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); 212257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", 213346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 214257062Sloos bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); 215257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", 216346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 217257062Sloos bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); 218257062Sloos SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", 219346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 220257062Sloos bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); 221346519Sian SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol2", 222346519Sian CTLFLAG_RD | CTLTYPE_UINT, sc, sizeof(*sc), 223346519Sian bcm_spi_cspol2_proc, "IU", "SPI BUS chip select 2 polarity"); 224257062Sloos} 225257062Sloos 226257062Sloosstatic int 227257062Sloosbcm_spi_probe(device_t dev) 228257062Sloos{ 229257062Sloos 230261410Sian if (!ofw_bus_status_okay(dev)) 231261410Sian return (ENXIO); 232261410Sian 233307575Sgonzo if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 234257062Sloos return (ENXIO); 235257062Sloos 236257062Sloos device_set_desc(dev, "BCM2708/2835 SPI controller"); 237257062Sloos 238257062Sloos return (BUS_PROBE_DEFAULT); 239257062Sloos} 240257062Sloos 241257062Sloosstatic int 242257062Sloosbcm_spi_attach(device_t dev) 243257062Sloos{ 244257062Sloos struct bcm_spi_softc *sc; 245257062Sloos device_t gpio; 246257062Sloos int i, rid; 247257062Sloos 248257062Sloos if (device_get_unit(dev) != 0) { 249257062Sloos device_printf(dev, "only one SPI controller supported\n"); 250257062Sloos return (ENXIO); 251257062Sloos } 252257062Sloos 253257062Sloos sc = device_get_softc(dev); 254257062Sloos sc->sc_dev = dev; 255257062Sloos 256257062Sloos /* Configure the GPIO pins to ALT0 function to enable SPI the pins. */ 257257062Sloos gpio = devclass_get_device(devclass_find("gpio"), 0); 258257062Sloos if (!gpio) { 259257062Sloos device_printf(dev, "cannot find gpio0\n"); 260257062Sloos return (ENXIO); 261257062Sloos } 262257062Sloos for (i = 0; i < nitems(bcm_spi_pins); i++) 263257062Sloos bcm_gpio_set_alternate(gpio, bcm_spi_pins[i], BCM_GPIO_ALT0); 264257062Sloos 265257062Sloos rid = 0; 266257062Sloos sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 267257062Sloos RF_ACTIVE); 268257062Sloos if (!sc->sc_mem_res) { 269257062Sloos device_printf(dev, "cannot allocate memory window\n"); 270257062Sloos return (ENXIO); 271257062Sloos } 272257062Sloos 273257062Sloos sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 274257062Sloos sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 275257062Sloos 276257062Sloos rid = 0; 277257062Sloos sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 278257062Sloos RF_ACTIVE); 279257062Sloos if (!sc->sc_irq_res) { 280257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 281257062Sloos device_printf(dev, "cannot allocate interrupt\n"); 282257062Sloos return (ENXIO); 283257062Sloos } 284257062Sloos 285257062Sloos /* Hook up our interrupt handler. */ 286257062Sloos if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 287257062Sloos NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) { 288257062Sloos bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 289257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 290257062Sloos device_printf(dev, "cannot setup the interrupt handler\n"); 291257062Sloos return (ENXIO); 292257062Sloos } 293257062Sloos 294257062Sloos mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF); 295257062Sloos 296257062Sloos /* Add sysctl nodes. */ 297257062Sloos bcm_spi_sysctl_init(sc); 298257062Sloos 299257062Sloos#ifdef BCM_SPI_DEBUG 300257062Sloos bcm_spi_printr(dev); 301257062Sloos#endif 302257062Sloos 303257062Sloos /* 304257062Sloos * Enable the SPI controller. Clear the rx and tx FIFO. 305257062Sloos * Defaults to SPI mode 0. 306257062Sloos */ 307257062Sloos BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); 308257062Sloos 309257062Sloos#ifdef BCM_SPI_DEBUG 310257062Sloos bcm_spi_printr(dev); 311257062Sloos#endif 312257062Sloos 313257062Sloos device_add_child(dev, "spibus", -1); 314257062Sloos 315257062Sloos return (bus_generic_attach(dev)); 316257062Sloos} 317257062Sloos 318257062Sloosstatic int 319257062Sloosbcm_spi_detach(device_t dev) 320257062Sloos{ 321257062Sloos struct bcm_spi_softc *sc; 322257062Sloos 323257062Sloos bus_generic_detach(dev); 324257062Sloos 325257062Sloos sc = device_get_softc(dev); 326257062Sloos mtx_destroy(&sc->sc_mtx); 327257062Sloos if (sc->sc_intrhand) 328257062Sloos bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); 329257062Sloos if (sc->sc_irq_res) 330257062Sloos bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 331257062Sloos if (sc->sc_mem_res) 332257062Sloos bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 333257062Sloos 334257062Sloos return (0); 335257062Sloos} 336257062Sloos 337257062Sloosstatic void 338257062Sloosbcm_spi_fill_fifo(struct bcm_spi_softc *sc) 339257062Sloos{ 340257062Sloos struct spi_command *cmd; 341257062Sloos uint32_t cs, written; 342257062Sloos uint8_t *data; 343257062Sloos 344257062Sloos cmd = sc->sc_cmd; 345257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); 346257062Sloos while (sc->sc_written < sc->sc_len && 347257062Sloos cs == (SPI_CS_TA | SPI_CS_TXD)) { 348257062Sloos data = (uint8_t *)cmd->tx_cmd; 349257062Sloos written = sc->sc_written++; 350257062Sloos if (written >= cmd->tx_cmd_sz) { 351257062Sloos data = (uint8_t *)cmd->tx_data; 352257062Sloos written -= cmd->tx_cmd_sz; 353257062Sloos } 354257062Sloos BCM_SPI_WRITE(sc, SPI_FIFO, data[written]); 355257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); 356257062Sloos } 357257062Sloos} 358257062Sloos 359257062Sloosstatic void 360257062Sloosbcm_spi_drain_fifo(struct bcm_spi_softc *sc) 361257062Sloos{ 362257062Sloos struct spi_command *cmd; 363257062Sloos uint32_t cs, read; 364257062Sloos uint8_t *data; 365257062Sloos 366257062Sloos cmd = sc->sc_cmd; 367257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; 368257062Sloos while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) { 369257062Sloos data = (uint8_t *)cmd->rx_cmd; 370257062Sloos read = sc->sc_read++; 371257062Sloos if (read >= cmd->rx_cmd_sz) { 372257062Sloos data = (uint8_t *)cmd->rx_data; 373257062Sloos read -= cmd->rx_cmd_sz; 374257062Sloos } 375257062Sloos data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff; 376257062Sloos cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; 377257062Sloos } 378257062Sloos} 379257062Sloos 380257062Sloosstatic void 381257062Sloosbcm_spi_intr(void *arg) 382257062Sloos{ 383257062Sloos struct bcm_spi_softc *sc; 384257062Sloos 385257062Sloos sc = (struct bcm_spi_softc *)arg; 386257062Sloos BCM_SPI_LOCK(sc); 387257062Sloos 388257062Sloos /* Filter stray interrupts. */ 389257062Sloos if ((sc->sc_flags & BCM_SPI_BUSY) == 0) { 390257062Sloos BCM_SPI_UNLOCK(sc); 391257062Sloos return; 392257062Sloos } 393257062Sloos 394257062Sloos /* TX - Fill up the FIFO. */ 395257062Sloos bcm_spi_fill_fifo(sc); 396257062Sloos 397257062Sloos /* RX - Drain the FIFO. */ 398257062Sloos bcm_spi_drain_fifo(sc); 399257062Sloos 400257062Sloos /* Check for end of transfer. */ 401257062Sloos if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { 402257062Sloos /* Disable interrupts and the SPI engine. */ 403257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 404257062Sloos SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); 405257062Sloos wakeup(sc->sc_dev); 406257062Sloos } 407257062Sloos 408257062Sloos BCM_SPI_UNLOCK(sc); 409257062Sloos} 410257062Sloos 411257062Sloosstatic int 412257062Sloosbcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 413257062Sloos{ 414257062Sloos struct bcm_spi_softc *sc; 415346519Sian uint32_t cs, mode, clock; 416310156Smanu int err; 417257062Sloos 418257062Sloos sc = device_get_softc(dev); 419257062Sloos 420257062Sloos KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 421257062Sloos ("TX/RX command sizes should be equal")); 422257062Sloos KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 423257062Sloos ("TX/RX data sizes should be equal")); 424257062Sloos 425346519Sian /* Get the bus speed, mode, and chip select for this child. */ 426346519Sian 427283918Sian spibus_get_cs(child, &cs); 428346519Sian if ((cs & (~SPIBUS_CS_HIGH)) > 2) { 429346519Sian device_printf(dev, 430346519Sian "Invalid chip select %u requested by %s\n", cs, 431346519Sian device_get_nameunit(child)); 432346519Sian return (EINVAL); 433346519Sian } 434331506Sian 435346519Sian spibus_get_clock(child, &clock); 436346519Sian if (clock == 0) { 437346519Sian device_printf(dev, 438346519Sian "Invalid clock %uHz requested by %s\n", clock, 439346519Sian device_get_nameunit(child)); 440346519Sian return (EINVAL); 441346519Sian } 442331506Sian 443346519Sian spibus_get_mode(child, &mode); 444346519Sian if (mode > 3) { 445283918Sian device_printf(dev, 446346519Sian "Invalid mode %u requested by %s\n", mode, 447283918Sian device_get_nameunit(child)); 448283918Sian return (EINVAL); 449283918Sian } 450283918Sian 451346519Sian /* If the controller is in use wait until it is available. */ 452257062Sloos BCM_SPI_LOCK(sc); 453257062Sloos while (sc->sc_flags & BCM_SPI_BUSY) 454257062Sloos mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); 455257062Sloos 456257062Sloos /* Now we have control over SPI controller. */ 457257062Sloos sc->sc_flags = BCM_SPI_BUSY; 458257062Sloos 459257062Sloos /* Clear the FIFO. */ 460257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 461257062Sloos SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO, 462257062Sloos SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); 463257062Sloos 464257062Sloos /* Save a pointer to the SPI command. */ 465257062Sloos sc->sc_cmd = cmd; 466257062Sloos sc->sc_read = 0; 467257062Sloos sc->sc_written = 0; 468257062Sloos sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; 469257062Sloos 470346519Sian#ifdef BCM2835_SPI_USE_CS_HIGH /* TODO: for when behavior is correct */ 471257062Sloos /* 472346519Sian * Assign CS polarity first, while the CS indicates 'inactive'. 473346519Sian * This will need to set the correct polarity bit based on the 'cs', and 474346519Sian * the polarity bit will remain in this state, even after the transaction 475346519Sian * is complete. 476346519Sian */ 477346519Sian if((cs & ~SPIBUS_CS_HIGH) == 0) { 478346519Sian bcm_spi_modifyreg(sc, SPI_CS, 479346519Sian SPI_CS_CSPOL0, 480346519Sian ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL0 : 0)); 481346519Sian } 482346519Sian else if((cs & ~SPIBUS_CS_HIGH) == 1) { 483346519Sian bcm_spi_modifyreg(sc, SPI_CS, 484346519Sian SPI_CS_CSPOL1, 485346519Sian ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL1 : 0)); 486346519Sian } 487346519Sian else if((cs & ~SPIBUS_CS_HIGH) == 2) { 488346519Sian bcm_spi_modifyreg(sc, SPI_CS, 489346519Sian SPI_CS_CSPOL2, 490346519Sian ((cs & (SPIBUS_CS_HIGH)) ? SPI_CS_CSPOL2 : 0)); 491346519Sian } 492346519Sian#endif 493346519Sian 494346519Sian /* 495346519Sian * Set the mode in 'SPI_CS' (clock phase and polarity bits). 496346519Sian * This must happen before CS output pin is active. 497346519Sian * Otherwise, you might glitch and drop the first bit. 498346519Sian */ 499346519Sian bcm_spi_modifyreg(sc, SPI_CS, 500346519Sian SPI_CS_CPOL | SPI_CS_CPHA, 501346519Sian ((mode & SPIBUS_MODE_CPHA) ? SPI_CS_CPHA : 0) | 502346519Sian ((mode & SPIBUS_MODE_CPOL) ? SPI_CS_CPOL : 0)); 503346519Sian 504346519Sian /* 505346519Sian * Set the clock divider in 'SPI_CLK - see 'bcm_spi_clock_proc()'. 506346519Sian */ 507346519Sian 508346519Sian /* calculate 'clock' as a divider value from freq */ 509346519Sian clock = SPI_CORE_CLK / clock; 510346519Sian if (clock <= 1) 511346519Sian clock = 2; 512346519Sian else if (clock % 2) 513346519Sian clock--; 514346519Sian if (clock > 0xffff) 515346519Sian clock = 0; 516346519Sian 517346519Sian BCM_SPI_WRITE(sc, SPI_CLK, clock); 518346519Sian 519346519Sian /* 520257062Sloos * Set the CS for this transaction, enable interrupts and announce 521257062Sloos * we're ready to tx. This will kick off the first interrupt. 522257062Sloos */ 523257062Sloos bcm_spi_modifyreg(sc, SPI_CS, 524257062Sloos SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 525346519Sian (cs & (~SPIBUS_CS_HIGH)) | /* cs is the lower 2 bits of the reg */ 526346519Sian SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); 527257062Sloos 528257062Sloos /* Wait for the transaction to complete. */ 529257062Sloos err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); 530257062Sloos 531257062Sloos /* Make sure the SPI engine and interrupts are disabled. */ 532257062Sloos bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); 533257062Sloos 534283918Sian /* Release the controller and wakeup the next thread waiting for it. */ 535257062Sloos sc->sc_flags = 0; 536283918Sian wakeup_one(dev); 537283918Sian BCM_SPI_UNLOCK(sc); 538257062Sloos 539257062Sloos /* 540257062Sloos * Check for transfer timeout. The SPI controller doesn't 541257062Sloos * return errors. 542257062Sloos */ 543257062Sloos if (err == EWOULDBLOCK) { 544346519Sian device_printf(sc->sc_dev, "SPI error (timeout)\n"); 545257062Sloos err = EIO; 546257062Sloos } 547257062Sloos 548257062Sloos return (err); 549257062Sloos} 550257062Sloos 551257062Sloosstatic phandle_t 552257062Sloosbcm_spi_get_node(device_t bus, device_t dev) 553257062Sloos{ 554257062Sloos 555257062Sloos /* We only have one child, the SPI bus, which needs our own node. */ 556257062Sloos return (ofw_bus_get_node(bus)); 557257062Sloos} 558257062Sloos 559257062Sloosstatic device_method_t bcm_spi_methods[] = { 560257062Sloos /* Device interface */ 561257062Sloos DEVMETHOD(device_probe, bcm_spi_probe), 562257062Sloos DEVMETHOD(device_attach, bcm_spi_attach), 563257062Sloos DEVMETHOD(device_detach, bcm_spi_detach), 564257062Sloos 565257062Sloos /* SPI interface */ 566257062Sloos DEVMETHOD(spibus_transfer, bcm_spi_transfer), 567257062Sloos 568257062Sloos /* ofw_bus interface */ 569257062Sloos DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node), 570257062Sloos 571257062Sloos DEVMETHOD_END 572257062Sloos}; 573257062Sloos 574257062Sloosstatic devclass_t bcm_spi_devclass; 575257062Sloos 576257062Sloosstatic driver_t bcm_spi_driver = { 577257062Sloos "spi", 578257062Sloos bcm_spi_methods, 579257062Sloos sizeof(struct bcm_spi_softc), 580257062Sloos}; 581257062Sloos 582257062SloosDRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0); 583