xref: /freebsd/sys/dev/proto/proto_busdma.c (revision f40c76d8dedcc7cf095b00787567a4f1d575280f)
14f027abdSMarcel Moolenaar /*-
24f027abdSMarcel Moolenaar  * Copyright (c) 2015 Marcel Moolenaar
34f027abdSMarcel Moolenaar  * All rights reserved.
44f027abdSMarcel Moolenaar  *
54f027abdSMarcel Moolenaar  * Redistribution and use in source and binary forms, with or without
64f027abdSMarcel Moolenaar  * modification, are permitted provided that the following conditions
74f027abdSMarcel Moolenaar  * are met:
84f027abdSMarcel Moolenaar  * 1. Redistributions of source code must retain the above copyright
94f027abdSMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer.
104f027abdSMarcel Moolenaar  * 2. Redistributions in binary form must reproduce the above copyright
114f027abdSMarcel Moolenaar  *    notice, this list of conditions and the following disclaimer in the
124f027abdSMarcel Moolenaar  *    documentation and/or other materials provided with the distribution.
134f027abdSMarcel Moolenaar  *
144f027abdSMarcel Moolenaar  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
154f027abdSMarcel Moolenaar  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
164f027abdSMarcel Moolenaar  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
174f027abdSMarcel Moolenaar  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
184f027abdSMarcel Moolenaar  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
194f027abdSMarcel Moolenaar  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
204f027abdSMarcel Moolenaar  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
214f027abdSMarcel Moolenaar  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
224f027abdSMarcel Moolenaar  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
234f027abdSMarcel Moolenaar  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
244f027abdSMarcel Moolenaar  */
254f027abdSMarcel Moolenaar 
264f027abdSMarcel Moolenaar #include <sys/cdefs.h>
274f027abdSMarcel Moolenaar __FBSDID("$FreeBSD$");
284f027abdSMarcel Moolenaar 
294f027abdSMarcel Moolenaar #include <sys/param.h>
304f027abdSMarcel Moolenaar #include <sys/systm.h>
314f027abdSMarcel Moolenaar #include <machine/bus.h>
324f027abdSMarcel Moolenaar #include <machine/bus_dma.h>
334f027abdSMarcel Moolenaar #include <machine/resource.h>
344f027abdSMarcel Moolenaar #include <sys/bus.h>
354f027abdSMarcel Moolenaar #include <sys/conf.h>
364f027abdSMarcel Moolenaar #include <sys/kernel.h>
374f027abdSMarcel Moolenaar #include <sys/malloc.h>
384f027abdSMarcel Moolenaar #include <sys/module.h>
3989abdea8SMarcel Moolenaar #include <sys/proc.h>
404f027abdSMarcel Moolenaar #include <sys/queue.h>
414f027abdSMarcel Moolenaar #include <sys/rman.h>
424f027abdSMarcel Moolenaar #include <sys/sbuf.h>
4389abdea8SMarcel Moolenaar #include <sys/uio.h>
445dcca8e8SMarcel Moolenaar #include <vm/vm.h>
455dcca8e8SMarcel Moolenaar #include <vm/pmap.h>
4689abdea8SMarcel Moolenaar #include <vm/vm_map.h>
474f027abdSMarcel Moolenaar 
484f027abdSMarcel Moolenaar #include <dev/proto/proto.h>
494f027abdSMarcel Moolenaar #include <dev/proto/proto_dev.h>
504f027abdSMarcel Moolenaar #include <dev/proto/proto_busdma.h>
514f027abdSMarcel Moolenaar 
524f027abdSMarcel Moolenaar MALLOC_DEFINE(M_PROTO_BUSDMA, "proto_busdma", "DMA management data");
534f027abdSMarcel Moolenaar 
54b2ce196cSMarcel Moolenaar #define	BNDRY_MIN(a, b)		\
55b2ce196cSMarcel Moolenaar 	(((a) == 0) ? (b) : (((b) == 0) ? (a) : MIN((a), (b))))
56b2ce196cSMarcel Moolenaar 
5789abdea8SMarcel Moolenaar struct proto_callback_bundle {
5889abdea8SMarcel Moolenaar 	struct proto_busdma *busdma;
5989abdea8SMarcel Moolenaar 	struct proto_md *md;
6089abdea8SMarcel Moolenaar 	struct proto_ioc_busdma *ioc;
6189abdea8SMarcel Moolenaar };
6289abdea8SMarcel Moolenaar 
634f027abdSMarcel Moolenaar static int
645dcca8e8SMarcel Moolenaar proto_busdma_tag_create(struct proto_busdma *busdma, struct proto_tag *parent,
655dcca8e8SMarcel Moolenaar     struct proto_ioc_busdma *ioc)
664f027abdSMarcel Moolenaar {
674f027abdSMarcel Moolenaar 	struct proto_tag *tag;
684f027abdSMarcel Moolenaar 
69b2ce196cSMarcel Moolenaar 	/* Make sure that when a boundary is specified, it's a power of 2 */
70b2ce196cSMarcel Moolenaar 	if (ioc->u.tag.bndry != 0 &&
71b2ce196cSMarcel Moolenaar 	    (ioc->u.tag.bndry & (ioc->u.tag.bndry - 1)) != 0)
72b2ce196cSMarcel Moolenaar 		return (EINVAL);
73b2ce196cSMarcel Moolenaar 
745dcca8e8SMarcel Moolenaar 	/*
755dcca8e8SMarcel Moolenaar 	 * If nsegs is 1, ignore maxsegsz. What this means is that if we have
765dcca8e8SMarcel Moolenaar 	 * just 1 segment, then maxsz should be equal to maxsegsz. To keep it
775dcca8e8SMarcel Moolenaar 	 * simple for us, limit maxsegsz to maxsz in any case.
785dcca8e8SMarcel Moolenaar 	 */
795dcca8e8SMarcel Moolenaar 	if (ioc->u.tag.maxsegsz > ioc->u.tag.maxsz || ioc->u.tag.nsegs == 1)
805dcca8e8SMarcel Moolenaar 		ioc->u.tag.maxsegsz = ioc->u.tag.maxsz;
815dcca8e8SMarcel Moolenaar 
824f027abdSMarcel Moolenaar 	tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
835dcca8e8SMarcel Moolenaar 	if (parent != NULL) {
845dcca8e8SMarcel Moolenaar 		tag->parent = parent;
855dcca8e8SMarcel Moolenaar 		LIST_INSERT_HEAD(&parent->children, tag, peers);
865dcca8e8SMarcel Moolenaar 		tag->align = MAX(ioc->u.tag.align, parent->align);
87b2ce196cSMarcel Moolenaar 		tag->bndry = BNDRY_MIN(ioc->u.tag.bndry, parent->bndry);
885dcca8e8SMarcel Moolenaar 		tag->maxaddr = MIN(ioc->u.tag.maxaddr, parent->maxaddr);
895dcca8e8SMarcel Moolenaar 		tag->maxsz = MIN(ioc->u.tag.maxsz, parent->maxsz);
905dcca8e8SMarcel Moolenaar 		tag->maxsegsz = MIN(ioc->u.tag.maxsegsz, parent->maxsegsz);
915dcca8e8SMarcel Moolenaar 		tag->nsegs = MIN(ioc->u.tag.nsegs, parent->nsegs);
925dcca8e8SMarcel Moolenaar 		tag->datarate = MIN(ioc->u.tag.datarate, parent->datarate);
935dcca8e8SMarcel Moolenaar 		/* Write constraints back */
945dcca8e8SMarcel Moolenaar 		ioc->u.tag.align = tag->align;
955dcca8e8SMarcel Moolenaar 		ioc->u.tag.bndry = tag->bndry;
965dcca8e8SMarcel Moolenaar 		ioc->u.tag.maxaddr = tag->maxaddr;
975dcca8e8SMarcel Moolenaar 		ioc->u.tag.maxsz = tag->maxsz;
985dcca8e8SMarcel Moolenaar 		ioc->u.tag.maxsegsz = tag->maxsegsz;
995dcca8e8SMarcel Moolenaar 		ioc->u.tag.nsegs = tag->nsegs;
1005dcca8e8SMarcel Moolenaar 		ioc->u.tag.datarate = tag->datarate;
1015dcca8e8SMarcel Moolenaar 	} else {
1025dcca8e8SMarcel Moolenaar 		tag->align = ioc->u.tag.align;
1035dcca8e8SMarcel Moolenaar 		tag->bndry = ioc->u.tag.bndry;
1045dcca8e8SMarcel Moolenaar 		tag->maxaddr = ioc->u.tag.maxaddr;
1055dcca8e8SMarcel Moolenaar 		tag->maxsz = ioc->u.tag.maxsz;
1065dcca8e8SMarcel Moolenaar 		tag->maxsegsz = ioc->u.tag.maxsegsz;
1075dcca8e8SMarcel Moolenaar 		tag->nsegs = ioc->u.tag.nsegs;
1085dcca8e8SMarcel Moolenaar 		tag->datarate = ioc->u.tag.datarate;
1095dcca8e8SMarcel Moolenaar 	}
1105dcca8e8SMarcel Moolenaar 	LIST_INSERT_HEAD(&busdma->tags, tag, tags);
1115dcca8e8SMarcel Moolenaar 	ioc->result = (uintptr_t)(void *)tag;
1124f027abdSMarcel Moolenaar 	return (0);
1134f027abdSMarcel Moolenaar }
1144f027abdSMarcel Moolenaar 
1155dcca8e8SMarcel Moolenaar static int
1165dcca8e8SMarcel Moolenaar proto_busdma_tag_destroy(struct proto_busdma *busdma, struct proto_tag *tag)
1174f027abdSMarcel Moolenaar {
1184f027abdSMarcel Moolenaar 
1195dcca8e8SMarcel Moolenaar 	if (!LIST_EMPTY(&tag->mds))
1205dcca8e8SMarcel Moolenaar 		return (EBUSY);
1215dcca8e8SMarcel Moolenaar 	if (!LIST_EMPTY(&tag->children))
1225dcca8e8SMarcel Moolenaar 		return (EBUSY);
1235dcca8e8SMarcel Moolenaar 
1245dcca8e8SMarcel Moolenaar 	if (tag->parent != NULL) {
1255dcca8e8SMarcel Moolenaar 		LIST_REMOVE(tag, peers);
1265dcca8e8SMarcel Moolenaar 		tag->parent = NULL;
1275dcca8e8SMarcel Moolenaar 	}
1285dcca8e8SMarcel Moolenaar 	LIST_REMOVE(tag, tags);
1294f027abdSMarcel Moolenaar 	free(tag, M_PROTO_BUSDMA);
1305dcca8e8SMarcel Moolenaar 	return (0);
1314f027abdSMarcel Moolenaar }
1324f027abdSMarcel Moolenaar 
1334f027abdSMarcel Moolenaar static struct proto_tag *
1344f027abdSMarcel Moolenaar proto_busdma_tag_lookup(struct proto_busdma *busdma, u_long key)
1354f027abdSMarcel Moolenaar {
1364f027abdSMarcel Moolenaar 	struct proto_tag *tag;
1374f027abdSMarcel Moolenaar 
1385dcca8e8SMarcel Moolenaar 	LIST_FOREACH(tag, &busdma->tags, tags) {
1394f027abdSMarcel Moolenaar 		if ((void *)tag == (void *)key)
1404f027abdSMarcel Moolenaar 			return (tag);
1414f027abdSMarcel Moolenaar 	}
1424f027abdSMarcel Moolenaar 	return (NULL);
1434f027abdSMarcel Moolenaar }
1444f027abdSMarcel Moolenaar 
14589abdea8SMarcel Moolenaar static int
14689abdea8SMarcel Moolenaar proto_busdma_md_destroy_internal(struct proto_busdma *busdma,
14789abdea8SMarcel Moolenaar     struct proto_md *md)
14889abdea8SMarcel Moolenaar {
14989abdea8SMarcel Moolenaar 
15089abdea8SMarcel Moolenaar 	LIST_REMOVE(md, mds);
15189abdea8SMarcel Moolenaar 	LIST_REMOVE(md, peers);
15289abdea8SMarcel Moolenaar 	if (md->physaddr)
15389abdea8SMarcel Moolenaar 		bus_dmamap_unload(md->bd_tag, md->bd_map);
15489abdea8SMarcel Moolenaar 	if (md->virtaddr != NULL)
15589abdea8SMarcel Moolenaar 		bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map);
15689abdea8SMarcel Moolenaar 	else
15789abdea8SMarcel Moolenaar 		bus_dmamap_destroy(md->bd_tag, md->bd_map);
15889abdea8SMarcel Moolenaar 	bus_dma_tag_destroy(md->bd_tag);
15989abdea8SMarcel Moolenaar 	free(md, M_PROTO_BUSDMA);
16089abdea8SMarcel Moolenaar 	return (0);
16189abdea8SMarcel Moolenaar }
16289abdea8SMarcel Moolenaar 
16390a1793cSMarcel Moolenaar static void
16490a1793cSMarcel Moolenaar proto_busdma_mem_alloc_callback(void *arg, bus_dma_segment_t *segs, int	nseg,
16590a1793cSMarcel Moolenaar     int error)
16690a1793cSMarcel Moolenaar {
16789abdea8SMarcel Moolenaar 	struct proto_callback_bundle *pcb = arg;
16890a1793cSMarcel Moolenaar 
16989abdea8SMarcel Moolenaar 	pcb->ioc->u.md.bus_nsegs = nseg;
17089abdea8SMarcel Moolenaar 	pcb->ioc->u.md.bus_addr = segs[0].ds_addr;
17190a1793cSMarcel Moolenaar }
17290a1793cSMarcel Moolenaar 
1735dcca8e8SMarcel Moolenaar static int
1745dcca8e8SMarcel Moolenaar proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag,
1755dcca8e8SMarcel Moolenaar     struct proto_ioc_busdma *ioc)
1765dcca8e8SMarcel Moolenaar {
17789abdea8SMarcel Moolenaar 	struct proto_callback_bundle pcb;
1785dcca8e8SMarcel Moolenaar 	struct proto_md *md;
1795dcca8e8SMarcel Moolenaar 	int error;
1805dcca8e8SMarcel Moolenaar 
1815dcca8e8SMarcel Moolenaar 	md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
1825dcca8e8SMarcel Moolenaar 	md->tag = tag;
1835dcca8e8SMarcel Moolenaar 
1845dcca8e8SMarcel Moolenaar 	error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry,
1855dcca8e8SMarcel Moolenaar 	    tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz,
1865dcca8e8SMarcel Moolenaar 	    tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag);
1875dcca8e8SMarcel Moolenaar 	if (error) {
1885dcca8e8SMarcel Moolenaar 		free(md, M_PROTO_BUSDMA);
1895dcca8e8SMarcel Moolenaar 		return (error);
1905dcca8e8SMarcel Moolenaar 	}
191cff0f135SMarcel Moolenaar 	error = bus_dmamem_alloc(md->bd_tag, &md->virtaddr, 0, &md->bd_map);
1925dcca8e8SMarcel Moolenaar 	if (error) {
1935dcca8e8SMarcel Moolenaar 		bus_dma_tag_destroy(md->bd_tag);
1945dcca8e8SMarcel Moolenaar 		free(md, M_PROTO_BUSDMA);
1955dcca8e8SMarcel Moolenaar 		return (error);
1965dcca8e8SMarcel Moolenaar 	}
197cff0f135SMarcel Moolenaar 	md->physaddr = pmap_kextract((uintptr_t)(md->virtaddr));
19889abdea8SMarcel Moolenaar 	pcb.busdma = busdma;
19989abdea8SMarcel Moolenaar 	pcb.md = md;
20089abdea8SMarcel Moolenaar 	pcb.ioc = ioc;
20190a1793cSMarcel Moolenaar 	error = bus_dmamap_load(md->bd_tag, md->bd_map, md->virtaddr,
20289abdea8SMarcel Moolenaar 	    tag->maxsz, proto_busdma_mem_alloc_callback, &pcb, BUS_DMA_NOWAIT);
20390a1793cSMarcel Moolenaar 	if (error) {
20490a1793cSMarcel Moolenaar 		bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map);
20590a1793cSMarcel Moolenaar 		bus_dma_tag_destroy(md->bd_tag);
20690a1793cSMarcel Moolenaar 		free(md, M_PROTO_BUSDMA);
20790a1793cSMarcel Moolenaar 		return (error);
20890a1793cSMarcel Moolenaar 	}
2095dcca8e8SMarcel Moolenaar 	LIST_INSERT_HEAD(&tag->mds, md, peers);
2105dcca8e8SMarcel Moolenaar 	LIST_INSERT_HEAD(&busdma->mds, md, mds);
21189abdea8SMarcel Moolenaar 	ioc->u.md.virt_addr = (uintptr_t)md->virtaddr;
21289abdea8SMarcel Moolenaar 	ioc->u.md.virt_size = tag->maxsz;
21389abdea8SMarcel Moolenaar 	ioc->u.md.phys_nsegs = 1;
21489abdea8SMarcel Moolenaar 	ioc->u.md.phys_addr = md->physaddr;
2155dcca8e8SMarcel Moolenaar 	ioc->result = (uintptr_t)(void *)md;
2165dcca8e8SMarcel Moolenaar 	return (0);
2175dcca8e8SMarcel Moolenaar }
2185dcca8e8SMarcel Moolenaar 
2195dcca8e8SMarcel Moolenaar static int
2205dcca8e8SMarcel Moolenaar proto_busdma_mem_free(struct proto_busdma *busdma, struct proto_md *md)
2215dcca8e8SMarcel Moolenaar {
2225dcca8e8SMarcel Moolenaar 
22389abdea8SMarcel Moolenaar 	if (md->virtaddr == NULL)
22489abdea8SMarcel Moolenaar 		return (ENXIO);
22589abdea8SMarcel Moolenaar 	return (proto_busdma_md_destroy_internal(busdma, md));
22689abdea8SMarcel Moolenaar }
22789abdea8SMarcel Moolenaar 
22889abdea8SMarcel Moolenaar static int
22989abdea8SMarcel Moolenaar proto_busdma_md_create(struct proto_busdma *busdma, struct proto_tag *tag,
23089abdea8SMarcel Moolenaar     struct proto_ioc_busdma *ioc)
23189abdea8SMarcel Moolenaar {
23289abdea8SMarcel Moolenaar 	struct proto_md *md;
23389abdea8SMarcel Moolenaar 	int error;
23489abdea8SMarcel Moolenaar 
23589abdea8SMarcel Moolenaar 	md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
23689abdea8SMarcel Moolenaar 	md->tag = tag;
23789abdea8SMarcel Moolenaar 
23889abdea8SMarcel Moolenaar 	error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry,
23989abdea8SMarcel Moolenaar 	    tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz,
24089abdea8SMarcel Moolenaar 	    tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag);
24189abdea8SMarcel Moolenaar 	if (error) {
24289abdea8SMarcel Moolenaar 		free(md, M_PROTO_BUSDMA);
24389abdea8SMarcel Moolenaar 		return (error);
24489abdea8SMarcel Moolenaar 	}
24589abdea8SMarcel Moolenaar 	error = bus_dmamap_create(md->bd_tag, 0, &md->bd_map);
24689abdea8SMarcel Moolenaar 	if (error) {
2475dcca8e8SMarcel Moolenaar 		bus_dma_tag_destroy(md->bd_tag);
2485dcca8e8SMarcel Moolenaar 		free(md, M_PROTO_BUSDMA);
24989abdea8SMarcel Moolenaar 		return (error);
25089abdea8SMarcel Moolenaar 	}
25189abdea8SMarcel Moolenaar 
25289abdea8SMarcel Moolenaar 	LIST_INSERT_HEAD(&tag->mds, md, peers);
25389abdea8SMarcel Moolenaar 	LIST_INSERT_HEAD(&busdma->mds, md, mds);
25489abdea8SMarcel Moolenaar 	ioc->result = (uintptr_t)(void *)md;
25589abdea8SMarcel Moolenaar 	return (0);
25689abdea8SMarcel Moolenaar }
25789abdea8SMarcel Moolenaar 
25889abdea8SMarcel Moolenaar static int
25989abdea8SMarcel Moolenaar proto_busdma_md_destroy(struct proto_busdma *busdma, struct proto_md *md)
26089abdea8SMarcel Moolenaar {
26189abdea8SMarcel Moolenaar 
26289abdea8SMarcel Moolenaar 	if (md->virtaddr != NULL)
26389abdea8SMarcel Moolenaar 		return (ENXIO);
26489abdea8SMarcel Moolenaar 	return (proto_busdma_md_destroy_internal(busdma, md));
26589abdea8SMarcel Moolenaar }
26689abdea8SMarcel Moolenaar 
26789abdea8SMarcel Moolenaar static void
26889abdea8SMarcel Moolenaar proto_busdma_md_load_callback(void *arg, bus_dma_segment_t *segs, int nseg,
26989abdea8SMarcel Moolenaar     bus_size_t sz, int error)
27089abdea8SMarcel Moolenaar {
27189abdea8SMarcel Moolenaar 	struct proto_callback_bundle *pcb = arg;
27289abdea8SMarcel Moolenaar 
27389abdea8SMarcel Moolenaar 	pcb->ioc->u.md.bus_nsegs = nseg;
27489abdea8SMarcel Moolenaar 	pcb->ioc->u.md.bus_addr = segs[0].ds_addr;
27589abdea8SMarcel Moolenaar }
27689abdea8SMarcel Moolenaar 
27789abdea8SMarcel Moolenaar static int
27889abdea8SMarcel Moolenaar proto_busdma_md_load(struct proto_busdma *busdma, struct proto_md *md,
27989abdea8SMarcel Moolenaar     struct proto_ioc_busdma *ioc, struct thread *td)
28089abdea8SMarcel Moolenaar {
28189abdea8SMarcel Moolenaar 	struct proto_callback_bundle pcb;
28289abdea8SMarcel Moolenaar 	struct iovec iov;
28389abdea8SMarcel Moolenaar 	struct uio uio;
28489abdea8SMarcel Moolenaar 	pmap_t pmap;
28589abdea8SMarcel Moolenaar 	int error;
28689abdea8SMarcel Moolenaar 
28789abdea8SMarcel Moolenaar 	iov.iov_base = (void *)(uintptr_t)ioc->u.md.virt_addr;
28889abdea8SMarcel Moolenaar 	iov.iov_len = ioc->u.md.virt_size;
28989abdea8SMarcel Moolenaar 	uio.uio_iov = &iov;
29089abdea8SMarcel Moolenaar 	uio.uio_iovcnt = 1;
29189abdea8SMarcel Moolenaar 	uio.uio_offset = 0;
29289abdea8SMarcel Moolenaar 	uio.uio_resid = iov.iov_len;
29389abdea8SMarcel Moolenaar 	uio.uio_segflg = UIO_USERSPACE;
29489abdea8SMarcel Moolenaar 	uio.uio_rw = UIO_READ;
29589abdea8SMarcel Moolenaar 	uio.uio_td = td;
29689abdea8SMarcel Moolenaar 
29789abdea8SMarcel Moolenaar 	pcb.busdma = busdma;
29889abdea8SMarcel Moolenaar 	pcb.md = md;
29989abdea8SMarcel Moolenaar 	pcb.ioc = ioc;
30089abdea8SMarcel Moolenaar 	error = bus_dmamap_load_uio(md->bd_tag, md->bd_map, &uio,
30189abdea8SMarcel Moolenaar 	    proto_busdma_md_load_callback, &pcb, BUS_DMA_NOWAIT);
30289abdea8SMarcel Moolenaar 	if (error)
30389abdea8SMarcel Moolenaar 		return (error);
30489abdea8SMarcel Moolenaar 
30589abdea8SMarcel Moolenaar 	/* XXX determine *all* physical memory segments */
30689abdea8SMarcel Moolenaar 	pmap = vmspace_pmap(td->td_proc->p_vmspace);
30789abdea8SMarcel Moolenaar 	md->physaddr = pmap_extract(pmap, ioc->u.md.virt_addr);
30889abdea8SMarcel Moolenaar 	ioc->u.md.phys_nsegs = 1;	/* XXX */
30989abdea8SMarcel Moolenaar 	ioc->u.md.phys_addr = md->physaddr;
3105dcca8e8SMarcel Moolenaar 	return (0);
3115dcca8e8SMarcel Moolenaar }
3125dcca8e8SMarcel Moolenaar 
31342d3ab5dSMarcel Moolenaar static int
31442d3ab5dSMarcel Moolenaar proto_busdma_md_unload(struct proto_busdma *busdma, struct proto_md *md)
31542d3ab5dSMarcel Moolenaar {
31642d3ab5dSMarcel Moolenaar 
31742d3ab5dSMarcel Moolenaar 	if (!md->physaddr)
31842d3ab5dSMarcel Moolenaar 		return (ENXIO);
31942d3ab5dSMarcel Moolenaar 	bus_dmamap_unload(md->bd_tag, md->bd_map);
32042d3ab5dSMarcel Moolenaar 	md->physaddr = 0;
32142d3ab5dSMarcel Moolenaar 	return (0);
32242d3ab5dSMarcel Moolenaar }
32342d3ab5dSMarcel Moolenaar 
32442d3ab5dSMarcel Moolenaar static int
32542d3ab5dSMarcel Moolenaar proto_busdma_sync(struct proto_busdma *busdma, struct proto_md *md,
32642d3ab5dSMarcel Moolenaar     struct proto_ioc_busdma *ioc)
32742d3ab5dSMarcel Moolenaar {
328*f40c76d8SMarcel Moolenaar 	u_int ops;
32942d3ab5dSMarcel Moolenaar 
330*f40c76d8SMarcel Moolenaar 	ops = BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE |
331*f40c76d8SMarcel Moolenaar 	    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE;
332*f40c76d8SMarcel Moolenaar 	if (ioc->u.sync.op & ~ops)
333*f40c76d8SMarcel Moolenaar 		return (EINVAL);
33442d3ab5dSMarcel Moolenaar 	if (!md->physaddr)
33542d3ab5dSMarcel Moolenaar 		return (ENXIO);
33642d3ab5dSMarcel Moolenaar 	bus_dmamap_sync(md->bd_tag, md->bd_map, ioc->u.sync.op);
33742d3ab5dSMarcel Moolenaar 	return (0);
33842d3ab5dSMarcel Moolenaar }
33942d3ab5dSMarcel Moolenaar 
3405dcca8e8SMarcel Moolenaar static struct proto_md *
3415dcca8e8SMarcel Moolenaar proto_busdma_md_lookup(struct proto_busdma *busdma, u_long key)
3425dcca8e8SMarcel Moolenaar {
3435dcca8e8SMarcel Moolenaar 	struct proto_md *md;
3445dcca8e8SMarcel Moolenaar 
3455dcca8e8SMarcel Moolenaar 	LIST_FOREACH(md, &busdma->mds, mds) {
3465dcca8e8SMarcel Moolenaar 		if ((void *)md == (void *)key)
3475dcca8e8SMarcel Moolenaar 			return (md);
3485dcca8e8SMarcel Moolenaar 	}
3495dcca8e8SMarcel Moolenaar 	return (NULL);
3505dcca8e8SMarcel Moolenaar }
3515dcca8e8SMarcel Moolenaar 
3524f027abdSMarcel Moolenaar struct proto_busdma *
3534f027abdSMarcel Moolenaar proto_busdma_attach(struct proto_softc *sc)
3544f027abdSMarcel Moolenaar {
3554f027abdSMarcel Moolenaar 	struct proto_busdma *busdma;
3564f027abdSMarcel Moolenaar 
3574f027abdSMarcel Moolenaar 	busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO);
3584f027abdSMarcel Moolenaar 	return (busdma);
3594f027abdSMarcel Moolenaar }
3604f027abdSMarcel Moolenaar 
3614f027abdSMarcel Moolenaar int
3624f027abdSMarcel Moolenaar proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma)
3634f027abdSMarcel Moolenaar {
3644f027abdSMarcel Moolenaar 
3654f027abdSMarcel Moolenaar 	proto_busdma_cleanup(sc, busdma);
3664f027abdSMarcel Moolenaar 	free(busdma, M_PROTO_BUSDMA);
3674f027abdSMarcel Moolenaar 	return (0);
3684f027abdSMarcel Moolenaar }
3694f027abdSMarcel Moolenaar 
3704f027abdSMarcel Moolenaar int
3714f027abdSMarcel Moolenaar proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma)
3724f027abdSMarcel Moolenaar {
3735dcca8e8SMarcel Moolenaar 	struct proto_md *md, *md1;
3744f027abdSMarcel Moolenaar 	struct proto_tag *tag, *tag1;
3754f027abdSMarcel Moolenaar 
3765dcca8e8SMarcel Moolenaar 	LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1)
37789abdea8SMarcel Moolenaar 		proto_busdma_md_destroy_internal(busdma, md);
3785dcca8e8SMarcel Moolenaar 	LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1)
3795dcca8e8SMarcel Moolenaar 		proto_busdma_tag_destroy(busdma, tag);
3804f027abdSMarcel Moolenaar 	return (0);
3814f027abdSMarcel Moolenaar }
3824f027abdSMarcel Moolenaar 
3834f027abdSMarcel Moolenaar int
3844f027abdSMarcel Moolenaar proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma,
38589abdea8SMarcel Moolenaar     struct proto_ioc_busdma *ioc, struct thread *td)
3864f027abdSMarcel Moolenaar {
3874f027abdSMarcel Moolenaar 	struct proto_tag *tag;
3885dcca8e8SMarcel Moolenaar 	struct proto_md *md;
3894f027abdSMarcel Moolenaar 	int error;
3904f027abdSMarcel Moolenaar 
3914f027abdSMarcel Moolenaar 	error = 0;
3924f027abdSMarcel Moolenaar 	switch (ioc->request) {
3934f027abdSMarcel Moolenaar 	case PROTO_IOC_BUSDMA_TAG_CREATE:
3945dcca8e8SMarcel Moolenaar 		busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev);
3955dcca8e8SMarcel Moolenaar 		error = proto_busdma_tag_create(busdma, NULL, ioc);
3964f027abdSMarcel Moolenaar 		break;
3974f027abdSMarcel Moolenaar 	case PROTO_IOC_BUSDMA_TAG_DERIVE:
3984f027abdSMarcel Moolenaar 		tag = proto_busdma_tag_lookup(busdma, ioc->key);
3994f027abdSMarcel Moolenaar 		if (tag == NULL) {
4004f027abdSMarcel Moolenaar 			error = EINVAL;
4014f027abdSMarcel Moolenaar 			break;
4024f027abdSMarcel Moolenaar 		}
4035dcca8e8SMarcel Moolenaar 		error = proto_busdma_tag_create(busdma, tag, ioc);
4044f027abdSMarcel Moolenaar 		break;
4054f027abdSMarcel Moolenaar 	case PROTO_IOC_BUSDMA_TAG_DESTROY:
4064f027abdSMarcel Moolenaar 		tag = proto_busdma_tag_lookup(busdma, ioc->key);
4074f027abdSMarcel Moolenaar 		if (tag == NULL) {
4084f027abdSMarcel Moolenaar 			error = EINVAL;
4094f027abdSMarcel Moolenaar 			break;
4104f027abdSMarcel Moolenaar 		}
4115dcca8e8SMarcel Moolenaar 		error = proto_busdma_tag_destroy(busdma, tag);
4125dcca8e8SMarcel Moolenaar 		break;
4135dcca8e8SMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MEM_ALLOC:
41489abdea8SMarcel Moolenaar 		tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag);
4155dcca8e8SMarcel Moolenaar 		if (tag == NULL) {
4165dcca8e8SMarcel Moolenaar 			error = EINVAL;
4175dcca8e8SMarcel Moolenaar 			break;
4185dcca8e8SMarcel Moolenaar 		}
4195dcca8e8SMarcel Moolenaar 		error = proto_busdma_mem_alloc(busdma, tag, ioc);
4205dcca8e8SMarcel Moolenaar 		break;
4215dcca8e8SMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MEM_FREE:
4225dcca8e8SMarcel Moolenaar 		md = proto_busdma_md_lookup(busdma, ioc->key);
4235dcca8e8SMarcel Moolenaar 		if (md == NULL) {
4245dcca8e8SMarcel Moolenaar 			error = EINVAL;
4255dcca8e8SMarcel Moolenaar 			break;
4265dcca8e8SMarcel Moolenaar 		}
4275dcca8e8SMarcel Moolenaar 		error = proto_busdma_mem_free(busdma, md);
4284f027abdSMarcel Moolenaar 		break;
42989abdea8SMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MD_CREATE:
43089abdea8SMarcel Moolenaar 		tag = proto_busdma_tag_lookup(busdma, ioc->u.md.tag);
43189abdea8SMarcel Moolenaar 		if (tag == NULL) {
43289abdea8SMarcel Moolenaar 			error = EINVAL;
43389abdea8SMarcel Moolenaar 			break;
43489abdea8SMarcel Moolenaar 		}
43589abdea8SMarcel Moolenaar 		error = proto_busdma_md_create(busdma, tag, ioc);
43689abdea8SMarcel Moolenaar 		break;
43789abdea8SMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MD_DESTROY:
43889abdea8SMarcel Moolenaar 		md = proto_busdma_md_lookup(busdma, ioc->key);
43989abdea8SMarcel Moolenaar 		if (md == NULL) {
44089abdea8SMarcel Moolenaar 			error = EINVAL;
44189abdea8SMarcel Moolenaar 			break;
44289abdea8SMarcel Moolenaar 		}
44389abdea8SMarcel Moolenaar 		error = proto_busdma_md_destroy(busdma, md);
44489abdea8SMarcel Moolenaar 		break;
44589abdea8SMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MD_LOAD:
44689abdea8SMarcel Moolenaar 		md = proto_busdma_md_lookup(busdma, ioc->key);
44789abdea8SMarcel Moolenaar 		if (md == NULL) {
44889abdea8SMarcel Moolenaar 			error = EINVAL;
44989abdea8SMarcel Moolenaar 			break;
45089abdea8SMarcel Moolenaar 		}
45189abdea8SMarcel Moolenaar 		error = proto_busdma_md_load(busdma, md, ioc, td);
45289abdea8SMarcel Moolenaar 		break;
45342d3ab5dSMarcel Moolenaar 	case PROTO_IOC_BUSDMA_MD_UNLOAD:
45442d3ab5dSMarcel Moolenaar 		md = proto_busdma_md_lookup(busdma, ioc->key);
45542d3ab5dSMarcel Moolenaar 		if (md == NULL) {
45642d3ab5dSMarcel Moolenaar 			error = EINVAL;
45742d3ab5dSMarcel Moolenaar 			break;
45842d3ab5dSMarcel Moolenaar 		}
45942d3ab5dSMarcel Moolenaar 		error = proto_busdma_md_unload(busdma, md);
46042d3ab5dSMarcel Moolenaar 		break;
46142d3ab5dSMarcel Moolenaar 	case PROTO_IOC_BUSDMA_SYNC:
46242d3ab5dSMarcel Moolenaar 		md = proto_busdma_md_lookup(busdma, ioc->key);
46342d3ab5dSMarcel Moolenaar 		if (md == NULL) {
46442d3ab5dSMarcel Moolenaar 			error = EINVAL;
46542d3ab5dSMarcel Moolenaar 			break;
46642d3ab5dSMarcel Moolenaar 		}
46742d3ab5dSMarcel Moolenaar 		error = proto_busdma_sync(busdma, md, ioc);
46842d3ab5dSMarcel Moolenaar 		break;
4694f027abdSMarcel Moolenaar 	default:
4704f027abdSMarcel Moolenaar 		error = EINVAL;
4714f027abdSMarcel Moolenaar 		break;
4724f027abdSMarcel Moolenaar 	}
4734f027abdSMarcel Moolenaar 	return (error);
4744f027abdSMarcel Moolenaar }
475cff0f135SMarcel Moolenaar 
476cff0f135SMarcel Moolenaar int
477cff0f135SMarcel Moolenaar proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr)
478cff0f135SMarcel Moolenaar {
479cff0f135SMarcel Moolenaar 	struct proto_md *md;
480cff0f135SMarcel Moolenaar 
481cff0f135SMarcel Moolenaar 	LIST_FOREACH(md, &busdma->mds, mds) {
482cff0f135SMarcel Moolenaar 		if (physaddr >= trunc_page(md->physaddr) &&
483cff0f135SMarcel Moolenaar 		    physaddr <= trunc_page(md->physaddr + md->tag->maxsz))
484cff0f135SMarcel Moolenaar 			return (1);
485cff0f135SMarcel Moolenaar 	}
486cff0f135SMarcel Moolenaar 	return (0);
487cff0f135SMarcel Moolenaar }
488