1155324Simp/*- 2236495Smarius * Copyright (c) 2006 M. Warner Losh. 3236495Smarius * Copyright (c) 2011-2012 Ian Lepore. 4236495Smarius * All rights reserved. 5155324Simp * 6155324Simp * Redistribution and use in source and binary forms, with or without 7155324Simp * modification, are permitted provided that the following conditions 8155324Simp * are met: 9155324Simp * 1. Redistributions of source code must retain the above copyright 10155324Simp * notice, this list of conditions and the following disclaimer. 11155324Simp * 2. Redistributions in binary form must reproduce the above copyright 12155324Simp * notice, this list of conditions and the following disclaimer in the 13155324Simp * documentation and/or other materials provided with the distribution. 14155324Simp * 15185265Simp * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16185265Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17185265Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18185265Simp * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 19185265Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20185265Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21185265Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22185265Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23185265Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24185265Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25185265Simp * SUCH DAMAGE. 26155324Simp */ 27155324Simp 28266196Sian#include "opt_platform.h" 29266196Sian 30155324Simp#include <sys/cdefs.h> 31155324Simp__FBSDID("$FreeBSD$"); 32155324Simp 33155324Simp#include <sys/param.h> 34155324Simp#include <sys/systm.h> 35155324Simp#include <sys/bus.h> 36155324Simp#include <sys/conf.h> 37155324Simp#include <sys/kernel.h> 38236495Smarius#include <sys/lock.h> 39155324Simp#include <sys/mbuf.h> 40155324Simp#include <sys/malloc.h> 41155324Simp#include <sys/module.h> 42155324Simp#include <sys/rman.h> 43236495Smarius#include <sys/sx.h> 44236495Smarius 45155324Simp#include <machine/bus.h> 46155324Simp 47238955Simp#include <arm/at91/at91var.h> 48155324Simp#include <arm/at91/at91_spireg.h> 49160358Simp#include <arm/at91/at91_pdcreg.h> 50155324Simp 51160358Simp#include <dev/spibus/spi.h> 52239626Simp#include <dev/spibus/spibusvar.h> 53236495Smarius 54266196Sian#ifdef FDT 55266196Sian#include <dev/fdt/fdt_common.h> 56266196Sian#include <dev/ofw/ofw_bus.h> 57266196Sian#include <dev/ofw/ofw_bus_subr.h> 58266196Sian#endif 59266196Sian 60160358Simp#include "spibus_if.h" 61160358Simp 62155324Simpstruct at91_spi_softc 63155324Simp{ 64155324Simp device_t dev; /* Myself */ 65155324Simp void *intrhand; /* Interrupt handle */ 66155324Simp struct resource *irq_res; /* IRQ resource */ 67155324Simp struct resource *mem_res; /* Memory resource */ 68236495Smarius bus_dma_tag_t dmatag; /* bus dma tag for transfers */ 69160358Simp bus_dmamap_t map[4]; /* Maps for the transaction */ 70236495Smarius struct sx xfer_mtx; /* Enforce one transfer at a time */ 71236495Smarius uint32_t xfer_done; /* interrupt<->mainthread signaling */ 72155324Simp}; 73155324Simp 74236495Smarius#define CS_TO_MR(cs) ((~(1 << (cs)) & 0x0f) << 16) 75236495Smarius 76155324Simpstatic inline uint32_t 77155324SimpRD4(struct at91_spi_softc *sc, bus_size_t off) 78155324Simp{ 79236495Smarius 80236495Smarius return (bus_read_4(sc->mem_res, off)); 81155324Simp} 82155324Simp 83155324Simpstatic inline void 84155324SimpWR4(struct at91_spi_softc *sc, bus_size_t off, uint32_t val) 85155324Simp{ 86236495Smarius 87155324Simp bus_write_4(sc->mem_res, off, val); 88155324Simp} 89155324Simp 90155324Simp/* bus entry points */ 91155324Simpstatic int at91_spi_attach(device_t dev); 92155324Simpstatic int at91_spi_detach(device_t dev); 93236495Smariusstatic int at91_spi_probe(device_t dev); 94236495Smariusstatic int at91_spi_transfer(device_t dev, device_t child, 95236495Smarius struct spi_command *cmd); 96155324Simp 97155324Simp/* helper routines */ 98236495Smariusstatic void at91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, 99236495Smarius int error); 100155324Simpstatic int at91_spi_activate(device_t dev); 101155324Simpstatic void at91_spi_deactivate(device_t dev); 102163526Simpstatic void at91_spi_intr(void *arg); 103155324Simp 104155324Simpstatic int 105155324Simpat91_spi_probe(device_t dev) 106155324Simp{ 107266196Sian#ifdef FDT 108266196Sian if (!ofw_bus_is_compatible(dev, "atmel,at91rm9200-spi")) 109266196Sian return (ENXIO); 110266196Sian#endif 111236495Smarius device_set_desc(dev, "AT91 SPI"); 112155324Simp return (0); 113155324Simp} 114155324Simp 115155324Simpstatic int 116155324Simpat91_spi_attach(device_t dev) 117155324Simp{ 118236495Smarius struct at91_spi_softc *sc; 119236495Smarius int err; 120236495Smarius uint32_t csr; 121155324Simp 122236495Smarius sc = device_get_softc(dev); 123236495Smarius 124155324Simp sc->dev = dev; 125236495Smarius sx_init(&sc->xfer_mtx, device_get_nameunit(dev)); 126236495Smarius 127236495Smarius /* 128236495Smarius * Allocate resources. 129236495Smarius */ 130155324Simp err = at91_spi_activate(dev); 131155324Simp if (err) 132155324Simp goto out; 133155324Simp 134266196Sian#ifdef FDT 135155324Simp /* 136266196Sian * Disable devices need to hold their resources, so return now and not attach 137266196Sian * the spibus, setup interrupt handlers, etc. 138266196Sian */ 139266196Sian if (!ofw_bus_status_okay(dev)) 140266196Sian return 0; 141266196Sian#endif 142266196Sian 143266196Sian /* 144236495Smarius * Set up the hardware. 145155324Simp */ 146155324Simp 147155324Simp WR4(sc, SPI_CR, SPI_CR_SWRST); 148236495Smarius /* "Software Reset must be Written Twice" erratum */ 149236495Smarius WR4(sc, SPI_CR, SPI_CR_SWRST); 150163526Simp WR4(sc, SPI_IDR, 0xffffffff); 151160358Simp 152160358Simp WR4(sc, SPI_MR, (0xf << 24) | SPI_MR_MSTR | SPI_MR_MODFDIS | 153236495Smarius CS_TO_MR(0)); 154160358Simp 155236495Smarius /* 156236495Smarius * For now, run the bus at the slowest speed possible as otherwise we 157236495Smarius * may encounter data corruption on transmit as seen with ETHERNUT5 158236495Smarius * and AT45DB321D even though both board and slave device can take 159236495Smarius * more. 160236495Smarius * This also serves as a work-around for the "NPCSx rises if no data 161236495Smarius * data is to be transmitted" erratum. The ideal workaround for the 162236495Smarius * latter is to take the chip select control away from the peripheral 163236495Smarius * and manage it directly as a GPIO line. The easy solution is to 164236495Smarius * slow down the bus so dramatically that it just never gets starved 165236495Smarius * as may be seen when the OCHI controller is running and consuming 166236495Smarius * memory and APB bandwidth. 167236495Smarius * Also, currently we lack a way for lettting both the board and the 168236495Smarius * slave devices take their maximum supported SPI clocks into account. 169238955Simp * Also, we hard-wire SPI mode to 3. 170236495Smarius */ 171236495Smarius csr = SPI_CSR_CPOL | (4 << 16) | (0xff << 8); 172236495Smarius WR4(sc, SPI_CSR0, csr); 173236495Smarius WR4(sc, SPI_CSR1, csr); 174236495Smarius WR4(sc, SPI_CSR2, csr); 175236495Smarius WR4(sc, SPI_CSR3, csr); 176236495Smarius 177160358Simp WR4(sc, SPI_CR, SPI_CR_SPIEN); 178160358Simp 179160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS); 180160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_RXTDIS); 181160358Simp WR4(sc, PDC_RNPR, 0); 182160358Simp WR4(sc, PDC_RNCR, 0); 183160358Simp WR4(sc, PDC_TNPR, 0); 184160358Simp WR4(sc, PDC_TNCR, 0); 185160358Simp WR4(sc, PDC_RPR, 0); 186160358Simp WR4(sc, PDC_RCR, 0); 187160358Simp WR4(sc, PDC_TPR, 0); 188160358Simp WR4(sc, PDC_TCR, 0); 189160358Simp RD4(sc, SPI_RDR); 190160358Simp RD4(sc, SPI_SR); 191160358Simp 192160358Simp device_add_child(dev, "spibus", -1); 193160358Simp bus_generic_attach(dev); 194225882Skevloout: 195155324Simp if (err) 196155324Simp at91_spi_deactivate(dev); 197155324Simp return (err); 198155324Simp} 199155324Simp 200155324Simpstatic int 201155324Simpat91_spi_detach(device_t dev) 202155324Simp{ 203236495Smarius 204155324Simp return (EBUSY); /* XXX */ 205155324Simp} 206155324Simp 207155324Simpstatic int 208155324Simpat91_spi_activate(device_t dev) 209155324Simp{ 210155324Simp struct at91_spi_softc *sc; 211236495Smarius int err, i, rid; 212155324Simp 213155324Simp sc = device_get_softc(dev); 214236495Smarius err = ENOMEM; 215236495Smarius 216155324Simp rid = 0; 217155324Simp sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 218155324Simp RF_ACTIVE); 219155324Simp if (sc->mem_res == NULL) 220236495Smarius goto out; 221236495Smarius 222155324Simp rid = 0; 223155324Simp sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 224155324Simp RF_ACTIVE); 225163526Simp if (sc->irq_res == NULL) 226236495Smarius goto out; 227163526Simp err = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE, 228166901Spiso NULL, at91_spi_intr, sc, &sc->intrhand); 229163526Simp if (err != 0) 230236495Smarius goto out; 231236495Smarius 232236495Smarius err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, 233236495Smarius BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 2048, 1, 234236495Smarius 2048, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->dmatag); 235236495Smarius if (err != 0) 236236495Smarius goto out; 237236495Smarius 238236495Smarius for (i = 0; i < 4; i++) { 239236495Smarius err = bus_dmamap_create(sc->dmatag, 0, &sc->map[i]); 240236495Smarius if (err != 0) 241236495Smarius goto out; 242236495Smarius } 243236495Smariusout: 244236495Smarius if (err != 0) 245236495Smarius at91_spi_deactivate(dev); 246163526Simp return (err); 247155324Simp} 248155324Simp 249155324Simpstatic void 250155324Simpat91_spi_deactivate(device_t dev) 251155324Simp{ 252155324Simp struct at91_spi_softc *sc; 253236495Smarius int i; 254155324Simp 255155324Simp sc = device_get_softc(dev); 256236495Smarius bus_generic_detach(dev); 257236495Smarius 258236495Smarius for (i = 0; i < 4; i++) 259236495Smarius if (sc->map[i]) 260236495Smarius bus_dmamap_destroy(sc->dmatag, sc->map[i]); 261236495Smarius 262236495Smarius if (sc->dmatag) 263236495Smarius bus_dma_tag_destroy(sc->dmatag); 264236495Smarius 265155324Simp if (sc->intrhand) 266155324Simp bus_teardown_intr(dev, sc->irq_res, sc->intrhand); 267236495Smarius sc->intrhand = NULL; 268155324Simp if (sc->irq_res) 269155324Simp bus_release_resource(dev, SYS_RES_IRQ, 270155324Simp rman_get_rid(sc->irq_res), sc->irq_res); 271236495Smarius sc->irq_res = NULL; 272236495Smarius 273236495Smarius if (sc->mem_res) 274236495Smarius bus_release_resource(dev, SYS_RES_MEMORY, 275236495Smarius rman_get_rid(sc->mem_res), sc->mem_res); 276236495Smarius sc->mem_res = NULL; 277155324Simp} 278155324Simp 279155324Simpstatic void 280236495Smariusat91_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs __unused, 281236495Smarius int error) 282155324Simp{ 283236495Smarius 284160358Simp if (error != 0) 285155324Simp return; 286160358Simp *(bus_addr_t *)arg = segs[0].ds_addr; 287155324Simp} 288155324Simp 289155324Simpstatic int 290160358Simpat91_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 291155324Simp{ 292155324Simp struct at91_spi_softc *sc; 293160358Simp bus_addr_t addr; 294239626Simp int err, i, j, mode[4], cs; 295155324Simp 296236495Smarius KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 297236495Smarius ("%s: TX/RX command sizes should be equal", __func__)); 298236495Smarius KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 299236495Smarius ("%s: TX/RX data sizes should be equal", __func__)); 300236495Smarius 301239626Simp /* get the proper chip select */ 302239626Simp spibus_get_cs(child, &cs); 303239626Simp 304160358Simp sc = device_get_softc(dev); 305236495Smarius i = 0; 306236495Smarius 307236495Smarius sx_xlock(&sc->xfer_mtx); 308236495Smarius 309236495Smarius /* 310236495Smarius * Disable transfers while we set things up. 311236495Smarius */ 312160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 313236495Smarius 314238955Simp /* 315238955Simp * PSCDEC = 0 has a range of 0..3 for chip select. We 316238955Simp * don't support PSCDEC = 1 which has a range of 0..15. 317238955Simp */ 318239626Simp if (cs < 0 || cs > 3) { 319236495Smarius device_printf(dev, 320239626Simp "Invalid chip select %d requested by %s\n", cs, 321236495Smarius device_get_nameunit(child)); 322236495Smarius err = EINVAL; 323160358Simp goto out; 324236495Smarius } 325238955Simp 326236495Smarius#ifdef SPI_CHIP_SELECT_HIGH_SUPPORT 327238955Simp /* 328238955Simp * The AT91RM9200 couldn't do CS high for CS 0. Other chips can, but we 329238955Simp * don't support that yet, or other spi modes. 330238955Simp */ 331239626Simp if (at91_is_rm92() && cs == 0 && 332236495Smarius (cmd->flags & SPI_CHIP_SELECT_HIGH) != 0) { 333236495Smarius device_printf(dev, 334238955Simp "Invalid chip select high requested by %s for cs 0.\n", 335236495Smarius device_get_nameunit(child)); 336236495Smarius err = EINVAL; 337236495Smarius goto out; 338236495Smarius } 339236495Smarius#endif 340239626Simp err = (RD4(sc, SPI_MR) & ~0x000f0000) | CS_TO_MR(cs); 341238955Simp WR4(sc, SPI_MR, err); 342236495Smarius 343236495Smarius /* 344236495Smarius * Set up the TX side of the transfer. 345236495Smarius */ 346236495Smarius if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->tx_cmd, 347236495Smarius cmd->tx_cmd_sz, at91_getaddr, &addr, 0)) != 0) 348236495Smarius goto out; 349160358Simp WR4(sc, PDC_TPR, addr); 350160358Simp WR4(sc, PDC_TCR, cmd->tx_cmd_sz); 351160358Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 352163526Simp mode[i++] = BUS_DMASYNC_POSTWRITE; 353163526Simp if (cmd->tx_data_sz > 0) { 354236495Smarius if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], 355236495Smarius cmd->tx_data, cmd->tx_data_sz, at91_getaddr, &addr, 0)) != 356236495Smarius 0) 357163526Simp goto out; 358163526Simp WR4(sc, PDC_TNPR, addr); 359164741Simp WR4(sc, PDC_TNCR, cmd->tx_data_sz); 360163526Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREWRITE); 361163526Simp mode[i++] = BUS_DMASYNC_POSTWRITE; 362163526Simp } 363236495Smarius 364236495Smarius /* 365236495Smarius * Set up the RX side of the transfer. 366236495Smarius */ 367236495Smarius if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], cmd->rx_cmd, 368236495Smarius cmd->rx_cmd_sz, at91_getaddr, &addr, 0)) != 0) 369160358Simp goto out; 370160358Simp WR4(sc, PDC_RPR, addr); 371236495Smarius WR4(sc, PDC_RCR, cmd->rx_cmd_sz); 372160358Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 373163526Simp mode[i++] = BUS_DMASYNC_POSTREAD; 374164741Simp if (cmd->rx_data_sz > 0) { 375236495Smarius if ((err = bus_dmamap_load(sc->dmatag, sc->map[i], 376236495Smarius cmd->rx_data, cmd->rx_data_sz, at91_getaddr, &addr, 0)) != 377236495Smarius 0) 378163526Simp goto out; 379163526Simp WR4(sc, PDC_RNPR, addr); 380164741Simp WR4(sc, PDC_RNCR, cmd->rx_data_sz); 381163526Simp bus_dmamap_sync(sc->dmatag, sc->map[i], BUS_DMASYNC_PREREAD); 382163526Simp mode[i++] = BUS_DMASYNC_POSTREAD; 383163526Simp } 384236495Smarius 385236495Smarius /* 386236495Smarius * Start the transfer, wait for it to complete. 387236495Smarius */ 388236495Smarius sc->xfer_done = 0; 389237239Smarius WR4(sc, SPI_IER, SPI_SR_RXBUFF); 390160358Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTEN | PDC_PTCR_RXTEN); 391236495Smarius do 392236495Smarius err = tsleep(&sc->xfer_done, PCATCH | PZERO, "at91_spi", hz); 393237239Smarius while (sc->xfer_done == 0 && err != EINTR); 394155324Simp 395236495Smarius /* 396236495Smarius * Stop the transfer and clean things up. 397236495Smarius */ 398163526Simp WR4(sc, PDC_PTCR, PDC_PTCR_TXTDIS | PDC_PTCR_RXTDIS); 399236495Smarius if (err == 0) 400236495Smarius for (j = 0; j < i; j++) 401163526Simp bus_dmamap_sync(sc->dmatag, sc->map[j], mode[j]); 402236495Smariusout: 403163526Simp for (j = 0; j < i; j++) 404163526Simp bus_dmamap_unload(sc->dmatag, sc->map[j]); 405236495Smarius 406236495Smarius sx_xunlock(&sc->xfer_mtx); 407236495Smarius 408163526Simp return (err); 409155324Simp} 410155324Simp 411163526Simpstatic void 412163526Simpat91_spi_intr(void *arg) 413163526Simp{ 414236495Smarius struct at91_spi_softc *sc; 415237239Smarius uint32_t sr; 416163526Simp 417236495Smarius sc = (struct at91_spi_softc*)arg; 418236495Smarius 419163526Simp sr = RD4(sc, SPI_SR) & RD4(sc, SPI_IMR); 420237239Smarius if ((sr & SPI_SR_RXBUFF) != 0) { 421237239Smarius sc->xfer_done = 1; 422237239Smarius WR4(sc, SPI_IDR, SPI_SR_RXBUFF); 423236495Smarius wakeup(&sc->xfer_done); 424163526Simp } 425237239Smarius if ((sr & ~SPI_SR_RXBUFF) != 0) { 426163526Simp device_printf(sc->dev, "Unexpected ISR %#x\n", sr); 427237239Smarius WR4(sc, SPI_IDR, sr & ~SPI_SR_RXBUFF); 428163526Simp } 429163526Simp} 430163526Simp 431163526Simpstatic devclass_t at91_spi_devclass; 432163526Simp 433155324Simpstatic device_method_t at91_spi_methods[] = { 434155324Simp /* Device interface */ 435155324Simp DEVMETHOD(device_probe, at91_spi_probe), 436155324Simp DEVMETHOD(device_attach, at91_spi_attach), 437155324Simp DEVMETHOD(device_detach, at91_spi_detach), 438155324Simp 439160358Simp /* spibus interface */ 440160358Simp DEVMETHOD(spibus_transfer, at91_spi_transfer), 441236495Smarius 442236495Smarius DEVMETHOD_END 443155324Simp}; 444155324Simp 445155324Simpstatic driver_t at91_spi_driver = { 446192059Sgonzo "spi", 447155324Simp at91_spi_methods, 448155324Simp sizeof(struct at91_spi_softc), 449155324Simp}; 450155324Simp 451266196Sian#ifdef FDT 452266196SianDRIVER_MODULE(at91_spi, simplebus, at91_spi_driver, at91_spi_devclass, NULL, 453266196Sian NULL); 454266196Sian#else 455236495SmariusDRIVER_MODULE(at91_spi, atmelarm, at91_spi_driver, at91_spi_devclass, NULL, 456236495Smarius NULL); 457266196Sian#endif 458