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