From 959daa43153ae871a3a3e6e288fb649a67fcc18d Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh@NetBSD.org>
Date: Wed, 16 Feb 2022 22:25:37 +0000
Subject: [PATCH] powerpc: Implement bus_dmamap_load_raw.

Can probably delete some of the round-trips between bus addresses and
physical addresses -- did these only to copy the logic already in
_bus_dmamap_load_buffer.
---
 sys/arch/powerpc/powerpc/bus_dma.c | 90 +++++++++++++++++++++++++++++-
 1 file changed, 88 insertions(+), 2 deletions(-)

diff --git a/sys/arch/powerpc/powerpc/bus_dma.c b/sys/arch/powerpc/powerpc/bus_dma.c
index c0a5c555cdad..dae5dfd99616 100644
--- a/sys/arch/powerpc/powerpc/bus_dma.c
+++ b/sys/arch/powerpc/powerpc/bus_dma.c
@@ -401,12 +401,98 @@ _bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, int fla
 /*
  * Like _bus_dmamap_load(), but for raw memory allocated with
  * bus_dmamem_alloc().
+ *
+ * XXX This is too much copypasta of _bus_dmamap_load_buffer.
  */
 int
-_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
+_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map,
+    bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags)
 {
+	bus_size_t sgsize, isgsize;
+	bus_size_t busaddr, curaddr, lastaddr, baddr, bmask;
+	int seg, iseg, first;
+
+	if (size == 0)
+		return 0;
+
+	lastaddr = 0;
+	bmask = ~(map->_dm_boundary - 1);
+
+	first = 0;
+	iseg = 0;
+	busaddr = segs[iseg].ds_addr;
+	isgsize = segs[iseg].ds_len;
+	for (seg = 0; size > 0;) {
+		/*
+		 * Get the physical address for this segment.
+		 */
+		curaddr = BUS_MEM_TO_PHYS(t, busaddr);
+
+		/*
+		 * If we're beyond the bounce threshold, notify
+		 * the caller.
+		 */
+		if (map->_dm_bounce_thresh != 0 &&
+		    curaddr >= map->_dm_bounce_thresh)
+			return EINVAL;
+
+		/*
+		 * Compute the segment size, and adjust counts.
+		 */
+		sgsize = PAGE_SIZE - ((u_long)curaddr & PGOFSET);
+		sgsize = MIN(sgsize, isgsize);
+		sgsize = MIN(sgsize, size);
+		sgsize = MIN(sgsize, map->dm_maxsegsz);
+
+		/*
+		 * Make sure we don't cross any boundaries.
+		 */
+		if (map->_dm_boundary > 0) {
+			baddr = (curaddr + map->_dm_boundary) & bmask;
+			if (sgsize > (baddr - curaddr))
+				sgsize = (baddr - curaddr);
+		}
+
+		/*
+		 * Insert chunk into a segment, coalescing with
+		 * the previous segment if possible.
+		 */
+		if (first) {
+			map->dm_segs[seg].ds_addr =
+			    PHYS_TO_BUS_MEM(t, curaddr);
+			map->dm_segs[seg].ds_len = sgsize;
+			first = 0;
+		} else {
+			if (curaddr == lastaddr &&
+			    (map->dm_segs[seg].ds_len + sgsize) <=
+			     map->dm_maxsegsz &&
+			    (map->_dm_boundary == 0 ||
+			     (map->dm_segs[seg].ds_addr & bmask) ==
+			     (PHYS_TO_BUS_MEM(t, curaddr) & bmask)))
+				map->dm_segs[seg].ds_len += sgsize;
+			else {
+				if (++seg >= map->_dm_segcnt)
+					break;
+				map->dm_segs[seg].ds_addr =
+					PHYS_TO_BUS_MEM(t, curaddr);
+				map->dm_segs[seg].ds_len = sgsize;
+			}
+		}
+
+		lastaddr = curaddr + sgsize;
+		size -= sgsize;
+		if ((isgsize -= sgsize) == 0) {
+			iseg++;
+			KASSERT(iseg < nsegs);
+			busaddr = segs[iseg].ds_addr;
+			isgsize = segs[iseg].ds_len;
+		}
+	}
+
+	if (size > 0)
+		return EFBIG;
 
-	panic("_bus_dmamap_load_raw: not implemented");
+	return 0;
 }
 
 /*