1 /* $NetBSD: isadma.c,v 1.12 1995/04/17 12:09:11 cgd 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/uio.h> 10 11 #include <vm/vm.h> 12 13 #include <machine/pio.h> 14 15 #include <dev/isa/isareg.h> 16 #include <dev/isa/isadmavar.h> 17 #include <dev/isa/isadmareg.h> 18 19 /* region of physical memory known to be contiguous */ 20 vm_offset_t isaphysmem; 21 static caddr_t dma_bounce[8]; /* XXX */ 22 static char bounced[8]; /* XXX */ 23 #define MAXDMASZ 512 /* XXX */ 24 25 /* high byte of address is stored in this port for i-th dma channel */ 26 static int dmapageport[8] = 27 { 0x87, 0x83, 0x81, 0x82, 0x8f, 0x8b, 0x89, 0x8a }; 28 29 /* 30 * isa_dmacascade(): program 8237 DMA controller channel to accept 31 * external dma control by a board. 32 */ 33 void 34 isa_dmacascade(chan) 35 int chan; 36 { 37 38 #ifdef DIAGNOSTIC 39 if (chan < 0 || chan > 7) 40 panic("isa_dmacascade: impossible request"); 41 #endif 42 43 /* set dma channel mode, and set dma channel mode */ 44 if ((chan & 4) == 0) { 45 outb(DMA1_MODE, DMA37MD_CASCADE | chan); 46 outb(DMA1_SMSK, chan); 47 } else { 48 outb(DMA2_MODE, DMA37MD_CASCADE | (chan & 3)); 49 outb(DMA2_SMSK, chan & 3); 50 } 51 } 52 53 /* 54 * isa_dmastart(): program 8237 DMA controller channel, avoid page alignment 55 * problems by using a bounce buffer. 56 */ 57 void 58 isa_dmastart(flags, addr, nbytes, chan) 59 int flags; 60 caddr_t addr; 61 vm_size_t nbytes; 62 int chan; 63 { 64 vm_offset_t phys; 65 int waport; 66 caddr_t newaddr; 67 68 #ifdef DIAGNOSTIC 69 if (chan < 0 || chan > 7 || 70 ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) : 71 (nbytes >= (1<<16)))) 72 panic("isa_dmastart: impossible request"); 73 #endif 74 75 if (isa_dmarangecheck(addr, nbytes, chan)) { 76 if (dma_bounce[chan] == 0) 77 dma_bounce[chan] = 78 /*(caddr_t)malloc(MAXDMASZ, M_TEMP, M_WAITOK);*/ 79 (caddr_t) isaphysmem + NBPG*chan; 80 bounced[chan] = 1; 81 newaddr = dma_bounce[chan]; 82 *(int *) newaddr = 0; /* XXX */ 83 /* copy bounce buffer on write */ 84 if ((flags & B_READ) == 0) 85 bcopy(addr, newaddr, nbytes); 86 addr = newaddr; 87 } 88 89 /* translate to physical */ 90 phys = pmap_extract(pmap_kernel(), (vm_offset_t)addr); 91 92 if ((chan & 4) == 0) { 93 /* 94 * Program one of DMA channels 0..3. These are 95 * byte mode channels. 96 */ 97 /* set dma channel mode, and reset address ff */ 98 if (flags & B_READ) 99 outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_WRITE); 100 else 101 outb(DMA1_MODE, chan | DMA37MD_SINGLE | DMA37MD_READ); 102 outb(DMA1_FFC, 0); 103 104 /* send start address */ 105 waport = DMA1_CHN(chan); 106 outb(waport, phys); 107 outb(waport, phys>>8); 108 outb(dmapageport[chan], phys>>16); 109 110 /* send count */ 111 outb(waport + 1, --nbytes); 112 outb(waport + 1, nbytes>>8); 113 114 /* unmask channel */ 115 outb(DMA1_SMSK, chan | DMA37SM_CLEAR); 116 } else { 117 /* 118 * Program one of DMA channels 4..7. These are 119 * word mode channels. 120 */ 121 /* set dma channel mode, and reset address ff */ 122 if (flags & B_READ) 123 outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_WRITE); 124 else 125 outb(DMA2_MODE, (chan & 3) | DMA37MD_SINGLE | DMA37MD_READ); 126 outb(DMA2_FFC, 0); 127 128 /* send start address */ 129 waport = DMA2_CHN(chan & 3); 130 outb(waport, phys>>1); 131 outb(waport, phys>>9); 132 outb(dmapageport[chan], phys>>16); 133 134 /* send count */ 135 nbytes >>= 1; 136 outb(waport + 2, --nbytes); 137 outb(waport + 2, nbytes>>8); 138 139 /* unmask channel */ 140 outb(DMA2_SMSK, (chan & 3) | DMA37SM_CLEAR); 141 } 142 } 143 144 void 145 isa_dmaabort(chan) 146 int chan; 147 { 148 149 #ifdef DIAGNOSTIC 150 if (chan < 0 || chan > 7) 151 panic("isa_dmaabort: impossible request"); 152 #endif 153 154 bounced[chan] = 0; 155 156 /* mask channel */ 157 if ((chan & 4) == 0) 158 outb(DMA1_SMSK, DMA37SM_SET | chan); 159 else 160 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 161 } 162 163 void 164 isa_dmadone(flags, addr, nbytes, chan) 165 int flags; 166 caddr_t addr; 167 vm_size_t nbytes; 168 int chan; 169 { 170 u_char tc; 171 172 #ifdef DIAGNOSTIC 173 if (chan < 0 || chan > 7) 174 panic("isa_dmadone: impossible request"); 175 #endif 176 177 /* check that the terminal count was reached */ 178 if ((chan & 4) == 0) 179 tc = inb(DMA1_SR) & (1 << chan); 180 else 181 tc = inb(DMA2_SR) & (1 << (chan & 3)); 182 if (tc == 0) 183 /* XXX probably should panic or something */ 184 log(LOG_ERR, "dma channel %d not finished\n", chan); 185 186 /* mask channel */ 187 if ((chan & 4) == 0) 188 outb(DMA1_SMSK, DMA37SM_SET | chan); 189 else 190 outb(DMA2_SMSK, DMA37SM_SET | (chan & 3)); 191 192 /* copy bounce buffer on read */ 193 if (bounced[chan]) { 194 bcopy(dma_bounce[chan], addr, nbytes); 195 bounced[chan] = 0; 196 } 197 } 198 199 /* 200 * Check for problems with the address range of a DMA transfer 201 * (non-contiguous physical pages, outside of bus address space, 202 * crossing DMA page boundaries). 203 * Return true if special handling needed. 204 */ 205 int 206 isa_dmarangecheck(va, length, chan) 207 vm_offset_t va; 208 u_long length; 209 int chan; 210 { 211 vm_offset_t phys, priorpage = 0, endva; 212 u_int dma_pgmsk = (chan & 4) ? ~(128*1024-1) : ~(64*1024-1); 213 214 endva = round_page(va + length); 215 for (; va < endva ; va += NBPG) { 216 phys = trunc_page(pmap_extract(pmap_kernel(), va)); 217 if (phys == 0) 218 panic("isa_dmacheck: no physical page present"); 219 if (phys >= (1<<24)) 220 return 1; 221 if (priorpage) { 222 if (priorpage + NBPG != phys) 223 return 1; 224 /* check if crossing a DMA page boundary */ 225 if ((priorpage ^ phys) & dma_pgmsk) 226 return 1; 227 } 228 priorpage = phys; 229 } 230 return 0; 231 } 232 233 /* head of queue waiting for physmem to become available */ 234 struct buf isa_physmemq; 235 236 /* blocked waiting for resource to become free for exclusive use */ 237 static isaphysmemflag; 238 /* if waited for and call requested when free (B_CALL) */ 239 static void (*isaphysmemunblock)(); /* needs to be a list */ 240 241 /* 242 * Allocate contiguous physical memory for transfer, returning 243 * a *virtual* address to region. May block waiting for resource. 244 * (assumed to be called at splbio()) 245 */ 246 caddr_t 247 isa_allocphysmem(caddr_t va, unsigned length, void (*func)()) { 248 249 isaphysmemunblock = func; 250 while (isaphysmemflag & B_BUSY) { 251 isaphysmemflag |= B_WANTED; 252 sleep((caddr_t)&isaphysmemflag, PRIBIO); 253 } 254 isaphysmemflag |= B_BUSY; 255 256 return((caddr_t)isaphysmem); 257 } 258 259 /* 260 * Free contiguous physical memory used for transfer. 261 * (assumed to be called at splbio()) 262 */ 263 void 264 isa_freephysmem(caddr_t va, unsigned length) { 265 266 isaphysmemflag &= ~B_BUSY; 267 if (isaphysmemflag & B_WANTED) { 268 isaphysmemflag &= B_WANTED; 269 wakeup((caddr_t)&isaphysmemflag); 270 if (isaphysmemunblock) 271 (*isaphysmemunblock)(); 272 } 273 } 274