11ca4eb3aSZbigniew Bodek /*-
21ca4eb3aSZbigniew Bodek * Copyright (c) 1997, 1998 Justin T. Gibbs.
31ca4eb3aSZbigniew Bodek * Copyright (c) 2013, 2015 The FreeBSD Foundation
41ca4eb3aSZbigniew Bodek * All rights reserved.
51ca4eb3aSZbigniew Bodek *
61ca4eb3aSZbigniew Bodek * This software was developed by Konstantin Belousov <kib@FreeBSD.org>
71ca4eb3aSZbigniew Bodek * under sponsorship from the FreeBSD Foundation.
81ca4eb3aSZbigniew Bodek *
91ca4eb3aSZbigniew Bodek * Portions of this software were developed by Semihalf
101ca4eb3aSZbigniew Bodek * under sponsorship of the FreeBSD Foundation.
111ca4eb3aSZbigniew Bodek *
121ca4eb3aSZbigniew Bodek * Redistribution and use in source and binary forms, with or without
131ca4eb3aSZbigniew Bodek * modification, are permitted provided that the following conditions
141ca4eb3aSZbigniew Bodek * are met:
151ca4eb3aSZbigniew Bodek * 1. Redistributions of source code must retain the above copyright
161ca4eb3aSZbigniew Bodek * notice, this list of conditions, and the following disclaimer,
171ca4eb3aSZbigniew Bodek * without modification, immediately at the beginning of the file.
181ca4eb3aSZbigniew Bodek * 2. The name of the author may not be used to endorse or promote products
191ca4eb3aSZbigniew Bodek * derived from this software without specific prior written permission.
201ca4eb3aSZbigniew Bodek *
211ca4eb3aSZbigniew Bodek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
221ca4eb3aSZbigniew Bodek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231ca4eb3aSZbigniew Bodek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241ca4eb3aSZbigniew Bodek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
251ca4eb3aSZbigniew Bodek * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261ca4eb3aSZbigniew Bodek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271ca4eb3aSZbigniew Bodek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281ca4eb3aSZbigniew Bodek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291ca4eb3aSZbigniew Bodek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301ca4eb3aSZbigniew Bodek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311ca4eb3aSZbigniew Bodek * SUCH DAMAGE.
321ca4eb3aSZbigniew Bodek */
33e5acd89cSAndrew Turner
34e5acd89cSAndrew Turner #include <sys/param.h>
35e5acd89cSAndrew Turner #include <sys/systm.h>
361ca4eb3aSZbigniew Bodek #include <sys/malloc.h>
371ca4eb3aSZbigniew Bodek #include <sys/bus.h>
381ca4eb3aSZbigniew Bodek #include <sys/kernel.h>
391ca4eb3aSZbigniew Bodek #include <sys/ktr.h>
401ca4eb3aSZbigniew Bodek #include <sys/lock.h>
411ca4eb3aSZbigniew Bodek #include <sys/memdesc.h>
421ca4eb3aSZbigniew Bodek #include <sys/mutex.h>
431ca4eb3aSZbigniew Bodek #include <sys/uio.h>
44e5acd89cSAndrew Turner #include <vm/vm.h>
451ca4eb3aSZbigniew Bodek #include <vm/vm_extern.h>
46271e669eSAndrew Turner #include <vm/vm_phys.h>
47e5acd89cSAndrew Turner #include <vm/pmap.h>
48e5acd89cSAndrew Turner
49e5acd89cSAndrew Turner #include <machine/bus.h>
501ca4eb3aSZbigniew Bodek #include <arm64/include/bus_dma_impl.h>
511ca4eb3aSZbigniew Bodek
521ca4eb3aSZbigniew Bodek int
common_bus_dma_tag_create(struct bus_dma_tag_common * parent,bus_size_t alignment,bus_addr_t boundary,bus_addr_t lowaddr,bus_addr_t highaddr,bus_size_t maxsize,int nsegments,bus_size_t maxsegsz,int flags,bus_dma_lock_t * lockfunc,void * lockfuncarg,size_t sz,void ** dmat)531ca4eb3aSZbigniew Bodek common_bus_dma_tag_create(struct bus_dma_tag_common *parent,
541ca4eb3aSZbigniew Bodek bus_size_t alignment, bus_addr_t boundary, bus_addr_t lowaddr,
55900907f4SMitchell Horne bus_addr_t highaddr, bus_size_t maxsize, int nsegments,
56900907f4SMitchell Horne bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
57900907f4SMitchell Horne void *lockfuncarg, size_t sz, void **dmat)
581ca4eb3aSZbigniew Bodek {
591ca4eb3aSZbigniew Bodek void *newtag;
601ca4eb3aSZbigniew Bodek struct bus_dma_tag_common *common;
611ca4eb3aSZbigniew Bodek
621ca4eb3aSZbigniew Bodek KASSERT(sz >= sizeof(struct bus_dma_tag_common), ("sz"));
631ca4eb3aSZbigniew Bodek /* Return a NULL tag on failure */
641ca4eb3aSZbigniew Bodek *dmat = NULL;
651ca4eb3aSZbigniew Bodek /* Basic sanity checking */
661ca4eb3aSZbigniew Bodek if (boundary != 0 && boundary < maxsegsz)
671ca4eb3aSZbigniew Bodek maxsegsz = boundary;
681ca4eb3aSZbigniew Bodek if (maxsegsz == 0)
691ca4eb3aSZbigniew Bodek return (EINVAL);
701ca4eb3aSZbigniew Bodek
711ca4eb3aSZbigniew Bodek newtag = malloc(sz, M_DEVBUF, M_ZERO | M_NOWAIT);
721ca4eb3aSZbigniew Bodek if (newtag == NULL) {
731ca4eb3aSZbigniew Bodek CTR4(KTR_BUSDMA, "%s returned tag %p tag flags 0x%x error %d",
741ca4eb3aSZbigniew Bodek __func__, newtag, 0, ENOMEM);
751ca4eb3aSZbigniew Bodek return (ENOMEM);
761ca4eb3aSZbigniew Bodek }
771ca4eb3aSZbigniew Bodek
781ca4eb3aSZbigniew Bodek common = newtag;
791ca4eb3aSZbigniew Bodek common->impl = &bus_dma_bounce_impl;
801ca4eb3aSZbigniew Bodek common->alignment = alignment;
811ca4eb3aSZbigniew Bodek common->boundary = boundary;
821ca4eb3aSZbigniew Bodek common->lowaddr = trunc_page((vm_paddr_t)lowaddr) + (PAGE_SIZE - 1);
831ca4eb3aSZbigniew Bodek common->highaddr = trunc_page((vm_paddr_t)highaddr) + (PAGE_SIZE - 1);
841ca4eb3aSZbigniew Bodek common->maxsize = maxsize;
851ca4eb3aSZbigniew Bodek common->nsegments = nsegments;
861ca4eb3aSZbigniew Bodek common->maxsegsz = maxsegsz;
871ca4eb3aSZbigniew Bodek common->flags = flags;
881ca4eb3aSZbigniew Bodek if (lockfunc != NULL) {
891ca4eb3aSZbigniew Bodek common->lockfunc = lockfunc;
901ca4eb3aSZbigniew Bodek common->lockfuncarg = lockfuncarg;
911ca4eb3aSZbigniew Bodek } else {
927def1e10SJohn Baldwin common->lockfunc = _busdma_dflt_lock;
931ca4eb3aSZbigniew Bodek common->lockfuncarg = NULL;
941ca4eb3aSZbigniew Bodek }
951ca4eb3aSZbigniew Bodek
961ca4eb3aSZbigniew Bodek /* Take into account any restrictions imposed by our parent tag */
971ca4eb3aSZbigniew Bodek if (parent != NULL) {
981ca4eb3aSZbigniew Bodek common->impl = parent->impl;
991ca4eb3aSZbigniew Bodek common->lowaddr = MIN(parent->lowaddr, common->lowaddr);
1001ca4eb3aSZbigniew Bodek common->highaddr = MAX(parent->highaddr, common->highaddr);
101ec9d0685SAndrew Turner common->alignment = MAX(parent->alignment, common->alignment);
1021ca4eb3aSZbigniew Bodek if (common->boundary == 0)
1031ca4eb3aSZbigniew Bodek common->boundary = parent->boundary;
1041ca4eb3aSZbigniew Bodek else if (parent->boundary != 0) {
1051ca4eb3aSZbigniew Bodek common->boundary = MIN(parent->boundary,
1061ca4eb3aSZbigniew Bodek common->boundary);
1071ca4eb3aSZbigniew Bodek }
108900907f4SMitchell Horne
109271e669eSAndrew Turner common->domain = parent->domain;
1101ca4eb3aSZbigniew Bodek }
111271e669eSAndrew Turner common->domain = vm_phys_domain_match(common->domain, 0ul,
112271e669eSAndrew Turner common->lowaddr);
1131ca4eb3aSZbigniew Bodek *dmat = common;
1141ca4eb3aSZbigniew Bodek return (0);
1151ca4eb3aSZbigniew Bodek }
1161ca4eb3aSZbigniew Bodek
1171ca4eb3aSZbigniew Bodek /*
1181ca4eb3aSZbigniew Bodek * Allocate a device specific dma_tag.
1191ca4eb3aSZbigniew Bodek */
1201ca4eb3aSZbigniew Bodek int
bus_dma_tag_create(bus_dma_tag_t parent,bus_size_t alignment,bus_addr_t boundary,bus_addr_t lowaddr,bus_addr_t highaddr,bus_dma_filter_t * filter,void * filterarg,bus_size_t maxsize,int nsegments,bus_size_t maxsegsz,int flags,bus_dma_lock_t * lockfunc,void * lockfuncarg,bus_dma_tag_t * dmat)1211ca4eb3aSZbigniew Bodek bus_dma_tag_create(bus_dma_tag_t parent, bus_size_t alignment,
1221ca4eb3aSZbigniew Bodek bus_addr_t boundary, bus_addr_t lowaddr, bus_addr_t highaddr,
1231ca4eb3aSZbigniew Bodek bus_dma_filter_t *filter, void *filterarg, bus_size_t maxsize,
1241ca4eb3aSZbigniew Bodek int nsegments, bus_size_t maxsegsz, int flags, bus_dma_lock_t *lockfunc,
1251ca4eb3aSZbigniew Bodek void *lockfuncarg, bus_dma_tag_t *dmat)
1261ca4eb3aSZbigniew Bodek {
1271ca4eb3aSZbigniew Bodek struct bus_dma_tag_common *tc;
1281ca4eb3aSZbigniew Bodek int error;
1291ca4eb3aSZbigniew Bodek
1307cb028deSMitchell Horne /* Filters are no longer supported. */
1317cb028deSMitchell Horne if (filter != NULL || filterarg != NULL)
1327cb028deSMitchell Horne return (EINVAL);
1337cb028deSMitchell Horne
1341ca4eb3aSZbigniew Bodek if (parent == NULL) {
1351ca4eb3aSZbigniew Bodek error = bus_dma_bounce_impl.tag_create(parent, alignment,
136900907f4SMitchell Horne boundary, lowaddr, highaddr, maxsize, nsegments, maxsegsz,
137900907f4SMitchell Horne flags, lockfunc, lockfuncarg, dmat);
1381ca4eb3aSZbigniew Bodek } else {
1391ca4eb3aSZbigniew Bodek tc = (struct bus_dma_tag_common *)parent;
1401ca4eb3aSZbigniew Bodek error = tc->impl->tag_create(parent, alignment,
141900907f4SMitchell Horne boundary, lowaddr, highaddr, maxsize, nsegments, maxsegsz,
142900907f4SMitchell Horne flags, lockfunc, lockfuncarg, dmat);
1431ca4eb3aSZbigniew Bodek }
1441ca4eb3aSZbigniew Bodek return (error);
1451ca4eb3aSZbigniew Bodek }
1461ca4eb3aSZbigniew Bodek
147757d4fbaSScott Long void
bus_dma_template_clone(bus_dma_template_t * t,bus_dma_tag_t dmat)14874c781edSScott Long bus_dma_template_clone(bus_dma_template_t *t, bus_dma_tag_t dmat)
149757d4fbaSScott Long {
150757d4fbaSScott Long struct bus_dma_tag_common *common;
151757d4fbaSScott Long
152757d4fbaSScott Long if (t == NULL || dmat == NULL)
153757d4fbaSScott Long return;
154757d4fbaSScott Long
155757d4fbaSScott Long common = (struct bus_dma_tag_common *)dmat;
156757d4fbaSScott Long
157757d4fbaSScott Long t->alignment = common->alignment;
158757d4fbaSScott Long t->boundary = common->boundary;
159757d4fbaSScott Long t->lowaddr = common->lowaddr;
160757d4fbaSScott Long t->highaddr = common->highaddr;
161757d4fbaSScott Long t->maxsize = common->maxsize;
162757d4fbaSScott Long t->nsegments = common->nsegments;
163757d4fbaSScott Long t->maxsegsize = common->maxsegsz;
164757d4fbaSScott Long t->flags = common->flags;
165757d4fbaSScott Long t->lockfunc = common->lockfunc;
166757d4fbaSScott Long t->lockfuncarg = common->lockfuncarg;
167757d4fbaSScott Long }
168757d4fbaSScott Long
1691ca4eb3aSZbigniew Bodek int
bus_dma_tag_destroy(bus_dma_tag_t dmat)1701ca4eb3aSZbigniew Bodek bus_dma_tag_destroy(bus_dma_tag_t dmat)
1711ca4eb3aSZbigniew Bodek {
1721ca4eb3aSZbigniew Bodek struct bus_dma_tag_common *tc;
1731ca4eb3aSZbigniew Bodek
1741ca4eb3aSZbigniew Bodek tc = (struct bus_dma_tag_common *)dmat;
1751ca4eb3aSZbigniew Bodek return (tc->impl->tag_destroy(dmat));
1761ca4eb3aSZbigniew Bodek }
1771ca4eb3aSZbigniew Bodek
1786f4acaf4SJeff Roberson int
bus_dma_tag_set_domain(bus_dma_tag_t dmat,int domain)1796f4acaf4SJeff Roberson bus_dma_tag_set_domain(bus_dma_tag_t dmat, int domain)
1806f4acaf4SJeff Roberson {
181271e669eSAndrew Turner struct bus_dma_tag_common *tc;
1826f4acaf4SJeff Roberson
183271e669eSAndrew Turner tc = (struct bus_dma_tag_common *)dmat;
184271e669eSAndrew Turner domain = vm_phys_domain_match(domain, 0ul, tc->lowaddr);
185271e669eSAndrew Turner /* Only call the callback if it changes. */
186271e669eSAndrew Turner if (domain == tc->domain)
1876f4acaf4SJeff Roberson return (0);
188271e669eSAndrew Turner tc->domain = domain;
189271e669eSAndrew Turner return (tc->impl->tag_set_domain(dmat));
1906f4acaf4SJeff Roberson }
191