isa_dma.c revision 233675
1116411Sphk/*- 2116411Sphk * Copyright (c) 1991 The Regents of the University of California. 3116411Sphk * All rights reserved. 4116411Sphk * 5116411Sphk * This code is derived from software contributed to Berkeley by 6116411Sphk * William Jolitz. 7116411Sphk * 8116411Sphk * Redistribution and use in source and binary forms, with or without 9116411Sphk * modification, are permitted provided that the following conditions 10116411Sphk * are met: 11116411Sphk * 1. Redistributions of source code must retain the above copyright 12116411Sphk * notice, this list of conditions and the following disclaimer. 13116411Sphk * 2. Redistributions in binary form must reproduce the above copyright 14116411Sphk * notice, this list of conditions and the following disclaimer in the 15116411Sphk * documentation and/or other materials provided with the distribution. 16116411Sphk * 4. Neither the name of the University nor the names of its contributors 17116411Sphk * may be used to endorse or promote products derived from this software 18116411Sphk * without specific prior written permission. 19116411Sphk * 20116411Sphk * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21116411Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22116411Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23116411Sphk * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24116411Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25116411Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26116411Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27116411Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28116411Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29116411Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30116411Sphk * SUCH DAMAGE. 31116411Sphk * 32116411Sphk * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 33116411Sphk */ 34116411Sphk 35116411Sphk#include <sys/cdefs.h> 36116411Sphk__FBSDID("$FreeBSD: head/sys/x86/isa/isa_dma.c 233675 2012-03-29 18:58:02Z jhb $"); 37116411Sphk 38116411Sphk/* 39116411Sphk * code to manage AT bus 40116411Sphk * 41116411Sphk * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): 42116411Sphk * Fixed uninitialized variable problem and added code to deal 43116411Sphk * with DMA page boundaries in isa_dmarangecheck(). Fixed word 44116411Sphk * mode DMA count compution and reorganized DMA setup code in 45116411Sphk * isa_dmastart() 46116411Sphk */ 47116411Sphk 48116411Sphk#include <sys/param.h> 49116411Sphk#include <sys/systm.h> 50116411Sphk#include <sys/bus.h> 51116411Sphk#include <sys/kernel.h> 52116411Sphk#include <sys/malloc.h> 53116411Sphk#include <sys/lock.h> 54116411Sphk#include <sys/proc.h> 55116411Sphk#include <sys/mutex.h> 56116411Sphk#include <sys/module.h> 57116411Sphk#include <vm/vm.h> 58116411Sphk#include <vm/vm_param.h> 59116411Sphk#include <vm/pmap.h> 60116411Sphk#include <isa/isareg.h> 61116411Sphk#include <isa/isavar.h> 62116411Sphk#include <isa/isa_dmareg.h> 63116411Sphk 64116411Sphk#define ISARAM_END RAM_END 65116411Sphk 66116411Sphkstatic int isa_dmarangecheck(caddr_t va, u_int length, int chan); 67116411Sphk 68116411Sphkstatic caddr_t dma_bouncebuf[8]; 69116411Sphkstatic u_int dma_bouncebufsize[8]; 70116411Sphkstatic u_int8_t dma_bounced = 0; 71116411Sphkstatic u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ 72116411Sphkstatic u_int8_t dma_inuse = 0; /* User for acquire/release */ 73116411Sphkstatic u_int8_t dma_auto_mode = 0; 74116411Sphkstatic struct mtx isa_dma_lock; 75160964SyarMTX_SYSINIT(isa_dma_lock, &isa_dma_lock, "isa DMA lock", MTX_DEF); 76116411Sphk 77116411Sphk#define VALID_DMA_MASK (7) 78116411Sphk 79116411Sphk/* high byte of address is stored in this port for i-th dma channel */ 80116411Sphkstatic int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 81116411Sphk 82116411Sphk/* 83116411Sphk * Setup a DMA channel's bounce buffer. 84116411Sphk */ 85116411Sphkint 86116411Sphkisa_dma_init(int chan, u_int bouncebufsize, int flag) 87116411Sphk{ 88116411Sphk void *buf; 89116411Sphk int contig; 90116411Sphk 91116411Sphk#ifdef DIAGNOSTIC 92116411Sphk if (chan & ~VALID_DMA_MASK) 93116411Sphk panic("isa_dma_init: channel out of range"); 94116411Sphk#endif 95116411Sphk 96116411Sphk 97116411Sphk /* Try malloc() first. It works better if it works. */ 98116411Sphk buf = malloc(bouncebufsize, M_DEVBUF, flag); 99116411Sphk if (buf != NULL) { 100116411Sphk if (isa_dmarangecheck(buf, bouncebufsize, chan) != 0) { 101116411Sphk free(buf, M_DEVBUF); 102119760Sjkoshy buf = NULL; 103116411Sphk } 104116411Sphk contig = 0; 105116411Sphk } 106116411Sphk 107116411Sphk if (buf == NULL) { 108116411Sphk buf = contigmalloc(bouncebufsize, M_DEVBUF, flag, 0ul, 0xfffffful, 109116411Sphk 1ul, chan & 4 ? 0x20000ul : 0x10000ul); 110116411Sphk contig = 1; 111116411Sphk } 112116411Sphk 113119760Sjkoshy if (buf == NULL) 114116411Sphk return (ENOMEM); 115116411Sphk 116116411Sphk mtx_lock(&isa_dma_lock); 117116411Sphk /* 118116411Sphk * If a DMA channel is shared, both drivers have to call isa_dma_init 119116411Sphk * since they don't know that the other driver will do it. 120116411Sphk * Just return if we're already set up good. 121116411Sphk * XXX: this only works if they agree on the bouncebuf size. This 122116411Sphk * XXX: is typically the case since they are multiple instances of 123116411Sphk * XXX: the same driver. 124116411Sphk */ 125116411Sphk if (dma_bouncebuf[chan] != NULL) { 126116411Sphk if (contig) 127116411Sphk contigfree(buf, bouncebufsize, M_DEVBUF); 128116411Sphk else 129116411Sphk free(buf, M_DEVBUF); 130116411Sphk mtx_unlock(&isa_dma_lock); 131116411Sphk return (0); 132116411Sphk } 133116411Sphk 134116411Sphk dma_bouncebufsize[chan] = bouncebufsize; 135116411Sphk dma_bouncebuf[chan] = buf; 136116411Sphk 137116411Sphk mtx_unlock(&isa_dma_lock); 138116411Sphk 139116411Sphk return (0); 140116411Sphk} 141116411Sphk 142116411Sphk/* 143116411Sphk * Register a DMA channel's usage. Usually called from a device driver 144116411Sphk * in open() or during its initialization. 145116411Sphk */ 146116411Sphkint 147116411Sphkisa_dma_acquire(chan) 148116411Sphk int chan; 149116411Sphk{ 150116411Sphk#ifdef DIAGNOSTIC 151116411Sphk if (chan & ~VALID_DMA_MASK) 152116411Sphk panic("isa_dma_acquire: channel out of range"); 153116411Sphk#endif 154116411Sphk 155116411Sphk mtx_lock(&isa_dma_lock); 156116411Sphk if (dma_inuse & (1 << chan)) { 157116411Sphk printf("isa_dma_acquire: channel %d already in use\n", chan); 158116411Sphk mtx_unlock(&isa_dma_lock); 159116411Sphk return (EBUSY); 160116411Sphk } 161116411Sphk dma_inuse |= (1 << chan); 162116411Sphk dma_auto_mode &= ~(1 << chan); 163116411Sphk mtx_unlock(&isa_dma_lock); 164116411Sphk 165116411Sphk return (0); 166116411Sphk} 167116411Sphk 168116411Sphk/* 169116411Sphk * Unregister a DMA channel's usage. Usually called from a device driver 170116411Sphk * during close() or during its shutdown. 171116411Sphk */ 172116411Sphkvoid 173116411Sphkisa_dma_release(chan) 174116411Sphk int chan; 175116411Sphk{ 176116411Sphk#ifdef DIAGNOSTIC 177116411Sphk if (chan & ~VALID_DMA_MASK) 178116411Sphk panic("isa_dma_release: channel out of range"); 179116411Sphk 180116411Sphk mtx_lock(&isa_dma_lock); 181116411Sphk if ((dma_inuse & (1 << chan)) == 0) 182116411Sphk printf("isa_dma_release: channel %d not in use\n", chan); 183116411Sphk#else 184116411Sphk mtx_lock(&isa_dma_lock); 185116411Sphk#endif 186116411Sphk 187116411Sphk if (dma_busy & (1 << chan)) { 188116411Sphk dma_busy &= ~(1 << chan); 189116411Sphk /* 190116411Sphk * XXX We should also do "dma_bounced &= (1 << chan);" 191116411Sphk * because we are acting on behalf of isa_dmadone() which 192116411Sphk * was not called to end the last DMA operation. This does 193116411Sphk * not matter now, but it may in the future. 194116411Sphk */ 195116411Sphk } 196116411Sphk 197116411Sphk dma_inuse &= ~(1 << chan); 198116411Sphk dma_auto_mode &= ~(1 << chan); 199116411Sphk 200116411Sphk mtx_unlock(&isa_dma_lock); 201116411Sphk} 202116411Sphk 203116411Sphk/* 204116411Sphk * isa_dmacascade(): program 8237 DMA controller channel to accept 205116411Sphk * external dma control by a board. 206116411Sphk */ 207116411Sphkvoid 208116411Sphkisa_dmacascade(chan) 209116411Sphk int chan; 210116411Sphk{ 211116411Sphk#ifdef DIAGNOSTIC 212116411Sphk if (chan & ~VALID_DMA_MASK) 213116411Sphk panic("isa_dmacascade: channel out of range"); 214116411Sphk#endif 215116411Sphk 216116411Sphk mtx_lock(&isa_dma_lock); 217116411Sphk /* set dma channel mode, and set dma channel mode */ 218116411Sphk if ((chan & 4) == 0) { 219116411Sphk outb(DMA1_MODE, DMA37MD_CASCADE | chan); 220116411Sphk outb(DMA1_SMSK, chan); 221116411Sphk } else { 222116411Sphk outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 223116411Sphk outb(DMA2_SMSK, chan & 3); 224116411Sphk } 225116411Sphk mtx_unlock(&isa_dma_lock); 226116411Sphk} 227116411Sphk 228116411Sphk/* 229116411Sphk * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 230116411Sphk * problems by using a bounce buffer. 231116411Sphk */ 232116411Sphkvoid 233116411Sphkisa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) 234116411Sphk{ 235116411Sphk vm_paddr_t phys; 236116411Sphk int waport; 237116411Sphk caddr_t newaddr; 238116411Sphk int dma_range_checked; 239116411Sphk 240116411Sphk dma_range_checked = isa_dmarangecheck(addr, nbytes, chan); 241116411Sphk 242116411Sphk#ifdef DIAGNOSTIC 243116411Sphk if (chan & ~VALID_DMA_MASK) 244116411Sphk panic("isa_dmastart: channel out of range"); 245116411Sphk 246116411Sphk if ((chan < 4 && nbytes > (1<<16)) 247116411Sphk || (chan >= 4 && (nbytes > (1<<17) || (uintptr_t)addr & 1))) 248116411Sphk panic("isa_dmastart: impossible request"); 249116411Sphk 250116411Sphk mtx_lock(&isa_dma_lock); 251116411Sphk if ((dma_inuse & (1 << chan)) == 0) 252116411Sphk printf("isa_dmastart: channel %d not acquired\n", chan); 253116411Sphk#else 254119760Sjkoshy mtx_lock(&isa_dma_lock); 255116411Sphk#endif 256116411Sphk 257116411Sphk#if 0 258116411Sphk /* 259116411Sphk * XXX This should be checked, but drivers like ad1848 only call 260116411Sphk * isa_dmastart() once because they use Auto DMA mode. If we 261116411Sphk * leave this in, drivers that do this will print this continuously. 262116411Sphk */ 263116411Sphk if (dma_busy & (1 << chan)) 264116411Sphk printf("isa_dmastart: channel %d busy\n", chan); 265116411Sphk#endif 266116411Sphk 267116411Sphk dma_busy |= (1 << chan); 268116411Sphk 269116411Sphk if (dma_range_checked) { 270116411Sphk if (dma_bouncebuf[chan] == NULL 271116411Sphk || dma_bouncebufsize[chan] < nbytes) 272116411Sphk panic("isa_dmastart: bad bounce buffer"); 273116411Sphk dma_bounced |= (1 << chan); 274116411Sphk newaddr = dma_bouncebuf[chan]; 275119760Sjkoshy 276116411Sphk /* copy bounce buffer on write */ 277116411Sphk if (!(flags & ISADMA_READ)) 278116411Sphk bcopy(addr, newaddr, nbytes); 279116411Sphk addr = newaddr; 280116411Sphk } 281116411Sphk 282116411Sphk /* translate to physical */ 283116411Sphk phys = pmap_extract(kernel_pmap, (vm_offset_t)addr); 284116411Sphk 285116411Sphk if (flags & ISADMA_RAW) { 286116411Sphk dma_auto_mode |= (1 << chan); 287116411Sphk } else { 288116411Sphk dma_auto_mode &= ~(1 << chan); 289116411Sphk } 290116411Sphk 291116411Sphk if ((chan & 4) == 0) { 292116411Sphk /* 293116411Sphk * Program one of DMA channels 0..3. These are 294116411Sphk * byte mode channels. 295116411Sphk */ 296116411Sphk /* set dma channel mode, and reset address ff */ 297116411Sphk 298116411Sphk /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 299116411Sphk if (flags & ISADMA_RAW) { 300116411Sphk if (flags & ISADMA_READ) 301116411Sphk outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); 302116411Sphk else 303116411Sphk outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); 304116411Sphk } 305116411Sphk else 306116411Sphk if (flags & ISADMA_READ) 307116411Sphk outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 308116411Sphk else 309116411Sphk outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 310116411Sphk outb(DMA1_FFC, 0); 311116411Sphk 312116411Sphk /* send start address */ 313116411Sphk waport = DMA1_CHN(chan); 314116411Sphk outb(waport, phys); 315116411Sphk outb(waport, phys>>8); 316116411Sphk outb(dmapageport[chan], phys>>16); 317116411Sphk 318116411Sphk /* send count */ 319116411Sphk outb(waport + 1, --nbytes); 320116411Sphk outb(waport + 1, nbytes>>8); 321116411Sphk 322116411Sphk /* unmask channel */ 323116411Sphk outb(DMA1_SMSK, chan); 324116411Sphk } else { 325116411Sphk /* 326116411Sphk * Program one of DMA channels 4..7. These are 327116411Sphk * word mode channels. 328116411Sphk */ 329116411Sphk /* set dma channel mode, and reset address ff */ 330116411Sphk 331116411Sphk /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 332116411Sphk if (flags & ISADMA_RAW) { 333116411Sphk if (flags & ISADMA_READ) 334116411Sphk outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); 335116411Sphk else 336116411Sphk outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); 337116411Sphk } 338116411Sphk else 339116411Sphk if (flags & ISADMA_READ) 340116411Sphk outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 341116411Sphk else 342116411Sphk outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 343116411Sphk outb(DMA2_FFC, 0); 344116411Sphk 345116411Sphk /* send start address */ 346116411Sphk waport = DMA2_CHN(chan - 4); 347116411Sphk outb(waport, phys>>1); 348116411Sphk outb(waport, phys>>9); 349116411Sphk outb(dmapageport[chan], phys>>16); 350116411Sphk 351116411Sphk /* send count */ 352116411Sphk nbytes >>= 1; 353116411Sphk outb(waport + 2, --nbytes); 354116411Sphk outb(waport + 2, nbytes>>8); 355116411Sphk 356116411Sphk /* unmask channel */ 357116411Sphk outb(DMA2_SMSK, chan & 3); 358116411Sphk } 359116411Sphk mtx_unlock(&isa_dma_lock); 360116411Sphk} 361116411Sphk 362116411Sphkvoid 363116411Sphkisa_dmadone(int flags, caddr_t addr, int nbytes, int chan) 364116411Sphk{ 365116411Sphk#ifdef DIAGNOSTIC 366116411Sphk if (chan & ~VALID_DMA_MASK) 367116411Sphk panic("isa_dmadone: channel out of range"); 368116411Sphk 369116411Sphk if ((dma_inuse & (1 << chan)) == 0) 370116411Sphk printf("isa_dmadone: channel %d not acquired\n", chan); 371116411Sphk#endif 372116411Sphk 373116411Sphk mtx_lock(&isa_dma_lock); 374116411Sphk if (((dma_busy & (1 << chan)) == 0) && 375116411Sphk (dma_auto_mode & (1 << chan)) == 0 ) 376116411Sphk printf("isa_dmadone: channel %d not busy\n", chan); 377116411Sphk 378116411Sphk if ((dma_auto_mode & (1 << chan)) == 0) 379116411Sphk outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); 380116411Sphk 381116411Sphk if (dma_bounced & (1 << chan)) { 382116411Sphk /* copy bounce buffer on read */ 383116411Sphk if (flags & ISADMA_READ) 384116411Sphk bcopy(dma_bouncebuf[chan], addr, nbytes); 385116411Sphk 386116411Sphk dma_bounced &= ~(1 << chan); 387116411Sphk } 388116411Sphk dma_busy &= ~(1 << chan); 389116411Sphk mtx_unlock(&isa_dma_lock); 390116411Sphk} 391116411Sphk 392116411Sphk/* 393116411Sphk * Check for problems with the address range of a DMA transfer 394116411Sphk * (non-contiguous physical pages, outside of bus address space, 395116411Sphk * crossing DMA page boundaries). 396116411Sphk * Return true if special handling needed. 397116411Sphk */ 398116411Sphk 399116411Sphkstatic int 400116411Sphkisa_dmarangecheck(caddr_t va, u_int length, int chan) 401116411Sphk{ 402116411Sphk vm_paddr_t phys, priorpage = 0; 403116411Sphk vm_offset_t endva; 404116411Sphk u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 405116411Sphk 406116411Sphk endva = (vm_offset_t)round_page((vm_offset_t)va + length); 407116411Sphk for (; va < (caddr_t) endva ; va += PAGE_SIZE) { 408116411Sphk phys = trunc_page(pmap_extract(kernel_pmap, (vm_offset_t)va)); 409116411Sphk if (phys == 0) 410116411Sphk panic("isa_dmacheck: no physical page present"); 411116411Sphk if (phys >= ISARAM_END) 412116411Sphk return (1); 413116411Sphk if (priorpage) { 414116411Sphk if (priorpage + PAGE_SIZE != phys) 415116411Sphk return (1); 416116411Sphk /* check if crossing a DMA page boundary */ 417116411Sphk if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) 418116411Sphk return (1); 419116411Sphk } 420116411Sphk priorpage = phys; 421116411Sphk } 422116411Sphk return (0); 423116411Sphk} 424116411Sphk 425116411Sphk/* 426116411Sphk * Query the progress of a transfer on a DMA channel. 427116411Sphk * 428116411Sphk * To avoid having to interrupt a transfer in progress, we sample 429116411Sphk * each of the high and low databytes twice, and apply the following 430116411Sphk * logic to determine the correct count. 431116411Sphk * 432116411Sphk * Reads are performed with interrupts disabled, thus it is to be 433116411Sphk * expected that the time between reads is very small. At most 434116411Sphk * one rollover in the low count byte can be expected within the 435116411Sphk * four reads that are performed. 436116411Sphk * 437116411Sphk * There are three gaps in which a rollover can occur : 438116411Sphk * 439116411Sphk * - read low1 440116411Sphk * gap1 441116411Sphk * - read high1 442116411Sphk * gap2 443116411Sphk * - read low2 444116411Sphk * gap3 445116411Sphk * - read high2 446116411Sphk * 447116411Sphk * If a rollover occurs in gap1 or gap2, the low2 value will be 448116411Sphk * greater than the low1 value. In this case, low2 and high2 are a 449116411Sphk * corresponding pair. 450116411Sphk * 451116411Sphk * In any other case, low1 and high1 can be considered to be correct. 452116411Sphk * 453116411Sphk * The function returns the number of bytes remaining in the transfer, 454116411Sphk * or -1 if the channel requested is not active. 455116411Sphk * 456116411Sphk */ 457116411Sphkstatic int 458116411Sphkisa_dmastatus_locked(int chan) 459116411Sphk{ 460116411Sphk u_long cnt = 0; 461116411Sphk int ffport, waport; 462116411Sphk u_long low1, high1, low2, high2; 463116411Sphk 464116411Sphk mtx_assert(&isa_dma_lock, MA_OWNED); 465116411Sphk 466116411Sphk /* channel active? */ 467116411Sphk if ((dma_inuse & (1 << chan)) == 0) { 468116411Sphk printf("isa_dmastatus: channel %d not active\n", chan); 469116411Sphk return(-1); 470116411Sphk } 471116411Sphk /* channel busy? */ 472116411Sphk 473116411Sphk if (((dma_busy & (1 << chan)) == 0) && 474116411Sphk (dma_auto_mode & (1 << chan)) == 0 ) { 475116411Sphk printf("chan %d not busy\n", chan); 476116411Sphk return -2 ; 477116411Sphk } 478116411Sphk if (chan < 4) { /* low DMA controller */ 479116411Sphk ffport = DMA1_FFC; 480116411Sphk waport = DMA1_CHN(chan) + 1; 481116411Sphk } else { /* high DMA controller */ 482116411Sphk ffport = DMA2_FFC; 483116411Sphk waport = DMA2_CHN(chan - 4) + 2; 484116411Sphk } 485116411Sphk 486116411Sphk disable_intr(); /* no interrupts Mr Jones! */ 487116411Sphk outb(ffport, 0); /* clear register LSB flipflop */ 488116411Sphk low1 = inb(waport); 489116411Sphk high1 = inb(waport); 490116411Sphk outb(ffport, 0); /* clear again */ 491116411Sphk low2 = inb(waport); 492116411Sphk high2 = inb(waport); 493116411Sphk enable_intr(); /* enable interrupts again */ 494116411Sphk 495116411Sphk /* 496116411Sphk * Now decide if a wrap has tried to skew our results. 497116411Sphk * Note that after TC, the count will read 0xffff, while we want 498116411Sphk * to return zero, so we add and then mask to compensate. 499116411Sphk */ 500116411Sphk if (low1 >= low2) { 501116411Sphk cnt = (low1 + (high1 << 8) + 1) & 0xffff; 502116411Sphk } else { 503116411Sphk cnt = (low2 + (high2 << 8) + 1) & 0xffff; 504116411Sphk } 505116411Sphk 506116411Sphk if (chan >= 4) /* high channels move words */ 507116411Sphk cnt *= 2; 508116411Sphk return(cnt); 509116411Sphk} 510116411Sphk 511116411Sphkint 512116411Sphkisa_dmastatus(int chan) 513116411Sphk{ 514116411Sphk int status; 515116411Sphk 516116411Sphk mtx_lock(&isa_dma_lock); 517116411Sphk status = isa_dmastatus_locked(chan); 518116411Sphk mtx_unlock(&isa_dma_lock); 519116411Sphk 520116411Sphk return (status); 521116411Sphk} 522116411Sphk 523116411Sphk/* 524116411Sphk * Reached terminal count yet ? 525116411Sphk */ 526116411Sphkint 527116411Sphkisa_dmatc(int chan) 528116411Sphk{ 529116411Sphk 530116411Sphk if (chan < 4) 531116411Sphk return(inb(DMA1_STATUS) & (1 << chan)); 532116411Sphk else 533116411Sphk return(inb(DMA2_STATUS) & (1 << (chan & 3))); 534116411Sphk} 535116411Sphk 536116411Sphk/* 537116411Sphk * Stop a DMA transfer currently in progress. 538116411Sphk */ 539116411Sphkint 540116411Sphkisa_dmastop(int chan) 541116411Sphk{ 542116411Sphk int status; 543116411Sphk 544116411Sphk mtx_lock(&isa_dma_lock); 545116411Sphk if ((dma_inuse & (1 << chan)) == 0) 546116411Sphk printf("isa_dmastop: channel %d not acquired\n", chan); 547116411Sphk 548116411Sphk if (((dma_busy & (1 << chan)) == 0) && 549116411Sphk ((dma_auto_mode & (1 << chan)) == 0)) { 550116411Sphk printf("chan %d not busy\n", chan); 551116411Sphk mtx_unlock(&isa_dma_lock); 552116411Sphk return -2 ; 553116411Sphk } 554116411Sphk 555116411Sphk if ((chan & 4) == 0) { 556116411Sphk outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); 557116411Sphk } else { 558116411Sphk outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); 559116411Sphk } 560116411Sphk 561116411Sphk status = isa_dmastatus_locked(chan); 562116411Sphk 563116411Sphk mtx_unlock(&isa_dma_lock); 564116411Sphk 565116411Sphk return (status); 566116411Sphk} 567116411Sphk 568116411Sphk/* 569116411Sphk * Attach to the ISA PnP descriptor for the AT DMA controller 570116411Sphk */ 571116411Sphkstatic struct isa_pnp_id atdma_ids[] = { 572116411Sphk { 0x0002d041 /* PNP0200 */, "AT DMA controller" }, 573116411Sphk { 0 } 574116411Sphk}; 575116411Sphk 576116411Sphkstatic int 577116411Sphkatdma_probe(device_t dev) 578116411Sphk{ 579116411Sphk int result; 580116411Sphk 581116411Sphk if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, atdma_ids)) <= 0) 582116411Sphk device_quiet(dev); 583116411Sphk return(result); 584116411Sphk} 585116411Sphk 586116411Sphkstatic int 587116411Sphkatdma_attach(device_t dev) 588116411Sphk{ 589116411Sphk return(0); 590116411Sphk} 591116411Sphk 592116411Sphkstatic device_method_t atdma_methods[] = { 593116411Sphk /* Device interface */ 594116411Sphk DEVMETHOD(device_probe, atdma_probe), 595116411Sphk DEVMETHOD(device_attach, atdma_attach), 596116411Sphk DEVMETHOD(device_detach, bus_generic_detach), 597116411Sphk DEVMETHOD(device_shutdown, bus_generic_shutdown), 598116411Sphk DEVMETHOD(device_suspend, bus_generic_suspend), 599116411Sphk DEVMETHOD(device_resume, bus_generic_resume), 600116411Sphk { 0, 0 } 601116411Sphk}; 602116411Sphk 603116411Sphkstatic driver_t atdma_driver = { 604116411Sphk "atdma", 605116411Sphk atdma_methods, 606116411Sphk 1, /* no softc */ 607116411Sphk}; 608116411Sphk 609116411Sphkstatic devclass_t atdma_devclass; 610116411Sphk 611116411SphkDRIVER_MODULE(atdma, isa, atdma_driver, atdma_devclass, 0, 0); 612116411SphkDRIVER_MODULE(atdma, acpi, atdma_driver, atdma_devclass, 0, 0); 613116411Sphk