xref: /freebsd/sys/riscv/riscv/busdma_machdep.c (revision 74c781ed)
128029b68SRuslan Bukin /*-
228029b68SRuslan Bukin  * Copyright (c) 1997, 1998 Justin T. Gibbs.
375cf8837SRuslan Bukin  * Copyright (c) 2013, 2015 The FreeBSD Foundation
475cf8837SRuslan Bukin  * All rights reserved.
575cf8837SRuslan Bukin  *
675cf8837SRuslan Bukin  * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
775cf8837SRuslan Bukin  * under sponsorship from the FreeBSD Foundation.
875cf8837SRuslan Bukin  *
975cf8837SRuslan Bukin  * Portions of this software were developed by Semihalf
1075cf8837SRuslan Bukin  * under sponsorship of the FreeBSD Foundation.
1128029b68SRuslan Bukin  *
1228029b68SRuslan Bukin  * Redistribution and use in source and binary forms, with or without
1328029b68SRuslan Bukin  * modification, are permitted provided that the following conditions
1428029b68SRuslan Bukin  * are met:
1528029b68SRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
1628029b68SRuslan Bukin  *    notice, this list of conditions, and the following disclaimer,
1728029b68SRuslan Bukin  *    without modification, immediately at the beginning of the file.
1828029b68SRuslan Bukin  * 2. The name of the author may not be used to endorse or promote products
1928029b68SRuslan Bukin  *    derived from this software without specific prior written permission.
2028029b68SRuslan Bukin  *
2128029b68SRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2228029b68SRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2328029b68SRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2428029b68SRuslan Bukin  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
2528029b68SRuslan Bukin  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2628029b68SRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2728029b68SRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2828029b68SRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2928029b68SRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3028029b68SRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3128029b68SRuslan Bukin  * SUCH DAMAGE.
3228029b68SRuslan Bukin  */
3328029b68SRuslan Bukin 
3428029b68SRuslan Bukin #include <sys/cdefs.h>
3528029b68SRuslan Bukin __FBSDID("$FreeBSD$");
3628029b68SRuslan Bukin 
3728029b68SRuslan Bukin #include <sys/param.h>
3828029b68SRuslan Bukin #include <sys/systm.h>
3928029b68SRuslan Bukin #include <sys/malloc.h>
4028029b68SRuslan Bukin #include <sys/bus.h>
4128029b68SRuslan Bukin #include <sys/kernel.h>
4228029b68SRuslan Bukin #include <sys/ktr.h>
4328029b68SRuslan Bukin #include <sys/lock.h>
4428029b68SRuslan Bukin #include <sys/memdesc.h>
4528029b68SRuslan Bukin #include <sys/mutex.h>
4628029b68SRuslan Bukin #include <sys/uio.h>
4728029b68SRuslan Bukin #include <vm/vm.h>
4828029b68SRuslan Bukin #include <vm/vm_extern.h>
4928029b68SRuslan Bukin #include <vm/pmap.h>
5028029b68SRuslan Bukin 
5128029b68SRuslan Bukin #include <machine/bus.h>
5275cf8837SRuslan Bukin #include <machine/bus_dma_impl.h>
5328029b68SRuslan Bukin 
5475cf8837SRuslan Bukin /*
5575cf8837SRuslan Bukin  * Convenience function for manipulating driver locks from busdma (during
5675cf8837SRuslan Bukin  * busdma_swi, for example).  Drivers that don't provide their own locks
5775cf8837SRuslan Bukin  * should specify &Giant to dmat->lockfuncarg.  Drivers that use their own
5875cf8837SRuslan Bukin  * non-mutex locking scheme don't have to use this at all.
5975cf8837SRuslan Bukin  */
6028029b68SRuslan Bukin void
6175cf8837SRuslan Bukin busdma_lock_mutex(void *arg, bus_dma_lock_op_t op)
6228029b68SRuslan Bukin {
6375cf8837SRuslan Bukin 	struct mtx *dmtx;
6428029b68SRuslan Bukin 
6575cf8837SRuslan Bukin 	dmtx = (struct mtx *)arg;
6675cf8837SRuslan Bukin 	switch (op) {
6775cf8837SRuslan Bukin 	case BUS_DMA_LOCK:
6875cf8837SRuslan Bukin 		mtx_lock(dmtx);
6975cf8837SRuslan Bukin 		break;
7075cf8837SRuslan Bukin 	case BUS_DMA_UNLOCK:
7175cf8837SRuslan Bukin 		mtx_unlock(dmtx);
7275cf8837SRuslan Bukin 		break;
7375cf8837SRuslan Bukin 	default:
7475cf8837SRuslan Bukin 		panic("Unknown operation 0x%x for busdma_lock_mutex!", op);
7528029b68SRuslan Bukin 	}
7628029b68SRuslan Bukin }
7728029b68SRuslan Bukin 
7828029b68SRuslan Bukin /*
7975cf8837SRuslan Bukin  * dflt_lock should never get called.  It gets put into the dma tag when
8075cf8837SRuslan Bukin  * lockfunc == NULL, which is only valid if the maps that are associated
8175cf8837SRuslan Bukin  * with the tag are meant to never be defered.
8275cf8837SRuslan Bukin  * XXX Should have a way to identify which driver is responsible here.
8328029b68SRuslan Bukin  */
8428029b68SRuslan Bukin void
8575cf8837SRuslan Bukin bus_dma_dflt_lock(void *arg, bus_dma_lock_op_t op)
8628029b68SRuslan Bukin {
8728029b68SRuslan Bukin 
8875cf8837SRuslan Bukin 	panic("driver error: busdma dflt_lock called");
8928029b68SRuslan Bukin }
9028029b68SRuslan Bukin 
9175cf8837SRuslan Bukin /*
9275cf8837SRuslan Bukin  * Return true if a match is made.
9375cf8837SRuslan Bukin  *
9475cf8837SRuslan Bukin  * To find a match walk the chain of bus_dma_tag_t's looking for 'paddr'.
9575cf8837SRuslan Bukin  *
9675cf8837SRuslan Bukin  * If paddr is within the bounds of the dma tag then call the filter callback
9775cf8837SRuslan Bukin  * to check for a match, if there is no filter callback then assume a match.
9875cf8837SRuslan Bukin  */
9975cf8837SRuslan Bukin int
10075cf8837SRuslan Bukin bus_dma_run_filter(struct bus_dma_tag_common *tc, bus_addr_t paddr)
10175cf8837SRuslan Bukin {
10275cf8837SRuslan Bukin 	int retval;
10375cf8837SRuslan Bukin 
10475cf8837SRuslan Bukin 	retval = 0;
10575cf8837SRuslan Bukin 	do {
10675cf8837SRuslan Bukin 		if (((paddr > tc->lowaddr && paddr <= tc->highaddr) ||
10775cf8837SRuslan Bukin 		    ((paddr & (tc->alignment - 1)) != 0)) &&
10875cf8837SRuslan Bukin 		    (tc->filter == NULL ||
10975cf8837SRuslan Bukin 		    (*tc->filter)(tc->filterarg, paddr) != 0))
11075cf8837SRuslan Bukin 			retval = 1;
11175cf8837SRuslan Bukin 
11275cf8837SRuslan Bukin 		tc = tc->parent;
11375cf8837SRuslan Bukin 	} while (retval == 0 && tc != NULL);
11475cf8837SRuslan Bukin 	return (retval);
11575cf8837SRuslan Bukin }
11675cf8837SRuslan Bukin 
11775cf8837SRuslan Bukin int
11875cf8837SRuslan Bukin common_bus_dma_tag_create(struct bus_dma_tag_common *parent,
11975cf8837SRuslan Bukin     bus_size_t alignment, bus_addr_t boundary, bus_addr_t lowaddr,
12075cf8837SRuslan Bukin     bus_addr_t highaddr, bus_dma_filter_t *filter, void *filterarg,
12175cf8837SRuslan Bukin     bus_size_t maxsize, int nsegments, bus_size_t maxsegsz, int flags,
12275cf8837SRuslan Bukin     bus_dma_lock_t *lockfunc, void *lockfuncarg, size_t sz, void **dmat)
12375cf8837SRuslan Bukin {
12475cf8837SRuslan Bukin 	void *newtag;
12575cf8837SRuslan Bukin 	struct bus_dma_tag_common *common;
12675cf8837SRuslan Bukin 
12775cf8837SRuslan Bukin 	KASSERT(sz >= sizeof(struct bus_dma_tag_common), ("sz"));
12875cf8837SRuslan Bukin 	/* Return a NULL tag on failure */
12975cf8837SRuslan Bukin 	*dmat = NULL;
13075cf8837SRuslan Bukin 	/* Basic sanity checking */
13175cf8837SRuslan Bukin 	if (boundary != 0 && boundary < maxsegsz)
13275cf8837SRuslan Bukin 		maxsegsz = boundary;
13375cf8837SRuslan Bukin 	if (maxsegsz == 0)
13475cf8837SRuslan Bukin 		return (EINVAL);
13575cf8837SRuslan Bukin 
13675cf8837SRuslan Bukin 	newtag = malloc(sz, M_DEVBUF, M_ZERO | M_NOWAIT);
13775cf8837SRuslan Bukin 	if (newtag == NULL) {
13875cf8837SRuslan Bukin 		CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
13975cf8837SRuslan Bukin 		    __func__, newtag, 0, ENOMEM);
14075cf8837SRuslan Bukin 		return (ENOMEM);
14175cf8837SRuslan Bukin 	}
14275cf8837SRuslan Bukin 
14375cf8837SRuslan Bukin 	common = newtag;
14475cf8837SRuslan Bukin 	common->impl = &bus_dma_bounce_impl;
14575cf8837SRuslan Bukin 	common->parent = parent;
14675cf8837SRuslan Bukin 	common->alignment = alignment;
14775cf8837SRuslan Bukin 	common->boundary = boundary;
14875cf8837SRuslan Bukin 	common->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
14975cf8837SRuslan Bukin 	common->highaddr = trunc_page((vm_paddr_t)highaddr) + (PAGE_SIZE - 1);
15075cf8837SRuslan Bukin 	common->filter = filter;
15175cf8837SRuslan Bukin 	common->filterarg = filterarg;
15275cf8837SRuslan Bukin 	common->maxsize = maxsize;
15375cf8837SRuslan Bukin 	common->nsegments = nsegments;
15475cf8837SRuslan Bukin 	common->maxsegsz = maxsegsz;
15575cf8837SRuslan Bukin 	common->flags = flags;
15675cf8837SRuslan Bukin 	common->ref_count = 1; /* Count ourself */
15775cf8837SRuslan Bukin 	if (lockfunc != NULL) {
15875cf8837SRuslan Bukin 		common->lockfunc = lockfunc;
15975cf8837SRuslan Bukin 		common->lockfuncarg = lockfuncarg;
16075cf8837SRuslan Bukin 	} else {
16175cf8837SRuslan Bukin 		common->lockfunc = bus_dma_dflt_lock;
16275cf8837SRuslan Bukin 		common->lockfuncarg = NULL;
16375cf8837SRuslan Bukin 	}
16475cf8837SRuslan Bukin 
16575cf8837SRuslan Bukin 	/* Take into account any restrictions imposed by our parent tag */
16675cf8837SRuslan Bukin 	if (parent != NULL) {
16775cf8837SRuslan Bukin 		common->impl = parent->impl;
16875cf8837SRuslan Bukin 		common->lowaddr = MIN(parent->lowaddr, common->lowaddr);
16975cf8837SRuslan Bukin 		common->highaddr = MAX(parent->highaddr, common->highaddr);
17075cf8837SRuslan Bukin 		if (common->boundary == 0)
17175cf8837SRuslan Bukin 			common->boundary = parent->boundary;
17275cf8837SRuslan Bukin 		else if (parent->boundary != 0) {
17375cf8837SRuslan Bukin 			common->boundary = MIN(parent->boundary,
17475cf8837SRuslan Bukin 			    common->boundary);
17575cf8837SRuslan Bukin 		}
17675cf8837SRuslan Bukin 		if (common->filter == NULL) {
17775cf8837SRuslan Bukin 			/*
17875cf8837SRuslan Bukin 			 * Short circuit looking at our parent directly
17975cf8837SRuslan Bukin 			 * since we have encapsulated all of its information
18075cf8837SRuslan Bukin 			 */
18175cf8837SRuslan Bukin 			common->filter = parent->filter;
18275cf8837SRuslan Bukin 			common->filterarg = parent->filterarg;
18375cf8837SRuslan Bukin 			common->parent = parent->parent;
18475cf8837SRuslan Bukin 		}
18575cf8837SRuslan Bukin 		atomic_add_int(&parent->ref_count, 1);
18675cf8837SRuslan Bukin 	}
18775cf8837SRuslan Bukin 	*dmat = common;
18875cf8837SRuslan Bukin 	return (0);
18975cf8837SRuslan Bukin }
19075cf8837SRuslan Bukin 
19175cf8837SRuslan Bukin /*
19275cf8837SRuslan Bukin  * Allocate a device specific dma_tag.
19375cf8837SRuslan Bukin  */
19475cf8837SRuslan Bukin int
19575cf8837SRuslan Bukin bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
19675cf8837SRuslan Bukin     bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
19775cf8837SRuslan Bukin     bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
19875cf8837SRuslan Bukin     int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
19975cf8837SRuslan Bukin     void *lockfuncarg, bus_dma_tag_t *dmat)
20075cf8837SRuslan Bukin {
20175cf8837SRuslan Bukin 	struct bus_dma_tag_common *tc;
20275cf8837SRuslan Bukin 	int error;
20375cf8837SRuslan Bukin 
20475cf8837SRuslan Bukin 	if (parent == NULL) {
20575cf8837SRuslan Bukin 		error = bus_dma_bounce_impl.tag_create(parent, alignment,
20675cf8837SRuslan Bukin 		    boundary, lowaddr, highaddr, filter, filterarg, maxsize,
20775cf8837SRuslan Bukin 		    nsegments, maxsegsz, flags, lockfunc, lockfuncarg, dmat);
20875cf8837SRuslan Bukin 	} else {
20975cf8837SRuslan Bukin 		tc = (struct bus_dma_tag_common *)parent;
21075cf8837SRuslan Bukin 		error = tc->impl->tag_create(parent, alignment,
21175cf8837SRuslan Bukin 		    boundary, lowaddr, highaddr, filter, filterarg, maxsize,
21275cf8837SRuslan Bukin 		    nsegments, maxsegsz, flags, lockfunc, lockfuncarg, dmat);
21375cf8837SRuslan Bukin 	}
21475cf8837SRuslan Bukin 	return (error);
21575cf8837SRuslan Bukin }
21675cf8837SRuslan Bukin 
217757d4fbaSScott Long void
21874c781edSScott Long bus_dma_template_clone(bus_dma_template_t *t, bus_dma_tag_t dmat)
219757d4fbaSScott Long {
220757d4fbaSScott Long 	struct bus_dma_tag_common *common;
221757d4fbaSScott Long 
222757d4fbaSScott Long 	if (t == NULL || dmat == NULL)
223757d4fbaSScott Long 		return;
224757d4fbaSScott Long 
225757d4fbaSScott Long 	common = (struct bus_dma_tag_common *)dmat;
226757d4fbaSScott Long 
227757d4fbaSScott Long 	t->parent = (bus_dma_tag_t)common->parent;
228757d4fbaSScott Long 	t->alignment = common->alignment;
229757d4fbaSScott Long 	t->boundary = common->boundary;
230757d4fbaSScott Long 	t->lowaddr = common->lowaddr;
231757d4fbaSScott Long 	t->highaddr = common->highaddr;
232757d4fbaSScott Long 	t->maxsize = common->maxsize;
233757d4fbaSScott Long 	t->nsegments = common->nsegments;
234757d4fbaSScott Long 	t->maxsegsize = common->maxsegsz;
235757d4fbaSScott Long 	t->flags = common->flags;
236757d4fbaSScott Long 	t->lockfunc = common->lockfunc;
237757d4fbaSScott Long 	t->lockfuncarg = common->lockfuncarg;
238757d4fbaSScott Long }
239757d4fbaSScott Long 
24075cf8837SRuslan Bukin int
24175cf8837SRuslan Bukin bus_dma_tag_destroy(bus_dma_tag_t dmat)
24275cf8837SRuslan Bukin {
24375cf8837SRuslan Bukin 	struct bus_dma_tag_common *tc;
24475cf8837SRuslan Bukin 
24575cf8837SRuslan Bukin 	tc = (struct bus_dma_tag_common *)dmat;
24675cf8837SRuslan Bukin 	return (tc->impl->tag_destroy(dmat));
24775cf8837SRuslan Bukin }
24875cf8837SRuslan Bukin 
24975cf8837SRuslan Bukin int
25075cf8837SRuslan Bukin bus_dma_tag_set_domain(bus_dma_tag_t dmat, int domain)
25128029b68SRuslan Bukin {
25228029b68SRuslan Bukin 
25375cf8837SRuslan Bukin 	return (0);
25428029b68SRuslan Bukin }
255