1cdafe74eSOleksandr Tymoshenko /* 2cdafe74eSOleksandr Tymoshenko * Copyright (c) 2013 Daisuke Aoyama <aoyama@peach.ne.jp> 3cdafe74eSOleksandr Tymoshenko * Copyright (c) 2013 Oleksandr Tymoshenko <gonzo@bluezbox.com> 4cdafe74eSOleksandr Tymoshenko * 5cdafe74eSOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 6cdafe74eSOleksandr Tymoshenko * modification, are permitted provided that the following conditions 7cdafe74eSOleksandr Tymoshenko * are met: 8cdafe74eSOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 9cdafe74eSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 10cdafe74eSOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 11cdafe74eSOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 12cdafe74eSOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 13cdafe74eSOleksandr Tymoshenko * 14cdafe74eSOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15cdafe74eSOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16cdafe74eSOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17cdafe74eSOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18cdafe74eSOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19cdafe74eSOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20cdafe74eSOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21cdafe74eSOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22cdafe74eSOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23cdafe74eSOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24cdafe74eSOleksandr Tymoshenko * SUCH DAMAGE. 25cdafe74eSOleksandr Tymoshenko * 26cdafe74eSOleksandr Tymoshenko */ 27cdafe74eSOleksandr Tymoshenko 28cdafe74eSOleksandr Tymoshenko #include <sys/cdefs.h> 29cdafe74eSOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 30cdafe74eSOleksandr Tymoshenko 31cdafe74eSOleksandr Tymoshenko #include <sys/param.h> 32cdafe74eSOleksandr Tymoshenko #include <sys/systm.h> 33cdafe74eSOleksandr Tymoshenko #include <sys/bus.h> 34cdafe74eSOleksandr Tymoshenko #include <sys/kernel.h> 35cdafe74eSOleksandr Tymoshenko #include <sys/lock.h> 36cdafe74eSOleksandr Tymoshenko #include <sys/malloc.h> 37cdafe74eSOleksandr Tymoshenko #include <sys/module.h> 38cdafe74eSOleksandr Tymoshenko #include <sys/mutex.h> 39cdafe74eSOleksandr Tymoshenko #include <sys/queue.h> 40cdafe74eSOleksandr Tymoshenko #include <sys/resource.h> 41cdafe74eSOleksandr Tymoshenko #include <sys/rman.h> 42cdafe74eSOleksandr Tymoshenko 43cdafe74eSOleksandr Tymoshenko #include <dev/fdt/fdt_common.h> 44cdafe74eSOleksandr Tymoshenko #include <dev/ofw/openfirm.h> 45cdafe74eSOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 46cdafe74eSOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 47cdafe74eSOleksandr Tymoshenko 48cdafe74eSOleksandr Tymoshenko #include <vm/vm.h> 49cdafe74eSOleksandr Tymoshenko #include <vm/pmap.h> 50cdafe74eSOleksandr Tymoshenko #include <machine/bus.h> 51cdafe74eSOleksandr Tymoshenko #include <machine/cpu.h> 52cdafe74eSOleksandr Tymoshenko #include <machine/cpufunc.h> 53cdafe74eSOleksandr Tymoshenko #include <machine/pmap.h> 54cdafe74eSOleksandr Tymoshenko 55cdafe74eSOleksandr Tymoshenko #include "bcm2835_dma.h" 56cdafe74eSOleksandr Tymoshenko #include "bcm2835_vcbus.h" 57cdafe74eSOleksandr Tymoshenko 58cdafe74eSOleksandr Tymoshenko #define MAX_REG 9 59cdafe74eSOleksandr Tymoshenko 60cdafe74eSOleksandr Tymoshenko /* private flags */ 61cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CH_USED 0x00000001 62cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CH_FREE 0x40000000 63cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CH_UNMAP 0x80000000 64cdafe74eSOleksandr Tymoshenko 65cdafe74eSOleksandr Tymoshenko /* Register Map (4.2.1.2) */ 66cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CS(n) (0x100*(n) + 0x00) 67cdafe74eSOleksandr Tymoshenko #define CS_ACTIVE (1 << 0) 68cdafe74eSOleksandr Tymoshenko #define CS_END (1 << 1) 69cdafe74eSOleksandr Tymoshenko #define CS_INT (1 << 2) 70cdafe74eSOleksandr Tymoshenko #define CS_DREQ (1 << 3) 71cdafe74eSOleksandr Tymoshenko #define CS_ISPAUSED (1 << 4) 72cdafe74eSOleksandr Tymoshenko #define CS_ISHELD (1 << 5) 73cdafe74eSOleksandr Tymoshenko #define CS_ISWAIT (1 << 6) 74cdafe74eSOleksandr Tymoshenko #define CS_ERR (1 << 8) 75cdafe74eSOleksandr Tymoshenko #define CS_WAITWRT (1 << 28) 76cdafe74eSOleksandr Tymoshenko #define CS_DISDBG (1 << 29) 77cdafe74eSOleksandr Tymoshenko #define CS_ABORT (1 << 30) 78cdafe74eSOleksandr Tymoshenko #define CS_RESET (1 << 31) 79cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CBADDR(n) (0x100*(n) + 0x04) 80cdafe74eSOleksandr Tymoshenko #define BCM_DMA_INFO(n) (0x100*(n) + 0x08) 81cdafe74eSOleksandr Tymoshenko #define INFO_INT_EN (1 << 0) 82cdafe74eSOleksandr Tymoshenko #define INFO_TDMODE (1 << 1) 83cdafe74eSOleksandr Tymoshenko #define INFO_WAIT_RESP (1 << 3) 84cdafe74eSOleksandr Tymoshenko #define INFO_D_INC (1 << 4) 85cdafe74eSOleksandr Tymoshenko #define INFO_D_WIDTH (1 << 5) 86cdafe74eSOleksandr Tymoshenko #define INFO_D_DREQ (1 << 6) 87cdafe74eSOleksandr Tymoshenko #define INFO_S_INC (1 << 8) 88cdafe74eSOleksandr Tymoshenko #define INFO_S_WIDTH (1 << 9) 89cdafe74eSOleksandr Tymoshenko #define INFO_S_DREQ (1 << 10) 90cdafe74eSOleksandr Tymoshenko #define INFO_WAITS_SHIFT (21) 91cdafe74eSOleksandr Tymoshenko #define INFO_PERMAP_SHIFT (16) 92cdafe74eSOleksandr Tymoshenko #define INFO_PERMAP_MASK (0x1f << INFO_PERMAP_SHIFT) 93cdafe74eSOleksandr Tymoshenko 94cdafe74eSOleksandr Tymoshenko #define BCM_DMA_SRC(n) (0x100*(n) + 0x0C) 95cdafe74eSOleksandr Tymoshenko #define BCM_DMA_DST(n) (0x100*(n) + 0x10) 96cdafe74eSOleksandr Tymoshenko #define BCM_DMA_LEN(n) (0x100*(n) + 0x14) 97cdafe74eSOleksandr Tymoshenko #define BCM_DMA_STRIDE(n) (0x100*(n) + 0x18) 98cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CBNEXT(n) (0x100*(n) + 0x1C) 99cdafe74eSOleksandr Tymoshenko #define BCM_DMA_DEBUG(n) (0x100*(n) + 0x20) 100cdafe74eSOleksandr Tymoshenko #define DEBUG_ERROR_MASK (7) 101cdafe74eSOleksandr Tymoshenko 102cdafe74eSOleksandr Tymoshenko #define BCM_DMA_INT_STATUS 0xfe0 103cdafe74eSOleksandr Tymoshenko #define BCM_DMA_ENABLE 0xff0 104cdafe74eSOleksandr Tymoshenko 105cdafe74eSOleksandr Tymoshenko /* relative offset from BCM_VC_DMA0_BASE (p.39) */ 106cdafe74eSOleksandr Tymoshenko #define BCM_DMA_CH(n) (0x100*(n)) 107cdafe74eSOleksandr Tymoshenko 108cdafe74eSOleksandr Tymoshenko /* DMA Control Block - 256bit aligned (p.40) */ 109cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb { 110cdafe74eSOleksandr Tymoshenko uint32_t info; /* Transfer Information */ 111cdafe74eSOleksandr Tymoshenko uint32_t src; /* Source Address */ 112cdafe74eSOleksandr Tymoshenko uint32_t dst; /* Destination Address */ 113cdafe74eSOleksandr Tymoshenko uint32_t len; /* Transfer Length */ 114cdafe74eSOleksandr Tymoshenko uint32_t stride; /* 2D Mode Stride */ 115cdafe74eSOleksandr Tymoshenko uint32_t next; /* Next Control Block Address */ 116cdafe74eSOleksandr Tymoshenko uint32_t rsvd1; /* Reserved */ 117cdafe74eSOleksandr Tymoshenko uint32_t rsvd2; /* Reserved */ 118cdafe74eSOleksandr Tymoshenko }; 119cdafe74eSOleksandr Tymoshenko 120cdafe74eSOleksandr Tymoshenko #ifdef DEBUG 121cdafe74eSOleksandr Tymoshenko static void bcm_dma_cb_dump(struct bcm_dma_cb *cb); 122cdafe74eSOleksandr Tymoshenko static void bcm_dma_reg_dump(int ch); 123cdafe74eSOleksandr Tymoshenko #endif 124cdafe74eSOleksandr Tymoshenko 125cdafe74eSOleksandr Tymoshenko /* DMA channel private info */ 126cdafe74eSOleksandr Tymoshenko struct bcm_dma_ch { 127cdafe74eSOleksandr Tymoshenko int ch; 128cdafe74eSOleksandr Tymoshenko uint32_t flags; 129cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb * cb; 130cdafe74eSOleksandr Tymoshenko uint32_t vc_cb; 131cdafe74eSOleksandr Tymoshenko bus_dmamap_t dma_map; 132cdafe74eSOleksandr Tymoshenko void (*intr_func)(int, void *); 133cdafe74eSOleksandr Tymoshenko void * intr_arg; 134cdafe74eSOleksandr Tymoshenko }; 135cdafe74eSOleksandr Tymoshenko 136cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc { 137cdafe74eSOleksandr Tymoshenko device_t sc_dev; 138cdafe74eSOleksandr Tymoshenko struct mtx sc_mtx; 139cdafe74eSOleksandr Tymoshenko struct resource * sc_mem; 140cdafe74eSOleksandr Tymoshenko struct resource * sc_irq[BCM_DMA_CH_MAX]; 141cdafe74eSOleksandr Tymoshenko void * sc_intrhand[BCM_DMA_CH_MAX]; 142cdafe74eSOleksandr Tymoshenko struct bcm_dma_ch sc_dma_ch[BCM_DMA_CH_MAX]; 143cdafe74eSOleksandr Tymoshenko bus_dma_tag_t sc_dma_tag; 144cdafe74eSOleksandr Tymoshenko }; 145cdafe74eSOleksandr Tymoshenko 146cdafe74eSOleksandr Tymoshenko static struct bcm_dma_softc *bcm_dma_sc = NULL; 147cdafe74eSOleksandr Tymoshenko 148cdafe74eSOleksandr Tymoshenko static void 149cdafe74eSOleksandr Tymoshenko bcm_dmamap_cb(void *arg, bus_dma_segment_t *segs, 150cdafe74eSOleksandr Tymoshenko int nseg, int err) 151cdafe74eSOleksandr Tymoshenko { 152cdafe74eSOleksandr Tymoshenko bus_addr_t *addr; 153cdafe74eSOleksandr Tymoshenko 154cdafe74eSOleksandr Tymoshenko if (err) 155cdafe74eSOleksandr Tymoshenko return; 156cdafe74eSOleksandr Tymoshenko 157cdafe74eSOleksandr Tymoshenko addr = (bus_addr_t*)arg; 158cdafe74eSOleksandr Tymoshenko *addr = PHYS_TO_VCBUS(segs[0].ds_addr); 159cdafe74eSOleksandr Tymoshenko } 160cdafe74eSOleksandr Tymoshenko 161cdafe74eSOleksandr Tymoshenko static void 162cdafe74eSOleksandr Tymoshenko bcm_dma_reset(device_t dev, int ch) 163cdafe74eSOleksandr Tymoshenko { 164cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = device_get_softc(dev); 165cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb *cb; 166cdafe74eSOleksandr Tymoshenko uint32_t cs; 167cdafe74eSOleksandr Tymoshenko int count; 168cdafe74eSOleksandr Tymoshenko 169cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 170cdafe74eSOleksandr Tymoshenko return; 171cdafe74eSOleksandr Tymoshenko 172cdafe74eSOleksandr Tymoshenko cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); 173cdafe74eSOleksandr Tymoshenko 174cdafe74eSOleksandr Tymoshenko if (cs & CS_ACTIVE) { 175cdafe74eSOleksandr Tymoshenko /* pause current task */ 176cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), 0); 177cdafe74eSOleksandr Tymoshenko 178cdafe74eSOleksandr Tymoshenko count = 1000; 179cdafe74eSOleksandr Tymoshenko do { 180cdafe74eSOleksandr Tymoshenko cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch)); 181cdafe74eSOleksandr Tymoshenko } while (!(cs & CS_ISPAUSED) && (count-- > 0)); 182cdafe74eSOleksandr Tymoshenko 183cdafe74eSOleksandr Tymoshenko if (!(cs & CS_ISPAUSED)) { 184cdafe74eSOleksandr Tymoshenko device_printf(dev, 185cdafe74eSOleksandr Tymoshenko "Can't abort DMA transfer at channel %d\n", ch); 186cdafe74eSOleksandr Tymoshenko } 187cdafe74eSOleksandr Tymoshenko 188cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); 189cdafe74eSOleksandr Tymoshenko 190cdafe74eSOleksandr Tymoshenko /* Complete everything, clear interrupt */ 191cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), 192cdafe74eSOleksandr Tymoshenko CS_ABORT | CS_INT | CS_END| CS_ACTIVE); 193cdafe74eSOleksandr Tymoshenko } 194cdafe74eSOleksandr Tymoshenko 195cdafe74eSOleksandr Tymoshenko /* clear control blocks */ 196cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), 0); 197cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CBNEXT(ch), 0); 198cdafe74eSOleksandr Tymoshenko 199cdafe74eSOleksandr Tymoshenko /* Reset control block */ 200cdafe74eSOleksandr Tymoshenko cb = sc->sc_dma_ch[ch].cb; 201cdafe74eSOleksandr Tymoshenko bzero(cb, sizeof(cb)); 202*e9401a9eSOleksandr Tymoshenko cb->info = INFO_WAIT_RESP; 203cdafe74eSOleksandr Tymoshenko } 204cdafe74eSOleksandr Tymoshenko 205cdafe74eSOleksandr Tymoshenko static int 206cdafe74eSOleksandr Tymoshenko bcm_dma_init(device_t dev) 207cdafe74eSOleksandr Tymoshenko { 208cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = device_get_softc(dev); 209cdafe74eSOleksandr Tymoshenko uint32_t mask; 210cdafe74eSOleksandr Tymoshenko struct bcm_dma_ch *ch; 211cdafe74eSOleksandr Tymoshenko void *cb_virt; 212cdafe74eSOleksandr Tymoshenko vm_paddr_t cb_phys; 213cdafe74eSOleksandr Tymoshenko int err; 214cdafe74eSOleksandr Tymoshenko int i; 215cdafe74eSOleksandr Tymoshenko 216cdafe74eSOleksandr Tymoshenko /* disable and clear interrupt status */ 217cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_ENABLE, 0); 218cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_INT_STATUS, 0); 219cdafe74eSOleksandr Tymoshenko 220cdafe74eSOleksandr Tymoshenko /* Allocate DMA chunks control blocks */ 221cdafe74eSOleksandr Tymoshenko /* p.40 of spec - control block should be 32-bit aligned */ 222cdafe74eSOleksandr Tymoshenko err = bus_dma_tag_create(bus_get_dma_tag(dev), 223cdafe74eSOleksandr Tymoshenko 1, 0, BUS_SPACE_MAXADDR_32BIT, 224cdafe74eSOleksandr Tymoshenko BUS_SPACE_MAXADDR, NULL, NULL, 225cdafe74eSOleksandr Tymoshenko sizeof(struct bcm_dma_cb), 1, 226cdafe74eSOleksandr Tymoshenko sizeof(struct bcm_dma_cb), 227cdafe74eSOleksandr Tymoshenko BUS_DMA_ALLOCNOW, NULL, NULL, 228cdafe74eSOleksandr Tymoshenko &sc->sc_dma_tag); 229cdafe74eSOleksandr Tymoshenko 230cdafe74eSOleksandr Tymoshenko if (err) { 231cdafe74eSOleksandr Tymoshenko device_printf(dev, "failed allocate DMA tag"); 232cdafe74eSOleksandr Tymoshenko return (err); 233cdafe74eSOleksandr Tymoshenko } 234cdafe74eSOleksandr Tymoshenko 235cdafe74eSOleksandr Tymoshenko /* setup initial settings */ 236cdafe74eSOleksandr Tymoshenko for (i = 0; i < BCM_DMA_CH_MAX; i++) { 237cdafe74eSOleksandr Tymoshenko ch = &sc->sc_dma_ch[i]; 238cdafe74eSOleksandr Tymoshenko 239cdafe74eSOleksandr Tymoshenko err = bus_dmamem_alloc(sc->sc_dma_tag, &cb_virt, 240cdafe74eSOleksandr Tymoshenko BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO, 241cdafe74eSOleksandr Tymoshenko &ch->dma_map); 242cdafe74eSOleksandr Tymoshenko if (err) { 243cdafe74eSOleksandr Tymoshenko device_printf(dev, "cannot allocate DMA memory\n"); 244cdafe74eSOleksandr Tymoshenko break; 245cdafe74eSOleksandr Tymoshenko } 246cdafe74eSOleksandr Tymoshenko 247cdafe74eSOleksandr Tymoshenko /* 248cdafe74eSOleksandr Tymoshenko * Least alignment for busdma-allocated stuff is cache 249cdafe74eSOleksandr Tymoshenko * line size, so just make sure nothing stupid happend 250cdafe74eSOleksandr Tymoshenko * and we got properly aligned address 251cdafe74eSOleksandr Tymoshenko */ 252cdafe74eSOleksandr Tymoshenko if ((uintptr_t)cb_virt & 0x1f) { 253cdafe74eSOleksandr Tymoshenko device_printf(dev, 254cdafe74eSOleksandr Tymoshenko "DMA address is not 32-bytes aligned: %p\n", 255cdafe74eSOleksandr Tymoshenko (void*)cb_virt); 256cdafe74eSOleksandr Tymoshenko break; 257cdafe74eSOleksandr Tymoshenko } 258cdafe74eSOleksandr Tymoshenko 259cdafe74eSOleksandr Tymoshenko err = bus_dmamap_load(sc->sc_dma_tag, ch->dma_map, cb_virt, 260cdafe74eSOleksandr Tymoshenko sizeof(struct bcm_dma_cb), bcm_dmamap_cb, &cb_phys, 261cdafe74eSOleksandr Tymoshenko BUS_DMA_WAITOK); 262cdafe74eSOleksandr Tymoshenko if (err) { 263cdafe74eSOleksandr Tymoshenko device_printf(dev, "cannot load DMA memory\n"); 264cdafe74eSOleksandr Tymoshenko break; 265cdafe74eSOleksandr Tymoshenko } 266cdafe74eSOleksandr Tymoshenko 267cdafe74eSOleksandr Tymoshenko bzero(ch, sizeof(struct bcm_dma_ch)); 268cdafe74eSOleksandr Tymoshenko ch->ch = i; 269cdafe74eSOleksandr Tymoshenko ch->cb = cb_virt; 270cdafe74eSOleksandr Tymoshenko ch->vc_cb = cb_phys; 271cdafe74eSOleksandr Tymoshenko ch->intr_func = NULL; 272cdafe74eSOleksandr Tymoshenko ch->intr_arg = NULL; 273cdafe74eSOleksandr Tymoshenko ch->flags = BCM_DMA_CH_UNMAP; 274cdafe74eSOleksandr Tymoshenko 275cdafe74eSOleksandr Tymoshenko ch->cb->info = INFO_WAIT_RESP; 276cdafe74eSOleksandr Tymoshenko 277cdafe74eSOleksandr Tymoshenko /* reset DMA engine */ 278cdafe74eSOleksandr Tymoshenko bcm_dma_reset(dev, i); 279cdafe74eSOleksandr Tymoshenko } 280cdafe74eSOleksandr Tymoshenko 281cdafe74eSOleksandr Tymoshenko /* now use DMA2/DMA3 only */ 282cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[2].flags = BCM_DMA_CH_FREE; 283cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[3].flags = BCM_DMA_CH_FREE; 284cdafe74eSOleksandr Tymoshenko 285cdafe74eSOleksandr Tymoshenko /* enable DMAs */ 286cdafe74eSOleksandr Tymoshenko mask = 0; 287cdafe74eSOleksandr Tymoshenko 288cdafe74eSOleksandr Tymoshenko for (i = 0; i < BCM_DMA_CH_MAX; i++) 289cdafe74eSOleksandr Tymoshenko if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) 290cdafe74eSOleksandr Tymoshenko mask |= (1 << i); 291cdafe74eSOleksandr Tymoshenko 292cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_ENABLE, mask); 293cdafe74eSOleksandr Tymoshenko 294cdafe74eSOleksandr Tymoshenko return (0); 295cdafe74eSOleksandr Tymoshenko } 296cdafe74eSOleksandr Tymoshenko 297cdafe74eSOleksandr Tymoshenko /* 298cdafe74eSOleksandr Tymoshenko * Allocate DMA channel for further use, returns channel # or 299cdafe74eSOleksandr Tymoshenko * BCM_DMA_CH_INVALID 300cdafe74eSOleksandr Tymoshenko */ 301cdafe74eSOleksandr Tymoshenko int 302cdafe74eSOleksandr Tymoshenko bcm_dma_allocate(int req_ch) 303cdafe74eSOleksandr Tymoshenko { 304cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 305cdafe74eSOleksandr Tymoshenko int ch = BCM_DMA_CH_INVALID; 306cdafe74eSOleksandr Tymoshenko int i; 307cdafe74eSOleksandr Tymoshenko 308cdafe74eSOleksandr Tymoshenko if (req_ch >= BCM_DMA_CH_MAX) 309cdafe74eSOleksandr Tymoshenko return (BCM_DMA_CH_INVALID); 310cdafe74eSOleksandr Tymoshenko 311cdafe74eSOleksandr Tymoshenko /* Auto(req_ch < 0) or CH specified */ 312cdafe74eSOleksandr Tymoshenko mtx_lock(&sc->sc_mtx); 313cdafe74eSOleksandr Tymoshenko 314cdafe74eSOleksandr Tymoshenko if (req_ch < 0) { 315cdafe74eSOleksandr Tymoshenko for (i = 0; i < BCM_DMA_CH_MAX; i++) { 316cdafe74eSOleksandr Tymoshenko if (sc->sc_dma_ch[i].flags & BCM_DMA_CH_FREE) { 317cdafe74eSOleksandr Tymoshenko ch = i; 318cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; 319cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; 320cdafe74eSOleksandr Tymoshenko break; 321cdafe74eSOleksandr Tymoshenko } 322cdafe74eSOleksandr Tymoshenko } 323cdafe74eSOleksandr Tymoshenko } 324cdafe74eSOleksandr Tymoshenko else { 325cdafe74eSOleksandr Tymoshenko if (sc->sc_dma_ch[req_ch].flags & BCM_DMA_CH_FREE) { 326cdafe74eSOleksandr Tymoshenko ch = req_ch; 327cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_FREE; 328cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_USED; 329cdafe74eSOleksandr Tymoshenko } 330cdafe74eSOleksandr Tymoshenko } 331cdafe74eSOleksandr Tymoshenko 332cdafe74eSOleksandr Tymoshenko mtx_unlock(&sc->sc_mtx); 333cdafe74eSOleksandr Tymoshenko return (ch); 334cdafe74eSOleksandr Tymoshenko } 335cdafe74eSOleksandr Tymoshenko 336cdafe74eSOleksandr Tymoshenko /* 337cdafe74eSOleksandr Tymoshenko * Frees allocated channel. Returns 0 on success, -1 otherwise 338cdafe74eSOleksandr Tymoshenko */ 339cdafe74eSOleksandr Tymoshenko int 340cdafe74eSOleksandr Tymoshenko bcm_dma_free(int ch) 341cdafe74eSOleksandr Tymoshenko { 342cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 343cdafe74eSOleksandr Tymoshenko 344cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 345cdafe74eSOleksandr Tymoshenko return (-1); 346cdafe74eSOleksandr Tymoshenko 347cdafe74eSOleksandr Tymoshenko mtx_lock(&sc->sc_mtx); 348cdafe74eSOleksandr Tymoshenko if (sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED) { 349cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags |= BCM_DMA_CH_FREE; 350cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].flags &= ~BCM_DMA_CH_USED; 351cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].intr_func = NULL; 352cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].intr_arg = NULL; 353cdafe74eSOleksandr Tymoshenko 354cdafe74eSOleksandr Tymoshenko /* reset DMA engine */ 355cdafe74eSOleksandr Tymoshenko bcm_dma_reset(sc->sc_dev, ch); 356cdafe74eSOleksandr Tymoshenko } 357cdafe74eSOleksandr Tymoshenko 358cdafe74eSOleksandr Tymoshenko mtx_unlock(&sc->sc_mtx); 359cdafe74eSOleksandr Tymoshenko return (0); 360cdafe74eSOleksandr Tymoshenko } 361cdafe74eSOleksandr Tymoshenko 362cdafe74eSOleksandr Tymoshenko /* 363cdafe74eSOleksandr Tymoshenko * Assign handler function for channel interrupt 364cdafe74eSOleksandr Tymoshenko * Returns 0 on success, -1 otherwise 365cdafe74eSOleksandr Tymoshenko */ 366cdafe74eSOleksandr Tymoshenko int 367cdafe74eSOleksandr Tymoshenko bcm_dma_setup_intr(int ch, void (*func)(int, void *), void *arg) 368cdafe74eSOleksandr Tymoshenko { 369cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 370cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb *cb; 371cdafe74eSOleksandr Tymoshenko 372cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 373cdafe74eSOleksandr Tymoshenko return (-1); 374cdafe74eSOleksandr Tymoshenko 375cdafe74eSOleksandr Tymoshenko if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) 376cdafe74eSOleksandr Tymoshenko return (-1); 377cdafe74eSOleksandr Tymoshenko 378cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].intr_func = func; 379cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].intr_arg = arg; 380cdafe74eSOleksandr Tymoshenko cb = sc->sc_dma_ch[ch].cb; 381cdafe74eSOleksandr Tymoshenko cb->info |= INFO_INT_EN; 382cdafe74eSOleksandr Tymoshenko 383cdafe74eSOleksandr Tymoshenko return (0); 384cdafe74eSOleksandr Tymoshenko } 385cdafe74eSOleksandr Tymoshenko 386cdafe74eSOleksandr Tymoshenko /* 387cdafe74eSOleksandr Tymoshenko * Setup DMA source parameters 388cdafe74eSOleksandr Tymoshenko * ch - channel number 389cdafe74eSOleksandr Tymoshenko * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if 390cdafe74eSOleksandr Tymoshenko * source is physical memory 391cdafe74eSOleksandr Tymoshenko * inc_addr - BCM_DMA_INC_ADDR if source address 392cdafe74eSOleksandr Tymoshenko * should be increased after each access or 393cdafe74eSOleksandr Tymoshenko * BCM_DMA_SAME_ADDR if address should remain 394cdafe74eSOleksandr Tymoshenko * the same 395cdafe74eSOleksandr Tymoshenko * width - size of read operation, BCM_DMA_32BIT 396cdafe74eSOleksandr Tymoshenko * for 32bit bursts, BCM_DMA_128BIT for 128 bits 397cdafe74eSOleksandr Tymoshenko * 398cdafe74eSOleksandr Tymoshenko * Returns 0 on success, -1 otherwise 399cdafe74eSOleksandr Tymoshenko */ 400cdafe74eSOleksandr Tymoshenko int 401cdafe74eSOleksandr Tymoshenko bcm_dma_setup_src(int ch, int dreq, int inc_addr, int width) 402cdafe74eSOleksandr Tymoshenko { 403cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 404cdafe74eSOleksandr Tymoshenko uint32_t info; 405cdafe74eSOleksandr Tymoshenko 406cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 407cdafe74eSOleksandr Tymoshenko return (-1); 408cdafe74eSOleksandr Tymoshenko 409cdafe74eSOleksandr Tymoshenko if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) 410cdafe74eSOleksandr Tymoshenko return (-1); 411cdafe74eSOleksandr Tymoshenko 412cdafe74eSOleksandr Tymoshenko info = sc->sc_dma_ch[ch].cb->info; 413cdafe74eSOleksandr Tymoshenko info &= ~INFO_PERMAP_MASK; 414cdafe74eSOleksandr Tymoshenko info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; 415cdafe74eSOleksandr Tymoshenko 416cdafe74eSOleksandr Tymoshenko if (dreq) 417cdafe74eSOleksandr Tymoshenko info |= INFO_S_DREQ; 418cdafe74eSOleksandr Tymoshenko else 419cdafe74eSOleksandr Tymoshenko info &= ~INFO_S_DREQ; 420cdafe74eSOleksandr Tymoshenko 421cdafe74eSOleksandr Tymoshenko if (width == BCM_DMA_128BIT) 422cdafe74eSOleksandr Tymoshenko info |= INFO_S_WIDTH; 423cdafe74eSOleksandr Tymoshenko else 424cdafe74eSOleksandr Tymoshenko info &= ~INFO_S_WIDTH; 425cdafe74eSOleksandr Tymoshenko 426cdafe74eSOleksandr Tymoshenko if (inc_addr == BCM_DMA_INC_ADDR) 427cdafe74eSOleksandr Tymoshenko info |= INFO_S_INC; 428cdafe74eSOleksandr Tymoshenko else 429cdafe74eSOleksandr Tymoshenko info &= ~INFO_S_INC; 430cdafe74eSOleksandr Tymoshenko 431cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].cb->info = info; 432cdafe74eSOleksandr Tymoshenko 433cdafe74eSOleksandr Tymoshenko return (0); 434cdafe74eSOleksandr Tymoshenko } 435cdafe74eSOleksandr Tymoshenko 436cdafe74eSOleksandr Tymoshenko /* 437cdafe74eSOleksandr Tymoshenko * Setup DMA destination parameters 438cdafe74eSOleksandr Tymoshenko * ch - channel number 439cdafe74eSOleksandr Tymoshenko * dreq - hardware DREQ # or BCM_DMA_DREQ_NONE if 440cdafe74eSOleksandr Tymoshenko * destination is physical memory 441cdafe74eSOleksandr Tymoshenko * inc_addr - BCM_DMA_INC_ADDR if source address 442cdafe74eSOleksandr Tymoshenko * should be increased after each access or 443cdafe74eSOleksandr Tymoshenko * BCM_DMA_SAME_ADDR if address should remain 444cdafe74eSOleksandr Tymoshenko * the same 445cdafe74eSOleksandr Tymoshenko * width - size of write operation, BCM_DMA_32BIT 446cdafe74eSOleksandr Tymoshenko * for 32bit bursts, BCM_DMA_128BIT for 128 bits 447cdafe74eSOleksandr Tymoshenko * 448cdafe74eSOleksandr Tymoshenko * Returns 0 on success, -1 otherwise 449cdafe74eSOleksandr Tymoshenko */ 450cdafe74eSOleksandr Tymoshenko int 451cdafe74eSOleksandr Tymoshenko bcm_dma_setup_dst(int ch, int dreq, int inc_addr, int width) 452cdafe74eSOleksandr Tymoshenko { 453cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 454cdafe74eSOleksandr Tymoshenko uint32_t info; 455cdafe74eSOleksandr Tymoshenko 456cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 457cdafe74eSOleksandr Tymoshenko return (-1); 458cdafe74eSOleksandr Tymoshenko 459cdafe74eSOleksandr Tymoshenko if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) 460cdafe74eSOleksandr Tymoshenko return (-1); 461cdafe74eSOleksandr Tymoshenko 462cdafe74eSOleksandr Tymoshenko info = sc->sc_dma_ch[ch].cb->info; 463cdafe74eSOleksandr Tymoshenko info &= ~INFO_PERMAP_MASK; 464cdafe74eSOleksandr Tymoshenko info |= (dreq << INFO_PERMAP_SHIFT) & INFO_PERMAP_MASK; 465cdafe74eSOleksandr Tymoshenko 466cdafe74eSOleksandr Tymoshenko if (dreq) 467cdafe74eSOleksandr Tymoshenko info |= INFO_D_DREQ; 468cdafe74eSOleksandr Tymoshenko else 469cdafe74eSOleksandr Tymoshenko info &= ~INFO_D_DREQ; 470cdafe74eSOleksandr Tymoshenko 471cdafe74eSOleksandr Tymoshenko if (width == BCM_DMA_128BIT) 472cdafe74eSOleksandr Tymoshenko info |= INFO_D_WIDTH; 473cdafe74eSOleksandr Tymoshenko else 474cdafe74eSOleksandr Tymoshenko info &= ~INFO_D_WIDTH; 475cdafe74eSOleksandr Tymoshenko 476cdafe74eSOleksandr Tymoshenko if (inc_addr == BCM_DMA_INC_ADDR) 477cdafe74eSOleksandr Tymoshenko info |= INFO_D_INC; 478cdafe74eSOleksandr Tymoshenko else 479cdafe74eSOleksandr Tymoshenko info &= ~INFO_D_INC; 480cdafe74eSOleksandr Tymoshenko 481cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].cb->info = info; 482cdafe74eSOleksandr Tymoshenko 483cdafe74eSOleksandr Tymoshenko return (0); 484cdafe74eSOleksandr Tymoshenko } 485cdafe74eSOleksandr Tymoshenko 486cdafe74eSOleksandr Tymoshenko #ifdef DEBUG 487cdafe74eSOleksandr Tymoshenko void 488cdafe74eSOleksandr Tymoshenko bcm_dma_cb_dump(struct bcm_dma_cb *cb) 489cdafe74eSOleksandr Tymoshenko { 490cdafe74eSOleksandr Tymoshenko 491cdafe74eSOleksandr Tymoshenko printf("DMA CB "); 492cdafe74eSOleksandr Tymoshenko printf("INFO: %8.8x ", cb->info); 493cdafe74eSOleksandr Tymoshenko printf("SRC: %8.8x ", cb->src); 494cdafe74eSOleksandr Tymoshenko printf("DST: %8.8x ", cb->dst); 495cdafe74eSOleksandr Tymoshenko printf("LEN: %8.8x ", cb->len); 496cdafe74eSOleksandr Tymoshenko printf("\n"); 497cdafe74eSOleksandr Tymoshenko printf("STRIDE: %8.8x ", cb->stride); 498cdafe74eSOleksandr Tymoshenko printf("NEXT: %8.8x ", cb->next); 499cdafe74eSOleksandr Tymoshenko printf("RSVD1: %8.8x ", cb->rsvd1); 500cdafe74eSOleksandr Tymoshenko printf("RSVD2: %8.8x ", cb->rsvd2); 501cdafe74eSOleksandr Tymoshenko printf("\n"); 502cdafe74eSOleksandr Tymoshenko } 503cdafe74eSOleksandr Tymoshenko 504cdafe74eSOleksandr Tymoshenko void 505cdafe74eSOleksandr Tymoshenko bcm_dma_reg_dump(int ch) 506cdafe74eSOleksandr Tymoshenko { 507cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 508cdafe74eSOleksandr Tymoshenko int i; 509cdafe74eSOleksandr Tymoshenko uint32_t reg; 510cdafe74eSOleksandr Tymoshenko 511cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 512cdafe74eSOleksandr Tymoshenko return; 513cdafe74eSOleksandr Tymoshenko 514cdafe74eSOleksandr Tymoshenko printf("DMA%d: ", ch); 515cdafe74eSOleksandr Tymoshenko for (i = 0; i < MAX_REG; i++) { 516cdafe74eSOleksandr Tymoshenko reg = bus_read_4(sc->sc_mem, BCM_DMA_CH(ch) + i*4); 517cdafe74eSOleksandr Tymoshenko printf("%8.8x ", reg); 518cdafe74eSOleksandr Tymoshenko } 519cdafe74eSOleksandr Tymoshenko printf("\n"); 520cdafe74eSOleksandr Tymoshenko } 521cdafe74eSOleksandr Tymoshenko #endif 522cdafe74eSOleksandr Tymoshenko 523cdafe74eSOleksandr Tymoshenko /* 524cdafe74eSOleksandr Tymoshenko * Start DMA transaction 525cdafe74eSOleksandr Tymoshenko * ch - channel number 526cdafe74eSOleksandr Tymoshenko * src, dst - source and destination address in 527cdafe74eSOleksandr Tymoshenko * ARM physical memory address space. 528cdafe74eSOleksandr Tymoshenko * len - amount of bytes to be transfered 529cdafe74eSOleksandr Tymoshenko * 530cdafe74eSOleksandr Tymoshenko * Returns 0 on success, -1 otherwise 531cdafe74eSOleksandr Tymoshenko */ 532cdafe74eSOleksandr Tymoshenko int 533cdafe74eSOleksandr Tymoshenko bcm_dma_start(int ch, vm_paddr_t src, vm_paddr_t dst, int len) 534cdafe74eSOleksandr Tymoshenko { 535cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 536cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb *cb; 537cdafe74eSOleksandr Tymoshenko 538cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 539cdafe74eSOleksandr Tymoshenko return (-1); 540cdafe74eSOleksandr Tymoshenko 541cdafe74eSOleksandr Tymoshenko if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) 542cdafe74eSOleksandr Tymoshenko return (-1); 543cdafe74eSOleksandr Tymoshenko 544cdafe74eSOleksandr Tymoshenko cb = sc->sc_dma_ch[ch].cb; 545cdafe74eSOleksandr Tymoshenko if (BCM2835_ARM_IS_IO(src)) 546cdafe74eSOleksandr Tymoshenko cb->src = IO_TO_VCBUS(src); 547cdafe74eSOleksandr Tymoshenko else 548cdafe74eSOleksandr Tymoshenko cb->src = PHYS_TO_VCBUS(src); 549cdafe74eSOleksandr Tymoshenko if (BCM2835_ARM_IS_IO(dst)) 550cdafe74eSOleksandr Tymoshenko cb->dst = IO_TO_VCBUS(dst); 551cdafe74eSOleksandr Tymoshenko else 552cdafe74eSOleksandr Tymoshenko cb->dst = PHYS_TO_VCBUS(dst); 553cdafe74eSOleksandr Tymoshenko cb->len = len; 554cdafe74eSOleksandr Tymoshenko 555cdafe74eSOleksandr Tymoshenko bus_dmamap_sync(sc->sc_dma_tag, 556cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].dma_map, BUS_DMASYNC_PREWRITE); 557cdafe74eSOleksandr Tymoshenko 558cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CBADDR(ch), 559cdafe74eSOleksandr Tymoshenko sc->sc_dma_ch[ch].vc_cb); 560cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CS(ch), CS_ACTIVE); 561cdafe74eSOleksandr Tymoshenko 562cdafe74eSOleksandr Tymoshenko #ifdef DEBUG 563cdafe74eSOleksandr Tymoshenko bcm_dma_cb_dump(sc->sc_dma_ch[ch].cb); 564cdafe74eSOleksandr Tymoshenko bcm_dma_reg_dump(ch); 565cdafe74eSOleksandr Tymoshenko #endif 566cdafe74eSOleksandr Tymoshenko 567cdafe74eSOleksandr Tymoshenko return (0); 568cdafe74eSOleksandr Tymoshenko } 569cdafe74eSOleksandr Tymoshenko 570cdafe74eSOleksandr Tymoshenko /* 571cdafe74eSOleksandr Tymoshenko * Get length requested for DMA transaction 572cdafe74eSOleksandr Tymoshenko * ch - channel number 573cdafe74eSOleksandr Tymoshenko * 574cdafe74eSOleksandr Tymoshenko * Returns size of transaction, 0 if channel is invalid 575cdafe74eSOleksandr Tymoshenko */ 576cdafe74eSOleksandr Tymoshenko uint32_t 577cdafe74eSOleksandr Tymoshenko bcm_dma_length(int ch) 578cdafe74eSOleksandr Tymoshenko { 579cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 580cdafe74eSOleksandr Tymoshenko struct bcm_dma_cb *cb; 581cdafe74eSOleksandr Tymoshenko 582cdafe74eSOleksandr Tymoshenko if (ch < 0 || ch >= BCM_DMA_CH_MAX) 583cdafe74eSOleksandr Tymoshenko return (0); 584cdafe74eSOleksandr Tymoshenko 585cdafe74eSOleksandr Tymoshenko if (!(sc->sc_dma_ch[ch].flags & BCM_DMA_CH_USED)) 586cdafe74eSOleksandr Tymoshenko return (0); 587cdafe74eSOleksandr Tymoshenko 588cdafe74eSOleksandr Tymoshenko cb = sc->sc_dma_ch[ch].cb; 589cdafe74eSOleksandr Tymoshenko 590cdafe74eSOleksandr Tymoshenko return (cb->len); 591cdafe74eSOleksandr Tymoshenko } 592cdafe74eSOleksandr Tymoshenko 593cdafe74eSOleksandr Tymoshenko static void 594cdafe74eSOleksandr Tymoshenko bcm_dma_intr(void *arg) 595cdafe74eSOleksandr Tymoshenko { 596cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = bcm_dma_sc; 597cdafe74eSOleksandr Tymoshenko struct bcm_dma_ch *ch = (struct bcm_dma_ch *)arg; 598cdafe74eSOleksandr Tymoshenko uint32_t cs, debug; 599cdafe74eSOleksandr Tymoshenko 600cdafe74eSOleksandr Tymoshenko /* my interrupt? */ 601cdafe74eSOleksandr Tymoshenko cs = bus_read_4(sc->sc_mem, BCM_DMA_CS(ch->ch)); 602cdafe74eSOleksandr Tymoshenko 603cdafe74eSOleksandr Tymoshenko if (!(cs & (CS_INT | CS_ERR))) 604cdafe74eSOleksandr Tymoshenko return; 605cdafe74eSOleksandr Tymoshenko 606cdafe74eSOleksandr Tymoshenko /* running? */ 607cdafe74eSOleksandr Tymoshenko if (!(ch->flags & BCM_DMA_CH_USED)) { 608cdafe74eSOleksandr Tymoshenko device_printf(sc->sc_dev, 609cdafe74eSOleksandr Tymoshenko "unused DMA intr CH=%d, CS=%x\n", ch->ch, cs); 610cdafe74eSOleksandr Tymoshenko return; 611cdafe74eSOleksandr Tymoshenko } 612cdafe74eSOleksandr Tymoshenko 613cdafe74eSOleksandr Tymoshenko if (cs & CS_ERR) { 614cdafe74eSOleksandr Tymoshenko debug = bus_read_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch)); 615cdafe74eSOleksandr Tymoshenko device_printf(sc->sc_dev, "DMA error %d on CH%d\n", 616cdafe74eSOleksandr Tymoshenko debug & DEBUG_ERROR_MASK, ch->ch); 617cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_DEBUG(ch->ch), 618cdafe74eSOleksandr Tymoshenko debug & DEBUG_ERROR_MASK); 619*e9401a9eSOleksandr Tymoshenko bcm_dma_reset(sc->sc_dev, ch->ch); 620cdafe74eSOleksandr Tymoshenko } 621cdafe74eSOleksandr Tymoshenko 622cdafe74eSOleksandr Tymoshenko if (cs & CS_INT) { 623cdafe74eSOleksandr Tymoshenko /* acknowledge interrupt */ 624cdafe74eSOleksandr Tymoshenko bus_write_4(sc->sc_mem, BCM_DMA_CS(ch->ch), 625cdafe74eSOleksandr Tymoshenko CS_INT | CS_END); 626cdafe74eSOleksandr Tymoshenko 627cdafe74eSOleksandr Tymoshenko /* Prepare for possible access to len field */ 628cdafe74eSOleksandr Tymoshenko bus_dmamap_sync(sc->sc_dma_tag, ch->dma_map, 629cdafe74eSOleksandr Tymoshenko BUS_DMASYNC_POSTWRITE); 630cdafe74eSOleksandr Tymoshenko 631cdafe74eSOleksandr Tymoshenko /* save callback function and argument */ 632cdafe74eSOleksandr Tymoshenko if (ch->intr_func) 633cdafe74eSOleksandr Tymoshenko ch->intr_func(ch->ch, ch->intr_arg); 634cdafe74eSOleksandr Tymoshenko } 635cdafe74eSOleksandr Tymoshenko } 636cdafe74eSOleksandr Tymoshenko 637cdafe74eSOleksandr Tymoshenko static int 638cdafe74eSOleksandr Tymoshenko bcm_dma_probe(device_t dev) 639cdafe74eSOleksandr Tymoshenko { 640cdafe74eSOleksandr Tymoshenko 641cdafe74eSOleksandr Tymoshenko if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-dma")) 642cdafe74eSOleksandr Tymoshenko return (ENXIO); 643cdafe74eSOleksandr Tymoshenko 644cdafe74eSOleksandr Tymoshenko device_set_desc(dev, "BCM2835 DMA Controller"); 645cdafe74eSOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 646cdafe74eSOleksandr Tymoshenko } 647cdafe74eSOleksandr Tymoshenko 648cdafe74eSOleksandr Tymoshenko static int 649cdafe74eSOleksandr Tymoshenko bcm_dma_attach(device_t dev) 650cdafe74eSOleksandr Tymoshenko { 651cdafe74eSOleksandr Tymoshenko struct bcm_dma_softc *sc = device_get_softc(dev); 652cdafe74eSOleksandr Tymoshenko int rid, err = 0; 653cdafe74eSOleksandr Tymoshenko int i; 654cdafe74eSOleksandr Tymoshenko 655cdafe74eSOleksandr Tymoshenko sc->sc_dev = dev; 656cdafe74eSOleksandr Tymoshenko 657cdafe74eSOleksandr Tymoshenko if (bcm_dma_sc) 658cdafe74eSOleksandr Tymoshenko return (ENXIO); 659cdafe74eSOleksandr Tymoshenko 660cdafe74eSOleksandr Tymoshenko for (i = 0; i < BCM_DMA_CH_MAX; i++) { 661cdafe74eSOleksandr Tymoshenko sc->sc_irq[i] = NULL; 662cdafe74eSOleksandr Tymoshenko sc->sc_intrhand[i] = NULL; 663cdafe74eSOleksandr Tymoshenko } 664cdafe74eSOleksandr Tymoshenko 665cdafe74eSOleksandr Tymoshenko /* DMA0 - DMA14 */ 666cdafe74eSOleksandr Tymoshenko rid = 0; 667cdafe74eSOleksandr Tymoshenko sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); 668cdafe74eSOleksandr Tymoshenko if (sc->sc_mem == NULL) { 669cdafe74eSOleksandr Tymoshenko device_printf(dev, "could not allocate memory resource\n"); 670cdafe74eSOleksandr Tymoshenko return (ENXIO); 671cdafe74eSOleksandr Tymoshenko } 672cdafe74eSOleksandr Tymoshenko 673cdafe74eSOleksandr Tymoshenko /* IRQ DMA0 - DMA11 XXX NOT USE DMA12(spurious?) */ 674cdafe74eSOleksandr Tymoshenko for (rid = 0; rid < BCM_DMA_CH_MAX; rid++) { 675cdafe74eSOleksandr Tymoshenko sc->sc_irq[rid] = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 676cdafe74eSOleksandr Tymoshenko RF_ACTIVE); 677cdafe74eSOleksandr Tymoshenko if (sc->sc_irq[rid] == NULL) { 678cdafe74eSOleksandr Tymoshenko device_printf(dev, "cannot allocate interrupt\n"); 679cdafe74eSOleksandr Tymoshenko err = ENXIO; 680cdafe74eSOleksandr Tymoshenko goto fail; 681cdafe74eSOleksandr Tymoshenko } 682cdafe74eSOleksandr Tymoshenko if (bus_setup_intr(dev, sc->sc_irq[rid], INTR_TYPE_MISC | INTR_MPSAFE, 683cdafe74eSOleksandr Tymoshenko NULL, bcm_dma_intr, &sc->sc_dma_ch[rid], 684cdafe74eSOleksandr Tymoshenko &sc->sc_intrhand[rid])) { 685cdafe74eSOleksandr Tymoshenko device_printf(dev, "cannot setup interrupt handler\n"); 686cdafe74eSOleksandr Tymoshenko err = ENXIO; 687cdafe74eSOleksandr Tymoshenko goto fail; 688cdafe74eSOleksandr Tymoshenko } 689cdafe74eSOleksandr Tymoshenko } 690cdafe74eSOleksandr Tymoshenko 691cdafe74eSOleksandr Tymoshenko mtx_init(&sc->sc_mtx, "bcmdma", "bcmdma", MTX_DEF); 692cdafe74eSOleksandr Tymoshenko bcm_dma_sc = sc; 693cdafe74eSOleksandr Tymoshenko 694cdafe74eSOleksandr Tymoshenko err = bcm_dma_init(dev); 695cdafe74eSOleksandr Tymoshenko if (err) 696cdafe74eSOleksandr Tymoshenko goto fail; 697cdafe74eSOleksandr Tymoshenko 698cdafe74eSOleksandr Tymoshenko return (err); 699cdafe74eSOleksandr Tymoshenko 700cdafe74eSOleksandr Tymoshenko fail: 701cdafe74eSOleksandr Tymoshenko if (sc->sc_mem) 702cdafe74eSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem); 703cdafe74eSOleksandr Tymoshenko 704cdafe74eSOleksandr Tymoshenko for (i = 0; i < BCM_DMA_CH_MAX; i++) { 705cdafe74eSOleksandr Tymoshenko if (sc->sc_intrhand[i]) 706cdafe74eSOleksandr Tymoshenko bus_teardown_intr(dev, sc->sc_irq[i], sc->sc_intrhand[i]); 707cdafe74eSOleksandr Tymoshenko if (sc->sc_irq[i]) 708cdafe74eSOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq[i]); 709cdafe74eSOleksandr Tymoshenko } 710cdafe74eSOleksandr Tymoshenko 711cdafe74eSOleksandr Tymoshenko return (err); 712cdafe74eSOleksandr Tymoshenko } 713cdafe74eSOleksandr Tymoshenko 714cdafe74eSOleksandr Tymoshenko static device_method_t bcm_dma_methods[] = { 715cdafe74eSOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_dma_probe), 716cdafe74eSOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_dma_attach), 717cdafe74eSOleksandr Tymoshenko { 0, 0 } 718cdafe74eSOleksandr Tymoshenko }; 719cdafe74eSOleksandr Tymoshenko 720cdafe74eSOleksandr Tymoshenko static driver_t bcm_dma_driver = { 721cdafe74eSOleksandr Tymoshenko "bcm_dma", 722cdafe74eSOleksandr Tymoshenko bcm_dma_methods, 723cdafe74eSOleksandr Tymoshenko sizeof(struct bcm_dma_softc), 724cdafe74eSOleksandr Tymoshenko }; 725cdafe74eSOleksandr Tymoshenko 726cdafe74eSOleksandr Tymoshenko static devclass_t bcm_dma_devclass; 727cdafe74eSOleksandr Tymoshenko 728cdafe74eSOleksandr Tymoshenko DRIVER_MODULE(bcm_dma, simplebus, bcm_dma_driver, bcm_dma_devclass, 0, 0); 729cdafe74eSOleksandr Tymoshenko MODULE_VERSION(bcm_dma, 1); 730