1 /*- 2 * Copyright (c) 1993 Charles Hannum. 3 * Copyright (c) 1991 The Regents of the University of California. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * William Jolitz. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by the University of 20 * California, Berkeley and its contributors. 21 * 4. Neither the name of the University nor the names of its contributors 22 * may be used to endorse or promote products derived from this software 23 * without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 * SUCH DAMAGE. 36 * 37 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 38 * $Id: isadma.c,v 1.4 1993/10/22 20:24:14 mycroft Exp $ 39 */ 40 41 /* 42 * code to deal with ISA DMA 43 */ 44 45 #include <sys/param.h> 46 #include <sys/systm.h> 47 #include <sys/conf.h> 48 #include <sys/file.h> 49 #include <sys/syslog.h> 50 #include <sys/device.h> 51 #include <vm/vm.h> 52 53 #include <machine/cpu.h> 54 #include <machine/pio.h> 55 56 #include <i386/isa/isa.h> 57 #include <i386/isa/isavar.h> 58 #include <i386/isa/ic/i8237.h> 59 60 /* 61 ** Register definitions for DMA controller 1 (channels 0..3): 62 */ 63 #define DMA1_CHN(c) (IO_DMA1 + 1*(2*(c))) /* addr reg for channel c */ 64 #define DMA1_SR (IO_DMA1 + 1*8) /* status register */ 65 #define DMA1_SMSK (IO_DMA1 + 1*10) /* single mask register */ 66 #define DMA1_MODE (IO_DMA1 + 1*11) /* mode register */ 67 #define DMA1_FFC (IO_DMA1 + 1*12) /* clear first/last FF */ 68 69 /* 70 ** Register definitions for DMA controller 2 (channels 4..7): 71 */ 72 #define DMA2_CHN(c) (IO_DMA2 + 2*(2*(c))) /* addr reg for channel c */ 73 #define DMA2_SR (IO_DMA2 + 2*8) /* status register */ 74 #define DMA2_SMSK (IO_DMA2 + 2*10) /* single mask register */ 75 #define DMA2_MODE (IO_DMA2 + 2*11) /* mode register */ 76 #define DMA2_FFC (IO_DMA2 + 2*12) /* clear first/last FF */ 77 78 /* region of physical memory known to be contiguous */ 79 caddr_t isaphysmem; 80 static caddr_t bouncebuf[8]; /* XXX */ 81 static caddr_t bounced[8]; /* XXX */ 82 static vm_size_t bouncesize[8]; /* XXX */ 83 84 /* high byte of address is stored in this port for i-th dma channel */ 85 static u_short dmapageport[8] = 86 { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 87 88 /* 89 * at_setup_dmachan(): allocate bounce buffer and check for conflicts. 90 * 91 * XXX 92 * This sucks. We should be able to bounce more than NBPG bytes, but I 93 * don't feel like writing the code to do contiguous allocation right now. 94 */ 95 void 96 at_setup_dmachan(chan, max) 97 int chan; 98 u_long max; 99 { 100 101 #ifdef DIAGNOSTIC 102 if (chan > 7 || chan < 0) 103 panic("at_setup_dmachan: impossible request"); 104 if (max > NBPG) 105 panic("at_setup_dmachan: what a lose"); 106 #endif 107 108 /* XXX check for drq conflict? */ 109 110 bouncebuf[chan] = (caddr_t) isaphysmem + NBPG*chan; 111 } 112 113 /* 114 * at_dma_cascade(): program 8237 DMA controller channel to accept 115 * external dma control by a board. 116 */ 117 void 118 at_dma_cascade(chan) 119 int chan; 120 { 121 122 #ifdef DIAGNOSTIC 123 if (chan > 7 || chan < 0) 124 panic("at_dma_cascade: impossible request"); 125 #endif 126 127 /* XXX check for drq conflict? */ 128 129 /* set dma channel mode, and set dma channel mode */ 130 if ((chan & 4) == 0) { 131 outb(DMA1_MODE, DMA37MD_CASCADE | chan); 132 outb(DMA1_SMSK, chan); 133 } else { 134 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 135 outb(DMA2_SMSK, chan & 3); 136 } 137 } 138 139 /* 140 * at_dma(): program 8237 DMA controller channel, avoid page alignment 141 * problems by using a bounce buffer. 142 */ 143 void 144 at_dma(read, addr, nbytes, chan) 145 int read; 146 caddr_t addr; 147 vm_size_t nbytes; 148 int chan; 149 { 150 vm_offset_t phys; 151 int waport; 152 caddr_t newaddr; 153 154 #ifdef DIAGNOSTIC 155 if (chan > 7 || chan < 0 || 156 (chan < 4 && nbytes > (1<<16)) || 157 (chan >= 4 && (nbytes > (1<<17) || nbytes & 1 || (u_int)addr & 1))) 158 panic("at_dma: impossible request"); 159 #endif 160 161 if (at_dma_rangecheck((vm_offset_t)addr, nbytes, chan)) { 162 if (bouncebuf[chan] == 0) 163 /* some twit forgot to call at_setup_dmachan() */ 164 panic("at_dma: no bounce buffer"); 165 /* XXX totally braindead; NBPG is not enough */ 166 if (nbytes > NBPG) 167 panic("at_dma: transfer too large"); 168 bouncesize[chan] = nbytes; 169 newaddr = bouncebuf[chan]; 170 /* copy bounce buffer on write */ 171 if (!read) { 172 bcopy(addr, newaddr, nbytes); 173 bounced[chan] = 0; 174 } else 175 bounced[chan] = addr; 176 addr = newaddr; 177 } 178 179 /* translate to physical */ 180 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 181 182 if ((chan & 4) == 0) { 183 /* 184 * Program one of DMA channels 0..3. These are 185 * byte mode channels. 186 */ 187 /* set dma channel mode, and reset address ff */ 188 if (read) 189 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|chan); 190 else 191 outb(DMA1_MODE, DMA37MD_SINGLE|DMA37MD_READ|chan); 192 outb(DMA1_FFC, 0); 193 194 /* send start address */ 195 waport = DMA1_CHN(chan); 196 outb(waport, phys); 197 outb(waport, phys>>8); 198 outb(dmapageport[chan], phys>>16); 199 200 /* send count */ 201 outb(waport + 1, --nbytes); 202 outb(waport + 1, nbytes>>8); 203 204 /* unmask channel */ 205 outb(DMA1_SMSK, DMA37SM_CLEAR | chan); 206 } else { 207 /* 208 * Program one of DMA channels 4..7. These are 209 * word mode channels. 210 */ 211 /* set dma channel mode, and reset address ff */ 212 if (read) 213 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_WRITE|(chan&3)); 214 else 215 outb(DMA2_MODE, DMA37MD_SINGLE|DMA37MD_READ|(chan&3)); 216 outb(DMA2_FFC, 0); 217 218 /* send start address */ 219 waport = DMA2_CHN(chan & 3); 220 outb(waport, phys>>1); 221 outb(waport, phys>>9); 222 outb(dmapageport[chan], phys>>16); 223 224 /* send count */ 225 nbytes >>= 1; 226 outb(waport + 2, --nbytes); 227 outb(waport + 2, nbytes>>8); 228 229 /* unmask channel */ 230 outb(DMA2_SMSK, DMA37SM_CLEAR | (chan & 3)); 231 } 232 } 233 234 /* 235 * Abort a DMA request, clearing the bounce buffer, if any. 236 */ 237 void 238 at_dma_abort(chan) 239 int chan; 240 { 241 242 #ifdef DIAGNOSTIC 243 if (chan > 7 || chan < 0) 244 panic("at_dma_abort: impossible request"); 245 #endif 246 247 bounced[chan] = 0; 248 249 /* mask channel */ 250 if ((chan & 4) == 0) 251 outb(DMA1_SMSK, DMA37SM_SET | chan); 252 else 253 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 254 } 255 256 /* 257 * End a DMA request, copying data from the bounce buffer, if any, 258 * when reading. 259 */ 260 void 261 at_dma_terminate(chan) 262 unsigned chan; 263 { 264 u_char tc; 265 266 #ifdef DIAGNOSTIC 267 if (chan > 7 || chan < 0) 268 panic("at_dma_terminate: impossible request"); 269 #endif 270 271 /* check that the terminal count was reached */ 272 if ((chan & 4) == 0) 273 tc = inb(DMA1_SR) & (1 << chan); 274 else 275 tc = inb(DMA2_SR) & (1 << (chan & 3)); 276 if (tc == 0) 277 /* XXX probably should panic or something */ 278 log(LOG_ERR, "dma channel %d not finished\n", chan); 279 280 /* copy bounce buffer on read */ 281 if (bounced[chan]) { 282 bcopy(bouncebuf[chan], bounced[chan], bouncesize[chan]); 283 bounced[chan] = 0; 284 } 285 286 /* mask channel */ 287 if ((chan & 4) == 0) 288 outb(DMA1_SMSK, DMA37SM_SET | chan); 289 else 290 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 291 } 292 293 /* 294 * Check for problems with the address range of a DMA transfer 295 * (non-contiguous physical pages, outside of bus address space, 296 * crossing DMA page boundaries). 297 * Return true if special handling needed. 298 */ 299 int 300 at_dma_rangecheck(va, length, chan) 301 vm_offset_t va; 302 u_long length; 303 int chan; 304 { 305 vm_offset_t phys, priorpage = 0, endva; 306 u_int dma_pgmsk = (chan&4) ? ~(128*1024-1) : ~(64*1024-1); 307 308 endva = round_page(va + length); 309 for (; va < endva ; va += NBPG) { 310 phys = trunc_page(pmap_extract(pmap_kernel(), va)); 311 if (phys == 0) 312 panic("at_dma_rangecheck: no physical page present"); 313 if (phys >= (1<<24)) 314 return 1; 315 if (priorpage) { 316 if (priorpage + NBPG != phys) 317 return 1; 318 /* check if crossing a DMA page boundary */ 319 if ((priorpage ^ phys) & dma_pgmsk) 320 return 1; 321 } 322 priorpage = phys; 323 } 324 return 0; 325 } 326