1 /* $NetBSD: bus_dma_jazz.c,v 1.18 2020/11/18 02:14:13 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2003 Izumi Tsutsui. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 /*- 28 * Copyright (C) 2000 Shuichiro URATA. All rights reserved. 29 * 30 * Redistribution and use in source and binary forms, with or without 31 * modification, are permitted provided that the following conditions 32 * are met: 33 * 1. Redistributions of source code must retain the above copyright 34 * notice, this list of conditions and the following disclaimer. 35 * 2. Redistributions in binary form must reproduce the above copyright 36 * notice, this list of conditions and the following disclaimer in the 37 * documentation and/or other materials provided with the distribution. 38 * 3. The name of the author may not be used to endorse or promote products 39 * derived from this software without specific prior written permission. 40 * 41 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 42 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 44 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 45 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 46 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 47 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 48 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 49 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 50 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 51 */ 52 53 #include <sys/cdefs.h> 54 __KERNEL_RCSID(0, "$NetBSD: bus_dma_jazz.c,v 1.18 2020/11/18 02:14:13 thorpej Exp $"); 55 56 #include <sys/param.h> 57 #include <sys/systm.h> 58 #include <sys/mbuf.h> 59 #include <sys/device.h> 60 #include <sys/proc.h> 61 #include <sys/kmem.h> 62 63 #include <uvm/uvm_extern.h> 64 65 #define _ARC_BUS_DMA_PRIVATE 66 #include <sys/bus.h> 67 68 #include <arc/jazz/jazzdmatlbreg.h> 69 #include <arc/jazz/jazzdmatlbvar.h> 70 71 typedef struct jazz_tlbmap { 72 struct jazz_dma_pte *ptebase; 73 bus_addr_t vaddr; 74 } *jazz_tlbmap_t; 75 76 static int jazz_bus_dmamap_alloc_sgmap(bus_dma_tag_t, 77 bus_dma_segment_t *, int, bus_size_t, int); 78 static void jazz_bus_dmamap_free_sgmap(bus_dma_tag_t, 79 bus_dma_segment_t *, int); 80 81 int jazz_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, 82 bus_size_t, bus_size_t, int, bus_dmamap_t *); 83 void jazz_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); 84 int jazz_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, 85 bus_size_t, struct proc *, int); 86 int jazz_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, 87 struct mbuf *, int); 88 int jazz_bus_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, 89 struct uio *, int); 90 int jazz_bus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 91 bus_dma_segment_t *, int, bus_size_t, int); 92 void jazz_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); 93 void jazz_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, 94 bus_addr_t, bus_size_t, int); 95 96 void 97 jazz_bus_dma_tag_init(bus_dma_tag_t t) 98 { 99 100 _bus_dma_tag_init(t); 101 102 t->_dmamap_create = jazz_bus_dmamap_create; 103 t->_dmamap_destroy = jazz_bus_dmamap_destroy; 104 t->_dmamap_load = jazz_bus_dmamap_load; 105 t->_dmamap_load_mbuf = jazz_bus_dmamap_load_mbuf; 106 t->_dmamap_load_uio = jazz_bus_dmamap_load_uio; 107 t->_dmamap_load_raw = jazz_bus_dmamap_load_raw; 108 t->_dmamap_unload = jazz_bus_dmamap_unload; 109 t->_dmamap_sync = jazz_bus_dmamap_sync; 110 t->_dmamem_alloc = _bus_dmamem_alloc; 111 t->_dmamem_free = _bus_dmamem_free; 112 } 113 114 static int 115 jazz_bus_dmamap_alloc_sgmap(bus_dma_tag_t t, bus_dma_segment_t *segs, 116 int nsegs, bus_size_t boundary, int flags) 117 { 118 jazz_dma_pte_t *dmapte; 119 bus_addr_t addr; 120 bus_size_t off; 121 int i, npte; 122 123 for (i = 0; i < nsegs; i++) { 124 off = jazz_dma_page_offs(segs[i]._ds_paddr); 125 npte = jazz_dma_page_round(segs[i].ds_len + off) / 126 JAZZ_DMA_PAGE_SIZE; 127 dmapte = jazz_dmatlb_alloc(npte, boundary, flags, &addr); 128 if (dmapte == NULL) 129 return ENOMEM; 130 segs[i].ds_addr = addr + off; 131 132 jazz_dmatlb_map_pa(segs[i]._ds_paddr, segs[i].ds_len, dmapte); 133 } 134 return 0; 135 } 136 137 static void 138 jazz_bus_dmamap_free_sgmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs) 139 { 140 int i, npte; 141 bus_addr_t addr; 142 143 for (i = 0; i < nsegs; i++) { 144 addr = (segs[i].ds_addr - t->dma_offset) & JAZZ_DMA_PAGE_NUM; 145 npte = jazz_dma_page_round(segs[i].ds_len + 146 jazz_dma_page_offs(segs[i].ds_addr)) / JAZZ_DMA_PAGE_SIZE; 147 jazz_dmatlb_free(addr, npte); 148 } 149 } 150 151 152 /* 153 * function to create a DMA map. If BUS_DMA_ALLOCNOW is specified and 154 * nsegments is 1, allocate jazzdmatlb here, too. 155 */ 156 int 157 jazz_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, 158 bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) 159 { 160 struct arc_bus_dmamap *map; 161 jazz_tlbmap_t tlbmap; 162 int error, npte; 163 164 if (nsegments > 1) 165 /* 166 * BUS_DMA_ALLOCNOW is allowed only with one segment for now. 167 * XXX needs re-think. 168 */ 169 flags &= ~BUS_DMA_ALLOCNOW; 170 171 if ((flags & BUS_DMA_ALLOCNOW) == 0) 172 return _bus_dmamap_create(t, size, nsegments, maxsegsz, 173 boundary, flags, dmamp); 174 175 tlbmap = kmem_alloc(sizeof(struct jazz_tlbmap), 176 (flags & BUS_DMA_NOWAIT) ? KM_NOSLEEP : KM_SLEEP); 177 if (tlbmap == NULL) 178 return ENOMEM; 179 180 npte = jazz_dma_page_round(maxsegsz) / JAZZ_DMA_PAGE_SIZE + 1; 181 tlbmap->ptebase = 182 jazz_dmatlb_alloc(npte, boundary, flags, &tlbmap->vaddr); 183 if (tlbmap->ptebase == NULL) { 184 kmem_free(tlbmap, sizeof(struct jazz_tlbmap)); 185 return ENOMEM; 186 } 187 188 error = _bus_dmamap_create(t, size, 1, maxsegsz, boundary, 189 flags, dmamp); 190 if (error != 0) { 191 jazz_dmatlb_free(tlbmap->vaddr, npte); 192 kmem_free(tlbmap, sizeof(struct jazz_tlbmap)); 193 return error; 194 } 195 map = *dmamp; 196 map->_dm_cookie = (void *)tlbmap; 197 198 return 0; 199 } 200 201 /* 202 * function to destroy a DMA map. If BUS_DMA_ALLOCNOW is specified, 203 * free jazzdmatlb, too. 204 */ 205 void 206 jazz_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) 207 { 208 209 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 210 jazz_tlbmap_t tlbmap; 211 int npte; 212 213 tlbmap = (jazz_tlbmap_t)map->_dm_cookie; 214 npte = jazz_dma_page_round(map->dm_maxsegsz) / 215 JAZZ_DMA_PAGE_SIZE + 1; 216 jazz_dmatlb_free(tlbmap->vaddr, npte); 217 kmem_free(tlbmap, sizeof(struct jazz_tlbmap)); 218 } 219 220 _bus_dmamap_destroy(t, map); 221 } 222 223 /* 224 * function for loading a direct-mapped DMA map with a linear buffer. 225 */ 226 int 227 jazz_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 228 bus_size_t buflen, struct proc *p, int flags) 229 { 230 int error; 231 232 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 233 /* just use pre-allocated DMA TLB for the buffer */ 234 jazz_tlbmap_t tlbmap; 235 bus_size_t off; 236 struct vmspace *vm; 237 238 if (p != NULL) { 239 vm = p->p_vmspace; 240 } else { 241 vm = vmspace_kernel(); 242 } 243 244 tlbmap = (jazz_tlbmap_t)map->_dm_cookie; 245 off = jazz_dma_page_offs(buf); 246 jazz_dmatlb_map_va(vm, (vaddr_t)buf, buflen, tlbmap->ptebase); 247 248 map->dm_segs[0].ds_addr = tlbmap->vaddr + off; 249 map->dm_segs[0].ds_len = buflen; 250 map->dm_segs[0]._ds_vaddr = (vaddr_t)buf; 251 map->dm_mapsize = buflen; 252 map->dm_nsegs = 1; 253 map->_dm_vmspace = vm; 254 255 if (buf >= (void *)MIPS_KSEG1_START && 256 buf < (void *)MIPS_KSEG2_START) 257 map->_dm_flags |= ARC_DMAMAP_COHERENT; 258 259 return 0; 260 } 261 262 error = _bus_dmamap_load(t, map, buf, buflen, p, flags); 263 if (error == 0) { 264 /* allocate DMA TLB for each dmamap segment */ 265 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 266 map->dm_nsegs, map->_dm_boundary, flags); 267 } 268 return error; 269 } 270 271 /* 272 * Like jazz_bus_dmamap_load(), but for mbufs. 273 */ 274 int 275 jazz_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, 276 int flags) 277 { 278 int error; 279 280 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 281 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 282 return ENODEV; /* XXX which errno is better? */ 283 284 error = _bus_dmamap_load_mbuf(t, map, m0, flags); 285 if (error == 0) { 286 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 287 map->dm_nsegs, map->_dm_boundary, flags); 288 } 289 return error; 290 } 291 292 /* 293 * Like jazz_bus_dmamap_load(), but for uios. 294 */ 295 int 296 jazz_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, 297 int flags) 298 { 299 int error; 300 301 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 302 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 303 return ENODEV; /* XXX which errno is better? */ 304 305 error = jazz_bus_dmamap_load_uio(t, map, uio, flags); 306 if (error == 0) { 307 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 308 map->dm_nsegs, map->_dm_boundary, flags); 309 } 310 return error; 311 } 312 313 /* 314 * Like _bus_dmamap_load(), but for raw memory. 315 */ 316 int 317 jazz_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 318 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 319 { 320 int error; 321 322 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 323 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 324 return ENODEV; /* XXX which errno is better? */ 325 326 error = _bus_dmamap_load_raw(t, map, segs, nsegs, size, flags); 327 if (error == 0) { 328 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 329 map->dm_nsegs, map->_dm_boundary, flags); 330 } 331 return error; 332 } 333 334 /* 335 * unload a DMA map. 336 */ 337 void 338 jazz_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 339 { 340 341 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 342 /* DMA TLB should be preserved */ 343 map->dm_mapsize = 0; 344 map->dm_nsegs = 0; 345 return; 346 } 347 348 jazz_bus_dmamap_free_sgmap(t, map->dm_segs, map->dm_nsegs); 349 _bus_dmamap_unload(t, map); 350 } 351 352 /* 353 * Function for MIPS3 DMA map synchronization. 354 */ 355 void 356 jazz_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, 357 bus_size_t len, int ops) 358 { 359 360 /* Flush DMA TLB */ 361 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0) 362 jazz_dmatlb_flush(); 363 364 return _bus_dmamap_sync(t, map, offset, len, ops); 365 } 366