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;

Reply via email to