1 /* $NetBSD: bus_dma_hacks.h,v 1.27 2026/02/09 10:48:23 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Taylor R. Campbell. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #ifndef _DRM_BUS_DMA_HACKS_H_ 33 #define _DRM_BUS_DMA_HACKS_H_ 34 35 #include <sys/cdefs.h> 36 #include <sys/bus.h> 37 #include <sys/kmem.h> 38 #include <sys/queue.h> 39 40 #include <uvm/uvm.h> 41 #include <uvm/uvm_extern.h> 42 43 #include <linux/mm_types.h> /* XXX struct page */ 44 45 #if defined(__i386__) || defined(__x86_64__) 46 # include <x86/bus_private.h> 47 # include <x86/machdep.h> 48 # define PHYS_TO_BUS_MEM(dmat, paddr) ((bus_addr_t)(paddr)) 49 # define BUS_MEM_TO_PHYS(dmat, baddr) ((paddr_t)(baddr)) 50 #elif defined(__arm__) || defined(__aarch64__) 51 static inline bus_addr_t 52 PHYS_TO_BUS_MEM(bus_dma_tag_t dmat, paddr_t pa) 53 { 54 unsigned i; 55 56 if (dmat->_nranges == 0) 57 return (bus_addr_t)pa; 58 59 for (i = 0; i < dmat->_nranges; i++) { 60 const struct arm32_dma_range *dr = &dmat->_ranges[i]; 61 62 if (dr->dr_sysbase <= pa && pa - dr->dr_sysbase <= dr->dr_len) 63 return pa - dr->dr_sysbase + dr->dr_busbase; 64 } 65 panic("paddr has no bus address in dma tag %p: %"PRIxPADDR, dmat, pa); 66 } 67 static inline paddr_t 68 BUS_MEM_TO_PHYS(bus_dma_tag_t dmat, bus_addr_t ba) 69 { 70 unsigned i; 71 72 if (dmat->_nranges == 0) 73 return (paddr_t)ba; 74 75 for (i = 0; i < dmat->_nranges; i++) { 76 const struct arm32_dma_range *dr = &dmat->_ranges[i]; 77 78 if (dr->dr_busbase <= ba && ba - dr->dr_busbase <= dr->dr_len) 79 return ba - dr->dr_busbase + dr->dr_sysbase; 80 } 81 panic("bus addr has no bus address in dma tag %p: %"PRIxPADDR, dmat, 82 ba); 83 } 84 #elif defined(__sparc__) || defined(__sparc64__) 85 # define PHYS_TO_BUS_MEM(dmat, paddr) ((bus_addr_t)(paddr)) 86 # define BUS_MEM_TO_PHYS(dmat, baddr) ((paddr_t)(baddr)) 87 #elif defined(__powerpc__) 88 #include "opt_ppcarch.h" 89 #elif defined(__alpha__) 90 # define PHYS_TO_BUS_MEM(dmat, paddr) \ 91 ((bus_addr_t)(paddr) | (dmat)->_wbase) 92 # define BUS_MEM_TO_PHYS(dmat, baddr) \ 93 ((paddr_t)((baddr) & ~(bus_addr_t)(dmat)->_wbase)) 94 #else 95 # error DRM GEM/TTM need new MI bus_dma APIs! Halp! 96 #endif 97 98 static inline int 99 bus_dmamem_pgfl(bus_dma_tag_t tag) 100 { 101 #if defined(__i386__) || defined(__x86_64__) 102 return x86_select_freelist(tag->_bounce_alloc_hi - 1); 103 #elif defined(__powerpc__) && (defined(PPC_OEA) || defined(PPC_OEA64_BRIDGE)) 104 return VM_FREELIST_DIRECT_MAPPED; /* first 3GB */ 105 #else 106 return VM_FREELIST_DEFAULT; 107 #endif 108 } 109 110 static inline bool 111 bus_dmatag_bounces_paddr(bus_dma_tag_t dmat, paddr_t pa) 112 { 113 #if defined(__i386__) || defined(__x86_64__) 114 return pa < dmat->_bounce_alloc_lo || dmat->_bounce_alloc_hi <= pa; 115 #elif defined(__arm__) || defined(__aarch64__) 116 unsigned i; 117 118 for (i = 0; i < dmat->_nranges; i++) { 119 const struct arm32_dma_range *dr = &dmat->_ranges[i]; 120 if (dr->dr_sysbase <= pa && pa - dr->dr_sysbase <= dr->dr_len) 121 return false; 122 } 123 return true; 124 #elif defined(__powerpc__) 125 if (dmat->_bounce_thresh_min != 0 && pa < dmat->_bounce_thresh_min) { 126 return true; 127 } 128 if (dmat->_bounce_thresh_max != 0 && pa >= dmat->_bounce_thresh_max) { 129 return true; 130 } 131 return false; 132 #elif defined(__sparc__) || defined(__sparc64__) 133 return false; /* no bounce buffers ever */ 134 #elif defined(__alpha__) 135 return (dmat->_wsize == 0 ? false : pa >= dmat->_wsize); 136 #endif 137 } 138 139 #define MAX_STACK_SEGS 32 /* XXXMRG: 512 bytes on 16 byte seg platforms */ 140 141 /* 142 * XXX This should really take an array of struct vm_page pointers, but 143 * Linux drm code stores arrays of struct page pointers, and these two 144 * types (struct page ** and struct vm_page **) are not compatible so 145 * naive conversion would violate strict aliasing rules. 146 */ 147 static inline int 148 bus_dmamap_load_pages(bus_dma_tag_t tag, bus_dmamap_t map, 149 struct page **pgs, bus_size_t size, int flags) 150 { 151 km_flag_t kmflags; 152 bus_dma_segment_t *segs; 153 bus_dma_segment_t stacksegs[MAX_STACK_SEGS]; 154 int nsegs, seg; 155 struct vm_page *page; 156 int error; 157 158 KASSERT((size & (PAGE_SIZE - 1)) == 0); 159 160 if ((size >> PAGE_SHIFT) > INT_MAX) 161 return ENOMEM; 162 nsegs = size >> PAGE_SHIFT; 163 164 KASSERT(nsegs <= (SIZE_MAX / sizeof(segs[0]))); 165 if (nsegs > MAX_STACK_SEGS) { 166 switch (flags & (BUS_DMA_WAITOK|BUS_DMA_NOWAIT)) { 167 case BUS_DMA_WAITOK: 168 kmflags = KM_SLEEP; 169 break; 170 case BUS_DMA_NOWAIT: 171 kmflags = KM_NOSLEEP; 172 break; 173 default: 174 panic("invalid flags: %d", flags); 175 } 176 segs = kmem_alloc((nsegs * sizeof(segs[0])), kmflags); 177 if (segs == NULL) 178 return ENOMEM; 179 } else { 180 segs = stacksegs; 181 } 182 183 for (seg = 0; seg < nsegs; seg++) { 184 page = &pgs[seg]->p_vmp; 185 paddr_t paddr = VM_PAGE_TO_PHYS(page); 186 bus_addr_t baddr = PHYS_TO_BUS_MEM(tag, paddr); 187 188 segs[seg].ds_addr = baddr; 189 segs[seg].ds_len = PAGE_SIZE; 190 } 191 192 error = bus_dmamap_load_raw(tag, map, segs, nsegs, size, flags); 193 if (error) 194 goto fail0; 195 196 /* Success! */ 197 error = 0; 198 goto out; 199 200 fail1: __unused 201 bus_dmamap_unload(tag, map); 202 fail0: KASSERT(error); 203 out: if (segs != stacksegs) { 204 KASSERT(nsegs > MAX_STACK_SEGS); 205 kmem_free(segs, (nsegs * sizeof(segs[0]))); 206 } 207 return error; 208 } 209 210 static inline int 211 bus_dmamem_export_pages(bus_dma_tag_t dmat, const bus_dma_segment_t *segs, 212 int nsegs, struct page **pgs, unsigned npgs) 213 { 214 int seg; 215 unsigned pg; 216 217 pg = 0; 218 for (seg = 0; seg < nsegs; seg++) { 219 bus_addr_t baddr = segs[seg].ds_addr; 220 bus_size_t len = segs[seg].ds_len; 221 222 while (len >= PAGE_SIZE) { 223 paddr_t paddr = BUS_MEM_TO_PHYS(dmat, baddr); 224 225 KASSERT(pg < npgs); 226 pgs[pg++] = container_of(PHYS_TO_VM_PAGE(paddr), 227 struct page, p_vmp); 228 229 baddr += PAGE_SIZE; 230 len -= PAGE_SIZE; 231 } 232 KASSERT(len == 0); 233 } 234 KASSERT(pg == npgs); 235 236 return 0; 237 } 238 239 static inline int 240 bus_dmamem_import_pages(bus_dma_tag_t dmat, bus_dma_segment_t *segs, 241 int nsegs, int *rsegs, struct page *const *pgs, unsigned npgs) 242 { 243 int seg; 244 unsigned i; 245 246 seg = 0; 247 for (i = 0; i < npgs; i++) { 248 paddr_t paddr = VM_PAGE_TO_PHYS(&pgs[i]->p_vmp); 249 bus_addr_t baddr = PHYS_TO_BUS_MEM(dmat, paddr); 250 251 if (seg > 0 && segs[seg - 1].ds_addr + PAGE_SIZE == baddr) { 252 segs[seg - 1].ds_len += PAGE_SIZE; 253 } else { 254 KASSERT(seg < nsegs); 255 segs[seg].ds_addr = baddr; 256 segs[seg].ds_len = PAGE_SIZE; 257 seg++; 258 KASSERT(seg <= nsegs); 259 } 260 } 261 KASSERT(seg <= nsegs); 262 *rsegs = seg; 263 264 return 0; 265 } 266 267 #endif /* _DRM_BUS_DMA_HACKS_H_ */ 268