1 /* $NetBSD: isadma.c,v 1.21 1996/10/13 01:37:54 christos 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 /* 45 * isa_dmacascade(): program 8237 DMA controller channel to accept 46 * external dma control by a board. 47 */ 48 void 49 isa_dmacascade(chan) 50 int chan; 51 { 52 53 #ifdef ISADMA_DEBUG 54 if (chan < 0 || chan > 7) 55 panic("isa_dmacascade: impossible request"); 56 #endif 57 58 /* set dma channel mode, and set dma channel mode */ 59 if ((chan & 4) == 0) { 60 outb(DMA1_MODE, chan | DMA37MD_CASCADE); 61 outb(DMA1_SMSK, chan); 62 } else { 63 chan &= 3; 64 65 outb(DMA2_MODE, chan | DMA37MD_CASCADE); 66 outb(DMA2_SMSK, chan); 67 } 68 } 69 70 /* 71 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 72 * problems by using a bounce buffer. 73 */ 74 void 75 isa_dmastart(flags, addr, nbytes, chan) 76 int flags; 77 caddr_t addr; 78 vm_size_t nbytes; 79 int chan; 80 { 81 vm_offset_t phys; 82 int waport; 83 caddr_t newaddr; 84 85 #ifdef ISADMA_DEBUG 86 if (chan < 0 || chan > 7 || 87 ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) : 88 (nbytes >= (1<<16)))) 89 panic("isa_dmastart: impossible request"); 90 #endif 91 92 if (isa_dmarangecheck((vm_offset_t) addr, nbytes, chan)) { 93 if (dma_bounce[chan] == 0) 94 dma_bounce[chan] = 95 /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ 96 (caddr_t) isaphysmem + NBPG*chan; 97 bounced[chan] = 1; 98 newaddr = dma_bounce[chan]; 99 *(int *) newaddr = 0; /* XXX */ 100 /* copy bounce buffer on write */ 101 if ((flags & DMAMODE_READ) == 0) 102 bcopy(addr, newaddr, nbytes); 103 addr = newaddr; 104 } 105 106 /* translate to physical */ 107 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 108 109 dma_finished &= ~(1 << chan); 110 111 if ((chan & 4) == 0) { 112 /* 113 * Program one of DMA channels 0..3. These are 114 * byte mode channels. 115 */ 116 /* set dma channel mode, and reset address ff */ 117 outb(DMA1_MODE, chan | dmamode[flags]); 118 outb(DMA1_FFC, 0); 119 120 /* send start address */ 121 waport = DMA1_CHN(chan); 122 outb(dmapageport[0][chan], phys>>16); 123 outb(waport, phys); 124 outb(waport, phys>>8); 125 126 /* send count */ 127 outb(waport + 1, --nbytes); 128 outb(waport + 1, nbytes>>8); 129 130 /* unmask channel */ 131 outb(DMA1_SMSK, chan | DMA37SM_CLEAR); 132 } else { 133 chan &= 3; 134 135 /* 136 * Program one of DMA channels 4..7. These are 137 * word mode channels. 138 */ 139 /* set dma channel mode, and reset address ff */ 140 outb(DMA2_MODE, chan | dmamode[flags]); 141 outb(DMA2_FFC, 0); 142 143 /* send start address */ 144 waport = DMA2_CHN(chan); 145 outb(dmapageport[1][chan], phys>>16); 146 phys >>= 1; 147 outb(waport, phys); 148 outb(waport, phys>>8); 149 150 /* send count */ 151 nbytes >>= 1; 152 outb(waport + 2, --nbytes); 153 outb(waport + 2, nbytes>>8); 154 155 /* unmask channel */ 156 outb(DMA2_SMSK, chan | DMA37SM_CLEAR); 157 } 158 } 159 160 void 161 isa_dmaabort(chan) 162 int chan; 163 { 164 165 #ifdef ISADMA_DEBUG 166 if (chan < 0 || chan > 7) 167 panic("isa_dmaabort: impossible request"); 168 #endif 169 170 bounced[chan] = 0; 171 172 /* mask channel */ 173 if ((chan & 4) == 0) 174 outb(DMA1_SMSK, DMA37SM_SET | chan); 175 else 176 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 177 } 178 179 int 180 isa_dmafinished(chan) 181 int chan; 182 { 183 184 #ifdef ISADMA_DEBUG 185 if (chan < 0 || chan > 7) 186 panic("isa_dmafinished: impossible request"); 187 #endif 188 189 /* check that the terminal count was reached */ 190 if ((chan & 4) == 0) 191 dma_finished |= inb(DMA1_SR) & 0x0f; 192 else 193 dma_finished |= (inb(DMA2_SR) & 0x0f) << 4; 194 195 return ((dma_finished & (1 << chan)) != 0); 196 } 197 198 void 199 isa_dmadone(flags, addr, nbytes, chan) 200 int flags; 201 caddr_t addr; 202 vm_size_t nbytes; 203 int chan; 204 { 205 206 #ifdef ISADMA_DEBUG 207 if (chan < 0 || chan > 7) 208 panic("isa_dmadone: impossible request"); 209 #endif 210 211 if (!isa_dmafinished(chan)) 212 printf("isa_dmadone: channel %d not finished\n", chan); 213 214 /* mask channel */ 215 if ((chan & 4) == 0) 216 outb(DMA1_SMSK, DMA37SM_SET | chan); 217 else 218 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 219 220 /* copy bounce buffer on read */ 221 if (bounced[chan]) { 222 bcopy(dma_bounce[chan], addr, nbytes); 223 bounced[chan] = 0; 224 } 225 } 226 227 /* 228 * Check for problems with the address range of a DMA transfer 229 * (non-contiguous physical pages, outside of bus address space, 230 * crossing DMA page boundaries). 231 * Return true if special handling needed. 232 */ 233 int 234 isa_dmarangecheck(va, length, chan) 235 vm_offset_t va; 236 u_long length; 237 int chan; 238 { 239 vm_offset_t phys, priorpage = 0, endva; 240 u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 241 242 endva = round_page(va + length); 243 for (; va < endva ; va += NBPG) { 244 phys = trunc_page(pmap_extract(pmap_kernel(), va)); 245 if (phys == 0) 246 panic("isa_dmacheck: no physical page present"); 247 if (phys >= (1<<24)) 248 return 1; 249 if (priorpage) { 250 if (priorpage + NBPG != phys) 251 return 1; 252 /* check if crossing a DMA page boundary */ 253 if ((priorpage ^ phys) & dma_pgmsk) 254 return 1; 255 } 256 priorpage = phys; 257 } 258 return 0; 259 } 260 261 /* head of queue waiting for physmem to become available */ 262 struct buf isa_physmemq; 263 264 /* blocked waiting for resource to become free for exclusive use */ 265 static isaphysmemflag; 266 /* if waited for and call requested when free (B_CALL) */ 267 static void (*isaphysmemunblock) __P((void)); /* needs to be a list */ 268 269 /* 270 * Allocate contiguous physical memory for transfer, returning 271 * a *virtual* address to region. May block waiting for resource. 272 * (assumed to be called at splbio()) 273 */ 274 caddr_t 275 isa_allocphysmem(ca, length, func) 276 caddr_t ca; 277 unsigned length; 278 void (*func) __P((void)); 279 { 280 281 isaphysmemunblock = func; 282 while (isaphysmemflag & B_BUSY) { 283 isaphysmemflag |= B_WANTED; 284 sleep((caddr_t)&isaphysmemflag, PRIBIO); 285 } 286 isaphysmemflag |= B_BUSY; 287 288 return((caddr_t)isaphysmem); 289 } 290 291 /* 292 * Free contiguous physical memory used for transfer. 293 * (assumed to be called at splbio()) 294 */ 295 void 296 isa_freephysmem(va, length) 297 caddr_t va; 298 unsigned length; 299 { 300 301 isaphysmemflag &= ~B_BUSY; 302 if (isaphysmemflag & B_WANTED) { 303 isaphysmemflag &= B_WANTED; 304 wakeup((caddr_t)&isaphysmemflag); 305 if (isaphysmemunblock) 306 (*isaphysmemunblock)(); 307 } 308 } 309