1 /* $NetBSD: isadma.c,v 1.23 1997/03/21 02:17:11 mycroft Exp $ */ 2 3 #include <sys/param.h> 4 #include <sys/systm.h> 5 #include <sys/file.h> 6 #include <sys/buf.h> 7 #include <sys/syslog.h> 8 #include <sys/malloc.h> 9 #include <sys/proc.h> 10 #include <sys/uio.h> 11 12 #include <vm/vm.h> 13 14 #include <machine/pio.h> 15 16 #include <dev/isa/isareg.h> 17 #include <dev/isa/isadmavar.h> 18 #include <dev/isa/isadmareg.h> 19 20 /* region of physical memory known to be contiguous */ 21 vm_offset_t isaphysmem; 22 static caddr_t dma_bounce[8]; /* XXX */ 23 static char bounced[8]; /* XXX */ 24 #define MAXDMASZ 512 /* XXX */ 25 static u_int8_t dma_finished; 26 27 /* high byte of address is stored in this port for i-th dma channel */ 28 static int dmapageport[2][4] = { 29 {0x87, 0x83, 0x81, 0x82}, 30 {0x8f, 0x8b, 0x89, 0x8a} 31 }; 32 33 static u_int8_t dmamode[4] = { 34 DMA37MD_READ | DMA37MD_SINGLE, 35 DMA37MD_WRITE | DMA37MD_SINGLE, 36 DMA37MD_READ | DMA37MD_LOOP, 37 DMA37MD_WRITE | DMA37MD_LOOP 38 }; 39 40 int isa_dmarangecheck __P((vm_offset_t, u_long, int)); 41 caddr_t isa_allocphysmem __P((caddr_t, unsigned, void (*)(void))); 42 void isa_freephysmem __P((caddr_t, unsigned)); 43 44 inline void 45 isa_dmaunmask(chan) 46 int chan; 47 { 48 int ochan = chan & 3; 49 50 #ifdef ISADMA_DEBUG 51 if (chan < 0 || chan > 7) 52 panic("isa_dmacascade: impossible request"); 53 #endif 54 55 /* set dma channel mode, and set dma channel mode */ 56 if ((chan & 4) == 0) 57 outb(DMA1_SMSK, ochan | DMA37SM_CLEAR); 58 else 59 outb(DMA2_SMSK, ochan | DMA37SM_CLEAR); 60 } 61 62 inline void 63 isa_dmamask(chan) 64 int chan; 65 { 66 int ochan = chan & 3; 67 68 #ifdef ISADMA_DEBUG 69 if (chan < 0 || chan > 7) 70 panic("isa_dmacascade: impossible request"); 71 #endif 72 73 /* set dma channel mode, and set dma channel mode */ 74 if ((chan & 4) == 0) { 75 outb(DMA1_SMSK, ochan | DMA37SM_SET); 76 outb(DMA1_FFC, 0); 77 } else { 78 outb(DMA2_SMSK, ochan | DMA37SM_SET); 79 outb(DMA2_FFC, 0); 80 } 81 } 82 83 /* 84 * isa_dmacascade(): program 8237 DMA controller channel to accept 85 * external dma control by a board. 86 */ 87 void 88 isa_dmacascade(chan) 89 int chan; 90 { 91 int ochan = chan & 3; 92 93 #ifdef ISADMA_DEBUG 94 if (chan < 0 || chan > 7) 95 panic("isa_dmacascade: impossible request"); 96 #endif 97 98 /* set dma channel mode, and set dma channel mode */ 99 if ((chan & 4) == 0) 100 outb(DMA1_MODE, ochan | DMA37MD_CASCADE); 101 else 102 outb(DMA2_MODE, ochan | DMA37MD_CASCADE); 103 104 isa_dmaunmask(chan); 105 } 106 107 /* 108 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 109 * problems by using a bounce buffer. 110 */ 111 void 112 isa_dmastart(flags, addr, nbytes, chan) 113 int flags; 114 caddr_t addr; 115 vm_size_t nbytes; 116 int chan; 117 { 118 vm_offset_t phys; 119 int waport; 120 caddr_t newaddr; 121 int ochan = chan & 3; 122 123 #ifdef ISADMA_DEBUG 124 if (chan < 0 || chan > 7 || 125 ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) : 126 (nbytes >= (1<<16)))) 127 panic("isa_dmastart: impossible request"); 128 #endif 129 130 if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) { 131 if (dma_bounce[chan] == 0) 132 dma_bounce[chan] = 133 /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ 134 (caddr_t) isaphysmem + NBPG*chan; 135 bounced[chan] = 1; 136 newaddr = dma_bounce[chan]; 137 *(int *) newaddr = 0; /* XXX */ 138 /* copy bounce buffer on write */ 139 if ((flags & DMAMODE_READ) == 0) 140 bcopy(addr, newaddr, nbytes); 141 addr = newaddr; 142 } 143 144 /* translate to physical */ 145 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 146 147 isa_dmamask(chan); 148 dma_finished &= ~(1 << chan); 149 150 if ((chan & 4) == 0) { 151 /* set dma channel mode */ 152 outb(DMA1_MODE, ochan | dmamode[flags]); 153 154 /* send start address */ 155 waport = DMA1_CHN(ochan); 156 outb(dmapageport[0][ochan], phys>>16); 157 outb(waport, phys); 158 outb(waport, phys>>8); 159 160 /* send count */ 161 outb(waport + 1, --nbytes); 162 outb(waport + 1, nbytes>>8); 163 } else { 164 /* set dma channel mode */ 165 outb(DMA2_MODE, ochan | dmamode[flags]); 166 167 /* send start address */ 168 waport = DMA2_CHN(ochan); 169 outb(dmapageport[1][ochan], phys>>16); 170 phys >>= 1; 171 outb(waport, phys); 172 outb(waport, phys>>8); 173 174 /* send count */ 175 nbytes >>= 1; 176 outb(waport + 2, --nbytes); 177 outb(waport + 2, nbytes>>8); 178 } 179 180 isa_dmaunmask(chan); 181 } 182 183 void 184 isa_dmaabort(chan) 185 int chan; 186 { 187 188 #ifdef ISADMA_DEBUG 189 if (chan < 0 || chan > 7) 190 panic("isa_dmaabort: impossible request"); 191 #endif 192 193 isa_dmamask(chan); 194 195 bounced[chan] = 0; 196 } 197 198 vm_size_t 199 isa_dmacount(chan) 200 int chan; 201 { 202 int waport; 203 vm_size_t nbytes; 204 int ochan = chan & 3; 205 206 #ifdef ISADMA_DEBUG 207 if (chan < 0 || chan > 7) 208 panic("isa_dmafinished: impossible request"); 209 #endif 210 211 isa_dmamask(chan); 212 213 /* check that the terminal count was reached */ 214 if (!isa_dmafinished(chan)) { 215 /* read count */ 216 if ((chan & 4) == 0) { 217 waport = DMA1_CHN(ochan); 218 nbytes = inb(waport + 1) + 1; 219 nbytes += inb(waport + 1) << 8; 220 } else { 221 waport = DMA2_CHN(ochan); 222 nbytes = inb(waport + 2) + 1; 223 nbytes += inb(waport + 2) << 8; 224 nbytes <<= 1; 225 } 226 } else 227 nbytes = 0; 228 229 isa_dmaunmask(chan); 230 return (nbytes); 231 } 232 233 int 234 isa_dmafinished(chan) 235 int chan; 236 { 237 238 #ifdef ISADMA_DEBUG 239 if (chan < 0 || chan > 7) 240 panic("isa_dmafinished: impossible request"); 241 #endif 242 243 /* check that the terminal count was reached */ 244 if ((chan & 4) == 0) 245 dma_finished |= inb(DMA1_SR) & 0x0f; 246 else 247 dma_finished |= (inb(DMA2_SR) & 0x0f) << 4; 248 249 return ((dma_finished & (1 << chan)) != 0); 250 } 251 252 void 253 isa_dmadone(flags, addr, nbytes, chan) 254 int flags; 255 caddr_t addr; 256 vm_size_t nbytes; 257 int chan; 258 { 259 260 #ifdef ISADMA_DEBUG 261 if (chan < 0 || chan > 7) 262 panic("isa_dmadone: impossible request"); 263 #endif 264 265 isa_dmamask(chan); 266 267 if (!isa_dmafinished(chan)) 268 printf("isa_dmadone: channel %d not finished\n", chan); 269 270 /* copy bounce buffer on read */ 271 if (bounced[chan]) { 272 bcopy(dma_bounce[chan], addr, nbytes); 273 bounced[chan] = 0; 274 } 275 } 276 277 /* 278 * Check for problems with the address range of a DMA transfer 279 * (non-contiguous physical pages, outside of bus address space, 280 * crossing DMA page boundaries). 281 * Return true if special handling needed. 282 */ 283 int 284 isa_dmarangecheck(va, length, chan) 285 vm_offset_t va; 286 u_long length; 287 int chan; 288 { 289 vm_offset_t phys, priorpage = 0, endva; 290 u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 291 292 endva = round_page(va + length); 293 for (; va < endva ; va += NBPG) { 294 phys = trunc_page(pmap_extract(pmap_kernel(), va)); 295 if (phys == 0) 296 panic("isa_dmacheck: no physical page present"); 297 if (phys >= (1<<24)) 298 return 1; 299 if (priorpage) { 300 if (priorpage + NBPG != phys) 301 return 1; 302 /* check if crossing a DMA page boundary */ 303 if ((priorpage ^ phys) & dma_pgmsk) 304 return 1; 305 } 306 priorpage = phys; 307 } 308 return 0; 309 } 310 311 /* head of queue waiting for physmem to become available */ 312 struct buf isa_physmemq; 313 314 /* blocked waiting for resource to become free for exclusive use */ 315 static isaphysmemflag; 316 /* if waited for and call requested when free (B_CALL) */ 317 static void (*isaphysmemunblock) __P((void)); /* needs to be a list */ 318 319 /* 320 * Allocate contiguous physical memory for transfer, returning 321 * a *virtual* address to region. May block waiting for resource. 322 * (assumed to be called at splbio()) 323 */ 324 caddr_t 325 isa_allocphysmem(ca, length, func) 326 caddr_t ca; 327 unsigned length; 328 void (*func) __P((void)); 329 { 330 331 isaphysmemunblock = func; 332 while (isaphysmemflag & B_BUSY) { 333 isaphysmemflag |= B_WANTED; 334 sleep((caddr_t)&isaphysmemflag, PRIBIO); 335 } 336 isaphysmemflag |= B_BUSY; 337 338 return((caddr_t)isaphysmem); 339 } 340 341 /* 342 * Free contiguous physical memory used for transfer. 343 * (assumed to be called at splbio()) 344 */ 345 void 346 isa_freephysmem(va, length) 347 caddr_t va; 348 unsigned length; 349 { 350 351 isaphysmemflag &= ~B_BUSY; 352 if (isaphysmemflag & B_WANTED) { 353 isaphysmemflag &= B_WANTED; 354 wakeup((caddr_t)&isaphysmemflag); 355 if (isaphysmemunblock) 356 (*isaphysmemunblock)(); 357 } 358 } 359