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 1325dcca8e8SMarcel Moolenaar static int 1335dcca8e8SMarcel Moolenaar proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag, 1345dcca8e8SMarcel Moolenaar struct proto_ioc_busdma *ioc) 1355dcca8e8SMarcel Moolenaar { 1365dcca8e8SMarcel Moolenaar struct proto_md *md; 1375dcca8e8SMarcel Moolenaar int error; 1385dcca8e8SMarcel Moolenaar 1395dcca8e8SMarcel Moolenaar md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 1405dcca8e8SMarcel Moolenaar md->tag = tag; 1415dcca8e8SMarcel Moolenaar 1425dcca8e8SMarcel Moolenaar error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, 1435dcca8e8SMarcel Moolenaar tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz, 1445dcca8e8SMarcel Moolenaar tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag); 1455dcca8e8SMarcel Moolenaar if (error) { 1465dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1475dcca8e8SMarcel Moolenaar return (error); 1485dcca8e8SMarcel Moolenaar } 149*cff0f135SMarcel Moolenaar error = bus_dmamem_alloc(md->bd_tag, &md->virtaddr, 0, &md->bd_map); 1505dcca8e8SMarcel Moolenaar if (error) { 1515dcca8e8SMarcel Moolenaar bus_dma_tag_destroy(md->bd_tag); 1525dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1535dcca8e8SMarcel Moolenaar return (error); 1545dcca8e8SMarcel Moolenaar } 155*cff0f135SMarcel Moolenaar md->physaddr = pmap_kextract((uintptr_t)(md->virtaddr)); 1565dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&tag->mds, md, peers); 1575dcca8e8SMarcel Moolenaar LIST_INSERT_HEAD(&busdma->mds, md, mds); 1585dcca8e8SMarcel Moolenaar ioc->u.mem.nsegs = 1; 159*cff0f135SMarcel Moolenaar ioc->u.mem.physaddr = md->physaddr; 1605dcca8e8SMarcel Moolenaar ioc->result = (uintptr_t)(void *)md; 1615dcca8e8SMarcel Moolenaar return (0); 1625dcca8e8SMarcel Moolenaar } 1635dcca8e8SMarcel Moolenaar 1645dcca8e8SMarcel Moolenaar static int 1655dcca8e8SMarcel Moolenaar proto_busdma_mem_free(struct proto_busdma *busdma, struct proto_md *md) 1665dcca8e8SMarcel Moolenaar { 1675dcca8e8SMarcel Moolenaar 1685dcca8e8SMarcel Moolenaar LIST_REMOVE(md, mds); 1695dcca8e8SMarcel Moolenaar LIST_REMOVE(md, peers); 170*cff0f135SMarcel Moolenaar bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 1715dcca8e8SMarcel Moolenaar bus_dma_tag_destroy(md->bd_tag); 1725dcca8e8SMarcel Moolenaar free(md, M_PROTO_BUSDMA); 1735dcca8e8SMarcel Moolenaar return (0); 1745dcca8e8SMarcel Moolenaar } 1755dcca8e8SMarcel Moolenaar 1765dcca8e8SMarcel Moolenaar static struct proto_md * 1775dcca8e8SMarcel Moolenaar proto_busdma_md_lookup(struct proto_busdma *busdma, u_long key) 1785dcca8e8SMarcel Moolenaar { 1795dcca8e8SMarcel Moolenaar struct proto_md *md; 1805dcca8e8SMarcel Moolenaar 1815dcca8e8SMarcel Moolenaar LIST_FOREACH(md, &busdma->mds, mds) { 1825dcca8e8SMarcel Moolenaar if ((void *)md == (void *)key) 1835dcca8e8SMarcel Moolenaar return (md); 1845dcca8e8SMarcel Moolenaar } 1855dcca8e8SMarcel Moolenaar return (NULL); 1865dcca8e8SMarcel Moolenaar } 1875dcca8e8SMarcel Moolenaar 1884f027abdSMarcel Moolenaar struct proto_busdma * 1894f027abdSMarcel Moolenaar proto_busdma_attach(struct proto_softc *sc) 1904f027abdSMarcel Moolenaar { 1914f027abdSMarcel Moolenaar struct proto_busdma *busdma; 1924f027abdSMarcel Moolenaar 1934f027abdSMarcel Moolenaar busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 1944f027abdSMarcel Moolenaar return (busdma); 1954f027abdSMarcel Moolenaar } 1964f027abdSMarcel Moolenaar 1974f027abdSMarcel Moolenaar int 1984f027abdSMarcel Moolenaar proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma) 1994f027abdSMarcel Moolenaar { 2004f027abdSMarcel Moolenaar 2014f027abdSMarcel Moolenaar proto_busdma_cleanup(sc, busdma); 2024f027abdSMarcel Moolenaar free(busdma, M_PROTO_BUSDMA); 2034f027abdSMarcel Moolenaar return (0); 2044f027abdSMarcel Moolenaar } 2054f027abdSMarcel Moolenaar 2064f027abdSMarcel Moolenaar int 2074f027abdSMarcel Moolenaar proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma) 2084f027abdSMarcel Moolenaar { 2095dcca8e8SMarcel Moolenaar struct proto_md *md, *md1; 2104f027abdSMarcel Moolenaar struct proto_tag *tag, *tag1; 2114f027abdSMarcel Moolenaar 2125dcca8e8SMarcel Moolenaar LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1) 2135dcca8e8SMarcel Moolenaar proto_busdma_mem_free(busdma, md); 2145dcca8e8SMarcel Moolenaar LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1) 2155dcca8e8SMarcel Moolenaar proto_busdma_tag_destroy(busdma, tag); 2164f027abdSMarcel Moolenaar return (0); 2174f027abdSMarcel Moolenaar } 2184f027abdSMarcel Moolenaar 2194f027abdSMarcel Moolenaar int 2204f027abdSMarcel Moolenaar proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma, 2214f027abdSMarcel Moolenaar struct proto_ioc_busdma *ioc) 2224f027abdSMarcel Moolenaar { 2234f027abdSMarcel Moolenaar struct proto_tag *tag; 2245dcca8e8SMarcel Moolenaar struct proto_md *md; 2254f027abdSMarcel Moolenaar int error; 2264f027abdSMarcel Moolenaar 2274f027abdSMarcel Moolenaar error = 0; 2284f027abdSMarcel Moolenaar switch (ioc->request) { 2294f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_CREATE: 2305dcca8e8SMarcel Moolenaar busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev); 2315dcca8e8SMarcel Moolenaar error = proto_busdma_tag_create(busdma, NULL, ioc); 2324f027abdSMarcel Moolenaar break; 2334f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_DERIVE: 2344f027abdSMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->key); 2354f027abdSMarcel Moolenaar if (tag == NULL) { 2364f027abdSMarcel Moolenaar error = EINVAL; 2374f027abdSMarcel Moolenaar break; 2384f027abdSMarcel Moolenaar } 2395dcca8e8SMarcel Moolenaar error = proto_busdma_tag_create(busdma, tag, ioc); 2404f027abdSMarcel Moolenaar break; 2414f027abdSMarcel Moolenaar case PROTO_IOC_BUSDMA_TAG_DESTROY: 2424f027abdSMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->key); 2434f027abdSMarcel Moolenaar if (tag == NULL) { 2444f027abdSMarcel Moolenaar error = EINVAL; 2454f027abdSMarcel Moolenaar break; 2464f027abdSMarcel Moolenaar } 2475dcca8e8SMarcel Moolenaar error = proto_busdma_tag_destroy(busdma, tag); 2485dcca8e8SMarcel Moolenaar break; 2495dcca8e8SMarcel Moolenaar case PROTO_IOC_BUSDMA_MEM_ALLOC: 2505dcca8e8SMarcel Moolenaar tag = proto_busdma_tag_lookup(busdma, ioc->u.mem.tag); 2515dcca8e8SMarcel Moolenaar if (tag == NULL) { 2525dcca8e8SMarcel Moolenaar error = EINVAL; 2535dcca8e8SMarcel Moolenaar break; 2545dcca8e8SMarcel Moolenaar } 2555dcca8e8SMarcel Moolenaar error = proto_busdma_mem_alloc(busdma, tag, ioc); 2565dcca8e8SMarcel Moolenaar break; 2575dcca8e8SMarcel Moolenaar case PROTO_IOC_BUSDMA_MEM_FREE: 2585dcca8e8SMarcel Moolenaar md = proto_busdma_md_lookup(busdma, ioc->key); 2595dcca8e8SMarcel Moolenaar if (md == NULL) { 2605dcca8e8SMarcel Moolenaar error = EINVAL; 2615dcca8e8SMarcel Moolenaar break; 2625dcca8e8SMarcel Moolenaar } 2635dcca8e8SMarcel Moolenaar error = proto_busdma_mem_free(busdma, md); 2644f027abdSMarcel Moolenaar break; 2654f027abdSMarcel Moolenaar default: 2664f027abdSMarcel Moolenaar error = EINVAL; 2674f027abdSMarcel Moolenaar break; 2684f027abdSMarcel Moolenaar } 2694f027abdSMarcel Moolenaar return (error); 2704f027abdSMarcel Moolenaar } 271*cff0f135SMarcel Moolenaar 272*cff0f135SMarcel Moolenaar int 273*cff0f135SMarcel Moolenaar proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) 274*cff0f135SMarcel Moolenaar { 275*cff0f135SMarcel Moolenaar struct proto_md *md; 276*cff0f135SMarcel Moolenaar 277*cff0f135SMarcel Moolenaar LIST_FOREACH(md, &busdma->mds, mds) { 278*cff0f135SMarcel Moolenaar if (physaddr >= trunc_page(md->physaddr) && 279*cff0f135SMarcel Moolenaar physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) 280*cff0f135SMarcel Moolenaar return (1); 281*cff0f135SMarcel Moolenaar } 282*cff0f135SMarcel Moolenaar return (0); 283*cff0f135SMarcel Moolenaar } 284