1 /* $NetBSD: bus_dma_jazz.c,v 1.17 2011/07/01 19:25:41 dyoung 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.17 2011/07/01 19:25:41 dyoung 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 62 #include <uvm/uvm_extern.h> 63 64 #define _ARC_BUS_DMA_PRIVATE 65 #include <sys/bus.h> 66 67 #include <arc/jazz/jazzdmatlbreg.h> 68 #include <arc/jazz/jazzdmatlbvar.h> 69 70 typedef struct jazz_tlbmap { 71 struct jazz_dma_pte *ptebase; 72 bus_addr_t vaddr; 73 } *jazz_tlbmap_t; 74 75 static int jazz_bus_dmamap_alloc_sgmap(bus_dma_tag_t, 76 bus_dma_segment_t *, int, bus_size_t, int); 77 static void jazz_bus_dmamap_free_sgmap(bus_dma_tag_t, 78 bus_dma_segment_t *, int); 79 80 int jazz_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, 81 bus_size_t, bus_size_t, int, bus_dmamap_t *); 82 void jazz_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); 83 int jazz_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, 84 bus_size_t, struct proc *, int); 85 int jazz_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, 86 struct mbuf *, int); 87 int jazz_bus_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, 88 struct uio *, int); 89 int jazz_bus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 90 bus_dma_segment_t *, int, bus_size_t, int); 91 void jazz_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); 92 void jazz_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, 93 bus_addr_t, bus_size_t, int); 94 95 void 96 jazz_bus_dma_tag_init(bus_dma_tag_t t) 97 { 98 99 _bus_dma_tag_init(t); 100 101 t->_dmamap_create = jazz_bus_dmamap_create; 102 t->_dmamap_destroy = jazz_bus_dmamap_destroy; 103 t->_dmamap_load = jazz_bus_dmamap_load; 104 t->_dmamap_load_mbuf = jazz_bus_dmamap_load_mbuf; 105 t->_dmamap_load_uio = jazz_bus_dmamap_load_uio; 106 t->_dmamap_load_raw = jazz_bus_dmamap_load_raw; 107 t->_dmamap_unload = jazz_bus_dmamap_unload; 108 t->_dmamap_sync = jazz_bus_dmamap_sync; 109 t->_dmamem_alloc = _bus_dmamem_alloc; 110 t->_dmamem_free = _bus_dmamem_free; 111 } 112 113 static int 114 jazz_bus_dmamap_alloc_sgmap(bus_dma_tag_t t, bus_dma_segment_t *segs, 115 int nsegs, bus_size_t boundary, int flags) 116 { 117 jazz_dma_pte_t *dmapte; 118 bus_addr_t addr; 119 bus_size_t off; 120 int i, npte; 121 122 for (i = 0; i < nsegs; i++) { 123 off = jazz_dma_page_offs(segs[i]._ds_paddr); 124 npte = jazz_dma_page_round(segs[i].ds_len + off) / 125 JAZZ_DMA_PAGE_SIZE; 126 dmapte = jazz_dmatlb_alloc(npte, boundary, flags, &addr); 127 if (dmapte == NULL) 128 return ENOMEM; 129 segs[i].ds_addr = addr + off; 130 131 jazz_dmatlb_map_pa(segs[i]._ds_paddr, segs[i].ds_len, dmapte); 132 } 133 return 0; 134 } 135 136 static void 137 jazz_bus_dmamap_free_sgmap(bus_dma_tag_t t, bus_dma_segment_t *segs, int nsegs) 138 { 139 int i, npte; 140 bus_addr_t addr; 141 142 for (i = 0; i < nsegs; i++) { 143 addr = (segs[i].ds_addr - t->dma_offset) & JAZZ_DMA_PAGE_NUM; 144 npte = jazz_dma_page_round(segs[i].ds_len + 145 jazz_dma_page_offs(segs[i].ds_addr)) / JAZZ_DMA_PAGE_SIZE; 146 jazz_dmatlb_free(addr, npte); 147 } 148 } 149 150 151 /* 152 * function to create a DMA map. If BUS_DMA_ALLOCNOW is specified and 153 * nsegments is 1, allocate jazzdmatlb here, too. 154 */ 155 int 156 jazz_bus_dmamap_create(bus_dma_tag_t t, bus_size_t size, int nsegments, 157 bus_size_t maxsegsz, bus_size_t boundary, int flags, bus_dmamap_t *dmamp) 158 { 159 struct arc_bus_dmamap *map; 160 jazz_tlbmap_t tlbmap; 161 int error, npte; 162 163 if (nsegments > 1) 164 /* 165 * BUS_DMA_ALLOCNOW is allowed only with one segment for now. 166 * XXX needs re-think. 167 */ 168 flags &= ~BUS_DMA_ALLOCNOW; 169 170 if ((flags & BUS_DMA_ALLOCNOW) == 0) 171 return _bus_dmamap_create(t, size, nsegments, maxsegsz, 172 boundary, flags, dmamp); 173 174 tlbmap = malloc(sizeof(struct jazz_tlbmap), M_DMAMAP, 175 (flags & BUS_DMA_NOWAIT) ? M_NOWAIT : M_WAITOK); 176 if (tlbmap == NULL) 177 return ENOMEM; 178 179 npte = jazz_dma_page_round(maxsegsz) / JAZZ_DMA_PAGE_SIZE + 1; 180 tlbmap->ptebase = 181 jazz_dmatlb_alloc(npte, boundary, flags, &tlbmap->vaddr); 182 if (tlbmap->ptebase == NULL) { 183 free(tlbmap, M_DMAMAP); 184 return ENOMEM; 185 } 186 187 error = _bus_dmamap_create(t, size, 1, maxsegsz, boundary, 188 flags, dmamp); 189 if (error != 0) { 190 jazz_dmatlb_free(tlbmap->vaddr, npte); 191 free(tlbmap, M_DMAMAP); 192 return error; 193 } 194 map = *dmamp; 195 map->_dm_cookie = (void *)tlbmap; 196 197 return 0; 198 } 199 200 /* 201 * function to destroy a DMA map. If BUS_DMA_ALLOCNOW is specified, 202 * free jazzdmatlb, too. 203 */ 204 void 205 jazz_bus_dmamap_destroy(bus_dma_tag_t t, bus_dmamap_t map) 206 { 207 208 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 209 jazz_tlbmap_t tlbmap; 210 int npte; 211 212 tlbmap = (jazz_tlbmap_t)map->_dm_cookie; 213 npte = jazz_dma_page_round(map->dm_maxsegsz) / 214 JAZZ_DMA_PAGE_SIZE + 1; 215 jazz_dmatlb_free(tlbmap->vaddr, npte); 216 free(tlbmap, M_DMAMAP); 217 } 218 219 _bus_dmamap_destroy(t, map); 220 } 221 222 /* 223 * function for loading a direct-mapped DMA map with a linear buffer. 224 */ 225 int 226 jazz_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 227 bus_size_t buflen, struct proc *p, int flags) 228 { 229 int error; 230 231 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 232 /* just use pre-allocated DMA TLB for the buffer */ 233 jazz_tlbmap_t tlbmap; 234 bus_size_t off; 235 struct vmspace *vm; 236 237 if (p != NULL) { 238 vm = p->p_vmspace; 239 } else { 240 vm = vmspace_kernel(); 241 } 242 243 tlbmap = (jazz_tlbmap_t)map->_dm_cookie; 244 off = jazz_dma_page_offs(buf); 245 jazz_dmatlb_map_va(vm, (vaddr_t)buf, buflen, tlbmap->ptebase); 246 247 map->dm_segs[0].ds_addr = tlbmap->vaddr + off; 248 map->dm_segs[0].ds_len = buflen; 249 map->dm_segs[0]._ds_vaddr = (vaddr_t)buf; 250 map->dm_mapsize = buflen; 251 map->dm_nsegs = 1; 252 map->_dm_vmspace = vm; 253 254 if (buf >= (void *)MIPS_KSEG1_START && 255 buf < (void *)MIPS_KSEG2_START) 256 map->_dm_flags |= ARC_DMAMAP_COHERENT; 257 258 return 0; 259 } 260 261 error = _bus_dmamap_load(t, map, buf, buflen, p, flags); 262 if (error == 0) { 263 /* allocate DMA TLB for each dmamap segment */ 264 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 265 map->dm_nsegs, map->_dm_boundary, flags); 266 } 267 return error; 268 } 269 270 /* 271 * Like jazz_bus_dmamap_load(), but for mbufs. 272 */ 273 int 274 jazz_bus_dmamap_load_mbuf(bus_dma_tag_t t, bus_dmamap_t map, struct mbuf *m0, 275 int flags) 276 { 277 int error; 278 279 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 280 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 281 return ENODEV; /* XXX which errno is better? */ 282 283 error = _bus_dmamap_load_mbuf(t, map, m0, flags); 284 if (error == 0) { 285 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 286 map->dm_nsegs, map->_dm_boundary, flags); 287 } 288 return error; 289 } 290 291 /* 292 * Like jazz_bus_dmamap_load(), but for uios. 293 */ 294 int 295 jazz_bus_dmamap_load_uio(bus_dma_tag_t t, bus_dmamap_t map, struct uio *uio, 296 int flags) 297 { 298 int error; 299 300 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 301 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 302 return ENODEV; /* XXX which errno is better? */ 303 304 error = jazz_bus_dmamap_load_uio(t, map, uio, flags); 305 if (error == 0) { 306 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 307 map->dm_nsegs, map->_dm_boundary, flags); 308 } 309 return error; 310 } 311 312 /* 313 * Like _bus_dmamap_load(), but for raw memory. 314 */ 315 int 316 jazz_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, 317 bus_dma_segment_t *segs, int nsegs, bus_size_t size, int flags) 318 { 319 int error; 320 321 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) 322 /* BUS_DMA_ALLOCNOW is valid only for linear buffer. */ 323 return ENODEV; /* XXX which errno is better? */ 324 325 error = _bus_dmamap_load_raw(t, map, segs, nsegs, size, flags); 326 if (error == 0) { 327 error = jazz_bus_dmamap_alloc_sgmap(t, map->dm_segs, 328 map->dm_nsegs, map->_dm_boundary, flags); 329 } 330 return error; 331 } 332 333 /* 334 * unload a DMA map. 335 */ 336 void 337 jazz_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 338 { 339 340 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) != 0) { 341 /* DMA TLB should be preserved */ 342 map->dm_mapsize = 0; 343 map->dm_nsegs = 0; 344 return; 345 } 346 347 jazz_bus_dmamap_free_sgmap(t, map->dm_segs, map->dm_nsegs); 348 _bus_dmamap_unload(t, map); 349 } 350 351 /* 352 * Function for MIPS3 DMA map synchronization. 353 */ 354 void 355 jazz_bus_dmamap_sync(bus_dma_tag_t t, bus_dmamap_t map, bus_addr_t offset, 356 bus_size_t len, int ops) 357 { 358 359 /* Flush DMA TLB */ 360 if ((ops & (BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE)) != 0) 361 jazz_dmatlb_flush(); 362 363 return _bus_dmamap_sync(t, map, offset, len, ops); 364 } 365