1 /*- 2 * Copyright (c) 2015 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include <sys/cdefs.h> 27 __FBSDID("$FreeBSD$"); 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <machine/bus.h> 32 #include <machine/bus_dma.h> 33 #include <machine/resource.h> 34 #include <sys/bus.h> 35 #include <sys/conf.h> 36 #include <sys/kernel.h> 37 #include <sys/malloc.h> 38 #include <sys/module.h> 39 #include <sys/queue.h> 40 #include <sys/rman.h> 41 #include <sys/sbuf.h> 42 #include <vm/vm.h> 43 #include <vm/pmap.h> 44 45 #include <dev/proto/proto.h> 46 #include <dev/proto/proto_dev.h> 47 #include <dev/proto/proto_busdma.h> 48 49 MALLOC_DEFINE(M_PROTO_BUSDMA, "proto_busdma", "DMA management data"); 50 51 static int 52 proto_busdma_tag_create(struct proto_busdma *busdma, struct proto_tag *parent, 53 struct proto_ioc_busdma *ioc) 54 { 55 struct proto_tag *tag; 56 57 /* 58 * If nsegs is 1, ignore maxsegsz. What this means is that if we have 59 * just 1 segment, then maxsz should be equal to maxsegsz. To keep it 60 * simple for us, limit maxsegsz to maxsz in any case. 61 */ 62 if (ioc->u.tag.maxsegsz > ioc->u.tag.maxsz || ioc->u.tag.nsegs == 1) 63 ioc->u.tag.maxsegsz = ioc->u.tag.maxsz; 64 65 /* A bndry of 0 really means ~0, or no boundary. */ 66 if (ioc->u.tag.bndry == 0) 67 ioc->u.tag.bndry = ~0U; 68 69 tag = malloc(sizeof(*tag), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 70 if (parent != NULL) { 71 tag->parent = parent; 72 LIST_INSERT_HEAD(&parent->children, tag, peers); 73 tag->align = MAX(ioc->u.tag.align, parent->align); 74 tag->bndry = MIN(ioc->u.tag.bndry, parent->bndry); 75 tag->maxaddr = MIN(ioc->u.tag.maxaddr, parent->maxaddr); 76 tag->maxsz = MIN(ioc->u.tag.maxsz, parent->maxsz); 77 tag->maxsegsz = MIN(ioc->u.tag.maxsegsz, parent->maxsegsz); 78 tag->nsegs = MIN(ioc->u.tag.nsegs, parent->nsegs); 79 tag->datarate = MIN(ioc->u.tag.datarate, parent->datarate); 80 /* Write constraints back */ 81 ioc->u.tag.align = tag->align; 82 ioc->u.tag.bndry = tag->bndry; 83 ioc->u.tag.maxaddr = tag->maxaddr; 84 ioc->u.tag.maxsz = tag->maxsz; 85 ioc->u.tag.maxsegsz = tag->maxsegsz; 86 ioc->u.tag.nsegs = tag->nsegs; 87 ioc->u.tag.datarate = tag->datarate; 88 } else { 89 tag->align = ioc->u.tag.align; 90 tag->bndry = ioc->u.tag.bndry; 91 tag->maxaddr = ioc->u.tag.maxaddr; 92 tag->maxsz = ioc->u.tag.maxsz; 93 tag->maxsegsz = ioc->u.tag.maxsegsz; 94 tag->nsegs = ioc->u.tag.nsegs; 95 tag->datarate = ioc->u.tag.datarate; 96 } 97 LIST_INSERT_HEAD(&busdma->tags, tag, tags); 98 ioc->result = (uintptr_t)(void *)tag; 99 return (0); 100 } 101 102 static int 103 proto_busdma_tag_destroy(struct proto_busdma *busdma, struct proto_tag *tag) 104 { 105 106 if (!LIST_EMPTY(&tag->mds)) 107 return (EBUSY); 108 if (!LIST_EMPTY(&tag->children)) 109 return (EBUSY); 110 111 if (tag->parent != NULL) { 112 LIST_REMOVE(tag, peers); 113 tag->parent = NULL; 114 } 115 LIST_REMOVE(tag, tags); 116 free(tag, M_PROTO_BUSDMA); 117 return (0); 118 } 119 120 static struct proto_tag * 121 proto_busdma_tag_lookup(struct proto_busdma *busdma, u_long key) 122 { 123 struct proto_tag *tag; 124 125 LIST_FOREACH(tag, &busdma->tags, tags) { 126 if ((void *)tag == (void *)key) 127 return (tag); 128 } 129 return (NULL); 130 } 131 132 static void 133 proto_busdma_mem_alloc_callback(void *arg, bus_dma_segment_t *segs, int nseg, 134 int error) 135 { 136 struct proto_ioc_busdma *ioc = arg; 137 138 ioc->u.mem.bus_nsegs = nseg; 139 ioc->u.mem.bus_addr = segs[0].ds_addr; 140 } 141 142 static int 143 proto_busdma_mem_alloc(struct proto_busdma *busdma, struct proto_tag *tag, 144 struct proto_ioc_busdma *ioc) 145 { 146 struct proto_md *md; 147 int error; 148 149 md = malloc(sizeof(*md), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 150 md->tag = tag; 151 152 error = bus_dma_tag_create(busdma->bd_roottag, tag->align, tag->bndry, 153 tag->maxaddr, BUS_SPACE_MAXADDR, NULL, NULL, tag->maxsz, 154 tag->nsegs, tag->maxsegsz, 0, NULL, NULL, &md->bd_tag); 155 if (error) { 156 free(md, M_PROTO_BUSDMA); 157 return (error); 158 } 159 error = bus_dmamem_alloc(md->bd_tag, &md->virtaddr, 0, &md->bd_map); 160 if (error) { 161 bus_dma_tag_destroy(md->bd_tag); 162 free(md, M_PROTO_BUSDMA); 163 return (error); 164 } 165 md->physaddr = pmap_kextract((uintptr_t)(md->virtaddr)); 166 error = bus_dmamap_load(md->bd_tag, md->bd_map, md->virtaddr, 167 tag->maxsz, proto_busdma_mem_alloc_callback, ioc, BUS_DMA_NOWAIT); 168 if (error) { 169 bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 170 bus_dma_tag_destroy(md->bd_tag); 171 free(md, M_PROTO_BUSDMA); 172 return (error); 173 } 174 LIST_INSERT_HEAD(&tag->mds, md, peers); 175 LIST_INSERT_HEAD(&busdma->mds, md, mds); 176 ioc->u.mem.phys_nsegs = 1; 177 ioc->u.mem.phys_addr = md->physaddr; 178 ioc->result = (uintptr_t)(void *)md; 179 return (0); 180 } 181 182 static int 183 proto_busdma_mem_free(struct proto_busdma *busdma, struct proto_md *md) 184 { 185 186 LIST_REMOVE(md, mds); 187 LIST_REMOVE(md, peers); 188 bus_dmamem_free(md->bd_tag, md->virtaddr, md->bd_map); 189 bus_dma_tag_destroy(md->bd_tag); 190 free(md, M_PROTO_BUSDMA); 191 return (0); 192 } 193 194 static struct proto_md * 195 proto_busdma_md_lookup(struct proto_busdma *busdma, u_long key) 196 { 197 struct proto_md *md; 198 199 LIST_FOREACH(md, &busdma->mds, mds) { 200 if ((void *)md == (void *)key) 201 return (md); 202 } 203 return (NULL); 204 } 205 206 struct proto_busdma * 207 proto_busdma_attach(struct proto_softc *sc) 208 { 209 struct proto_busdma *busdma; 210 211 busdma = malloc(sizeof(*busdma), M_PROTO_BUSDMA, M_WAITOK | M_ZERO); 212 return (busdma); 213 } 214 215 int 216 proto_busdma_detach(struct proto_softc *sc, struct proto_busdma *busdma) 217 { 218 219 proto_busdma_cleanup(sc, busdma); 220 free(busdma, M_PROTO_BUSDMA); 221 return (0); 222 } 223 224 int 225 proto_busdma_cleanup(struct proto_softc *sc, struct proto_busdma *busdma) 226 { 227 struct proto_md *md, *md1; 228 struct proto_tag *tag, *tag1; 229 230 LIST_FOREACH_SAFE(md, &busdma->mds, mds, md1) 231 proto_busdma_mem_free(busdma, md); 232 LIST_FOREACH_SAFE(tag, &busdma->tags, tags, tag1) 233 proto_busdma_tag_destroy(busdma, tag); 234 return (0); 235 } 236 237 int 238 proto_busdma_ioctl(struct proto_softc *sc, struct proto_busdma *busdma, 239 struct proto_ioc_busdma *ioc) 240 { 241 struct proto_tag *tag; 242 struct proto_md *md; 243 int error; 244 245 error = 0; 246 switch (ioc->request) { 247 case PROTO_IOC_BUSDMA_TAG_CREATE: 248 busdma->bd_roottag = bus_get_dma_tag(sc->sc_dev); 249 error = proto_busdma_tag_create(busdma, NULL, ioc); 250 break; 251 case PROTO_IOC_BUSDMA_TAG_DERIVE: 252 tag = proto_busdma_tag_lookup(busdma, ioc->key); 253 if (tag == NULL) { 254 error = EINVAL; 255 break; 256 } 257 error = proto_busdma_tag_create(busdma, tag, ioc); 258 break; 259 case PROTO_IOC_BUSDMA_TAG_DESTROY: 260 tag = proto_busdma_tag_lookup(busdma, ioc->key); 261 if (tag == NULL) { 262 error = EINVAL; 263 break; 264 } 265 error = proto_busdma_tag_destroy(busdma, tag); 266 break; 267 case PROTO_IOC_BUSDMA_MEM_ALLOC: 268 tag = proto_busdma_tag_lookup(busdma, ioc->u.mem.tag); 269 if (tag == NULL) { 270 error = EINVAL; 271 break; 272 } 273 error = proto_busdma_mem_alloc(busdma, tag, ioc); 274 break; 275 case PROTO_IOC_BUSDMA_MEM_FREE: 276 md = proto_busdma_md_lookup(busdma, ioc->key); 277 if (md == NULL) { 278 error = EINVAL; 279 break; 280 } 281 error = proto_busdma_mem_free(busdma, md); 282 break; 283 default: 284 error = EINVAL; 285 break; 286 } 287 return (error); 288 } 289 290 int 291 proto_busdma_mmap_allowed(struct proto_busdma *busdma, vm_paddr_t physaddr) 292 { 293 struct proto_md *md; 294 295 LIST_FOREACH(md, &busdma->mds, mds) { 296 if (physaddr >= trunc_page(md->physaddr) && 297 physaddr <= trunc_page(md->physaddr + md->tag->maxsz)) 298 return (1); 299 } 300 return (0); 301 } 302