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> 394f027abdSMarcel Moolenaar #include <sys/queue.h> 404f027abdSMarcel Moolenaar #include <sys/rman.h> 414f027abdSMarcel Moolenaar #include <sys/sbuf.h> 425dcca8e8SMarcel Moolenaar #include <vm/vm.h> 435dcca8e8SMarcel Moolenaar #include <vm/pmap.h> 444f027abdSMarcel Moolenaar 454f027abdSMarcel Moolenaar #include <dev/proto/proto.h> 464f027abdSMarcel Moolenaar #include <dev/proto/proto_dev.h> 474f027abdSMarcel Moolenaar #include <dev/proto/proto_busdma.h> 484f027abdSMarcel Moolenaar 494f027abdSMarcel Moolenaar MALLOC_DEFINE(M_PROTO_BUSDMA, "proto_busdma", "DMA management data"); 504f027abdSMarcel Moolenaar 514f027abdSMarcel Moolenaar static int 525dcca8e8SMarcel Moolenaar proto_busdma_tag_create(struct proto_busdma *busdma, struct proto_tag *parent, 535dcca8e8SMarcel Moolenaar struct proto_ioc_busdma *ioc) 544f027abdSMarcel Moolenaar { 554f027abdSMarcel Moolenaar struct proto_tag *tag; 564f027abdSMarcel Moolenaar 575dcca8e8SMarcel Moolenaar /* 585dcca8e8SMarcel Moolenaar * If nsegs is 1, ignore maxsegsz. What this means is that if we have 595dcca8e8SMarcel Moolenaar * just 1 segment, then maxsz should be equal to maxsegsz. To keep it 605dcca8e8SMarcel Moolenaar * simple for us, limit maxsegsz to maxsz in any case. 615dcca8e8SMarcel Moolenaar */ 625dcca8e8SMarcel Moolenaar if (ioc->u.tag.maxsegsz > ioc->u.tag.maxsz || ioc->u.tag.nsegs == 1) 635dcca8e8SMarcel Moolenaar ioc->u.tag.maxsegsz = ioc->u.tag.maxsz; 645dcca8e8SMarcel Moolenaar 655dcca8e8SMarcel Moolenaar /* A bndry of 0 really means ~0, or no boundary. */ 665dcca8e8SMarcel Moolenaar if (ioc->u.tag.bndry == 0) 675dcca8e8SMarcel Moolenaar ioc->u.tag.bndry = ~0U; 684f027abdSMarcel Moolenaar 694f027abdSMarcel Moolenaar tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 705dcca8e8SMarcel Moolenaar if (parent != NULL) { 715dcca8e8SMarcel Moolenaar tag->parent = parent; 725dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&parent->children, tag, peers); 735dcca8e8SMarcel Moolenaar tag->align = MAX(ioc->u.tag.align, parent->align); 745dcca8e8SMarcel Moolenaar tag->bndry = MIN(ioc->u.tag.bndry, parent->bndry); 755dcca8e8SMarcel Moolenaar tag->maxaddr = MIN(ioc->u.tag.maxaddr, parent->maxaddr); 765dcca8e8SMarcel Moolenaar tag->maxsz = MIN(ioc->u.tag.maxsz, parent->maxsz); 775dcca8e8SMarcel Moolenaar tag->maxsegsz = MIN(ioc->u.tag.maxsegsz, parent->maxsegsz); 785dcca8e8SMarcel Moolenaar tag->nsegs = MIN(ioc->u.tag.nsegs, parent->nsegs); 795dcca8e8SMarcel Moolenaar tag->datarate = MIN(ioc->u.tag.datarate, parent->datarate); 805dcca8e8SMarcel Moolenaar /* Write constraints back */ 815dcca8e8SMarcel Moolenaar ioc->u.tag.align = tag->align; 825dcca8e8SMarcel Moolenaar ioc->u.tag.bndry = tag->bndry; 835dcca8e8SMarcel Moolenaar ioc->u.tag.maxaddr = tag->maxaddr; 845dcca8e8SMarcel Moolenaar ioc->u.tag.maxsz = tag->maxsz; 855dcca8e8SMarcel Moolenaar ioc->u.tag.maxsegsz = tag->maxsegsz; 865dcca8e8SMarcel Moolenaar ioc->u.tag.nsegs = tag->nsegs; 875dcca8e8SMarcel Moolenaar ioc->u.tag.datarate = tag->datarate; 885dcca8e8SMarcel Moolenaar } else { 895dcca8e8SMarcel Moolenaar tag->align = ioc->u.tag.align; 905dcca8e8SMarcel Moolenaar tag->bndry = ioc->u.tag.bndry; 915dcca8e8SMarcel Moolenaar tag->maxaddr = ioc->u.tag.maxaddr; 925dcca8e8SMarcel Moolenaar tag->maxsz = ioc->u.tag.maxsz; 935dcca8e8SMarcel Moolenaar tag->maxsegsz = ioc->u.tag.maxsegsz; 945dcca8e8SMarcel Moolenaar tag->nsegs = ioc->u.tag.nsegs; 955dcca8e8SMarcel Moolenaar tag->datarate = ioc->u.tag.datarate; 965dcca8e8SMarcel Moolenaar } 975dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&busdma->tags, tag, tags); 985dcca8e8SMarcel Moolenaar ioc->result = (uintptr_t)(void *)tag; 994f027abdSMarcel Moolenaar return (0); 1004f027abdSMarcel Moolenaar } 1014f027abdSMarcel Moolenaar 1025dcca8e8SMarcel Moolenaar static int 1035dcca8e8SMarcel Moolenaar proto_busdma_tag_destroy(struct proto_busdma *busdma, struct proto_tag *tag) 1044f027abdSMarcel Moolenaar { 1054f027abdSMarcel Moolenaar 1065dcca8e8SMarcel Moolenaar if (!LIST_EMPTY(&tag->mds)) 1075dcca8e8SMarcel Moolenaar return (EBUSY); 1085dcca8e8SMarcel Moolenaar if (!LIST_EMPTY(&tag->children)) 1095dcca8e8SMarcel Moolenaar return (EBUSY); 1105dcca8e8SMarcel Moolenaar 1115dcca8e8SMarcel Moolenaar if (tag->parent != NULL) { 1125dcca8e8SMarcel Moolenaar LIST_REMOVE(tag, peers); 1135dcca8e8SMarcel Moolenaar tag->parent = NULL; 1145dcca8e8SMarcel Moolenaar } 1155dcca8e8SMarcel Moolenaar LIST_REMOVE(tag, tags); 1164f027abdSMarcel Moolenaar free(tag, M_PROTO_BUSDMA); 1175dcca8e8SMarcel Moolenaar return (0); 1184f027abdSMarcel Moolenaar } 1194f027abdSMarcel Moolenaar 1204f027abdSMarcel Moolenaar static struct proto_tag * 1214f027abdSMarcel Moolenaar proto_busdma_tag_lookup(struct proto_busdma *busdma, u_long key) 1224f027abdSMarcel Moolenaar { 1234f027abdSMarcel Moolenaar struct proto_tag *tag; 1244f027abdSMarcel Moolenaar 1255dcca8e8SMarcel Moolenaar LIST_FOREACH(tag, &busdma->tags, tags) { 1264f027abdSMarcel Moolenaar if ((void *)tag == (void *)key) 1274f027abdSMarcel Moolenaar return (tag); 1284f027abdSMarcel Moolenaar } 1294f027abdSMarcel Moolenaar return (NULL); 1304f027abdSMarcel Moolenaar } 1314f027abdSMarcel Moolenaar 132*90a1793cSMarcel Moolenaar static void 133*90a1793cSMarcel Moolenaar proto_busdma_mem_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, 134*90a1793cSMarcel Moolenaar int error) 135*90a1793cSMarcel Moolenaar { 136*90a1793cSMarcel Moolenaar struct proto_ioc_busdma *ioc = arg; 137*90a1793cSMarcel Moolenaar 138*90a1793cSMarcel Moolenaar ioc->u.mem.bus_nsegs = nseg; 139*90a1793cSMarcel Moolenaar ioc->u.mem.bus_addr = segs[0].ds_addr; 140*90a1793cSMarcel Moolenaar } 141*90a1793cSMarcel Moolenaar 1425dcca8e8SMarcel Moolenaar static int 1435dcca8e8SMarcel Moolenaar proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag, 1445dcca8e8SMarcel Moolenaar struct proto_ioc_busdma *ioc) 1455dcca8e8SMarcel Moolenaar { 1465dcca8e8SMarcel Moolenaar struct proto_md *md; 1475dcca8e8SMarcel Moolenaar int error; 1485dcca8e8SMarcel Moolenaar 1495dcca8e8SMarcel Moolenaar md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 1505dcca8e8SMarcel Moolenaar md->tag = tag; 1515dcca8e8SMarcel Moolenaar 1525dcca8e8SMarcel Moolenaar error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, 1535dcca8e8SMarcel Moolenaar tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz, 1545dcca8e8SMarcel Moolenaar tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag); 1555dcca8e8SMarcel Moolenaar if (error) { 1565dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1575dcca8e8SMarcel Moolenaar return (error); 1585dcca8e8SMarcel Moolenaar } 159cff0f135SMarcel Moolenaar error = bus_dmamem_alloc(md->bd_tag, &md->virtaddr, 0, &md->bd_map); 1605dcca8e8SMarcel Moolenaar if (error) { 1615dcca8e8SMarcel Moolenaar bus_dma_tag_destroy(md->bd_tag); 1625dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1635dcca8e8SMarcel Moolenaar return (error); 1645dcca8e8SMarcel Moolenaar } 165cff0f135SMarcel Moolenaar md->physaddr = pmap_kextract((uintptr_t)(md->virtaddr)); 166*90a1793cSMarcel Moolenaar error = bus_dmamap_load(md->bd_tag, md->bd_map, md->virtaddr, 167*90a1793cSMarcel Moolenaar tag->maxsz, proto_busdma_mem_alloc_callback, ioc, BUS_DMA_NOWAIT); 168*90a1793cSMarcel Moolenaar if (error) { 169*90a1793cSMarcel Moolenaar bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 170*90a1793cSMarcel Moolenaar bus_dma_tag_destroy(md->bd_tag); 171*90a1793cSMarcel Moolenaar free(md, M_PROTO_BUSDMA); 172*90a1793cSMarcel Moolenaar return (error); 173*90a1793cSMarcel Moolenaar } 1745dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&tag->mds, md, peers); 1755dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&busdma->mds, md, mds); 176*90a1793cSMarcel Moolenaar ioc->u.mem.phys_nsegs = 1; 177*90a1793cSMarcel Moolenaar ioc->u.mem.phys_addr = md->physaddr; 1785dcca8e8SMarcel Moolenaar ioc->result = (uintptr_t)(void *)md; 1795dcca8e8SMarcel Moolenaar return (0); 1805dcca8e8SMarcel Moolenaar } 1815dcca8e8SMarcel Moolenaar 1825dcca8e8SMarcel Moolenaar static int 1835dcca8e8SMarcel Moolenaar proto_busdma_mem_free(struct proto_busdma *busdma, struct proto_md *md) 1845dcca8e8SMarcel Moolenaar { 1855dcca8e8SMarcel Moolenaar 1865dcca8e8SMarcel Moolenaar LIST_REMOVE(md, mds); 1875dcca8e8SMarcel Moolenaar LIST_REMOVE(md, peers); 188cff0f135SMarcel Moolenaar bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 1895dcca8e8SMarcel Moolenaar bus_dma_tag_destroy(md->bd_tag); 1905dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1915dcca8e8SMarcel Moolenaar return (0); 1925dcca8e8SMarcel Moolenaar } 1935dcca8e8SMarcel Moolenaar 1945dcca8e8SMarcel Moolenaar static struct proto_md * 1955dcca8e8SMarcel Moolenaar proto_busdma_md_lookup(struct proto_busdma *busdma, u_long key) 1965dcca8e8SMarcel Moolenaar { 1975dcca8e8SMarcel Moolenaar struct proto_md *md; 1985dcca8e8SMarcel Moolenaar 1995dcca8e8SMarcel Moolenaar LIST_FOREACH(md, &busdma->mds, mds) { 2005dcca8e8SMarcel Moolenaar if ((void *)md == (void *)key) 2015dcca8e8SMarcel Moolenaar return (md); 2025dcca8e8SMarcel Moolenaar } 2035dcca8e8SMarcel Moolenaar return (NULL); 2045dcca8e8SMarcel Moolenaar } 2055dcca8e8SMarcel Moolenaar 2064f027abdSMarcel Moolenaar struct proto_busdma * 2074f027abdSMarcel Moolenaar proto_busdma_attach(struct proto_softc *sc) 2084f027abdSMarcel Moolenaar { 2094f027abdSMarcel Moolenaar struct proto_busdma *busdma; 2104f027abdSMarcel Moolenaar 2114f027abdSMarcel Moolenaar busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 2124f027abdSMarcel Moolenaar return (busdma); 2134f027abdSMarcel Moolenaar } 2144f027abdSMarcel Moolenaar 2154f027abdSMarcel Moolenaar int 2164f027abdSMarcel Moolenaar proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma) 2174f027abdSMarcel Moolenaar { 2184f027abdSMarcel Moolenaar 2194f027abdSMarcel Moolenaar proto_busdma_cleanup(sc, busdma); 2204f027abdSMarcel Moolenaar free(busdma, M_PROTO_BUSDMA); 2214f027abdSMarcel Moolenaar return (0); 2224f027abdSMarcel Moolenaar } 2234f027abdSMarcel Moolenaar 2244f027abdSMarcel Moolenaar int 2254f027abdSMarcel Moolenaar proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma) 2264f027abdSMarcel Moolenaar { 2275dcca8e8SMarcel Moolenaar struct proto_md *md, *md1; 2284f027abdSMarcel Moolenaar struct proto_tag *tag, *tag1; 2294f027abdSMarcel Moolenaar 2305dcca8e8SMarcel Moolenaar LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1) 2315dcca8e8SMarcel Moolenaar proto_busdma_mem_free(busdma, md); 2325dcca8e8SMarcel Moolenaar LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1) 2335dcca8e8SMarcel Moolenaar proto_busdma_tag_destroy(busdma, tag); 2344f027abdSMarcel Moolenaar return (0); 2354f027abdSMarcel Moolenaar } 2364f027abdSMarcel Moolenaar 2374f027abdSMarcel Moolenaar int 2384f027abdSMarcel Moolenaar proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma, 2394f027abdSMarcel Moolenaar struct proto_ioc_busdma *ioc) 2404f027abdSMarcel Moolenaar { 2414f027abdSMarcel Moolenaar struct proto_tag *tag; 2425dcca8e8SMarcel Moolenaar struct proto_md *md; 2434f027abdSMarcel Moolenaar int error; 2444f027abdSMarcel Moolenaar 2454f027abdSMarcel Moolenaar error = 0; 2464f027abdSMarcel Moolenaar switch (ioc->request) { 2474f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_CREATE: 2485dcca8e8SMarcel Moolenaar busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev); 2495dcca8e8SMarcel Moolenaar error = proto_busdma_tag_create(busdma, NULL, ioc); 2504f027abdSMarcel Moolenaar break; 2514f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_DERIVE: 2524f027abdSMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->key); 2534f027abdSMarcel Moolenaar if (tag == NULL) { 2544f027abdSMarcel Moolenaar error = EINVAL; 2554f027abdSMarcel Moolenaar break; 2564f027abdSMarcel Moolenaar } 2575dcca8e8SMarcel Moolenaar error = proto_busdma_tag_create(busdma, tag, ioc); 2584f027abdSMarcel Moolenaar break; 2594f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_DESTROY: 2604f027abdSMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->key); 2614f027abdSMarcel Moolenaar if (tag == NULL) { 2624f027abdSMarcel Moolenaar error = EINVAL; 2634f027abdSMarcel Moolenaar break; 2644f027abdSMarcel Moolenaar } 2655dcca8e8SMarcel Moolenaar error = proto_busdma_tag_destroy(busdma, tag); 2665dcca8e8SMarcel Moolenaar break; 2675dcca8e8SMarcel Moolenaar case PROTO_IOC_BUSDMA_MEM_ALLOC: 2685dcca8e8SMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->u.mem.tag); 2695dcca8e8SMarcel Moolenaar if (tag == NULL) { 2705dcca8e8SMarcel Moolenaar error = EINVAL; 2715dcca8e8SMarcel Moolenaar break; 2725dcca8e8SMarcel Moolenaar } 2735dcca8e8SMarcel Moolenaar error = proto_busdma_mem_alloc(busdma, tag, ioc); 2745dcca8e8SMarcel Moolenaar break; 2755dcca8e8SMarcel Moolenaar case PROTO_IOC_BUSDMA_MEM_FREE: 2765dcca8e8SMarcel Moolenaar md = proto_busdma_md_lookup(busdma, ioc->key); 2775dcca8e8SMarcel Moolenaar if (md == NULL) { 2785dcca8e8SMarcel Moolenaar error = EINVAL; 2795dcca8e8SMarcel Moolenaar break; 2805dcca8e8SMarcel Moolenaar } 2815dcca8e8SMarcel Moolenaar error = proto_busdma_mem_free(busdma, md); 2824f027abdSMarcel Moolenaar break; 2834f027abdSMarcel Moolenaar default: 2844f027abdSMarcel Moolenaar error = EINVAL; 2854f027abdSMarcel Moolenaar break; 2864f027abdSMarcel Moolenaar } 2874f027abdSMarcel Moolenaar return (error); 2884f027abdSMarcel Moolenaar } 289cff0f135SMarcel Moolenaar 290cff0f135SMarcel Moolenaar int 291cff0f135SMarcel Moolenaar proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) 292cff0f135SMarcel Moolenaar { 293cff0f135SMarcel Moolenaar struct proto_md *md; 294cff0f135SMarcel Moolenaar 295cff0f135SMarcel Moolenaar LIST_FOREACH(md, &busdma->mds, mds) { 296cff0f135SMarcel Moolenaar if (physaddr >= trunc_page(md->physaddr) && 297cff0f135SMarcel Moolenaar physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) 298cff0f135SMarcel Moolenaar return (1); 299cff0f135SMarcel Moolenaar } 300cff0f135SMarcel Moolenaar return (0); 301cff0f135SMarcel Moolenaar } 302