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