isa_dma.c revision 199104
11541Srgrimes/*- 21541Srgrimes * Copyright (c) 1991 The Regents of the University of California. 31541Srgrimes * All rights reserved. 41541Srgrimes * 51541Srgrimes * This code is derived from software contributed to Berkeley by 61541Srgrimes * William Jolitz. 71541Srgrimes * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 4. Neither the name of the University nor the names of its contributors 171541Srgrimes * may be used to endorse or promote products derived from this software 181541Srgrimes * without specific prior written permission. 191541Srgrimes * 201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301541Srgrimes * SUCH DAMAGE. 311541Srgrimes * 321541Srgrimes * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 331541Srgrimes */ 3450477Speter 351541Srgrimes#include <sys/cdefs.h> 361541Srgrimes__FBSDID("$FreeBSD: head/sys/i386/isa/isa_dma.c 199104 2009-11-09 20:29:10Z rdivacky $"); 372169Spaul 382169Spaul/* 392169Spaul * code to manage AT bus 401541Srgrimes * 411541Srgrimes * 92/08/18 Frank P. MacLachlan (fpm@crash.cts.com): 421541Srgrimes * Fixed uninitialized variable problem and added code to deal 431541Srgrimes * with DMA page boundaries in isa_dmarangecheck(). Fixed word 441541Srgrimes * mode DMA count compution and reorganized DMA setup code in 451541Srgrimes * isa_dmastart() 4633804Sjulian */ 471541Srgrimes 481541Srgrimes#include <sys/param.h> 4952904Sshin#include <sys/systm.h> 501541Srgrimes#include <sys/bus.h> 511541Srgrimes#include <sys/kernel.h> 521541Srgrimes#include <sys/malloc.h> 5352904Sshin#include <sys/lock.h> 5452904Sshin#include <sys/proc.h> 551541Srgrimes#include <sys/mutex.h> 5633804Sjulian#include <sys/module.h> 571541Srgrimes#include <vm/vm.h> 5833804Sjulian#include <vm/vm_param.h> 5933804Sjulian#include <vm/pmap.h> 6033804Sjulian#include <isa/isareg.h> 611541Srgrimes#include <isa/isavar.h> 6233804Sjulian#include <isa/isa_dmareg.h> 6333804Sjulian 6433804Sjulianstatic int isa_dmarangecheck(caddr_t va, u_int length, int chan); 6533804Sjulian 661541Srgrimesstatic caddr_t dma_bouncebuf[8]; 6733804Sjulianstatic u_int dma_bouncebufsize[8]; 6833804Sjulianstatic u_int8_t dma_bounced = 0; 6933804Sjulianstatic u_int8_t dma_busy = 0; /* Used in isa_dmastart() */ 7033804Sjulianstatic u_int8_t dma_inuse = 0; /* User for acquire/release */ 711541Srgrimesstatic u_int8_t dma_auto_mode = 0; 7233804Sjulianstatic struct mtx isa_dma_lock; 7333804SjulianMTX_SYSINIT(isa_dma_lock, &isa_dma_lock, "isa DMA lock", MTX_DEF); 7433804Sjulian 7533804Sjulian#define VALID_DMA_MASK (7) 7633804Sjulian 7733804Sjulian/* high byte of address is stored in this port for i-th dma channel */ 781541Srgrimesstatic int dmapageport[8] = { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 7933804Sjulian 8033804Sjulian/* 8133804Sjulian * Setup a DMA channel's bounce buffer. 8233804Sjulian */ 8333804Sjulianint 8433804Sjulianisa_dma_init(int chan, u_int bouncebufsize, int flag) 8533804Sjulian{ 8633804Sjulian void *buf; 8733804Sjulian int contig; 8833804Sjulian 8933804Sjulian#ifdef DIAGNOSTIC 9052904Sshin if (chan & ~VALID_DMA_MASK) 9133804Sjulian panic("isa_dma_init: channel out of range"); 9252904Sshin#endif 9352904Sshin 9433804Sjulian 9552904Sshin /* Try malloc() first. It works better if it works. */ 9633804Sjulian buf = malloc(bouncebufsize, M_DEVBUF, flag); 9733804Sjulian if (buf != NULL) { 9833804Sjulian if (isa_dmarangecheck(buf, bouncebufsize, chan) != 0) { 9952904Sshin free(buf, M_DEVBUF); 10052904Sshin buf = NULL; 10133804Sjulian } 10233804Sjulian contig = 0; 10333804Sjulian } 10472486Sasmodai 10572486Sasmodai if (buf == NULL) { 10672486Sasmodai buf = contigmalloc(bouncebufsize, M_DEVBUF, flag, 0ul, 0xfffffful, 10752904Sshin 1ul, chan & 4 ? 0x20000ul : 0x10000ul); 10852904Sshin contig = 1; 10952904Sshin } 11033804Sjulian 11133804Sjulian if (buf == NULL) 11233804Sjulian return (ENOMEM); 11333804Sjulian 11433804Sjulian mtx_lock(&isa_dma_lock); 11533804Sjulian /* 11633804Sjulian * If a DMA channel is shared, both drivers have to call isa_dma_init 11733804Sjulian * since they don't know that the other driver will do it. 11833804Sjulian * Just return if we're already set up good. 11933804Sjulian * XXX: this only works if they agree on the bouncebuf size. This 12033804Sjulian * XXX: is typically the case since they are multiple instances of 12133804Sjulian * XXX: the same driver. 12233804Sjulian */ 12333804Sjulian if (dma_bouncebuf[chan] != NULL) { 12433804Sjulian if (contig) 12533804Sjulian contigfree(buf, bouncebufsize, M_DEVBUF); 12633804Sjulian else 12733804Sjulian free(buf, M_DEVBUF); 12833804Sjulian mtx_unlock(&isa_dma_lock); 1291541Srgrimes return (0); 13033804Sjulian } 13133804Sjulian 13233804Sjulian dma_bouncebufsize[chan] = bouncebufsize; 13333804Sjulian dma_bouncebuf[chan] = buf; 13433804Sjulian 13533804Sjulian mtx_unlock(&isa_dma_lock); 13633814Sjulian 13733804Sjulian return (0); 13833804Sjulian} 13933804Sjulian 14033804Sjulian/* 14133804Sjulian * Register a DMA channel's usage. Usually called from a device driver 14233804Sjulian * in open() or during its initialization. 14333804Sjulian */ 14433804Sjulianint 14533804Sjulianisa_dma_acquire(chan) 14633804Sjulian int chan; 1471541Srgrimes{ 14833804Sjulian#ifdef DIAGNOSTIC 14933804Sjulian if (chan & ~VALID_DMA_MASK) 15052904Sshin panic("isa_dma_acquire: channel out of range"); 15146420Sluigi#endif 15252904Sshin 15346420Sluigi mtx_lock(&isa_dma_lock); 15433804Sjulian if (dma_inuse & (1 << chan)) { 15533804Sjulian printf("isa_dma_acquire: channel %d already in use\n", chan); 15617072Sjulian mtx_unlock(&isa_dma_lock); 1571541Srgrimes return (EBUSY); 1581541Srgrimes } 1591541Srgrimes dma_inuse |= (1 << chan); 16052904Sshin dma_auto_mode &= ~(1 << chan); 16152904Sshin mtx_unlock(&isa_dma_lock); 1621541Srgrimes 1631541Srgrimes return (0); 1641541Srgrimes} 16514195Speter 16614195Speter/* 16714195Speter * Unregister a DMA channel's usage. Usually called from a device driver 16814195Speter * during close() or during its shutdown. 16914195Speter */ 17014195Spetervoid 17114195Speterisa_dma_release(chan) 17214195Speter int chan; 17314195Speter{ 17414195Speter#ifdef DIAGNOSTIC 17514195Speter if (chan & ~VALID_DMA_MASK) 17614195Speter panic("isa_dma_release: channel out of range"); 17714195Speter 17814195Speter mtx_lock(&isa_dma_lock); 17914195Speter if ((dma_inuse & (1 << chan)) == 0) 18014195Speter printf("isa_dma_release: channel %d not in use\n", chan); 18114195Speter#else 18214195Speter mtx_lock(&isa_dma_lock); 18314195Speter#endif 18414195Speter 18514195Speter if (dma_busy & (1 << chan)) { 18617541Speter dma_busy &= ~(1 << chan); 18714195Speter /* 18814195Speter * XXX We should also do "dma_bounced &= (1 << chan);" 18914195Speter * because we are acting on behalf of isa_dmadone() which 19014195Speter * was not called to end the last DMA operation. This does 19114195Speter * not matter now, but it may in the future. 19214195Speter */ 19314195Speter } 19414195Speter 19535304Sphk dma_inuse &= ~(1 << chan); 19635304Sphk dma_auto_mode &= ~(1 << chan); 19735304Sphk 19835304Sphk mtx_unlock(&isa_dma_lock); 19935304Sphk} 20035304Sphk 20135304Sphk/* 20235304Sphk * isa_dmacascade(): program 8237 DMA controller channel to accept 20335304Sphk * external dma control by a board. 20435304Sphk */ 20535304Sphkvoid 20614195Speterisa_dmacascade(chan) 20714195Speter int chan; 20814195Speter{ 2091541Srgrimes#ifdef DIAGNOSTIC 21014195Speter if (chan & ~VALID_DMA_MASK) 2111541Srgrimes panic("isa_dmacascade: channel out of range"); 21214195Speter#endif 2131541Srgrimes 2141541Srgrimes mtx_lock(&isa_dma_lock); 2151541Srgrimes /* set dma channel mode, and set dma channel mode */ 2161541Srgrimes if ((chan & 4) == 0) { 2171541Srgrimes outb(DMA1_MODE, DMA37MD_CASCADE | chan); 21814195Speter outb(DMA1_SMSK, chan); 21913491Speter } else { 22035304Sphk outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 22135304Sphk outb(DMA2_SMSK, chan & 3); 22213491Speter } 22313491Speter mtx_unlock(&isa_dma_lock); 22417541Speter} 22517541Speter 22617541Speter/* 22717541Speter * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 22817541Speter * problems by using a bounce buffer. 22917541Speter */ 23017541Spetervoid 23117541Speterisa_dmastart(int flags, caddr_t addr, u_int nbytes, int chan) 2321541Srgrimes{ 2331541Srgrimes vm_paddr_t phys; 2341541Srgrimes int waport; 23574700Sume caddr_t newaddr; 2361541Srgrimes int dma_range_checked; 2371541Srgrimes 2381541Srgrimes /* translate to physical */ 2391541Srgrimes phys = pmap_extract(kernel_pmap, (vm_offset_t)addr); 2401541Srgrimes dma_range_checked = isa_dmarangecheck(addr, nbytes, chan); 2411541Srgrimes 2421541Srgrimes#ifdef DIAGNOSTIC 24335919Sjb if (chan & ~VALID_DMA_MASK) 2441541Srgrimes panic("isa_dmastart: channel out of range"); 2451541Srgrimes 2461541Srgrimes if ((chan < 4 && nbytes > (1<<16)) 2471541Srgrimes || (chan >= 4 && (nbytes > (1<<17) || (u_int)addr & 1))) 2481541Srgrimes panic("isa_dmastart: impossible request"); 24935919Sjb 2501541Srgrimes mtx_lock(&isa_dma_lock); 2511541Srgrimes if ((dma_inuse & (1 << chan)) == 0) 2521541Srgrimes printf("isa_dmastart: channel %d not acquired\n", chan); 2531541Srgrimes#else 2541541Srgrimes mtx_lock(&isa_dma_lock); 25535919Sjb#endif 2561541Srgrimes 2571541Srgrimes#if 0 2581541Srgrimes /* 2591541Srgrimes * XXX This should be checked, but drivers like ad1848 only call 26035919Sjb * isa_dmastart() once because they use Auto DMA mode. If we 2611541Srgrimes * leave this in, drivers that do this will print this continuously. 2621541Srgrimes */ 2631541Srgrimes if (dma_busy & (1 << chan)) 2641541Srgrimes printf("isa_dmastart: channel %d busy\n", chan); 2651541Srgrimes#endif 26635919Sjb 26735919Sjb dma_busy |= (1 << chan); 2681541Srgrimes 26935919Sjb if (dma_range_checked) { 27035919Sjb if (dma_bouncebuf[chan] == NULL 27135919Sjb || dma_bouncebufsize[chan] < nbytes) 27255205Speter panic("isa_dmastart: bad bounce buffer"); 2731541Srgrimes dma_bounced |= (1 << chan); 2741541Srgrimes newaddr = dma_bouncebuf[chan]; 2751541Srgrimes 27635919Sjb /* copy bounce buffer on write */ 27735919Sjb if (!(flags & ISADMA_READ)) 27835919Sjb bcopy(addr, newaddr, nbytes); 27935919Sjb addr = newaddr; 2801541Srgrimes } 2811541Srgrimes 2821541Srgrimes if (flags & ISADMA_RAW) { 2831541Srgrimes dma_auto_mode |= (1 << chan); 2841541Srgrimes } else { 2851541Srgrimes dma_auto_mode &= ~(1 << chan); 2861541Srgrimes } 2871541Srgrimes 2881541Srgrimes if ((chan & 4) == 0) { 2891541Srgrimes /* 2901541Srgrimes * Program one of DMA channels 0..3. These are 2911541Srgrimes * byte mode channels. 2921541Srgrimes */ 2931541Srgrimes /* set dma channel mode, and reset address ff */ 29452904Sshin 29552904Sshin /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 2961541Srgrimes if (flags & ISADMA_RAW) { 2971541Srgrimes if (flags & ISADMA_READ) 2981541Srgrimes outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_WRITE|chan); 2991541Srgrimes else 3001541Srgrimes outb(DMA1_MODE, DMA37MD_AUTO|DMA37MD_READ|chan); 3011541Srgrimes } 3021541Srgrimes else 3031541Srgrimes if (flags & ISADMA_READ) 3041541Srgrimes outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 3051541Srgrimes else 3061541Srgrimes outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 3071541Srgrimes outb(DMA1_FFC, 0); 3081541Srgrimes 3091541Srgrimes /* send start address */ 3101541Srgrimes waport = DMA1_CHN(chan); 3111541Srgrimes outb(waport, phys); 3121541Srgrimes outb(waport, phys>>8); 3131541Srgrimes outb(dmapageport[chan], phys>>16); 3141541Srgrimes 3151541Srgrimes /* send count */ 3161541Srgrimes outb(waport + 1, --nbytes); 3171541Srgrimes outb(waport + 1, nbytes>>8); 3181541Srgrimes 3191541Srgrimes /* unmask channel */ 3201541Srgrimes outb(DMA1_SMSK, chan); 3211541Srgrimes } else { 3221541Srgrimes /* 3231541Srgrimes * Program one of DMA channels 4..7. These are 3241541Srgrimes * word mode channels. 3252531Swollman */ 3262531Swollman /* set dma channel mode, and reset address ff */ 3272531Swollman 3289209Swollman /* If ISADMA_RAW flag is set, then use autoinitialise mode */ 3299209Swollman if (flags & ISADMA_RAW) { 33014195Speter if (flags & ISADMA_READ) 33119622Sfenner outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_WRITE|(chan&3)); 33252904Sshin else 33352904Sshin outb(DMA2_MODE, DMA37MD_AUTO|DMA37MD_READ|(chan&3)); 33452904Sshin } 3351541Srgrimes else 33652904Sshin if (flags & ISADMA_READ) 33752904Sshin outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 33852904Sshin else 33952904Sshin outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 34052904Sshin outb(DMA2_FFC, 0); 34152904Sshin 34217758Ssos /* send start address */ 34341793Sluigi waport = DMA2_CHN(chan - 4); 34441793Sluigi outb(waport, phys>>1); 34541793Sluigi outb(waport, phys>>9); 34641793Sluigi outb(dmapageport[chan], phys>>16); 34741793Sluigi 3481541Srgrimes /* send count */ 3491541Srgrimes nbytes >>= 1; 3501541Srgrimes outb(waport + 2, --nbytes); 3511541Srgrimes outb(waport + 2, nbytes>>8); 3521541Srgrimes 3539209Swollman /* unmask channel */ 3541541Srgrimes outb(DMA2_SMSK, chan & 3); 3551541Srgrimes } 3561541Srgrimes mtx_unlock(&isa_dma_lock); 3571541Srgrimes} 3581541Srgrimes 3591541Srgrimesvoid 3601541Srgrimesisa_dmadone(int flags, caddr_t addr, int nbytes, int chan) 3611541Srgrimes{ 3621541Srgrimes#ifdef DIAGNOSTIC 3631541Srgrimes if (chan & ~VALID_DMA_MASK) 36414195Speter panic("isa_dmadone: channel out of range"); 36514195Speter 36614195Speter if ((dma_inuse & (1 << chan)) == 0) 36714195Speter printf("isa_dmadone: channel %d not acquired\n", chan); 36814195Speter#endif 36914195Speter 37014195Speter mtx_lock(&isa_dma_lock); 37114195Speter if (((dma_busy & (1 << chan)) == 0) && 3721541Srgrimes (dma_auto_mode & (1 << chan)) == 0 ) 3731541Srgrimes printf("isa_dmadone: channel %d not busy\n", chan); 3741541Srgrimes 3751541Srgrimes if ((dma_auto_mode & (1 << chan)) == 0) 3761541Srgrimes outb(chan & 4 ? DMA2_SMSK : DMA1_SMSK, (chan & 3) | 4); 37762587Sitojun 3781541Srgrimes if (dma_bounced & (1 << chan)) { 3791541Srgrimes /* copy bounce buffer on read */ 3801541Srgrimes if (flags & ISADMA_READ) 3811541Srgrimes bcopy(dma_bouncebuf[chan], addr, nbytes); 3821541Srgrimes 3831541Srgrimes dma_bounced &= ~(1 << chan); 3841541Srgrimes } 3851541Srgrimes dma_busy &= ~(1 << chan); 3861541Srgrimes mtx_unlock(&isa_dma_lock); 3871541Srgrimes} 3881541Srgrimes 3891541Srgrimes/* 3901541Srgrimes * Check for problems with the address range of a DMA transfer 3911541Srgrimes * (non-contiguous physical pages, outside of bus address space, 3921541Srgrimes * crossing DMA page boundaries). 3931541Srgrimes * Return true if special handling needed. 3941541Srgrimes */ 3951541Srgrimes 3961541Srgrimesstatic int 3971541Srgrimesisa_dmarangecheck(caddr_t va, u_int length, int chan) 3981541Srgrimes{ 3991541Srgrimes vm_paddr_t phys, priorpage = 0; 4001541Srgrimes vm_offset_t endva; 4011541Srgrimes u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 4021541Srgrimes 40362587Sitojun endva = (vm_offset_t)round_page((vm_offset_t)va + length); 40462587Sitojun for (; va < (caddr_t) endva ; va += PAGE_SIZE) { 40562587Sitojun phys = trunc_page(pmap_extract(kernel_pmap, (vm_offset_t)va)); 40662587Sitojun#define ISARAM_END RAM_END 40762587Sitojun if (phys == 0) 40862587Sitojun panic("isa_dmacheck: no physical page present"); 40962587Sitojun if (phys >= ISARAM_END) 41062587Sitojun return (1); 41162587Sitojun if (priorpage) { 41262587Sitojun if (priorpage + PAGE_SIZE != phys) 41362587Sitojun return (1); 41462587Sitojun /* check if crossing a DMA page boundary */ 41562587Sitojun if (((u_int)priorpage ^ (u_int)phys) & dma_pgmsk) 41662587Sitojun return (1); 41762587Sitojun } 41862587Sitojun priorpage = phys; 41962587Sitojun } 42062587Sitojun return (0); 42162587Sitojun} 42262587Sitojun 42362587Sitojun/* 42462587Sitojun * Query the progress of a transfer on a DMA channel. 42562587Sitojun * 42662587Sitojun * To avoid having to interrupt a transfer in progress, we sample 42762587Sitojun * each of the high and low databytes twice, and apply the following 42862587Sitojun * logic to determine the correct count. 42962587Sitojun * 43062587Sitojun * Reads are performed with interrupts disabled, thus it is to be 43162587Sitojun * expected that the time between reads is very small. At most 4321541Srgrimes * one rollover in the low count byte can be expected within the 4331541Srgrimes * four reads that are performed. 4341541Srgrimes * 4351541Srgrimes * There are three gaps in which a rollover can occur : 4361541Srgrimes * 4371541Srgrimes * - read low1 4381541Srgrimes * gap1 4391541Srgrimes * - read high1 4401541Srgrimes * gap2 4411541Srgrimes * - read low2 4421541Srgrimes * gap3 4435109Swollman * - read high2 4446399Swollman * 4456399Swollman * If a rollover occurs in gap1 or gap2, the low2 value will be 4467091Swollman * greater than the low1 value. In this case, low2 and high2 are a 4479575Speter * corresponding pair. 44812003Swollman * 44952904Sshin * In any other case, low1 and high1 can be considered to be correct. 45029838Swollman * 45133440Sguido * The function returns the number of bytes remaining in the transfer, 45252904Sshin * or -1 if the channel requested is not active. 45355009Sshin * 45452904Sshin */ 45552904Sshinstatic int 4561541Srgrimesisa_dmastatus_locked(int chan) 4571541Srgrimes{ 4581541Srgrimes u_long cnt = 0; 4591541Srgrimes int ffport, waport; 4601541Srgrimes u_long low1, high1, low2, high2; 4611541Srgrimes 4621541Srgrimes mtx_assert(&isa_dma_lock, MA_OWNED); 4635109Swollman 4646399Swollman /* channel active? */ 4656399Swollman if ((dma_inuse & (1 << chan)) == 0) { 4667091Swollman printf("isa_dmastatus: channel %d not active\n", chan); 4679575Speter return(-1); 46812003Swollman } 46912003Swollman /* channel busy? */ 47029838Swollman 47133440Sguido if (((dma_busy & (1 << chan)) == 0) && 47236192Sdg (dma_auto_mode & (1 << chan)) == 0 ) { 4731541Srgrimes printf("chan %d not busy\n", chan); 4741541Srgrimes return -2 ; 47578243Speter } 47678243Speter if (chan < 4) { /* low DMA controller */ 47778243Speter ffport = DMA1_FFC; 47878243Speter waport = DMA1_CHN(chan) + 1; 47952904Sshin } else { /* high DMA controller */ 48057120Sshin ffport = DMA2_FFC; 48152904Sshin waport = DMA2_CHN(chan - 4) + 2; 48257120Sshin } 4831541Srgrimes 48455205Speter disable_intr(); /* no interrupts Mr Jones! */ 4857088Swollman outb(ffport, 0); /* clear register LSB flipflop */ 4861541Srgrimes low1 = inb(waport); 4871541Srgrimes high1 = inb(waport); 4881541Srgrimes outb(ffport, 0); /* clear again */ 4897088Swollman low2 = inb(waport); 49070951Sbmilekic high2 = inb(waport); 4912169Spaul enable_intr(); /* enable interrupts again */ 49255205Speter 49315026Sphk /* 4942169Spaul * Now decide if a wrap has tried to skew our results. 495 * Note that after TC, the count will read 0xffff, while we want 496 * to return zero, so we add and then mask to compensate. 497 */ 498 if (low1 >= low2) { 499 cnt = (low1 + (high1 << 8) + 1) & 0xffff; 500 } else { 501 cnt = (low2 + (high2 << 8) + 1) & 0xffff; 502 } 503 504 if (chan >= 4) /* high channels move words */ 505 cnt *= 2; 506 return(cnt); 507} 508 509int 510isa_dmastatus(int chan) 511{ 512 int status; 513 514 mtx_lock(&isa_dma_lock); 515 status = isa_dmastatus_locked(chan); 516 mtx_unlock(&isa_dma_lock); 517 518 return (status); 519} 520 521/* 522 * Reached terminal count yet ? 523 */ 524int 525isa_dmatc(int chan) 526{ 527 528 if (chan < 4) 529 return(inb(DMA1_STATUS) & (1 << chan)); 530 else 531 return(inb(DMA2_STATUS) & (1 << (chan & 3))); 532} 533 534/* 535 * Stop a DMA transfer currently in progress. 536 */ 537int 538isa_dmastop(int chan) 539{ 540 int status; 541 542 mtx_lock(&isa_dma_lock); 543 if ((dma_inuse & (1 << chan)) == 0) 544 printf("isa_dmastop: channel %d not acquired\n", chan); 545 546 if (((dma_busy & (1 << chan)) == 0) && 547 ((dma_auto_mode & (1 << chan)) == 0)) { 548 printf("chan %d not busy\n", chan); 549 mtx_unlock(&isa_dma_lock); 550 return -2 ; 551 } 552 553 if ((chan & 4) == 0) { 554 outb(DMA1_SMSK, (chan & 3) | 4 /* disable mask */); 555 } else { 556 outb(DMA2_SMSK, (chan & 3) | 4 /* disable mask */); 557 } 558 559 status = isa_dmastatus_locked(chan); 560 561 mtx_unlock(&isa_dma_lock); 562 563 return (status); 564} 565 566/* 567 * Attach to the ISA PnP descriptor for the AT DMA controller 568 */ 569static struct isa_pnp_id atdma_ids[] = { 570 { 0x0002d041 /* PNP0200 */, "AT DMA controller" }, 571 { 0 } 572}; 573 574static int 575atdma_probe(device_t dev) 576{ 577 int result; 578 579 if ((result = ISA_PNP_PROBE(device_get_parent(dev), dev, atdma_ids)) <= 0) 580 device_quiet(dev); 581 return(result); 582} 583 584static int 585atdma_attach(device_t dev) 586{ 587 return(0); 588} 589 590static device_method_t atdma_methods[] = { 591 /* Device interface */ 592 DEVMETHOD(device_probe, atdma_probe), 593 DEVMETHOD(device_attach, atdma_attach), 594 DEVMETHOD(device_detach, bus_generic_detach), 595 DEVMETHOD(device_shutdown, bus_generic_shutdown), 596 DEVMETHOD(device_suspend, bus_generic_suspend), 597 DEVMETHOD(device_resume, bus_generic_resume), 598 { 0, 0 } 599}; 600 601static driver_t atdma_driver = { 602 "atdma", 603 atdma_methods, 604 1, /* no softc */ 605}; 606 607static devclass_t atdma_devclass; 608 609DRIVER_MODULE(atdma, isa, atdma_driver, atdma_devclass, 0, 0); 610DRIVER_MODULE(atdma, acpi, atdma_driver, atdma_devclass, 0, 0); 611