isa_dma.c revision 332304
1139735Simp/*- 2129198Scognet * Copyright (c) 1991 The Regents of the University of California. 3129198Scognet * All rights reserved. 4129198Scognet * 5129198Scognet * This code is derived from software contributed to Berkeley by 6129198Scognet * William Jolitz. 7129198Scognet * 8129198Scognet * Redistribution and use in source and binary forms, with or without 9129198Scognet * modification, are permitted provided that the following conditions 10129198Scognet * are met: 11129198Scognet * 1. Redistributions of source code must retain the above copyright 12129198Scognet * notice, this list of conditions and the following disclaimer. 13129198Scognet * 2. Redistributions in binary form must reproduce the above copyright 14129198Scognet * notice, this list of conditions and the following disclaimer in the 15129198Scognet * documentation and/or other materials provided with the distribution. 16129198Scognet * 4. Neither the name of the University nor the names of its contributors 17129198Scognet * may be used to endorse or promote products derived from this software 18129198Scognet * without specific prior written permission. 19129198Scognet * 20129198Scognet * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21129198Scognet * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22129198Scognet * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23129198Scognet * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24129198Scognet * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25129198Scognet * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26129198Scognet * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27129198Scognet * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28129198Scognet * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29129198Scognet * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30129198Scognet * SUCH DAMAGE. 31129198Scognet * 32129198Scognet * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 33129198Scognet */ 34129198Scognet 35166686Skevlo#include <sys/cdefs.h> 36129198Scognet__FBSDID("$FreeBSD: stable/11/sys/x86/isa/isa_dma.c 332304 2018-04-08 20:52:09Z emaste $"); 37129198Scognet 38129198Scognet/* 39129198Scognet * code to manage AT bus 40129198Scognet * 41129198Scognet * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): 42129198Scognet * Fixed uninitialized variable problem and added code to deal 43129198Scognet * with DMA page boundaries in isa_dmarangecheck(). Fixed word 44129198Scognet * mode DMA count compution and reorganized DMA setup code in 45129198Scognet * isa_dmastart() 46129198Scognet */ 47129198Scognet 48129198Scognet#include <sys/param.h> 49140310Scognet#include <sys/systm.h> 50146597Scognet#include <sys/bus.h> 51166063Scognet#include <sys/kernel.h> 52129198Scognet#include <sys/malloc.h> 53129198Scognet#include <sys/lock.h> 54129198Scognet#include <sys/proc.h> 55129198Scognet#include <sys/mutex.h> 56129198Scognet#include <sys/module.h> 57129198Scognet#include <vm/vm.h> 58129198Scognet#include <vm/vm_param.h> 59129198Scognet#include <vm/pmap.h> 60166063Scognet#include <isa/isareg.h> 61129198Scognet#include <isa/isavar.h> 62166063Scognet#include <isa/isa_dmareg.h> 63166063Scognet 64166063Scognet#define ISARAM_END 0x1000000 65166063Scognet 66166063Scognetstatic int isa_dmarangecheck(caddr_t va, u_int length, int chan); 67166063Scognet 68129198Scognetstatic caddr_t dma_bouncebuf[8]; 69129198Scognetstatic u_int dma_bouncebufsize[8]; 70129198Scognetstatic u_int8_t dma_bounced = 0; 71129198Scognetstatic u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ 72129198Scognetstatic u_int8_t dma_inuse = 0; /* User for acquire/release */ 73129198Scognetstatic u_int8_t dma_auto_mode = 0; 74129198Scognetstatic struct mtx isa_dma_lock; 75129198ScognetMTX_SYSINIT(isa_dma_lock, &isa_dma_lock, "isa DMA lock", MTX_DEF); 76129198Scognet 77129198Scognet#define VALID_DMA_MASK (7) 78129198Scognet 79129198Scognet/* high byte of address is stored in this port for i-th dma channel */ 80129198Scognetstatic int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 81129198Scognet 82129198Scognet/* 83129198Scognet * Setup a DMA channel's bounce buffer. 84129198Scognet */ 85129198Scognetint 86129198Scognetisa_dma_init(int chan, u_int bouncebufsize, int flag) 87129198Scognet{ 88129198Scognet void *buf; 89129198Scognet int contig; 90129198Scognet 91129198Scognet#ifdef DIAGNOSTIC 92166063Scognet if (chan & ~VALID_DMA_MASK) 93129198Scognet panic("isa_dma_init: channel out of range"); 94129198Scognet#endif 95166063Scognet 96166063Scognet 97166063Scognet /* Try malloc() first. It works better if it works. */ 98166063Scognet buf = malloc(bouncebufsize, M_DEVBUF, flag); 99166063Scognet if (buf != NULL) { 100166063Scognet if (isa_dmarangecheck(buf, bouncebufsize, chan) != 0) { 101166063Scognet free(buf, M_DEVBUF); 102166063Scognet buf = NULL; 103166063Scognet } 104166063Scognet contig = 0; 105166063Scognet } 106166063Scognet 107166063Scognet if (buf == NULL) { 108166063Scognet buf = contigmalloc(bouncebufsize, M_DEVBUF, flag, 0ul, 0xfffffful, 109166063Scognet 1ul, chan & 4 ? 0x20000ul : 0x10000ul); 110166063Scognet contig = 1; 111166063Scognet } 112166063Scognet 113166063Scognet if (buf == NULL) 114166063Scognet return (ENOMEM); 115188403Scognet 116166063Scognet mtx_lock(&isa_dma_lock); 117166063Scognet /* 118166063Scognet * If a DMA channel is shared, both drivers have to call isa_dma_init 119166063Scognet * since they don't know that the other driver will do it. 120166063Scognet * Just return if we're already set up good. 121166063Scognet * XXX: this only works if they agree on the bouncebuf size. This 122166063Scognet * XXX: is typically the case since they are multiple instances of 123166063Scognet * XXX: the same driver. 124166063Scognet */ 125166063Scognet if (dma_bouncebuf[chan] != NULL) { 126166063Scognet if (contig) 127166063Scognet contigfree(buf, bouncebufsize, M_DEVBUF); 128166063Scognet else 129166063Scognet free(buf, M_DEVBUF); 130166063Scognet mtx_unlock(&isa_dma_lock); 131166063Scognet return (0); 132166063Scognet } 133166063Scognet 134135644Scognet dma_bouncebufsize[chan] = bouncebufsize; 135135644Scognet dma_bouncebuf[chan] = buf; 136135644Scognet 137146597Scognet mtx_unlock(&isa_dma_lock); 138135644Scognet 139135644Scognet return (0); 140129198Scognet} 141166063Scognet 142166063Scognet/* 143166063Scognet * Register a DMA channel's usage. Usually called from a device driver 144135644Scognet * in open() or during its initialization. 145135644Scognet */ 146135644Scognetint 147156191Scognetisa_dma_acquire(int chan) 148156191Scognet{ 149147591Scognet#ifdef DIAGNOSTIC 150135644Scognet if (chan & ~VALID_DMA_MASK) 151166063Scognet panic("isa_dma_acquire: channel out of range"); 152166063Scognet#endif 153166063Scognet 154166063Scognet mtx_lock(&isa_dma_lock); 155129198Scognet if (dma_inuse & (1 << chan)) { 156129198Scognet printf("isa_dma_acquire: channel %d already in use\n", chan); 157166063Scognet mtx_unlock(&isa_dma_lock); 158166063Scognet return (EBUSY); 159166063Scognet } 160147591Scognet dma_inuse |= (1 << chan); 161147591Scognet dma_auto_mode &= ~(1 << chan); 162147591Scognet mtx_unlock(&isa_dma_lock); 163146597Scognet 164146597Scognet return (0); 165146597Scognet} 166146597Scognet 167146597Scognet/* 168146597Scognet * Unregister a DMA channel's usage. Usually called from a device driver 169146597Scognet * during close() or during its shutdown. 170166063Scognet */ 171166063Scognetvoid 172166063Scognetisa_dma_release(int chan) 173166063Scognet{ 174166063Scognet#ifdef DIAGNOSTIC 175166063Scognet if (chan & ~VALID_DMA_MASK) 176166063Scognet panic("isa_dma_release: channel out of range"); 177166063Scognet 178166063Scognet mtx_lock(&isa_dma_lock); 179166063Scognet if ((dma_inuse & (1 << chan)) == 0) 180166063Scognet printf("isa_dma_release: channel %d not in use\n", chan); 181166063Scognet#else 182166063Scognet mtx_lock(&isa_dma_lock); 183166063Scognet#endif 184166063Scognet 185166063Scognet if (dma_busy & (1 << chan)) { 186166063Scognet dma_busy &= ~(1 << chan); 187166063Scognet /* 188166063Scognet * XXX We should also do "dma_bounced &= (1 << chan);" 189166063Scognet * because we are acting on behalf of isa_dmadone() which 190166063Scognet * was not called to end the last DMA operation. This does 191166063Scognet * not matter now, but it may in the future. 192166063Scognet */ 193166063Scognet } 194166063Scognet 195166063Scognet dma_inuse &= ~(1 << chan); 196166063Scognet dma_auto_mode &= ~(1 << chan); 197166063Scognet 198166063Scognet mtx_unlock(&isa_dma_lock); 199166063Scognet} 200166063Scognet 201166063Scognet/* 202166063Scognet * isa_dmacascade(): program 8237 DMA controller channel to accept 203166063Scognet * external dma control by a board. 204166063Scognet */ 205166063Scognetvoid 206166063Scognetisa_dmacascade(int chan) 207166063Scognet{ 208166063Scognet#ifdef DIAGNOSTIC 209147591Scognet if (chan & ~VALID_DMA_MASK) 210147591Scognet panic("isa_dmacascade: channel out of range"); 211147591Scognet#endif 212147591Scognet 213147591Scognet mtx_lock(&isa_dma_lock); 214147591Scognet /* set dma channel mode, and set dma channel mode */ 215147591Scognet if ((chan & 4) == 0) { 216147591Scognet outb(DMA1_MODE, DMA37MD_CASCADE | chan); 217147591Scognet outb(DMA1_SMSK, chan); 218147591Scognet } else { 219147591Scognet outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 220129198Scognet outb(DMA2_SMSK, chan & 3); 221129198Scognet } 222129198Scognet mtx_unlock(&isa_dma_lock); 223129198Scognet} 224137758Scognet 225140349Scognet/* 226137758Scognet * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 227137760Scognet * problems by using a bounce buffer. 228135644Scognet */ 229166063Scognetvoid 230166063Scognetisa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) 231166063Scognet{ 232166063Scognet vm_paddr_t phys; 233166063Scognet int waport; 234166063Scognet caddr_t newaddr; 235166063Scognet int dma_range_checked; 236166063Scognet 237166063Scognet dma_range_checked = isa_dmarangecheck(addr, nbytes, chan); 238166063Scognet 239166063Scognet#ifdef DIAGNOSTIC 240166063Scognet if (chan & ~VALID_DMA_MASK) 241166063Scognet panic("isa_dmastart: channel out of range"); 242129198Scognet 243129198Scognet if ((chan < 4 && nbytes > (1<<16)) 244129198Scognet || (chan >= 4 && (nbytes > (1<<17) || (uintptr_t)addr & 1))) 245129198Scognet panic("isa_dmastart: impossible request"); 246129198Scognet 247129198Scognet mtx_lock(&isa_dma_lock); 248129198Scognet if ((dma_inuse & (1 << chan)) == 0) 249129198Scognet printf("isa_dmastart: channel %d not acquired\n", chan); 250129198Scognet#else 251129198Scognet mtx_lock(&isa_dma_lock); 252129198Scognet#endif 253129198Scognet 254129198Scognet#if 0 255129198Scognet /* 256129198Scognet * XXX This should be checked, but drivers like ad1848 only call 257129198Scognet * isa_dmastart() once because they use Auto DMA mode. If we 258129198Scognet * leave this in, drivers that do this will print this continuously. 259129198Scognet */ 260129198Scognet if (dma_busy & (1 << chan)) 261129198Scognet printf("isa_dmastart: channel %d busy\n", chan); 262129198Scognet#endif 263129198Scognet 264129198Scognet dma_busy |= (1 << chan); 265129198Scognet 266129198Scognet if (dma_range_checked) { 267129198Scognet if (dma_bouncebuf[chan] == NULL 268129198Scognet || dma_bouncebufsize[chan] < nbytes) 269129198Scognet panic("isa_dmastart: bad bounce buffer"); 270129198Scognet dma_bounced |= (1 << chan); 271129198Scognet newaddr = dma_bouncebuf[chan]; 272129198Scognet 273129198Scognet /* copy bounce buffer on write */ 274129198Scognet if (!(flags & ISADMA_READ)) 275129198Scognet bcopy(addr, newaddr, nbytes); 276129198Scognet addr = newaddr; 277129198Scognet } 278129198Scognet 279129198Scognet /* translate to physical */ 280129198Scognet phys = pmap_extract(kernel_pmap, (vm_offset_t)addr); 281129198Scognet 282129198Scognet if (flags & ISADMA_RAW) { 283129198Scognet dma_auto_mode |= (1 << chan); 284129198Scognet } else { 285129198Scognet dma_auto_mode &= ~(1 << chan); 286129198Scognet } 287129198Scognet 288129198Scognet if ((chan & 4) == 0) { 289129198Scognet /* 290129198Scognet * Program one of DMA channels 0..3. These are 291129198Scognet * byte mode channels. 292129198Scognet */ 293129198Scognet /* set dma channel mode, and reset address ff */ 294129198Scognet 295129198Scognet /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 296129198Scognet if (flags & ISADMA_RAW) { 297147591Scognet if (flags & ISADMA_READ) 298146597Scognet outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); 299146597Scognet else 300146597Scognet outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); 301146597Scognet } 302146597Scognet else 303147591Scognet if (flags & ISADMA_READ) 304150860Scognet outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 305150860Scognet else 306146597Scognet outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 307147591Scognet outb(DMA1_FFC, 0); 308166063Scognet 309147591Scognet /* send start address */ 310147591Scognet waport = DMA1_CHN(chan); 311147591Scognet outb(waport, phys); 312147591Scognet outb(waport, phys>>8); 313166063Scognet outb(dmapageport[chan], phys>>16); 314146597Scognet 315146597Scognet /* send count */ 316146597Scognet outb(waport + 1, --nbytes); 317147591Scognet outb(waport + 1, nbytes>>8); 318146597Scognet 319146597Scognet /* unmask channel */ 320146597Scognet outb(DMA1_SMSK, chan); 321146597Scognet } else { 322146597Scognet /* 323146597Scognet * Program one of DMA channels 4..7. These are 324147591Scognet * word mode channels. 325146597Scognet */ 326146597Scognet /* set dma channel mode, and reset address ff */ 327146597Scognet 328146597Scognet /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 329129198Scognet if (flags & ISADMA_RAW) { 330129198Scognet if (flags & ISADMA_READ) 331129198Scognet outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); 332135644Scognet else 333135644Scognet outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); 334129198Scognet } 335129198Scognet else 336129198Scognet if (flags & ISADMA_READ) 337129198Scognet outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 338129198Scognet else 339129198Scognet outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 340129198Scognet outb(DMA2_FFC, 0); 341129198Scognet 342129198Scognet /* send start address */ 343129198Scognet waport = DMA2_CHN(chan - 4); 344129198Scognet outb(waport, phys>>1); 345129198Scognet outb(waport, phys>>9); 346166063Scognet outb(dmapageport[chan], phys>>16); 347166063Scognet 348129198Scognet /* send count */ 349129198Scognet nbytes >>= 1; 350140313Scognet outb(waport + 2, --nbytes); 351143294Smux outb(waport + 2, nbytes>>8); 352143284Smux 353129198Scognet /* unmask channel */ 354140313Scognet outb(DMA2_SMSK, chan & 3); 355129198Scognet } 356129198Scognet mtx_unlock(&isa_dma_lock); 357129198Scognet} 358129198Scognet 359129198Scognetvoid 360129198Scognetisa_dmadone(int flags, caddr_t addr, int nbytes, int chan) 361129198Scognet{ 362129198Scognet#ifdef DIAGNOSTIC 363129198Scognet if (chan & ~VALID_DMA_MASK) 364129198Scognet panic("isa_dmadone: channel out of range"); 365129198Scognet 366129198Scognet if ((dma_inuse & (1 << chan)) == 0) 367129198Scognet printf("isa_dmadone: channel %d not acquired\n", chan); 368129198Scognet#endif 369129198Scognet 370135644Scognet mtx_lock(&isa_dma_lock); 371129198Scognet if (((dma_busy & (1 << chan)) == 0) && 372129198Scognet (dma_auto_mode & (1 << chan)) == 0 ) 373129198Scognet printf("isa_dmadone: channel %d not busy\n", chan); 374129198Scognet 375129198Scognet if ((dma_auto_mode & (1 << chan)) == 0) 376129198Scognet outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); 377129198Scognet 378129198Scognet if (dma_bounced & (1 << chan)) { 379129198Scognet /* copy bounce buffer on read */ 380129198Scognet if (flags & ISADMA_READ) 381129198Scognet bcopy(dma_bouncebuf[chan], addr, nbytes); 382129198Scognet 383129198Scognet dma_bounced &= ~(1 << chan); 384134934Sscottl } 385134934Sscottl dma_busy &= ~(1 << chan); 386134934Sscottl mtx_unlock(&isa_dma_lock); 387134934Sscottl} 388134934Sscottl 389166063Scognet/* 390166063Scognet * Check for problems with the address range of a DMA transfer 391166063Scognet * (non-contiguous physical pages, outside of bus address space, 392129198Scognet * crossing DMA page boundaries). 393129198Scognet * Return true if special handling needed. 394129198Scognet */ 395129198Scognet 396129198Scognetstatic int 397129198Scognetisa_dmarangecheck(caddr_t va, u_int length, int chan) 398129198Scognet{ 399129198Scognet vm_paddr_t phys, priorpage = 0; 400129198Scognet vm_offset_t endva; 401129198Scognet u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 402129198Scognet 403129198Scognet endva = (vm_offset_t)round_page((vm_offset_t)va + length); 404166063Scognet for (; va < (caddr_t) endva ; va += PAGE_SIZE) { 405166063Scognet phys = trunc_page(pmap_extract(kernel_pmap, (vm_offset_t)va)); 406166063Scognet if (phys == 0) 407129198Scognet panic("isa_dmacheck: no physical page present"); 408166063Scognet if (phys >= ISARAM_END) 409166063Scognet return (1); 410166063Scognet if (priorpage) { 411166063Scognet if (priorpage + PAGE_SIZE != phys) 412166063Scognet return (1); 413166063Scognet /* check if crossing a DMA page boundary */ 414166063Scognet if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) 415166063Scognet return (1); 416166063Scognet } 417166063Scognet priorpage = phys; 418166063Scognet } 419166063Scognet return (0); 420166063Scognet} 421166063Scognet 422166063Scognet/* 423166063Scognet * Query the progress of a transfer on a DMA channel. 424166063Scognet * 425166063Scognet * To avoid having to interrupt a transfer in progress, we sample 426166063Scognet * each of the high and low databytes twice, and apply the following 427166063Scognet * logic to determine the correct count. 428166063Scognet * 429166063Scognet * Reads are performed with interrupts disabled, thus it is to be 430166063Scognet * expected that the time between reads is very small. At most 431170502Scognet * one rollover in the low count byte can be expected within the 432170502Scognet * four reads that are performed. 433166063Scognet * 434166063Scognet * There are three gaps in which a rollover can occur : 435166063Scognet * 436166063Scognet * - read low1 437143294Smux * gap1 438143284Smux * - read high1 439140313Scognet * gap2 440129198Scognet * - read low2 441129198Scognet * gap3 442129198Scognet * - read high2 443129198Scognet * 444129198Scognet * If a rollover occurs in gap1 or gap2, the low2 value will be 445129198Scognet * greater than the low1 value. In this case, low2 and high2 are a 446140680Scognet * corresponding pair. 447140313Scognet * 448140680Scognet * In any other case, low1 and high1 can be considered to be correct. 449140313Scognet * 450129198Scognet * The function returns the number of bytes remaining in the transfer, 451129198Scognet * or -1 if the channel requested is not active. 452129198Scognet * 453129198Scognet */ 454129198Scognetstatic int 455129198Scognetisa_dmastatus_locked(int chan) 456129198Scognet{ 457129198Scognet u_long cnt = 0; 458129198Scognet int ffport, waport; 459129198Scognet u_long low1, high1, low2, high2; 460129198Scognet 461129198Scognet mtx_assert(&isa_dma_lock, MA_OWNED); 462129198Scognet 463129198Scognet /* channel active? */ 464129198Scognet if ((dma_inuse & (1 << chan)) == 0) { 465129198Scognet printf("isa_dmastatus: channel %d not active\n", chan); 466129198Scognet return(-1); 467129198Scognet } 468129198Scognet /* channel busy? */ 469129198Scognet 470129198Scognet if (((dma_busy & (1 << chan)) == 0) && 471129198Scognet (dma_auto_mode & (1 << chan)) == 0 ) { 472143294Smux printf("chan %d not busy\n", chan); 473140313Scognet return -2 ; 474129198Scognet } 475129198Scognet if (chan < 4) { /* low DMA controller */ 476129198Scognet ffport = DMA1_FFC; 477166063Scognet waport = DMA1_CHN(chan) + 1; 478129198Scognet } else { /* high DMA controller */ 479129198Scognet ffport = DMA2_FFC; 480129198Scognet waport = DMA2_CHN(chan - 4) + 2; 481129198Scognet } 482129198Scognet 483129198Scognet disable_intr(); /* no interrupts Mr Jones! */ 484129198Scognet outb(ffport, 0); /* clear register LSB flipflop */ 485129198Scognet low1 = inb(waport); 486140313Scognet high1 = inb(waport); 487129198Scognet outb(ffport, 0); /* clear again */ 488146597Scognet low2 = inb(waport); 489140313Scognet high2 = inb(waport); 490143294Smux enable_intr(); /* enable interrupts again */ 491129198Scognet 492140313Scognet /* 493129198Scognet * Now decide if a wrap has tried to skew our results. 494135644Scognet * Note that after TC, the count will read 0xffff, while we want 495161618Scognet * to return zero, so we add and then mask to compensate. 496129198Scognet */ 497129198Scognet if (low1 >= low2) { 498166063Scognet cnt = (low1 + (high1 << 8) + 1) & 0xffff; 499166063Scognet } else { 500166063Scognet cnt = (low2 + (high2 << 8) + 1) & 0xffff; 501166063Scognet } 502166063Scognet 503166063Scognet if (chan >= 4) /* high channels move words */ 504166063Scognet cnt *= 2; 505166063Scognet return(cnt); 506166063Scognet} 507166063Scognet 508166063Scognetint 509166063Scognetisa_dmastatus(int chan) 510166063Scognet{ 511166063Scognet int status; 512166063Scognet 513166063Scognet mtx_lock(&isa_dma_lock); 514166063Scognet status = isa_dmastatus_locked(chan); 515166063Scognet mtx_unlock(&isa_dma_lock); 516166063Scognet 517166063Scognet return (status); 518166063Scognet} 519166063Scognet 520166063Scognet/* 521166063Scognet * Reached terminal count yet ? 522166063Scognet */ 523166063Scognetint 524166063Scognetisa_dmatc(int chan) 525166063Scognet{ 526166063Scognet 527188403Scognet if (chan < 4) 528166063Scognet return(inb(DMA1_STATUS) & (1 << chan)); 529166063Scognet else 530166063Scognet return(inb(DMA2_STATUS) & (1 << (chan & 3))); 531166063Scognet} 532166063Scognet 533166063Scognet/* 534166063Scognet * Stop a DMA transfer currently in progress. 535166063Scognet */ 536166063Scognetint 537166063Scognetisa_dmastop(int chan) 538166063Scognet{ 539166063Scognet int status; 540166063Scognet 541166063Scognet mtx_lock(&isa_dma_lock); 542166063Scognet if ((dma_inuse & (1 << chan)) == 0) 543188403Scognet printf("isa_dmastop: channel %d not acquired\n", chan); 544166063Scognet 545143294Smux if (((dma_busy & (1 << chan)) == 0) && 546143284Smux ((dma_auto_mode & (1 << chan)) == 0)) { 547140313Scognet printf("chan %d not busy\n", chan); 548129198Scognet mtx_unlock(&isa_dma_lock); 549129198Scognet return -2 ; 550129198Scognet } 551129198Scognet 552129198Scognet if ((chan & 4) == 0) { 553129198Scognet outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); 554129198Scognet } else { 555129198Scognet outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); 556129198Scognet } 557129198Scognet 558135644Scognet status = isa_dmastatus_locked(chan); 559146597Scognet 560166063Scognet mtx_unlock(&isa_dma_lock); 561166063Scognet 562166063Scognet return (status); 563166063Scognet} 564166063Scognet 565188403Scognet/* 566188403Scognet * Attach to the ISA PnP descriptor for the AT DMA controller 567129198Scognet */ 568143294Smuxstatic struct isa_pnp_id atdma_ids[] = { 569129198Scognet { 0x0002d041 /* PNP0200 */, "AT DMA controller" }, 570129198Scognet { 0 } 571129198Scognet}; 572129198Scognet 573129198Scognetstatic int 574129198Scognetatdma_probe(device_t dev) 575129198Scognet{ 576129198Scognet int result; 577129198Scognet 578129198Scognet if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, atdma_ids)) <= 0) 579129198Scognet device_quiet(dev); 580129198Scognet return(result); 581135644Scognet} 582129198Scognet 583129198Scognetstatic int 584129198Scognetatdma_attach(device_t dev) 585129198Scognet{ 586129198Scognet return(0); 587129198Scognet} 588129198Scognet 589129198Scognetstatic device_method_t atdma_methods[] = { 590129198Scognet /* Device interface */ 591129198Scognet DEVMETHOD(device_probe, atdma_probe), 592146597Scognet DEVMETHOD(device_attach, atdma_attach), 593143671Sjmg DEVMETHOD(device_detach, bus_generic_detach), 594143671Sjmg DEVMETHOD(device_shutdown, bus_generic_shutdown), 595143671Sjmg DEVMETHOD(device_suspend, bus_generic_suspend), 596143671Sjmg DEVMETHOD(device_resume, bus_generic_resume), 597135644Scognet { 0, 0 } 598143671Sjmg}; 599143671Sjmg 600143671Sjmgstatic driver_t atdma_driver = { 601135644Scognet "atdma", 602166063Scognet atdma_methods, 603166063Scognet 1, /* no softc */ 604166063Scognet}; 605129198Scognet 606129198Scognetstatic devclass_t atdma_devclass; 607129198Scognet 608129198ScognetDRIVER_MODULE(atdma, isa, atdma_driver, atdma_devclass, 0, 0); 609129198ScognetDRIVER_MODULE(atdma, acpi, atdma_driver, atdma_devclass, 0, 0); 610129198Scognet