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 71typedef struct jazz_tlbmap { 72 struct jazz_dma_pte *ptebase; 73 bus_addr_t vaddr; 74} *jazz_tlbmap_t; 75 76static int jazz_bus_dmamap_alloc_sgmap(bus_dma_tag_t, 77 bus_dma_segment_t *, int, bus_size_t, int); 78static void jazz_bus_dmamap_free_sgmap(bus_dma_tag_t, 79 bus_dma_segment_t *, int); 80 81int jazz_bus_dmamap_create(bus_dma_tag_t, bus_size_t, int, 82 bus_size_t, bus_size_t, int, bus_dmamap_t *); 83void jazz_bus_dmamap_destroy(bus_dma_tag_t, bus_dmamap_t); 84int jazz_bus_dmamap_load(bus_dma_tag_t, bus_dmamap_t, void *, 85 bus_size_t, struct proc *, int); 86int jazz_bus_dmamap_load_mbuf(bus_dma_tag_t, bus_dmamap_t, 87 struct mbuf *, int); 88int jazz_bus_dmamap_load_uio(bus_dma_tag_t, bus_dmamap_t, 89 struct uio *, int); 90int jazz_bus_dmamap_load_raw(bus_dma_tag_t, bus_dmamap_t, 91 bus_dma_segment_t *, int, bus_size_t, int); 92void jazz_bus_dmamap_unload(bus_dma_tag_t, bus_dmamap_t); 93void jazz_bus_dmamap_sync(bus_dma_tag_t, bus_dmamap_t, 94 bus_addr_t, bus_size_t, int); 95 96void 97jazz_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 114static int 115jazz_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 137static void 138jazz_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 */ 156int 157jazz_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 */ 205void 206jazz_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 */ 226int 227jazz_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 */ 274int 275jazz_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 */ 295int 296jazz_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 */ 316int 317jazz_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 */ 337void 338jazz_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 */ 355void 356jazz_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