cbus_dma.c revision 46870
1/*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 37 * $Id: isa_dma.c,v 1.2 1999/04/21 12:17:00 kato Exp $ 38 */ 39 40/* 41 * code to manage AT bus 42 * 43 * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): 44 * Fixed uninitialized variable problem and added code to deal 45 * with DMA page boundaries in isa_dmarangecheck(). Fixed word 46 * mode DMA count compution and reorganized DMA setup code in 47 * isa_dmastart() 48 */ 49 50#ifdef PC98 51#include "opt_pc98.h" 52#endif 53 54#include <sys/param.h> 55#include <sys/systm.h> 56#include <sys/buf.h> /* B_READ and B_RAW */ 57#include <sys/malloc.h> 58#include <vm/vm.h> 59#include <vm/vm_param.h> 60#include <vm/pmap.h> 61#ifdef PC98 62#include <pc98/pc98/pc98.h> 63#else 64#include <i386/isa/isa.h> 65#endif 66#include <i386/isa/isa_dma.h> 67#include <i386/isa/ic/i8237.h> 68 69/* 70** Register definitions for DMA controller 1 (channels 0..3): 71*/ 72#ifdef PC98 73#define DMA1_CHN(c) (IO_DMA + (4*(c))) /* addr reg for channel c */ 74#define DMA1_SMSK (IO_DMA + 0x14) /* single mask register */ 75#define DMA1_MODE (IO_DMA + 0x16) /* mode register */ 76#define DMA1_FFC (IO_DMA + 0x18) /* clear first/last FF */ 77#else 78#define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ 79#define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ 80#define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ 81#define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ 82#endif 83 84/* 85** Register definitions for DMA controller 2 (channels 4..7): 86*/ 87#define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ 88#define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ 89#define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ 90#define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ 91 92static int isa_dmarangecheck __P((caddr_t va, u_int length, int chan)); 93 94#ifdef PC98 95static caddr_t dma_bouncebuf[4]; 96static u_int dma_bouncebufsize[4]; 97#else 98static caddr_t dma_bouncebuf[8]; 99static u_int dma_bouncebufsize[8]; 100#endif 101static u_int8_t dma_bounced = 0; 102static u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ 103static u_int8_t dma_inuse = 0; /* User for acquire/release */ 104static u_int8_t dma_auto_mode = 0; 105 106#ifdef PC98 107#define VALID_DMA_MASK (3) 108#else 109#define VALID_DMA_MASK (7) 110#endif 111 112/* high byte of address is stored in this port for i-th dma channel */ 113#ifdef PC98 114static int dmapageport[8] = { 0x27, 0x21, 0x23, 0x25 }; 115#else 116static int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 117#endif 118 119/* 120 * Setup a DMA channel's bounce buffer. 121 */ 122void 123isa_dmainit(chan, bouncebufsize) 124 int chan; 125 u_int bouncebufsize; 126{ 127 void *buf; 128 129#ifdef DIAGNOSTIC 130 if (chan & ~VALID_DMA_MASK) 131 panic("isa_dmainit: channel out of range"); 132 133 if (dma_bouncebuf[chan] != NULL) 134 panic("isa_dmainit: impossible request"); 135#endif 136 137 dma_bouncebufsize[chan] = bouncebufsize; 138 139 /* Try malloc() first. It works better if it works. */ 140 buf = malloc(bouncebufsize, M_DEVBUF, M_NOWAIT); 141 if (buf != NULL) { 142 if (isa_dmarangecheck(buf, bouncebufsize, chan) == 0) { 143 dma_bouncebuf[chan] = buf; 144 return; 145 } 146 free(buf, M_DEVBUF); 147 } 148 buf = contigmalloc(bouncebufsize, M_DEVBUF, M_NOWAIT, 0ul, 0xfffffful, 149 1ul, chan & 4 ? 0x20000ul : 0x10000ul); 150 if (buf == NULL) 151 printf("isa_dmainit(%d, %d) failed\n", chan, bouncebufsize); 152 else 153 dma_bouncebuf[chan] = buf; 154} 155 156/* 157 * Register a DMA channel's usage. Usually called from a device driver 158 * in open() or during its initialization. 159 */ 160int 161isa_dma_acquire(chan) 162 int chan; 163{ 164#ifdef DIAGNOSTIC 165 if (chan & ~VALID_DMA_MASK) 166 panic("isa_dma_acquire: channel out of range"); 167#endif 168 169 if (dma_inuse & (1 << chan)) { 170 printf("isa_dma_acquire: channel %d already in use\n", chan); 171 return (EBUSY); 172 } 173 dma_inuse |= (1 << chan); 174 dma_auto_mode &= ~(1 << chan); 175 176 return (0); 177} 178 179/* 180 * Unregister a DMA channel's usage. Usually called from a device driver 181 * during close() or during its shutdown. 182 */ 183void 184isa_dma_release(chan) 185 int chan; 186{ 187#ifdef DIAGNOSTIC 188 if (chan & ~VALID_DMA_MASK) 189 panic("isa_dma_release: channel out of range"); 190 191 if ((dma_inuse & (1 << chan)) == 0) 192 printf("isa_dma_release: channel %d not in use\n", chan); 193#endif 194 195 if (dma_busy & (1 << chan)) { 196 dma_busy &= ~(1 << chan); 197 /* 198 * XXX We should also do "dma_bounced &= (1 << chan);" 199 * because we are acting on behalf of isa_dmadone() which 200 * was not called to end the last DMA operation. This does 201 * not matter now, but it may in the future. 202 */ 203 } 204 205 dma_inuse &= ~(1 << chan); 206 dma_auto_mode &= ~(1 << chan); 207} 208 209#ifndef PC98 210/* 211 * isa_dmacascade(): program 8237 DMA controller channel to accept 212 * external dma control by a board. 213 */ 214void 215isa_dmacascade(chan) 216 int chan; 217{ 218#ifdef DIAGNOSTIC 219 if (chan & ~VALID_DMA_MASK) 220 panic("isa_dmacascade: channel out of range"); 221#endif 222 223 /* set dma channel mode, and set dma channel mode */ 224 if ((chan & 4) == 0) { 225 outb(DMA1_MODE, DMA37MD_CASCADE | chan); 226 outb(DMA1_SMSK, chan); 227 } else { 228 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 229 outb(DMA2_SMSK, chan & 3); 230 } 231} 232#endif 233 234/* 235 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 236 * problems by using a bounce buffer. 237 */ 238void 239isa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) 240{ 241 vm_offset_t phys; 242 int waport; 243 caddr_t newaddr; 244 245#ifdef DIAGNOSTIC 246 if (chan & ~VALID_DMA_MASK) 247 panic("isa_dmastart: channel out of range"); 248 249 if ((chan < 4 && nbytes > (1<<16)) 250 || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) 251 panic("isa_dmastart: impossible request"); 252 253 if ((dma_inuse & (1 << chan)) == 0) 254 printf("isa_dmastart: channel %d not acquired\n", chan); 255#endif 256 257#if 0 258 /* 259 * XXX This should be checked, but drivers like ad1848 only call 260 * isa_dmastart() once because they use Auto DMA mode. If we 261 * leave this in, drivers that do this will print this continuously. 262 */ 263 if (dma_busy & (1 << chan)) 264 printf("isa_dmastart: channel %d busy\n", chan); 265#endif 266 267 dma_busy |= (1 << chan); 268 269 if (isa_dmarangecheck(addr, nbytes, chan)) { 270 if (dma_bouncebuf[chan] == NULL 271 || dma_bouncebufsize[chan] < nbytes) 272 panic("isa_dmastart: bad bounce buffer"); 273 dma_bounced |= (1 << chan); 274 newaddr = dma_bouncebuf[chan]; 275 276 /* copy bounce buffer on write */ 277 if (!(flags & B_READ)) 278 bcopy(addr, newaddr, nbytes); 279 addr = newaddr; 280 } 281 282 /* translate to physical */ 283 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 284 285 if (flags & B_RAW) { 286 dma_auto_mode |= (1 << chan); 287 } else { 288 dma_auto_mode &= ~(1 << chan); 289 } 290 291#ifndef PC98 292 if ((chan & 4) == 0) { 293 /* 294 * Program one of DMA channels 0..3. These are 295 * byte mode channels. 296 */ 297#endif 298 /* set dma channel mode, and reset address ff */ 299 300 /* If B_RAW flag is set, then use autoinitialise mode */ 301 if (flags & B_RAW) { 302 if (flags & B_READ) 303 outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); 304 else 305 outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); 306 } 307 else 308 if (flags & B_READ) 309 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 310 else 311 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 312 outb(DMA1_FFC, 0); 313 314 /* send start address */ 315 waport = DMA1_CHN(chan); 316 outb(waport, phys); 317 outb(waport, phys>>8); 318 outb(dmapageport[chan], phys>>16); 319 320 /* send count */ 321 outb(waport + 1, --nbytes); 322 outb(waport + 1, nbytes>>8); 323 324 /* unmask channel */ 325 outb(DMA1_SMSK, chan); 326#ifndef PC98 327 } else { 328 /* 329 * Program one of DMA channels 4..7. These are 330 * word mode channels. 331 */ 332 /* set dma channel mode, and reset address ff */ 333 334 /* If B_RAW flag is set, then use autoinitialise mode */ 335 if (flags & B_RAW) { 336 if (flags & B_READ) 337 outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); 338 else 339 outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); 340 } 341 else 342 if (flags & B_READ) 343 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 344 else 345 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 346 outb(DMA2_FFC, 0); 347 348 /* send start address */ 349 waport = DMA2_CHN(chan - 4); 350 outb(waport, phys>>1); 351 outb(waport, phys>>9); 352 outb(dmapageport[chan], phys>>16); 353 354 /* send count */ 355 nbytes >>= 1; 356 outb(waport + 2, --nbytes); 357 outb(waport + 2, nbytes>>8); 358 359 /* unmask channel */ 360 outb(DMA2_SMSK, chan & 3); 361 } 362#endif 363} 364 365void 366isa_dmadone(int flags, caddr_t addr, int nbytes, int chan) 367{ 368#ifdef DIAGNOSTIC 369 if (chan & ~VALID_DMA_MASK) 370 panic("isa_dmadone: channel out of range"); 371 372 if ((dma_inuse & (1 << chan)) == 0) 373 printf("isa_dmadone: channel %d not acquired\n", chan); 374#endif 375 376 if (((dma_busy & (1 << chan)) == 0) && 377 (dma_auto_mode & (1 << chan)) == 0 ) 378 printf("isa_dmadone: channel %d not busy\n", chan); 379 380#ifdef PC98 381 if ((dma_auto_mode & (1 << chan)) == 0) 382 outb(DMA1_SMSK, (chan & 3) | 4); 383#else 384 if ((dma_auto_mode & (1 << chan)) == 0) 385 outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); 386#endif 387 388 if (dma_bounced & (1 << chan)) { 389 /* copy bounce buffer on read */ 390 if (flags & B_READ) 391 bcopy(dma_bouncebuf[chan], addr, nbytes); 392 393 dma_bounced &= ~(1 << chan); 394 } 395 dma_busy &= ~(1 << chan); 396} 397 398/* 399 * Check for problems with the address range of a DMA transfer 400 * (non-contiguous physical pages, outside of bus address space, 401 * crossing DMA page boundaries). 402 * Return true if special handling needed. 403 */ 404 405static int 406isa_dmarangecheck(caddr_t va, u_int length, int chan) 407{ 408 vm_offset_t phys, priorpage = 0, endva; 409 u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 410 411 endva = (vm_offset_t)round_page((vm_offset_t)va + length); 412 for (; va < (caddr_t) endva ; va += PAGE_SIZE) { 413 phys = trunc_page(pmap_extract(pmap_kernel(), (vm_offset_t)va)); 414#ifdef EPSON_BOUNCEDMA 415#define ISARAM_END 0xf00000 416#else 417#define ISARAM_END RAM_END 418#endif 419 if (phys == 0) 420 panic("isa_dmacheck: no physical page present"); 421 if (phys >= ISARAM_END) 422 return (1); 423 if (priorpage) { 424 if (priorpage + PAGE_SIZE != phys) 425 return (1); 426 /* check if crossing a DMA page boundary */ 427 if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) 428 return (1); 429 } 430 priorpage = phys; 431 } 432 return (0); 433} 434 435/* 436 * Query the progress of a transfer on a DMA channel. 437 * 438 * To avoid having to interrupt a transfer in progress, we sample 439 * each of the high and low databytes twice, and apply the following 440 * logic to determine the correct count. 441 * 442 * Reads are performed with interrupts disabled, thus it is to be 443 * expected that the time between reads is very small. At most 444 * one rollover in the low count byte can be expected within the 445 * four reads that are performed. 446 * 447 * There are three gaps in which a rollover can occur : 448 * 449 * - read low1 450 * gap1 451 * - read high1 452 * gap2 453 * - read low2 454 * gap3 455 * - read high2 456 * 457 * If a rollover occurs in gap1 or gap2, the low2 value will be 458 * greater than the low1 value. In this case, low2 and high2 are a 459 * corresponding pair. 460 * 461 * In any other case, low1 and high1 can be considered to be correct. 462 * 463 * The function returns the number of bytes remaining in the transfer, 464 * or -1 if the channel requested is not active. 465 * 466 */ 467int 468isa_dmastatus(int chan) 469{ 470 u_long cnt = 0; 471 int ffport, waport; 472 u_long low1, high1, low2, high2; 473 474 /* channel active? */ 475 if ((dma_inuse & (1 << chan)) == 0) { 476 printf("isa_dmastatus: channel %d not active\n", chan); 477 return(-1); 478 } 479 /* channel busy? */ 480 481 if (((dma_busy & (1 << chan)) == 0) && 482 (dma_auto_mode & (1 << chan)) == 0 ) { 483 printf("chan %d not busy\n", chan); 484 return -2 ; 485 } 486#ifdef PC98 487 ffport = DMA1_FFC; 488 waport = DMA1_CHN(chan) + 2; 489#else 490 if (chan < 4) { /* low DMA controller */ 491 ffport = DMA1_FFC; 492 waport = DMA1_CHN(chan) + 1; 493 } else { /* high DMA controller */ 494 ffport = DMA2_FFC; 495 waport = DMA2_CHN(chan - 4) + 2; 496 } 497#endif 498 499 disable_intr(); /* no interrupts Mr Jones! */ 500 outb(ffport, 0); /* clear register LSB flipflop */ 501 low1 = inb(waport); 502 high1 = inb(waport); 503 outb(ffport, 0); /* clear again */ 504 low2 = inb(waport); 505 high2 = inb(waport); 506 enable_intr(); /* enable interrupts again */ 507 508 /* 509 * Now decide if a wrap has tried to skew our results. 510 * Note that after TC, the count will read 0xffff, while we want 511 * to return zero, so we add and then mask to compensate. 512 */ 513 if (low1 >= low2) { 514 cnt = (low1 + (high1 << 8) + 1) & 0xffff; 515 } else { 516 cnt = (low2 + (high2 << 8) + 1) & 0xffff; 517 } 518 519 if (chan >= 4) /* high channels move words */ 520 cnt *= 2; 521 return(cnt); 522} 523 524/* 525 * Stop a DMA transfer currently in progress. 526 */ 527int 528isa_dmastop(int chan) 529{ 530 if ((dma_inuse & (1 << chan)) == 0) 531 printf("isa_dmastop: channel %d not acquired\n", chan); 532 533 if (((dma_busy & (1 << chan)) == 0) && 534 ((dma_auto_mode & (1 << chan)) == 0)) { 535 printf("chan %d not busy\n", chan); 536 return -2 ; 537 } 538 539 if ((chan & 4) == 0) { 540 outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); 541 } else { 542#ifndef PC98 543 outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); 544#endif 545 } 546 return(isa_dmastatus(chan)); 547} 548