Module Name: src Committed By: matt Date: Tue Sep 11 17:54:12 UTC 2012
Modified Files: src/sys/arch/arm/arm32: bus_dma.c Log Message: Rework dmasync routines so that secondary caches can be flushed as well as the primary caches. To generate a diff of this commit: cvs rdiff -u -r1.56 -r1.57 src/sys/arch/arm/arm32/bus_dma.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/arm32/bus_dma.c diff -u src/sys/arch/arm/arm32/bus_dma.c:1.56 src/sys/arch/arm/arm32/bus_dma.c:1.57 --- src/sys/arch/arm/arm32/bus_dma.c:1.56 Sun Sep 2 14:46:38 2012 +++ src/sys/arch/arm/arm32/bus_dma.c Tue Sep 11 17:54:12 2012 @@ -1,4 +1,4 @@ -/* $NetBSD: bus_dma.c,v 1.56 2012/09/02 14:46:38 matt Exp $ */ +/* $NetBSD: bus_dma.c,v 1.57 2012/09/11 17:54:12 matt Exp $ */ /*- * Copyright (c) 1996, 1997, 1998 The NetBSD Foundation, Inc. @@ -33,7 +33,7 @@ #define _ARM32_BUS_DMA_PRIVATE #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.56 2012/09/02 14:46:38 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: bus_dma.c,v 1.57 2012/09/11 17:54:12 matt Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -331,6 +331,13 @@ _bus_dmamap_load_mbuf(bus_dma_tag_t t, b if (m->m_len == 0) continue; + /* + * Don't allow reads in read-only mbufs. + */ + if (M_ROMAP(m) && (flags & BUS_DMA_READ)) { + error = EFAULT; + break; + } switch (m->m_flags & (M_EXT|M_CLUSTER|M_EXT_PAGES)) { case M_EXT|M_CLUSTER: /* XXX KDASSERT */ @@ -482,57 +489,105 @@ _bus_dmamap_unload(bus_dma_tag_t t, bus_ map->_dm_vmspace = NULL; } -static inline void -_bus_dmamap_sync_linear(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, - bus_size_t len, int ops) +static void +_bus_dmamap_sync_segment(vaddr_t va, paddr_t pa, vsize_t len, int ops, bool readonly_p) { - vaddr_t addr = (vaddr_t) map->_dm_origbuf; - - addr += offset; + KASSERT((va & PAGE_MASK) == (pa & PAGE_MASK)); switch (ops) { case BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE: - cpu_dcache_wbinv_range(addr, len); - break; + if (!readonly_p) { + cpu_dcache_wbinv_range(va, len); + cpu_sdcache_wbinv_range(va, pa, len); + break; + } + /* FALLTHROUGH */ - case BUS_DMASYNC_PREREAD: - if (((addr | len) & arm_dcache_align_mask) == 0) - cpu_dcache_inv_range(addr, len); - else - cpu_dcache_wbinv_range(addr, len); + case BUS_DMASYNC_PREREAD: { + vsize_t misalignment = va & arm_dcache_align_mask; + if (misalignment) { + va &= ~arm_dcache_align_mask; + pa &= ~arm_dcache_align_mask; + cpu_dcache_wbinv_range(va, arm_dcache_align); + cpu_sdcache_wbinv_range(va, pa, arm_dcache_align); + if (len <= arm_dcache_align - misalignment) + break; + len -= arm_dcache_align - misalignment; + va += arm_dcache_align; + pa += arm_dcache_align; + } + misalignment = len & arm_dcache_align_mask; + len -= misalignment; + cpu_dcache_inv_range(va, len); + cpu_sdcache_inv_range(va, pa, len); + if (misalignment) { + va += len; + pa += len; + cpu_dcache_wbinv_range(va, arm_dcache_align); + cpu_sdcache_wbinv_range(va, pa, arm_dcache_align); + } break; + } case BUS_DMASYNC_PREWRITE: - cpu_dcache_wb_range(addr, len); + cpu_dcache_wb_range(va, len); + cpu_sdcache_wb_range(va, pa, len); break; } } static inline void -_bus_dmamap_sync_mbuf(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, +_bus_dmamap_sync_linear(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, bus_size_t len, int ops) { - struct mbuf *m, *m0 = map->_dm_origbuf; - bus_size_t minlen, moff; - vaddr_t maddr; - - for (moff = offset, m = m0; m != NULL && len != 0; m = m->m_next) { - /* Find the beginning mbuf. */ - if (moff >= m->m_len) { - moff -= m->m_len; - continue; + bus_dma_segment_t *ds = map->dm_segs; + vaddr_t va = (vaddr_t) map->_dm_origbuf; + + while (len > 0) { + while (offset >= ds->ds_len) { + offset -= ds->ds_len; + va += ds->ds_len; + ds++; + } + + paddr_t pa = ds->ds_addr + offset; + size_t seglen = min(len, ds->ds_len - offset); + + _bus_dmamap_sync_segment(va + offset, pa, seglen, ops, false); + + offset += seglen; + len -= seglen; + } +} + +static inline void +_bus_dmamap_sync_mbuf(bus_dma_tag_t t, bus_dmamap_t map, bus_size_t offset, + bus_size_t len, int ops) +{ + bus_dma_segment_t *ds = map->dm_segs; + struct mbuf *m = map->_dm_origbuf; + bus_size_t voff = offset; + bus_size_t ds_off = offset; + + while (len > 0) { + /* Find the current dma segment */ + while (ds_off >= ds->ds_len) { + ds_off -= ds->ds_len; + ds++; + } + /* Find the current mbuf. */ + while (voff >= m->m_len) { + voff -= m->m_len; + m = m->m_next; } /* * Now at the first mbuf to sync; nail each one until * we have exhausted the length. */ - minlen = m->m_len - moff; - if (len < minlen) - minlen = len; - - maddr = mtod(m, vaddr_t); - maddr += moff; + vsize_t seglen = min(len, min(m->m_len - voff, ds->ds_len - ds_off)); + vaddr_t va = mtod(m, vaddr_t) + voff; + paddr_t pa = ds->ds_addr + ds_off; /* * We can save a lot of work here if we know the mapping @@ -550,28 +605,11 @@ _bus_dmamap_sync_mbuf(bus_dma_tag_t t, b * this ever becomes non-true (e.g. Physically Indexed * cache), this will have to be revisited. */ - switch (ops) { - case BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE: - if (! M_ROMAP(m)) { - cpu_dcache_wbinv_range(maddr, minlen); - break; - } - /* else FALLTHROUGH */ - - case BUS_DMASYNC_PREREAD: - if (((maddr | minlen) & arm_dcache_align_mask) == 0) - cpu_dcache_inv_range(maddr, minlen); - else - cpu_dcache_wbinv_range(maddr, minlen); - break; - case BUS_DMASYNC_PREWRITE: - if (! M_ROMAP(m)) - cpu_dcache_wb_range(maddr, minlen); - break; - } - moff = 0; - len -= minlen; + _bus_dmamap_sync_segment(va, pa, seglen, ops, M_ROMAP(m)); + voff += seglen; + ds_off += seglen; + len -= seglen; } } @@ -579,47 +617,38 @@ static inline void _bus_dmamap_sync_uio(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, bus_size_t len, int ops) { + bus_dma_segment_t *ds = map->dm_segs; struct uio *uio = map->_dm_origbuf; - struct iovec *iov; - bus_size_t minlen, ioff; - vaddr_t addr; + struct iovec *iov = uio->uio_iov; + bus_size_t voff = offset; + bus_size_t ds_off = offset; + + while (len > 0) { + /* Find the current dma segment */ + while (ds_off >= ds->ds_len) { + ds_off -= ds->ds_len; + ds++; + } - for (iov = uio->uio_iov, ioff = offset; len != 0; iov++) { - /* Find the beginning iovec. */ - if (ioff >= iov->iov_len) { - ioff -= iov->iov_len; - continue; + /* Find the current iovec. */ + while (voff >= iov->iov_len) { + voff -= iov->iov_len; + iov++; } /* * Now at the first iovec to sync; nail each one until * we have exhausted the length. */ - minlen = iov->iov_len - ioff; - if (len < minlen) - minlen = len; - - addr = (vaddr_t) iov->iov_base; - addr += ioff; - - switch (ops) { - case BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE: - cpu_dcache_wbinv_range(addr, minlen); - break; - - case BUS_DMASYNC_PREREAD: - if (((addr | minlen) & arm_dcache_align_mask) == 0) - cpu_dcache_inv_range(addr, minlen); - else - cpu_dcache_wbinv_range(addr, minlen); - break; - - case BUS_DMASYNC_PREWRITE: - cpu_dcache_wb_range(addr, minlen); - break; - } - ioff = 0; - len -= minlen; + vsize_t seglen = min(len, min(iov->iov_len - voff, ds->ds_len - ds_off)); + vaddr_t va = (vaddr_t) iov->iov_base + voff; + paddr_t pa = ds->ds_addr + ds_off; + + _bus_dmamap_sync_segment(va, pa, seglen, ops, false); + + voff += seglen; + ds_off += seglen; + len -= seglen; } } @@ -813,7 +842,7 @@ _bus_dmamem_map(bus_dma_tag_t t, bus_dma size_t size, void **kvap, int flags) { vaddr_t va; - bus_addr_t addr; + paddr_t pa; int curseg; pt_entry_t *ptep/*, pte*/; const uvm_flag_t kmflags = @@ -833,17 +862,18 @@ _bus_dmamem_map(bus_dma_tag_t t, bus_dma *kvap = (void *)va; for (curseg = 0; curseg < nsegs; curseg++) { - for (addr = segs[curseg].ds_addr; - addr < (segs[curseg].ds_addr + segs[curseg].ds_len); - addr += PAGE_SIZE, va += PAGE_SIZE, size -= PAGE_SIZE) { + for (pa = segs[curseg].ds_addr; + pa < (segs[curseg].ds_addr + segs[curseg].ds_len); + pa += PAGE_SIZE, va += PAGE_SIZE, size -= PAGE_SIZE) { #ifdef DEBUG_DMA - printf("wiring p%lx to v%lx", addr, va); + printf("wiring p%lx to v%lx", pa, va); #endif /* DEBUG_DMA */ if (size == 0) panic("_bus_dmamem_map: size botch"); - pmap_enter(pmap_kernel(), va, addr, + pmap_enter(pmap_kernel(), va, pa, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE | PMAP_WIRED); + /* * If the memory must remain coherent with the * cache then we must make the memory uncacheable @@ -854,6 +884,7 @@ _bus_dmamem_map(bus_dma_tag_t t, bus_dma */ if (flags & BUS_DMA_COHERENT) { cpu_dcache_wbinv_range(va, PAGE_SIZE); + cpu_sdcache_wbinv_range(va, pa, PAGE_SIZE); cpu_drain_writebuf(); ptep = vtopte(va); *ptep &= ~L2_S_CACHE_MASK;