dvma.c revision 1.35
1/* $NetBSD: dvma.c,v 1.35 2007/02/21 22:59:54 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Gordon W. Ross and Jeremy Cooper. 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39/* 40 * DVMA (Direct Virtual Memory Access - like DMA) 41 * 42 * In the Sun3 architecture, memory cycles initiated by secondary bus 43 * masters (DVMA devices) passed through the same MMU that governed CPU 44 * accesses. All DVMA devices were wired in such a way so that an offset 45 * was added to the addresses they issued, causing them to access virtual 46 * memory starting at address 0x0FF00000 - the offset. The task of 47 * enabling a DVMA device to access main memory only involved creating 48 * valid mapping in the MMU that translated these high addresses into the 49 * appropriate physical addresses. 50 * 51 * The Sun3x presents a challenge to programming DVMA because the MMU is no 52 * longer shared by both secondary bus masters and the CPU. The MC68030's 53 * built-in MMU serves only to manage virtual memory accesses initiated by 54 * the CPU. Secondary bus master bus accesses pass through a different MMU, 55 * aptly named the 'I/O Mapper'. To enable every device driver that uses 56 * DVMA to understand that these two address spaces are disconnected would 57 * require a tremendous amount of code re-writing. To avoid this, we will 58 * ensure that the I/O Mapper and the MC68030 MMU are programmed together, 59 * so that DVMA mappings are consistent in both the CPU virtual address 60 * space and secondary bus master address space - creating an environment 61 * just like the Sun3 system. 62 * 63 * The maximum address space that any DVMA device in the Sun3x architecture 64 * is capable of addressing is 24 bits wide (16 Megabytes.) We can alias 65 * all of the mappings that exist in the I/O mapper by duplicating them in 66 * a specially reserved section of the CPU's virtual address space, 16 67 * Megabytes in size. Whenever a DVMA buffer is allocated, the allocation 68 * code will enter in a mapping both in the MC68030 MMU page tables and the 69 * I/O mapper. 70 * 71 * The address returned by the allocation routine is a virtual address that 72 * the requesting driver must use to access the buffer. It is up to the 73 * device driver to convert this virtual address into the appropriate slave 74 * address that its device should issue to access the buffer. (There will be 75 * routines that assist the driver in doing so.) 76 */ 77 78#include <sys/cdefs.h> 79__KERNEL_RCSID(0, "$NetBSD: dvma.c,v 1.35 2007/02/21 22:59:54 thorpej Exp $"); 80 81#include <sys/param.h> 82#include <sys/systm.h> 83#include <sys/device.h> 84#include <sys/proc.h> 85#include <sys/malloc.h> 86#include <sys/extent.h> 87#include <sys/buf.h> 88#include <sys/vnode.h> 89#include <sys/user.h> 90#include <sys/core.h> 91#include <sys/exec.h> 92 93#include <uvm/uvm_extern.h> 94 95#define _SUN68K_BUS_DMA_PRIVATE 96#include <machine/autoconf.h> 97#include <machine/bus.h> 98#include <machine/cpu.h> 99#include <machine/dvma.h> 100#include <machine/pmap.h> 101 102#include <sun3/sun3/machdep.h> 103 104#include <sun3/sun3x/enable.h> 105#include <sun3/sun3x/iommu.h> 106 107/* 108 * Use an extent map to manage DVMA scratch-memory pages. 109 * Note: SunOS says last three pages are reserved (PROM?) 110 * Note: need a separate map (sub-map?) for last 1MB for 111 * use by VME slave interface. 112 */ 113 114/* Number of slots in dvmamap. */ 115struct extent *dvma_extent; 116 117void 118dvma_init(void) 119{ 120 121 /* 122 * Create the extent map for DVMA pages. 123 */ 124 dvma_extent = extent_create("dvma", DVMA_MAP_BASE, 125 DVMA_MAP_BASE + (DVMA_MAP_AVAIL - 1), M_DEVBUF, 126 NULL, 0, EX_NOCOALESCE|EX_NOWAIT); 127 128 /* 129 * Enable DVMA in the System Enable register. 130 * Note: This is only necessary for VME slave accesses. 131 * On-board devices are always capable of DVMA. 132 */ 133 *enable_reg |= ENA_SDVMA; 134} 135 136 137/* 138 * Given a DVMA address, return the physical address that 139 * would be used by some OTHER bus-master besides the CPU. 140 * (Examples: on-board ie/le, VME xy board). 141 */ 142u_long 143dvma_kvtopa(void *kva, int bustype) 144{ 145 u_long addr, mask; 146 147 addr = (u_long)kva; 148 if ((addr & DVMA_MAP_BASE) != DVMA_MAP_BASE) 149 panic("dvma_kvtopa: bad dmva addr=0x%lx", addr); 150 151 switch (bustype) { 152 case BUS_OBIO: 153 case BUS_OBMEM: 154 mask = DVMA_OBIO_SLAVE_MASK; 155 break; 156 default: /* VME bus device. */ 157 mask = DVMA_VME_SLAVE_MASK; 158 break; 159 } 160 161 return addr & mask; 162} 163 164 165/* 166 * Map a range [va, va+len] of wired virtual addresses in the given map 167 * to a kernel address in DVMA space. 168 */ 169void * 170dvma_mapin(void *kmem_va, int len, int canwait) 171{ 172 void * dvma_addr; 173 vaddr_t kva, tva; 174 int npf, s, error; 175 paddr_t pa; 176 long off; 177 bool rv; 178 179 kva = (vaddr_t)kmem_va; 180#ifdef DIAGNOSTIC 181 /* 182 * Addresses below VM_MIN_KERNEL_ADDRESS are not part of the kernel 183 * map and should not participate in DVMA. 184 */ 185 if (kva < VM_MIN_KERNEL_ADDRESS) 186 panic("dvma_mapin: bad kva"); 187#endif 188 189 /* 190 * Calculate the offset of the data buffer from a page boundary. 191 */ 192 off = kva & PGOFSET; 193 kva -= off; /* Truncate starting address to nearest page. */ 194 len = round_page(len + off); /* Round the buffer length to pages. */ 195 npf = btoc(len); /* Determine the number of pages to be mapped. */ 196 197 /* 198 * Try to allocate DVMA space of the appropriate size 199 * in which to do a transfer. 200 */ 201 s = splvm(); 202 error = extent_alloc(dvma_extent, len, PAGE_SIZE, 0, 203 EX_FAST | EX_NOWAIT | (canwait ? EX_WAITSPACE : 0), &tva); 204 splx(s); 205 if (error) 206 return NULL; 207 208 /* 209 * Tva is the starting page to which the data buffer will be double 210 * mapped. Dvma_addr is the starting address of the buffer within 211 * that page and is the return value of the function. 212 */ 213 dvma_addr = (void *)(tva + off); 214 215 for (; npf--; kva += PAGE_SIZE, tva += PAGE_SIZE) { 216 /* 217 * Retrieve the physical address of each page in the buffer 218 * and enter mappings into the I/O MMU so they may be seen 219 * by external bus masters and into the special DVMA space 220 * in the MC68030 MMU so they may be seen by the CPU. 221 */ 222 rv = pmap_extract(pmap_kernel(), kva, &pa); 223#ifdef DEBUG 224 if (rv == FALSE) 225 panic("dvma_mapin: null page frame"); 226#endif /* DEBUG */ 227 228 iommu_enter((tva & IOMMU_VA_MASK), pa); 229 pmap_kenter_pa(tva, pa | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE); 230 } 231 pmap_update(pmap_kernel()); 232 233 return dvma_addr; 234} 235 236/* 237 * Remove double map of `va' in DVMA space at `kva'. 238 * 239 * TODO - This function might be the perfect place to handle the 240 * synchronization between the DVMA cache and central RAM 241 * on the 3/470. 242 */ 243void 244dvma_mapout(void *dvma_addr, int len) 245{ 246 u_long kva; 247 int s, off; 248 249 kva = (u_long)dvma_addr; 250 off = (int)kva & PGOFSET; 251 kva -= off; 252 len = round_page(len + off); 253 254 iommu_remove((kva & IOMMU_VA_MASK), len); 255 pmap_kremove(kva, len); 256 pmap_update(pmap_kernel()); 257 258 s = splvm(); 259 if (extent_free(dvma_extent, kva, len, EX_NOWAIT | EX_MALLOCOK)) 260 panic("dvma_mapout: unable to free region: 0x%lx,0x%x", 261 kva, len); 262 splx(s); 263} 264 265/* 266 * Allocate actual memory pages in DVMA space. 267 * (For sun3 compatibility - the ie driver.) 268 */ 269void * 270dvma_malloc(size_t bytes) 271{ 272 void *new_mem, *dvma_mem; 273 vsize_t new_size; 274 275 if (bytes == 0) 276 return NULL; 277 new_size = m68k_round_page(bytes); 278 new_mem = (void *)uvm_km_alloc(kernel_map, new_size, 0, UVM_KMF_WIRED); 279 if (new_mem == 0) 280 return NULL; 281 dvma_mem = dvma_mapin(new_mem, new_size, 1); 282 return dvma_mem; 283} 284 285/* 286 * Free pages from dvma_malloc() 287 */ 288void 289dvma_free(void *addr, size_t size) 290{ 291 vsize_t sz = m68k_round_page(size); 292 293 dvma_mapout(addr, sz); 294 /* XXX: need kmem address to free it... 295 Oh well, we never call this anyway. */ 296} 297 298int 299_bus_dmamap_load_raw(bus_dma_tag_t t, bus_dmamap_t map, bus_dma_segment_t *segs, 300 int nsegs, bus_size_t size, int flags) 301{ 302 303 panic("_bus_dmamap_load_raw(): not implemented yet."); 304} 305 306int 307_bus_dmamap_load(bus_dma_tag_t t, bus_dmamap_t map, void *buf, 308 bus_size_t buflen, struct proc *p, int flags) 309{ 310 vaddr_t kva, dva; 311 vsize_t off, sgsize; 312 paddr_t pa; 313 pmap_t pmap; 314 int error, rv, s; 315 316 /* 317 * Make sure that on error condition we return "no valid mappings". 318 */ 319 map->dm_nsegs = 0; 320 map->dm_mapsize = 0; 321 322 if (buflen > map->_dm_size) 323 return EINVAL; 324 325 kva = (vaddr_t)buf; 326 off = kva & PGOFSET; 327 sgsize = round_page(off + buflen); 328 329 /* Try to allocate DVMA space. */ 330 s = splvm(); 331 error = extent_alloc(dvma_extent, sgsize, PAGE_SIZE, 0, 332 EX_FAST | ((flags & BUS_DMA_NOWAIT) == 0 ? EX_WAITOK : EX_NOWAIT), 333 &dva); 334 splx(s); 335 if (error) 336 return ENOMEM; 337 338 /* Fill in the segment. */ 339 map->dm_segs[0].ds_addr = dva + off; 340 map->dm_segs[0].ds_len = buflen; 341 map->dm_segs[0]._ds_va = dva; 342 map->dm_segs[0]._ds_sgsize = sgsize; 343 344 /* 345 * Now map the DVMA addresses we allocated to point to the 346 * pages of the caller's buffer. 347 */ 348 if (p != NULL) 349 pmap = p->p_vmspace->vm_map.pmap; 350 else 351 pmap = pmap_kernel(); 352 353 while (sgsize > 0) { 354 rv = pmap_extract(pmap, kva, &pa); 355#ifdef DIAGNOSTIC 356 if (rv == FALSE) 357 panic("%s: unmapped VA", __func__); 358#endif 359 iommu_enter((dva & IOMMU_VA_MASK), pa); 360 pmap_kenter_pa(dva, pa | PMAP_NC, VM_PROT_READ | VM_PROT_WRITE); 361 kva += PAGE_SIZE; 362 dva += PAGE_SIZE; 363 sgsize -= PAGE_SIZE; 364 } 365 366 map->dm_nsegs = 1; 367 map->dm_mapsize = map->dm_segs[0].ds_len; 368 369 return 0; 370} 371 372void 373_bus_dmamap_unload(bus_dma_tag_t t, bus_dmamap_t map) 374{ 375 bus_dma_segment_t *segs; 376 vaddr_t dva; 377 vsize_t sgsize; 378 int error, s; 379 380#ifdef DIAGNOSTIC 381 if (map->dm_nsegs != 1) 382 panic("%s: invalid nsegs = %d", __func__, map->dm_nsegs); 383#endif 384 385 segs = map->dm_segs; 386 dva = segs[0]._ds_va & ~PGOFSET; 387 sgsize = segs[0]._ds_sgsize; 388 389 /* Unmap the DVMA addresses. */ 390 iommu_remove((dva & IOMMU_VA_MASK), sgsize); 391 pmap_kremove(dva, sgsize); 392 pmap_update(pmap_kernel()); 393 394 /* Free the DVMA addresses. */ 395 s = splvm(); 396 error = extent_free(dvma_extent, dva, sgsize, EX_NOWAIT); 397 splx(s); 398#ifdef DIAGNOSTIC 399 if (error) 400 panic("%s: unable to free DVMA region", __func__); 401#endif 402 403 /* Mark the mappings as invalid. */ 404 map->dm_mapsize = 0; 405 map->dm_nsegs = 0; 406} 407