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