1 /* $NetBSD: sgmap.c,v 1.9 2000/11/16 19:25:42 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 * NASA Ames Research Center. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 #include <sys/proc.h> 44 #include <sys/malloc.h> 45 46 #include <uvm/uvm_extern.h> 47 48 #include <machine/bus.h> 49 #include <machine/sgmap.h> 50 51 void 52 vax_sgmap_init(t, sgmap, name, sgvabase, sgvasize, ptva, minptalign) 53 bus_dma_tag_t t; 54 struct vax_sgmap *sgmap; 55 const char *name; 56 bus_addr_t sgvabase; 57 bus_size_t sgvasize; 58 struct pte *ptva; 59 bus_size_t minptalign; 60 { 61 bus_dma_segment_t seg; 62 size_t ptsize; 63 int rseg; 64 65 if (sgvasize & PGOFSET) { 66 printf("size botch for sgmap `%s'\n", name); 67 goto die; 68 } 69 70 sgmap->aps_sgvabase = sgvabase; 71 sgmap->aps_sgvasize = sgvasize; 72 73 if (ptva != NULL) { 74 /* 75 * We already have a page table; this may be a system 76 * where the page table resides in bridge-resident SRAM. 77 */ 78 sgmap->aps_pt = ptva; 79 } else { 80 /* 81 * Compute the page table size and allocate it. At minimum, 82 * this must be aligned to the page table size. However, 83 * some platforms have more strict alignment reqirements. 84 */ 85 ptsize = (sgvasize / VAX_NBPG) * sizeof(struct pte); 86 if (minptalign != 0) { 87 if (minptalign < ptsize) 88 minptalign = ptsize; 89 } else 90 minptalign = ptsize; 91 if (bus_dmamem_alloc(t, ptsize, minptalign, 0, &seg, 1, &rseg, 92 BUS_DMA_NOWAIT)) { 93 panic("unable to allocate page table for sgmap `%s'\n", 94 name); 95 goto die; 96 } 97 sgmap->aps_pt = (struct pte *)(seg.ds_addr | KERNBASE); 98 } 99 100 /* 101 * Create the extent map used to manage the virtual address 102 * space. 103 */ 104 sgmap->aps_ex = extent_create((char *)name, sgvabase, sgvasize - 1, 105 M_DMAMAP, NULL, 0, EX_NOWAIT|EX_NOCOALESCE); 106 if (sgmap->aps_ex == NULL) { 107 printf("unable to create extent map for sgmap `%s'\n", name); 108 goto die; 109 } 110 111 return; 112 die: 113 panic("vax_sgmap_init"); 114 } 115 116 int 117 vax_sgmap_alloc(map, origlen, sgmap, flags) 118 bus_dmamap_t map; 119 bus_size_t origlen; 120 struct vax_sgmap *sgmap; 121 int flags; 122 { 123 int error; 124 bus_size_t len = origlen; 125 126 #ifdef DIAGNOSTIC 127 if (map->_dm_flags & DMAMAP_HAS_SGMAP) 128 panic("vax_sgmap_alloc: already have sgva space"); 129 #endif 130 131 /* If we need a spill page (for the VS4000 SCSI), make sure we 132 * allocate enough space for an extra page. 133 */ 134 if (flags & VAX_BUS_DMA_SPILLPAGE) { 135 len += VAX_NBPG; 136 } 137 138 map->_dm_sgvalen = vax_round_page(len); 139 #if 0 140 printf("len %x -> %x, _dm_sgvalen %x _dm_boundary %x boundary %x -> ", 141 origlen, len, map->_dm_sgvalen, map->_dm_boundary, boundary); 142 #endif 143 144 error = extent_alloc(sgmap->aps_ex, map->_dm_sgvalen, VAX_NBPG, 145 0, (flags & BUS_DMA_NOWAIT) ? EX_NOWAIT : EX_WAITOK, 146 &map->_dm_sgva); 147 #if 0 148 printf("error %d _dm_sgva %x\n", error, map->_dm_sgva); 149 #endif 150 151 if (error == 0) 152 map->_dm_flags |= DMAMAP_HAS_SGMAP; 153 else 154 map->_dm_flags &= ~DMAMAP_HAS_SGMAP; 155 156 return (error); 157 } 158 159 void 160 vax_sgmap_free(map, sgmap) 161 bus_dmamap_t map; 162 struct vax_sgmap *sgmap; 163 { 164 165 #ifdef DIAGNOSTIC 166 if ((map->_dm_flags & DMAMAP_HAS_SGMAP) == 0) 167 panic("vax_sgmap_free: no sgva space to free"); 168 #endif 169 170 if (extent_free(sgmap->aps_ex, map->_dm_sgva, map->_dm_sgvalen, 171 EX_NOWAIT)) 172 panic("vax_sgmap_free"); 173 174 map->_dm_flags &= ~DMAMAP_HAS_SGMAP; 175 } 176 177 int 178 vax_sgmap_load(t, map, buf, buflen, p, flags, sgmap) 179 bus_dma_tag_t t; 180 bus_dmamap_t map; 181 void *buf; 182 bus_size_t buflen; 183 struct proc *p; 184 int flags; 185 struct vax_sgmap *sgmap; 186 { 187 vaddr_t endva, va = (vaddr_t)buf; 188 paddr_t pa; 189 bus_addr_t dmaoffset; 190 bus_size_t dmalen; 191 long *pte, *page_table = (long *)sgmap->aps_pt; 192 int pteidx, error; 193 194 /* 195 * Make sure that on error condition we return "no valid mappings". 196 */ 197 map->dm_mapsize = 0; 198 map->dm_nsegs = 0; 199 200 if (buflen > map->_dm_size) 201 return (EINVAL); 202 203 /* 204 * Remember the offset into the first page and the total 205 * transfer length. 206 */ 207 dmaoffset = ((u_long)buf) & VAX_PGOFSET; 208 dmalen = buflen; 209 210 211 /* 212 * Allocate the necessary virtual address space for the 213 * mapping. Round the size, since we deal with whole pages. 214 */ 215 endva = vax_round_page(va + buflen); 216 va = vax_trunc_page(va); 217 if ((map->_dm_flags & DMAMAP_HAS_SGMAP) == 0) { 218 error = vax_sgmap_alloc(map, (endva - va), sgmap, flags); 219 if (error) 220 return (error); 221 } 222 223 pteidx = map->_dm_sgva >> VAX_PGSHIFT; 224 pte = &page_table[pteidx]; 225 226 /* 227 * Generate the DMA address. 228 */ 229 map->dm_segs[0].ds_addr = map->_dm_sgva + dmaoffset; 230 map->dm_segs[0].ds_len = dmalen; 231 232 233 map->_dm_pteidx = pteidx; 234 map->_dm_ptecnt = 0; 235 236 /* 237 * Create the bus-specific page tables. 238 * Can be done much more efficient than this. 239 */ 240 for (; va < endva; va += VAX_NBPG, pte++, map->_dm_ptecnt++) { 241 /* 242 * Get the physical address for this segment. 243 */ 244 if (p != NULL) 245 (void) pmap_extract(p->p_vmspace->vm_map.pmap, va, &pa); 246 else 247 pa = kvtophys(va); 248 249 /* 250 * Load the current PTE with this page. 251 */ 252 *pte = (pa >> VAX_PGSHIFT) | PG_V; 253 } 254 /* The VS4000 SCSI prefetcher doesn't like to end on a page boundary 255 * so add an extra page to quiet it down. 256 */ 257 if (flags & VAX_BUS_DMA_SPILLPAGE) { 258 *pte = pte[-1]; 259 map->_dm_ptecnt++; 260 } 261 262 map->dm_mapsize = buflen; 263 map->dm_nsegs = 1; 264 return (0); 265 } 266 267 int 268 vax_sgmap_load_mbuf(t, map, m, flags, sgmap) 269 bus_dma_tag_t t; 270 bus_dmamap_t map; 271 struct mbuf *m; 272 int flags; 273 struct vax_sgmap *sgmap; 274 { 275 276 panic("vax_sgmap_load_mbuf : not implemented"); 277 } 278 279 int 280 vax_sgmap_load_uio(t, map, uio, flags, sgmap) 281 bus_dma_tag_t t; 282 bus_dmamap_t map; 283 struct uio *uio; 284 int flags; 285 struct vax_sgmap *sgmap; 286 { 287 288 panic("vax_sgmap_load_uio : not implemented"); 289 } 290 291 int 292 vax_sgmap_load_raw(t, map, segs, nsegs, size, flags, sgmap) 293 bus_dma_tag_t t; 294 bus_dmamap_t map; 295 bus_dma_segment_t *segs; 296 int nsegs; 297 bus_size_t size; 298 int flags; 299 struct vax_sgmap *sgmap; 300 { 301 302 panic("vax_sgmap_load_raw : not implemented"); 303 } 304 305 void 306 vax_sgmap_unload(t, map, sgmap) 307 bus_dma_tag_t t; 308 bus_dmamap_t map; 309 struct vax_sgmap *sgmap; 310 { 311 long *pte, *page_table = (long *)sgmap->aps_pt; 312 int ptecnt; 313 314 /* 315 * Invalidate the PTEs for the mapping. 316 */ 317 for (ptecnt = map->_dm_ptecnt, pte = &page_table[map->_dm_pteidx]; 318 ptecnt-- != 0; ) { 319 *pte++ = 0; 320 } 321 322 /* 323 * Free the virtual address space used by the mapping 324 * if necessary. 325 */ 326 if ((map->_dm_flags & BUS_DMA_ALLOCNOW) == 0) 327 vax_sgmap_free(map, sgmap); 328 /* 329 * Mark the mapping invalid. 330 */ 331 map->dm_mapsize = 0; 332 map->dm_nsegs = 0; 333 } 334