Index: head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 307087) +++ head/sys/arm/broadcom/bcm2835/bcm2835_bsc.c (revision 307088) @@ -1,507 +1,513 @@ /*- * Copyright (c) 2001 Tsubai Masanari. * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iicbus_if.h" +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-bsc", 1}, + {"brcm,bcm2708-i2c", 1}, + {NULL, 0} +}; + static void bcm_bsc_intr(void *); static int bcm_bsc_detach(device_t); static void bcm_bsc_modifyreg(struct bcm_bsc_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_BSC_READ(sc, off); reg &= ~mask; reg |= value; BCM_BSC_WRITE(sc, off, reg); } static int bcm_bsc_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); BCM_BSC_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 32768; clk = BCM_BSC_CORE_CLK / clk; return (sysctl_handle_int(oidp, &clk, 0, req)); } static int bcm_bsc_clkt_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clkt; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); clkt = BCM_BSC_READ(sc, BCM_BSC_CLKT); BCM_BSC_UNLOCK(sc); clkt &= 0xffff; error = sysctl_handle_int(oidp, &clkt, sizeof(clkt), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); BCM_BSC_WRITE(sc, BCM_BSC_CLKT, clkt & 0xffff); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_fall_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg >>= 16; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff0000, reg << 16); BCM_BSC_UNLOCK(sc); return (0); } static int bcm_bsc_rise_proc(SYSCTL_HANDLER_ARGS) { struct bcm_bsc_softc *sc; uint32_t clk, reg; int error; sc = (struct bcm_bsc_softc *)arg1; BCM_BSC_LOCK(sc); reg = BCM_BSC_READ(sc, BCM_BSC_DELAY); BCM_BSC_UNLOCK(sc); reg &= 0xffff; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); BCM_BSC_LOCK(sc); clk = BCM_BSC_READ(sc, BCM_BSC_CLOCK); clk = BCM_BSC_CORE_CLK / clk; if (reg > clk / 2) reg = clk / 2 - 1; bcm_bsc_modifyreg(sc, BCM_BSC_DELAY, 0xffff, reg); BCM_BSC_UNLOCK(sc); return (0); } static void bcm_bsc_sysctl_init(struct bcm_bsc_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "frequency", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clock_proc, "IU", "I2C BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock_stretch", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_clkt_proc, "IU", "I2C BUS clock stretch timeout"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "fall_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_fall_proc, "IU", "I2C BUS falling edge delay"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "rise_edge_delay", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_bsc_rise_proc, "IU", "I2C BUS rising edge delay"); } static void bcm_bsc_reset(struct bcm_bsc_softc *sc) { /* Enable the BSC Controller, disable interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN); /* Clear pending interrupts. */ BCM_BSC_WRITE(sc, BCM_BSC_STATUS, BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR | BCM_BSC_STATUS_DONE); /* Clear the FIFO. */ bcm_bsc_modifyreg(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_CLEAR0, BCM_BSC_CTRL_CLEAR0); } static int bcm_bsc_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-bsc")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 BSC controller"); return (BUS_PROBE_DEFAULT); } static int bcm_bsc_attach(device_t dev) { struct bcm_bsc_softc *sc; unsigned long start; device_t gpio; int i, rid; sc = device_get_softc(dev); sc->sc_dev = dev; rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); /* Check the unit we are attaching by its base address. */ start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(bcm_bsc_pins); i++) { if (bcm_bsc_pins[i].start == (start & BCM_BSC_BASE_MASK)) break; } if (i == nitems(bcm_bsc_pins)) { device_printf(dev, "only bsc0 and bsc1 are supported\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } /* * Configure the GPIO pins to ALT0 function to enable BSC control * over the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (ENXIO); } bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].sda, BCM_GPIO_ALT0); bcm_gpio_set_alternate(gpio, bcm_bsc_pins[i].scl, BCM_GPIO_ALT0); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_bsc_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_bsc", NULL, MTX_DEF); bcm_bsc_sysctl_init(sc); /* Enable the BSC controller. Flush the FIFO. */ BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); BCM_BSC_UNLOCK(sc); sc->sc_iicbus = device_add_child(dev, "iicbus", -1); if (sc->sc_iicbus == NULL) { bcm_bsc_detach(dev); return (ENXIO); } return (bus_generic_attach(dev)); } static int bcm_bsc_detach(device_t dev) { struct bcm_bsc_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_bsc_intr(void *arg) { struct bcm_bsc_softc *sc; uint32_t status; sc = (struct bcm_bsc_softc *)arg; BCM_BSC_LOCK(sc); /* The I2C interrupt is shared among all the BSC controllers. */ if ((sc->sc_flags & BCM_I2C_BUSY) == 0) { BCM_BSC_UNLOCK(sc); return; } status = BCM_BSC_READ(sc, BCM_BSC_STATUS); /* Check for errors. */ if (status & (BCM_BSC_STATUS_CLKT | BCM_BSC_STATUS_ERR)) { /* Disable interrupts. */ bcm_bsc_reset(sc); sc->sc_flags |= BCM_I2C_ERROR; wakeup(sc->sc_dev); BCM_BSC_UNLOCK(sc); return; } if (sc->sc_flags & BCM_I2C_READ) { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_RXD)) { *sc->sc_data++ = BCM_BSC_READ(sc, BCM_BSC_DATA); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } else { while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data++); sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } } if (status & BCM_BSC_STATUS_DONE) { /* Disable interrupts. */ bcm_bsc_reset(sc); wakeup(sc->sc_dev); } BCM_BSC_UNLOCK(sc); } static int bcm_bsc_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs) { struct bcm_bsc_softc *sc; uint32_t intr, read, status; int i, err; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); /* If the controller is busy wait until it is available. */ while (sc->sc_flags & BCM_I2C_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bscbusw", 0); /* Now we have control over the BSC controller. */ sc->sc_flags = BCM_I2C_BUSY; /* Clear the FIFO and the pending interrupts. */ bcm_bsc_reset(sc); err = 0; for (i = 0; i < nmsgs; i++) { /* Write the slave address. */ BCM_BSC_WRITE(sc, BCM_BSC_SLAVE, msgs[i].slave >> 1); /* Write the data length. */ BCM_BSC_WRITE(sc, BCM_BSC_DLEN, msgs[i].len); sc->sc_data = msgs[i].buf; sc->sc_resid = msgs[i].len; if ((msgs[i].flags & IIC_M_RD) == 0) { /* Fill up the TX FIFO. */ status = BCM_BSC_READ(sc, BCM_BSC_STATUS); while (sc->sc_resid > 0 && (status & BCM_BSC_STATUS_TXD)) { BCM_BSC_WRITE(sc, BCM_BSC_DATA, *sc->sc_data); sc->sc_data++; sc->sc_resid--; status = BCM_BSC_READ(sc, BCM_BSC_STATUS); } read = 0; intr = BCM_BSC_CTRL_INTT; sc->sc_flags &= ~BCM_I2C_READ; } else { sc->sc_flags |= BCM_I2C_READ; read = BCM_BSC_CTRL_READ; intr = BCM_BSC_CTRL_INTR; } intr |= BCM_BSC_CTRL_INTD; /* Start the transfer. */ BCM_BSC_WRITE(sc, BCM_BSC_CTRL, BCM_BSC_CTRL_I2CEN | BCM_BSC_CTRL_ST | read | intr); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bsciow", hz); /* Check for errors. */ if (err == 0 && (sc->sc_flags & BCM_I2C_ERROR)) err = EIO; if (err != 0) break; } /* Clean the controller flags. */ sc->sc_flags = 0; /* Wake up the threads waiting for bus. */ wakeup(dev); BCM_BSC_UNLOCK(sc); return (err); } static int bcm_bsc_iicbus_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr) { struct bcm_bsc_softc *sc; uint32_t busfreq; sc = device_get_softc(dev); BCM_BSC_LOCK(sc); bcm_bsc_reset(sc); if (sc->sc_iicbus == NULL) busfreq = 100000; else busfreq = IICBUS_GET_FREQUENCY(sc->sc_iicbus, speed); BCM_BSC_WRITE(sc, BCM_BSC_CLOCK, BCM_BSC_CORE_CLK / busfreq); BCM_BSC_UNLOCK(sc); return (IIC_ENOADDR); } static phandle_t bcm_bsc_get_node(device_t bus, device_t dev) { /* We only have one child, the I2C bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_bsc_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_bsc_probe), DEVMETHOD(device_attach, bcm_bsc_attach), DEVMETHOD(device_detach, bcm_bsc_detach), /* iicbus interface */ DEVMETHOD(iicbus_reset, bcm_bsc_iicbus_reset), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_transfer, bcm_bsc_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_bsc_get_node), DEVMETHOD_END }; static devclass_t bcm_bsc_devclass; static driver_t bcm_bsc_driver = { "iichb", bcm_bsc_methods, sizeof(struct bcm_bsc_softc), }; DRIVER_MODULE(iicbus, bcm2835_bsc, iicbus_driver, iicbus_devclass, 0, 0); DRIVER_MODULE(bcm2835_bsc, simplebus, bcm_bsc_driver, bcm_bsc_devclass, 0, 0); Index: head/sys/arm/broadcom/bcm2835/bcm2835_dma.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_dma.c (revision 307087) +++ head/sys/arm/broadcom/bcm2835/bcm2835_dma.c (revision 307088) @@ -1,764 +1,770 @@ /* * Copyright (c) 2013 Daisuke Aoyama * Copyright (c) 2013 Oleksandr Tymoshenko * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "bcm2835_dma.h" #include "bcm2835_vcbus.h" #define MAX_REG 9 /* private flags */ #define BCM_DMA_CH_USED 0x00000001 #define BCM_DMA_CH_FREE 0x40000000 #define BCM_DMA_CH_UNMAP 0x80000000 /* Register Map (4.2.1.2) */ #define BCM_DMA_CS(n) (0x100*(n) + 0x00) #define CS_ACTIVE (1 << 0) #define CS_END (1 << 1) #define CS_INT (1 << 2) #define CS_DREQ (1 << 3) #define CS_ISPAUSED (1 << 4) #define CS_ISHELD (1 << 5) #define CS_ISWAIT (1 << 6) #define CS_ERR (1 << 8) #define CS_WAITWRT (1 << 28) #define CS_DISDBG (1 << 29) #define CS_ABORT (1 << 30) #define CS_RESET (1U << 31) #define BCM_DMA_CBADDR(n) (0x100*(n) + 0x04) #define BCM_DMA_INFO(n) (0x100*(n) + 0x08) #define INFO_INT_EN (1 << 0) #define INFO_TDMODE (1 << 1) #define INFO_WAIT_RESP (1 << 3) #define INFO_D_INC (1 << 4) #define INFO_D_WIDTH (1 << 5) #define INFO_D_DREQ (1 << 6) #define INFO_S_INC (1 << 8) #define INFO_S_WIDTH (1 << 9) #define INFO_S_DREQ (1 << 10) #define INFO_WAITS_SHIFT (21) #define INFO_PERMAP_SHIFT (16) #define INFO_PERMAP_MASK (0x1f << INFO_PERMAP_SHIFT) #define BCM_DMA_SRC(n) (0x100*(n) + 0x0C) #define BCM_DMA_DST(n) (0x100*(n) + 0x10) #define BCM_DMA_LEN(n) (0x100*(n) + 0x14) #define BCM_DMA_STRIDE(n) (0x100*(n) + 0x18) #define BCM_DMA_CBNEXT(n) (0x100*(n) + 0x1C) #define BCM_DMA_DEBUG(n) (0x100*(n) + 0x20) #define DEBUG_ERROR_MASK (7) #define BCM_DMA_INT_STATUS 0xfe0 #define BCM_DMA_ENABLE 0xff0 /* relative offset from BCM_VC_DMA0_BASE (p.39) */ #define BCM_DMA_CH(n) (0x100*(n)) /* channels used by GPU */ #define BCM_DMA_CH_BULK 0 #define BCM_DMA_CH_FAST1 2 #define BCM_DMA_CH_FAST2 3 #define BCM_DMA_CH_GPU_MASK ((1 << BCM_DMA_CH_BULK) | \ (1 << BCM_DMA_CH_FAST1) | \ (1 << BCM_DMA_CH_FAST2)) /* DMA Control Block - 256bit aligned (p.40) */ struct bcm_dma_cb { uint32_t info; /* Transfer Information */ uint32_t src; /* Source Address */ uint32_t dst; /* Destination Address */ uint32_t len; /* Transfer Length */ uint32_t stride; /* 2D Mode Stride */ uint32_t next; /* Next Control Block Address */ uint32_t rsvd1; /* Reserved */ uint32_t rsvd2; /* Reserved */ }; #ifdef DEBUG static void bcm_dma_cb_dump(struct bcm_dma_cb *cb); static void bcm_dma_reg_dump(int ch); #endif /* DMA channel private info */ struct bcm_dma_ch { int ch; uint32_t flags; struct bcm_dma_cb * cb; uint32_t vc_cb; bus_dmamap_t dma_map; void (*intr_func)(int, void *); void * intr_arg; }; struct bcm_dma_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem; struct resource * sc_irq[BCM_DMA_CH_MAX]; void * sc_intrhand[BCM_DMA_CH_MAX]; struct bcm_dma_ch sc_dma_ch[BCM_DMA_CH_MAX]; bus_dma_tag_t sc_dma_tag; }; static struct bcm_dma_softc *bcm_dma_sc = NULL; static uint32_t bcm_dma_channel_mask; +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-dma", 1}, + {"brcm,bcm2835-dma", 1}, + {NULL, 0} +}; + static void bcm_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { bus_addr_t *addr; if (err) return; addr = (bus_addr_t*)arg; *addr = PHYS_TO_VCBUS(segs[0].ds_addr); } static void bcm_dma_reset(device_t dev, int ch) { struct bcm_dma_softc *sc = device_get_softc(dev); struct bcm_dma_cb *cb; uint32_t cs; int count; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return; cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); if (cs & CS_ACTIVE) { /* pause current task */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), 0); count = 1000; do { cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); } while (!(cs & CS_ISPAUSED) && (count-- > 0)); if (!(cs & CS_ISPAUSED)) { device_printf(dev, "Can't abort DMA transfer at channel %d\n", ch); } bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); /* Complete everything, clear interrupt */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ABORT | CS_INT | CS_END| CS_ACTIVE); } /* clear control blocks */ bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), 0); bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); /* Reset control block */ cb = sc->sc_dma_ch[ch].cb; bzero(cb, sizeof(*cb)); cb->info = INFO_WAIT_RESP; } static int bcm_dma_init(device_t dev) { struct bcm_dma_softc *sc = device_get_softc(dev); uint32_t reg; struct bcm_dma_ch *ch; void *cb_virt; vm_paddr_t cb_phys; int err; int i; /* * Only channels set in bcm_dma_channel_mask can be controlled by us. * The others are out of our control as well as the corresponding bits * in both BCM_DMA_ENABLE and BCM_DMA_INT_STATUS global registers. As * these registers are RW ones, there is no safe way how to write only * the bits which can be controlled by us. * * Fortunately, after reset, all channels are enabled in BCM_DMA_ENABLE * register and all statuses are cleared in BCM_DMA_INT_STATUS one. * Not touching these registers is a trade off between correct * initialization which does not count on anything and not messing up * something we have no control over. */ reg = bus_read_4(sc->sc_mem, BCM_DMA_ENABLE); if ((reg & bcm_dma_channel_mask) != bcm_dma_channel_mask) device_printf(dev, "channels are not enabled\n"); reg = bus_read_4(sc->sc_mem, BCM_DMA_INT_STATUS); if ((reg & bcm_dma_channel_mask) != 0) device_printf(dev, "statuses are not cleared\n"); /* Allocate DMA chunks control blocks */ /* p.40 of spec - control block should be 32-bit aligned */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, sizeof(struct bcm_dma_cb), 1, sizeof(struct bcm_dma_cb), BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); if (err) { device_printf(dev, "failed allocate DMA tag\n"); return (err); } /* setup initial settings */ for (i = 0; i < BCM_DMA_CH_MAX; i++) { ch = &sc->sc_dma_ch[i]; bzero(ch, sizeof(struct bcm_dma_ch)); ch->ch = i; ch->flags = BCM_DMA_CH_UNMAP; if ((bcm_dma_channel_mask & (1 << i)) == 0) continue; err = bus_dmamem_alloc(sc->sc_dma_tag, &cb_virt, BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, &ch->dma_map); if (err) { device_printf(dev, "cannot allocate DMA memory\n"); break; } /* * Least alignment for busdma-allocated stuff is cache * line size, so just make sure nothing stupid happened * and we got properly aligned address */ if ((uintptr_t)cb_virt & 0x1f) { device_printf(dev, "DMA address is not 32-bytes aligned: %p\n", (void*)cb_virt); break; } err = bus_dmamap_load(sc->sc_dma_tag, ch->dma_map, cb_virt, sizeof(struct bcm_dma_cb), bcm_dmamap_cb, &cb_phys, BUS_DMA_WAITOK); if (err) { device_printf(dev, "cannot load DMA memory\n"); break; } ch->cb = cb_virt; ch->vc_cb = cb_phys; ch->flags = BCM_DMA_CH_FREE; ch->cb->info = INFO_WAIT_RESP; /* reset DMA engine */ bus_write_4(sc->sc_mem, BCM_DMA_CS(i), CS_RESET); } return (0); } /* * Allocate DMA channel for further use, returns channel # or * BCM_DMA_CH_INVALID */ int bcm_dma_allocate(int req_ch) { struct bcm_dma_softc *sc = bcm_dma_sc; int ch = BCM_DMA_CH_INVALID; int i; if (req_ch >= BCM_DMA_CH_MAX) return (BCM_DMA_CH_INVALID); /* Auto(req_ch < 0) or CH specified */ mtx_lock(&sc->sc_mtx); if (req_ch < 0) { for (i = 0; i < BCM_DMA_CH_MAX; i++) { if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) { ch = i; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; break; } } } else { if (sc->sc_dma_ch[req_ch].flags & BCM_DMA_CH_FREE) { ch = req_ch; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; } } mtx_unlock(&sc->sc_mtx); return (ch); } /* * Frees allocated channel. Returns 0 on success, -1 otherwise */ int bcm_dma_free(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); mtx_lock(&sc->sc_mtx); if (sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED) { sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_FREE; sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_USED; sc->sc_dma_ch[ch].intr_func = NULL; sc->sc_dma_ch[ch].intr_arg = NULL; /* reset DMA engine */ bcm_dma_reset(sc->sc_dev, ch); } mtx_unlock(&sc->sc_mtx); return (0); } /* * Assign handler function for channel interrupt * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); sc->sc_dma_ch[ch].intr_func = func; sc->sc_dma_ch[ch].intr_arg = arg; cb = sc->sc_dma_ch[ch].cb; cb->info |= INFO_INT_EN; return (0); } /* * Setup DMA source parameters * ch - channel number * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if * source is physical memory * inc_addr - BCM_DMA_INC_ADDR if source address * should be increased after each access or * BCM_DMA_SAME_ADDR if address should remain * the same * width - size of read operation, BCM_DMA_32BIT * for 32bit bursts, BCM_DMA_128BIT for 128 bits * * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width) { struct bcm_dma_softc *sc = bcm_dma_sc; uint32_t info; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); info = sc->sc_dma_ch[ch].cb->info; info &= ~INFO_PERMAP_MASK; info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; if (dreq) info |= INFO_S_DREQ; else info &= ~INFO_S_DREQ; if (width == BCM_DMA_128BIT) info |= INFO_S_WIDTH; else info &= ~INFO_S_WIDTH; if (inc_addr == BCM_DMA_INC_ADDR) info |= INFO_S_INC; else info &= ~INFO_S_INC; sc->sc_dma_ch[ch].cb->info = info; return (0); } /* * Setup DMA destination parameters * ch - channel number * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if * destination is physical memory * inc_addr - BCM_DMA_INC_ADDR if source address * should be increased after each access or * BCM_DMA_SAME_ADDR if address should remain * the same * width - size of write operation, BCM_DMA_32BIT * for 32bit bursts, BCM_DMA_128BIT for 128 bits * * Returns 0 on success, -1 otherwise */ int bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width) { struct bcm_dma_softc *sc = bcm_dma_sc; uint32_t info; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); info = sc->sc_dma_ch[ch].cb->info; info &= ~INFO_PERMAP_MASK; info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; if (dreq) info |= INFO_D_DREQ; else info &= ~INFO_D_DREQ; if (width == BCM_DMA_128BIT) info |= INFO_D_WIDTH; else info &= ~INFO_D_WIDTH; if (inc_addr == BCM_DMA_INC_ADDR) info |= INFO_D_INC; else info &= ~INFO_D_INC; sc->sc_dma_ch[ch].cb->info = info; return (0); } #ifdef DEBUG void bcm_dma_cb_dump(struct bcm_dma_cb *cb) { printf("DMA CB "); printf("INFO: %8.8x ", cb->info); printf("SRC: %8.8x ", cb->src); printf("DST: %8.8x ", cb->dst); printf("LEN: %8.8x ", cb->len); printf("\n"); printf("STRIDE: %8.8x ", cb->stride); printf("NEXT: %8.8x ", cb->next); printf("RSVD1: %8.8x ", cb->rsvd1); printf("RSVD2: %8.8x ", cb->rsvd2); printf("\n"); } void bcm_dma_reg_dump(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; int i; uint32_t reg; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return; printf("DMA%d: ", ch); for (i = 0; i < MAX_REG; i++) { reg = bus_read_4(sc->sc_mem, BCM_DMA_CH(ch) + i*4); printf("%8.8x ", reg); } printf("\n"); } #endif /* * Start DMA transaction * ch - channel number * src, dst - source and destination address in * ARM physical memory address space. * len - amount of bytes to be transferred * * Returns 0 on success, -1 otherwise */ int bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (-1); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (-1); cb = sc->sc_dma_ch[ch].cb; if (BCM2835_ARM_IS_IO(src)) cb->src = IO_TO_VCBUS(src); else cb->src = PHYS_TO_VCBUS(src); if (BCM2835_ARM_IS_IO(dst)) cb->dst = IO_TO_VCBUS(dst); else cb->dst = PHYS_TO_VCBUS(dst); cb->len = len; bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_ch[ch].dma_map, BUS_DMASYNC_PREWRITE); bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), sc->sc_dma_ch[ch].vc_cb); bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ACTIVE); #ifdef DEBUG bcm_dma_cb_dump(sc->sc_dma_ch[ch].cb); bcm_dma_reg_dump(ch); #endif return (0); } /* * Get length requested for DMA transaction * ch - channel number * * Returns size of transaction, 0 if channel is invalid */ uint32_t bcm_dma_length(int ch) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_cb *cb; if (ch < 0 || ch >= BCM_DMA_CH_MAX) return (0); if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) return (0); cb = sc->sc_dma_ch[ch].cb; return (cb->len); } static void bcm_dma_intr(void *arg) { struct bcm_dma_softc *sc = bcm_dma_sc; struct bcm_dma_ch *ch = (struct bcm_dma_ch *)arg; uint32_t cs, debug; /* my interrupt? */ cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch->ch)); if (!(cs & (CS_INT | CS_ERR))) { device_printf(sc->sc_dev, "unexpected DMA intr CH=%d, CS=%x\n", ch->ch, cs); return; } /* running? */ if (!(ch->flags & BCM_DMA_CH_USED)) { device_printf(sc->sc_dev, "unused DMA intr CH=%d, CS=%x\n", ch->ch, cs); return; } if (cs & CS_ERR) { debug = bus_read_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch)); device_printf(sc->sc_dev, "DMA error %d on CH%d\n", debug & DEBUG_ERROR_MASK, ch->ch); bus_write_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch), debug & DEBUG_ERROR_MASK); bcm_dma_reset(sc->sc_dev, ch->ch); } if (cs & CS_INT) { /* acknowledge interrupt */ bus_write_4(sc->sc_mem, BCM_DMA_CS(ch->ch), CS_INT | CS_END); /* Prepare for possible access to len field */ bus_dmamap_sync(sc->sc_dma_tag, ch->dma_map, BUS_DMASYNC_POSTWRITE); /* save callback function and argument */ if (ch->intr_func) ch->intr_func(ch->ch, ch->intr_arg); } } static int bcm_dma_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-dma")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2835 DMA Controller"); return (BUS_PROBE_DEFAULT); } static int bcm_dma_attach(device_t dev) { struct bcm_dma_softc *sc = device_get_softc(dev); phandle_t node; int rid, err = 0; int i; sc->sc_dev = dev; if (bcm_dma_sc) return (ENXIO); for (i = 0; i < BCM_DMA_CH_MAX; i++) { sc->sc_irq[i] = NULL; sc->sc_intrhand[i] = NULL; } /* Get DMA channel mask. */ node = ofw_bus_get_node(sc->sc_dev); if (OF_getencprop(node, "brcm,dma-channel-mask", &bcm_dma_channel_mask, sizeof(bcm_dma_channel_mask)) == -1 && OF_getencprop(node, "broadcom,channels", &bcm_dma_channel_mask, sizeof(bcm_dma_channel_mask)) == -1) { device_printf(dev, "could not get channel mask property\n"); return (ENXIO); } /* Mask out channels used by GPU. */ bcm_dma_channel_mask &= ~BCM_DMA_CH_GPU_MASK; /* DMA0 - DMA14 */ rid = 0; sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->sc_mem == NULL) { device_printf(dev, "could not allocate memory resource\n"); return (ENXIO); } /* IRQ DMA0 - DMA11 XXX NOT USE DMA12(spurious?) */ for (rid = 0; rid < BCM_DMA_CH_MAX; rid++) { if ((bcm_dma_channel_mask & (1 << rid)) == 0) continue; sc->sc_irq[rid] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (sc->sc_irq[rid] == NULL) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq[rid], INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_dma_intr, &sc->sc_dma_ch[rid], &sc->sc_intrhand[rid])) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } } mtx_init(&sc->sc_mtx, "bcmdma", "bcmdma", MTX_DEF); bcm_dma_sc = sc; err = bcm_dma_init(dev); if (err) goto fail; return (err); fail: if (sc->sc_mem) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem); for (i = 0; i < BCM_DMA_CH_MAX; i++) { if (sc->sc_intrhand[i]) bus_teardown_intr(dev, sc->sc_irq[i], sc->sc_intrhand[i]); if (sc->sc_irq[i]) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq[i]); } return (err); } static device_method_t bcm_dma_methods[] = { DEVMETHOD(device_probe, bcm_dma_probe), DEVMETHOD(device_attach, bcm_dma_attach), { 0, 0 } }; static driver_t bcm_dma_driver = { "bcm_dma", bcm_dma_methods, sizeof(struct bcm_dma_softc), }; static devclass_t bcm_dma_devclass; DRIVER_MODULE(bcm_dma, simplebus, bcm_dma_driver, bcm_dma_devclass, 0, 0); MODULE_VERSION(bcm_dma, 1); Index: head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 307087) +++ head/sys/arm/broadcom/bcm2835/bcm2835_sdhci.c (revision 307088) @@ -1,675 +1,682 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sdhci_if.h" #include "bcm2835_dma.h" #include #include "bcm2835_vcbus.h" #define BCM2835_DEFAULT_SDHCI_FREQ 50 #define BCM_SDHCI_BUFFER_SIZE 512 #define NUM_DMA_SEGS 2 #ifdef DEBUG #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ printf(fmt,##args); } while (0) #else #define dprintf(fmt, args...) #endif static int bcm2835_sdhci_hs = 1; static int bcm2835_sdhci_pio_mode = 0; +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-sdhci", 1}, + {"brcm,bcm2835-mmc", 1}, + {NULL, 0} +}; + TUNABLE_INT("hw.bcm2835.sdhci.hs", &bcm2835_sdhci_hs); TUNABLE_INT("hw.bcm2835.sdhci.pio_mode", &bcm2835_sdhci_pio_mode); struct bcm_sdhci_softc { device_t sc_dev; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; struct mmc_request * sc_req; struct sdhci_slot sc_slot; int sc_dma_ch; bus_dma_tag_t sc_dma_tag; bus_dmamap_t sc_dma_map; vm_paddr_t sc_sdhci_buffer_phys; uint32_t cmd_and_mode; bus_addr_t dmamap_seg_addrs[NUM_DMA_SEGS]; bus_size_t dmamap_seg_sizes[NUM_DMA_SEGS]; int dmamap_seg_count; int dmamap_seg_index; int dmamap_status; }; static int bcm_sdhci_probe(device_t); static int bcm_sdhci_attach(device_t); static int bcm_sdhci_detach(device_t); static void bcm_sdhci_intr(void *); static int bcm_sdhci_get_ro(device_t, device_t); static void bcm_sdhci_dma_intr(int ch, void *arg); static void bcm_sdhci_dmacb(void *arg, bus_dma_segment_t *segs, int nseg, int err) { struct bcm_sdhci_softc *sc = arg; int i; sc->dmamap_status = err; sc->dmamap_seg_count = nseg; /* Note nseg is guaranteed to be zero if err is non-zero. */ for (i = 0; i < nseg; i++) { sc->dmamap_seg_addrs[i] = segs[i].ds_addr; sc->dmamap_seg_sizes[i] = segs[i].ds_len; } } static int bcm_sdhci_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-sdhci")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "Broadcom 2708 SDHCI controller"); + return (BUS_PROBE_DEFAULT); } static int bcm_sdhci_attach(device_t dev) { struct bcm_sdhci_softc *sc = device_get_softc(dev); int rid, err; phandle_t node; pcell_t cell; u_int default_freq; sc->sc_dev = dev; sc->sc_req = NULL; err = bcm2835_mbox_set_power_state(BCM2835_MBOX_POWER_ID_EMMC, TRUE); if (err != 0) { if (bootverbose) device_printf(dev, "Unable to enable the power\n"); return (err); } default_freq = 0; err = bcm2835_mbox_get_clock_rate(BCM2835_MBOX_CLOCK_ID_EMMC, &default_freq); if (err == 0) { /* Convert to MHz */ default_freq /= 1000000; } if (default_freq == 0) { node = ofw_bus_get_node(sc->sc_dev); if ((OF_getencprop(node, "clock-frequency", &cell, sizeof(cell))) > 0) default_freq = cell / 1000000; } if (default_freq == 0) default_freq = BCM2835_DEFAULT_SDHCI_FREQ; if (bootverbose) device_printf(dev, "SDHCI frequency: %dMHz\n", default_freq); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); err = ENXIO; goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { device_printf(dev, "cannot allocate interrupt\n"); err = ENXIO; goto fail; } if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_BIO | INTR_MPSAFE, NULL, bcm_sdhci_intr, sc, &sc->sc_intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); err = ENXIO; goto fail; } if (!bcm2835_sdhci_pio_mode) sc->sc_slot.opt = SDHCI_PLATFORM_TRANSFER; sc->sc_slot.caps = SDHCI_CAN_VDD_330 | SDHCI_CAN_VDD_180; if (bcm2835_sdhci_hs) sc->sc_slot.caps |= SDHCI_CAN_DO_HISPD; sc->sc_slot.caps |= (default_freq << SDHCI_CLOCK_BASE_SHIFT); sc->sc_slot.quirks = SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_MISSING_CAPS; sdhci_init_slot(dev, &sc->sc_slot, 0); sc->sc_dma_ch = bcm_dma_allocate(BCM_DMA_CH_ANY); if (sc->sc_dma_ch == BCM_DMA_CH_INVALID) goto fail; bcm_dma_setup_intr(sc->sc_dma_ch, bcm_sdhci_dma_intr, sc); /* Allocate bus_dma resources. */ err = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, BCM_SDHCI_BUFFER_SIZE, NUM_DMA_SEGS, BCM_SDHCI_BUFFER_SIZE, BUS_DMA_ALLOCNOW, NULL, NULL, &sc->sc_dma_tag); if (err) { device_printf(dev, "failed allocate DMA tag"); goto fail; } err = bus_dmamap_create(sc->sc_dma_tag, 0, &sc->sc_dma_map); if (err) { device_printf(dev, "bus_dmamap_create failed\n"); goto fail; } sc->sc_sdhci_buffer_phys = BUS_SPACE_PHYSADDR(sc->sc_mem_res, SDHCI_BUFFER); bus_generic_probe(dev); bus_generic_attach(dev); sdhci_start_slot(&sc->sc_slot); return (0); fail: if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (err); } static int bcm_sdhci_detach(device_t dev) { return (EBUSY); } static void bcm_sdhci_intr(void *arg) { struct bcm_sdhci_softc *sc = arg; sdhci_generic_intr(&sc->sc_slot); } static int bcm_sdhci_get_ro(device_t bus, device_t child) { return (0); } static inline uint32_t RD4(struct bcm_sdhci_softc *sc, bus_size_t off) { uint32_t val = bus_space_read_4(sc->sc_bst, sc->sc_bsh, off); return val; } static inline void WR4(struct bcm_sdhci_softc *sc, bus_size_t off, uint32_t val) { bus_space_write_4(sc->sc_bst, sc->sc_bsh, off, val); /* * The Arasan HC has a bug where it may lose the content of * consecutive writes to registers that are within two SD-card * clock cycles of each other (a clock domain crossing problem). */ if (sc->sc_slot.clock > 0) DELAY(((2 * 1000000) / sc->sc_slot.clock) + 1); } static uint8_t bcm_sdhci_read_1(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); return ((val >> (off & 3)*8) & 0xff); } static uint16_t bcm_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val = RD4(sc, off & ~3); /* * Standard 32-bit handling of command and transfer mode. */ if (off == SDHCI_TRANSFER_MODE) { return (sc->cmd_and_mode >> 16); } else if (off == SDHCI_COMMAND_FLAGS) { return (sc->cmd_and_mode & 0x0000ffff); } return ((val >> (off & 3)*8) & 0xffff); } static uint32_t bcm_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off) { struct bcm_sdhci_softc *sc = device_get_softc(dev); return RD4(sc, off); } static void bcm_sdhci_read_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_read_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32 = RD4(sc, off & ~3); val32 &= ~(0xff << (off & 3)*8); val32 |= (val << (off & 3)*8); WR4(sc, off & ~3, val32); } static void bcm_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint16_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); uint32_t val32; if (off == SDHCI_COMMAND_FLAGS) val32 = sc->cmd_and_mode; else val32 = RD4(sc, off & ~3); val32 &= ~(0xffff << (off & 3)*8); val32 |= (val << (off & 3)*8); if (off == SDHCI_TRANSFER_MODE) sc->cmd_and_mode = val32; else { WR4(sc, off & ~3, val32); if (off == SDHCI_COMMAND_FLAGS) sc->cmd_and_mode = val32; } } static void bcm_sdhci_write_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t val) { struct bcm_sdhci_softc *sc = device_get_softc(dev); WR4(sc, off, val); } static void bcm_sdhci_write_multi_4(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint32_t *data, bus_size_t count) { struct bcm_sdhci_softc *sc = device_get_softc(dev); bus_space_write_multi_4(sc->sc_bst, sc->sc_bsh, off, data, count); } static void bcm_sdhci_start_dma_seg(struct bcm_sdhci_softc *sc) { struct sdhci_slot *slot; vm_paddr_t pdst, psrc; int err, idx, len, sync_op; slot = &sc->sc_slot; idx = sc->dmamap_seg_index++; len = sc->dmamap_seg_sizes[idx]; slot->offset += len; if (slot->curcmd->data->flags & MMC_DATA_READ) { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); psrc = sc->sc_sdhci_buffer_phys; pdst = sc->dmamap_seg_addrs[idx]; sync_op = BUS_DMASYNC_PREREAD; } else { bcm_dma_setup_src(sc->sc_dma_ch, BCM_DMA_DREQ_NONE, BCM_DMA_INC_ADDR, (len & 0xf) ? BCM_DMA_32BIT : BCM_DMA_128BIT); bcm_dma_setup_dst(sc->sc_dma_ch, BCM_DMA_DREQ_EMMC, BCM_DMA_SAME_ADDR, BCM_DMA_32BIT); psrc = sc->dmamap_seg_addrs[idx]; pdst = sc->sc_sdhci_buffer_phys; sync_op = BUS_DMASYNC_PREWRITE; } /* * When starting a new DMA operation do the busdma sync operation, and * disable SDCHI data interrrupts because we'll be driven by DMA * interrupts (or SDHCI error interrupts) until the IO is done. */ if (idx == 0) { bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); slot->intmask &= ~(SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END); bcm_sdhci_write_4(sc->sc_dev, &sc->sc_slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } /* * Start the DMA transfer. Only programming errors (like failing to * allocate a channel) cause a non-zero return from bcm_dma_start(). */ err = bcm_dma_start(sc->sc_dma_ch, psrc, pdst, len); KASSERT((err == 0), ("bcm2835_sdhci: failed DMA start")); } static void bcm_sdhci_dma_intr(int ch, void *arg) { struct bcm_sdhci_softc *sc = (struct bcm_sdhci_softc *)arg; struct sdhci_slot *slot = &sc->sc_slot; uint32_t reg, mask; int left, sync_op; mtx_lock(&slot->mtx); /* * If there are more segments for the current dma, start the next one. * Otherwise unload the dma map and decide what to do next based on the * status of the sdhci controller and whether there's more data left. */ if (sc->dmamap_seg_index < sc->dmamap_seg_count) { bcm_sdhci_start_dma_seg(sc); mtx_unlock(&slot->mtx); return; } if (slot->curcmd->data->flags & MMC_DATA_READ) { sync_op = BUS_DMASYNC_POSTREAD; mask = SDHCI_INT_DATA_AVAIL; } else { sync_op = BUS_DMASYNC_POSTWRITE; mask = SDHCI_INT_SPACE_AVAIL; } bus_dmamap_sync(sc->sc_dma_tag, sc->sc_dma_map, sync_op); bus_dmamap_unload(sc->sc_dma_tag, sc->sc_dma_map); sc->dmamap_seg_count = 0; sc->dmamap_seg_index = 0; left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); /* DATA END? */ reg = bcm_sdhci_read_4(slot->bus, slot, SDHCI_INT_STATUS); if (reg & SDHCI_INT_DATA_END) { /* ACK for all outstanding interrupts */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, reg); /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); /* finish this data */ sdhci_finish_data(slot); } else { /* already available? */ if (reg & mask) { /* ACK for DATA_AVAIL or SPACE_AVAIL */ bcm_sdhci_write_4(slot->bus, slot, SDHCI_INT_STATUS, mask); /* continue next DMA transfer */ if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; sdhci_finish_data(slot); } else { bcm_sdhci_start_dma_seg(sc); } } else { /* wait for next data by INT */ /* enable INT */ slot->intmask |= SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_END; bcm_sdhci_write_4(slot->bus, slot, SDHCI_SIGNAL_ENABLE, slot->intmask); } } mtx_unlock(&slot->mtx); } static void bcm_sdhci_read_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %d, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static void bcm_sdhci_write_dma(device_t dev, struct sdhci_slot *slot) { struct bcm_sdhci_softc *sc = device_get_softc(slot->bus); size_t left; if (sc->dmamap_seg_count != 0) { device_printf(sc->sc_dev, "DMA in use\n"); return; } left = min(BCM_SDHCI_BUFFER_SIZE, slot->curcmd->data->len - slot->offset); KASSERT((left & 3) == 0, ("%s: len = %d, not word-aligned", __func__, left)); if (bus_dmamap_load(sc->sc_dma_tag, sc->sc_dma_map, (uint8_t *)slot->curcmd->data->data + slot->offset, left, bcm_sdhci_dmacb, sc, BUS_DMA_NOWAIT) != 0 || sc->dmamap_status != 0) { slot->curcmd->error = MMC_ERR_NO_MEMORY; return; } /* DMA start */ bcm_sdhci_start_dma_seg(sc); } static int bcm_sdhci_will_handle_transfer(device_t dev, struct sdhci_slot *slot) { size_t left; /* * Do not use DMA for transfers less than block size or with a length * that is not a multiple of four. */ left = min(BCM_DMA_BLOCK_SIZE, slot->curcmd->data->len - slot->offset); if (left < BCM_DMA_BLOCK_SIZE) return (0); if (left & 0x03) return (0); return (1); } static void bcm_sdhci_start_transfer(device_t dev, struct sdhci_slot *slot, uint32_t *intmask) { /* DMA transfer FIFO 1KB */ if (slot->curcmd->data->flags & MMC_DATA_READ) bcm_sdhci_read_dma(dev, slot); else bcm_sdhci_write_dma(dev, slot); } static void bcm_sdhci_finish_transfer(device_t dev, struct sdhci_slot *slot) { sdhci_finish_data(slot); } static device_method_t bcm_sdhci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_sdhci_probe), DEVMETHOD(device_attach, bcm_sdhci_attach), DEVMETHOD(device_detach, bcm_sdhci_detach), /* Bus interface */ DEVMETHOD(bus_read_ivar, sdhci_generic_read_ivar), DEVMETHOD(bus_write_ivar, sdhci_generic_write_ivar), DEVMETHOD(bus_print_child, bus_generic_print_child), /* MMC bridge interface */ DEVMETHOD(mmcbr_update_ios, sdhci_generic_update_ios), DEVMETHOD(mmcbr_request, sdhci_generic_request), DEVMETHOD(mmcbr_get_ro, bcm_sdhci_get_ro), DEVMETHOD(mmcbr_acquire_host, sdhci_generic_acquire_host), DEVMETHOD(mmcbr_release_host, sdhci_generic_release_host), /* Platform transfer methods */ DEVMETHOD(sdhci_platform_will_handle, bcm_sdhci_will_handle_transfer), DEVMETHOD(sdhci_platform_start_transfer, bcm_sdhci_start_transfer), DEVMETHOD(sdhci_platform_finish_transfer, bcm_sdhci_finish_transfer), /* SDHCI registers accessors */ DEVMETHOD(sdhci_read_1, bcm_sdhci_read_1), DEVMETHOD(sdhci_read_2, bcm_sdhci_read_2), DEVMETHOD(sdhci_read_4, bcm_sdhci_read_4), DEVMETHOD(sdhci_read_multi_4, bcm_sdhci_read_multi_4), DEVMETHOD(sdhci_write_1, bcm_sdhci_write_1), DEVMETHOD(sdhci_write_2, bcm_sdhci_write_2), DEVMETHOD(sdhci_write_4, bcm_sdhci_write_4), DEVMETHOD(sdhci_write_multi_4, bcm_sdhci_write_multi_4), { 0, 0 } }; static devclass_t bcm_sdhci_devclass; static driver_t bcm_sdhci_driver = { "sdhci_bcm", bcm_sdhci_methods, sizeof(struct bcm_sdhci_softc), }; DRIVER_MODULE(sdhci_bcm, simplebus, bcm_sdhci_driver, bcm_sdhci_devclass, 0, 0); MODULE_DEPEND(sdhci_bcm, sdhci, 1, 1, 1); DRIVER_MODULE(mmc, sdhci_bcm, mmc_driver, mmc_devclass, NULL, NULL); MODULE_DEPEND(sdhci_bcm, mmc, 1, 1, 1); Index: head/sys/arm/broadcom/bcm2835/bcm2835_spi.c =================================================================== --- head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 307087) +++ head/sys/arm/broadcom/bcm2835/bcm2835_spi.c (revision 307088) @@ -1,519 +1,525 @@ /*- * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2013 Luiz Otavio O Souza * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "spibus_if.h" +static struct ofw_compat_data compat_data[] = { + {"broadcom,bcm2835-spi", 1}, + {"brcm,bcm2835-spi", 1}, + {NULL, 0} +}; + static void bcm_spi_intr(void *); #ifdef BCM_SPI_DEBUG static void bcm_spi_printr(device_t dev) { struct bcm_spi_softc *sc; uint32_t reg; sc = device_get_softc(dev); reg = BCM_SPI_READ(sc, SPI_CS); device_printf(dev, "CS=%b\n", reg, "\20\1CS0\2CS1\3CPHA\4CPOL\7CSPOL" "\10TA\11DMAEN\12INTD\13INTR\14ADCS\15REN\16LEN" "\21DONE\22RXD\23TXD\24RXR\25RXF\26CSPOL0\27CSPOL1" "\30CSPOL2\31DMA_LEN\32LEN_LONG"); reg = BCM_SPI_READ(sc, SPI_CLK) & SPI_CLK_MASK; if (reg % 2) reg--; if (reg == 0) reg = 65536; device_printf(dev, "CLK=%uMhz/%d=%luhz\n", SPI_CORE_CLK / 1000000, reg, SPI_CORE_CLK / reg); reg = BCM_SPI_READ(sc, SPI_DLEN) & SPI_DLEN_MASK; device_printf(dev, "DLEN=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_LTOH) & SPI_LTOH_MASK; device_printf(dev, "LTOH=%d\n", reg); reg = BCM_SPI_READ(sc, SPI_DC); device_printf(dev, "DC=RPANIC=%#x RDREQ=%#x TPANIC=%#x TDREQ=%#x\n", (reg & SPI_DC_RPANIC_MASK) >> SPI_DC_RPANIC_SHIFT, (reg & SPI_DC_RDREQ_MASK) >> SPI_DC_RDREQ_SHIFT, (reg & SPI_DC_TPANIC_MASK) >> SPI_DC_TPANIC_SHIFT, (reg & SPI_DC_TDREQ_MASK) >> SPI_DC_TDREQ_SHIFT); } #endif static void bcm_spi_modifyreg(struct bcm_spi_softc *sc, uint32_t off, uint32_t mask, uint32_t value) { uint32_t reg; mtx_assert(&sc->sc_mtx, MA_OWNED); reg = BCM_SPI_READ(sc, off); reg &= ~mask; reg |= value; BCM_SPI_WRITE(sc, off, reg); } static int bcm_spi_clock_proc(SYSCTL_HANDLER_ARGS) { struct bcm_spi_softc *sc; uint32_t clk; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); clk = BCM_SPI_READ(sc, SPI_CLK); BCM_SPI_UNLOCK(sc); clk &= 0xffff; if (clk == 0) clk = 65536; clk = SPI_CORE_CLK / clk; error = sysctl_handle_int(oidp, &clk, sizeof(clk), req); if (error != 0 || req->newptr == NULL) return (error); clk = SPI_CORE_CLK / clk; if (clk <= 1) clk = 2; else if (clk % 2) clk--; if (clk > 0xffff) clk = 0; BCM_SPI_LOCK(sc); BCM_SPI_WRITE(sc, SPI_CLK, clk); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cs_bit_proc(SYSCTL_HANDLER_ARGS, uint32_t bit) { struct bcm_spi_softc *sc; uint32_t reg; int error; sc = (struct bcm_spi_softc *)arg1; BCM_SPI_LOCK(sc); reg = BCM_SPI_READ(sc, SPI_CS); BCM_SPI_UNLOCK(sc); reg = (reg & bit) ? 1 : 0; error = sysctl_handle_int(oidp, ®, sizeof(reg), req); if (error != 0 || req->newptr == NULL) return (error); if (reg) reg = bit; BCM_SPI_LOCK(sc); bcm_spi_modifyreg(sc, SPI_CS, bit, reg); BCM_SPI_UNLOCK(sc); return (0); } static int bcm_spi_cpol_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPOL)); } static int bcm_spi_cpha_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CPHA)); } static int bcm_spi_cspol0_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL0)); } static int bcm_spi_cspol1_proc(SYSCTL_HANDLER_ARGS) { return (bcm_spi_cs_bit_proc(oidp, arg1, arg2, req, SPI_CS_CSPOL1)); } static void bcm_spi_sysctl_init(struct bcm_spi_softc *sc) { struct sysctl_ctx_list *ctx; struct sysctl_oid *tree_node; struct sysctl_oid_list *tree; /* * Add system sysctl tree/handlers. */ ctx = device_get_sysctl_ctx(sc->sc_dev); tree_node = device_get_sysctl_tree(sc->sc_dev); tree = SYSCTL_CHILDREN(tree_node); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "clock", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_clock_proc, "IU", "SPI BUS clock frequency"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpol", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpol_proc, "IU", "SPI BUS clock polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cpha", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cpha_proc, "IU", "SPI BUS clock phase"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol0", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol0_proc, "IU", "SPI BUS chip select 0 polarity"); SYSCTL_ADD_PROC(ctx, tree, OID_AUTO, "cspol1", CTLFLAG_RW | CTLTYPE_UINT, sc, sizeof(*sc), bcm_spi_cspol1_proc, "IU", "SPI BUS chip select 1 polarity"); } static int bcm_spi_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); - if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-spi")) + if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "BCM2708/2835 SPI controller"); return (BUS_PROBE_DEFAULT); } static int bcm_spi_attach(device_t dev) { struct bcm_spi_softc *sc; device_t gpio; int i, rid; if (device_get_unit(dev) != 0) { device_printf(dev, "only one SPI controller supported\n"); return (ENXIO); } sc = device_get_softc(dev); sc->sc_dev = dev; /* Configure the GPIO pins to ALT0 function to enable SPI the pins. */ gpio = devclass_get_device(devclass_find("gpio"), 0); if (!gpio) { device_printf(dev, "cannot find gpio0\n"); return (ENXIO); } for (i = 0; i < nitems(bcm_spi_pins); i++) bcm_gpio_set_alternate(gpio, bcm_spi_pins[i], BCM_GPIO_ALT0); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); return (ENXIO); } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); return (ENXIO); } /* Hook up our interrupt handler. */ if (bus_setup_intr(dev, sc->sc_irq_res, INTR_TYPE_MISC | INTR_MPSAFE, NULL, bcm_spi_intr, sc, &sc->sc_intrhand)) { bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot setup the interrupt handler\n"); return (ENXIO); } mtx_init(&sc->sc_mtx, "bcm_spi", NULL, MTX_DEF); /* Add sysctl nodes. */ bcm_spi_sysctl_init(sc); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif /* * Enable the SPI controller. Clear the rx and tx FIFO. * Defaults to SPI mode 0. */ BCM_SPI_WRITE(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); /* Set the SPI clock to 500Khz. */ BCM_SPI_WRITE(sc, SPI_CLK, SPI_CORE_CLK / 500000); #ifdef BCM_SPI_DEBUG bcm_spi_printr(dev); #endif device_add_child(dev, "spibus", -1); return (bus_generic_attach(dev)); } static int bcm_spi_detach(device_t dev) { struct bcm_spi_softc *sc; bus_generic_detach(dev); sc = device_get_softc(dev); mtx_destroy(&sc->sc_mtx); if (sc->sc_intrhand) bus_teardown_intr(dev, sc->sc_irq_res, sc->sc_intrhand); if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); return (0); } static void bcm_spi_fill_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, written; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); while (sc->sc_written < sc->sc_len && cs == (SPI_CS_TA | SPI_CS_TXD)) { data = (uint8_t *)cmd->tx_cmd; written = sc->sc_written++; if (written >= cmd->tx_cmd_sz) { data = (uint8_t *)cmd->tx_data; written -= cmd->tx_cmd_sz; } BCM_SPI_WRITE(sc, SPI_FIFO, data[written]); cs = BCM_SPI_READ(sc, SPI_CS) & (SPI_CS_TA | SPI_CS_TXD); } } static void bcm_spi_drain_fifo(struct bcm_spi_softc *sc) { struct spi_command *cmd; uint32_t cs, read; uint8_t *data; cmd = sc->sc_cmd; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; while (sc->sc_read < sc->sc_len && cs == SPI_CS_RXD) { data = (uint8_t *)cmd->rx_cmd; read = sc->sc_read++; if (read >= cmd->rx_cmd_sz) { data = (uint8_t *)cmd->rx_data; read -= cmd->rx_cmd_sz; } data[read] = BCM_SPI_READ(sc, SPI_FIFO) & 0xff; cs = BCM_SPI_READ(sc, SPI_CS) & SPI_CS_RXD; } } static void bcm_spi_intr(void *arg) { struct bcm_spi_softc *sc; sc = (struct bcm_spi_softc *)arg; BCM_SPI_LOCK(sc); /* Filter stray interrupts. */ if ((sc->sc_flags & BCM_SPI_BUSY) == 0) { BCM_SPI_UNLOCK(sc); return; } /* TX - Fill up the FIFO. */ bcm_spi_fill_fifo(sc); /* RX - Drain the FIFO. */ bcm_spi_drain_fifo(sc); /* Check for end of transfer. */ if (sc->sc_written == sc->sc_len && sc->sc_read == sc->sc_len) { /* Disable interrupts and the SPI engine. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); wakeup(sc->sc_dev); } BCM_SPI_UNLOCK(sc); } static int bcm_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) { struct bcm_spi_softc *sc; int cs, err; sc = device_get_softc(dev); KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("TX/RX command sizes should be equal")); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, ("TX/RX data sizes should be equal")); /* Get the proper chip select for this child. */ spibus_get_cs(child, &cs); if (cs < 0 || cs > 2) { device_printf(dev, "Invalid chip select %d requested by %s\n", cs, device_get_nameunit(child)); return (EINVAL); } BCM_SPI_LOCK(sc); /* If the controller is in use wait until it is available. */ while (sc->sc_flags & BCM_SPI_BUSY) mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", 0); /* Now we have control over SPI controller. */ sc->sc_flags = BCM_SPI_BUSY; /* Clear the FIFO. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO, SPI_CS_CLEAR_RXFIFO | SPI_CS_CLEAR_TXFIFO); /* Save a pointer to the SPI command. */ sc->sc_cmd = cmd; sc->sc_read = 0; sc->sc_written = 0; sc->sc_len = cmd->tx_cmd_sz + cmd->tx_data_sz; /* * Set the CS for this transaction, enable interrupts and announce * we're ready to tx. This will kick off the first interrupt. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_MASK | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, cs | SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD); /* Wait for the transaction to complete. */ err = mtx_sleep(dev, &sc->sc_mtx, 0, "bcm_spi", hz * 2); /* Make sure the SPI engine and interrupts are disabled. */ bcm_spi_modifyreg(sc, SPI_CS, SPI_CS_TA | SPI_CS_INTR | SPI_CS_INTD, 0); /* Release the controller and wakeup the next thread waiting for it. */ sc->sc_flags = 0; wakeup_one(dev); BCM_SPI_UNLOCK(sc); /* * Check for transfer timeout. The SPI controller doesn't * return errors. */ if (err == EWOULDBLOCK) { device_printf(sc->sc_dev, "SPI error\n"); err = EIO; } return (err); } static phandle_t bcm_spi_get_node(device_t bus, device_t dev) { /* We only have one child, the SPI bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t bcm_spi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, bcm_spi_probe), DEVMETHOD(device_attach, bcm_spi_attach), DEVMETHOD(device_detach, bcm_spi_detach), /* SPI interface */ DEVMETHOD(spibus_transfer, bcm_spi_transfer), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, bcm_spi_get_node), DEVMETHOD_END }; static devclass_t bcm_spi_devclass; static driver_t bcm_spi_driver = { "spi", bcm_spi_methods, sizeof(struct bcm_spi_softc), }; DRIVER_MODULE(bcm2835_spi, simplebus, bcm_spi_driver, bcm_spi_devclass, 0, 0);