1/* $NetBSD: isadma.c,v 1.11 2008/07/05 08:46:25 tsutsui Exp $ */ 2/* $OpenBSD: isadma.c,v 1.2 1996/11/23 21:45:34 kstailey Exp $ */ 3/* NetBSD: isadma.c,v 1.19 1996/04/29 20:03:26 christos Exp */ 4 5#include <sys/cdefs.h> 6__KERNEL_RCSID(0, "$NetBSD: isadma.c,v 1.11 2008/07/05 08:46:25 tsutsui Exp $"); 7 8#include <sys/param.h> 9#include <sys/systm.h> 10#include <sys/device.h> 11#include <sys/file.h> 12#include <sys/buf.h> 13#include <sys/syslog.h> 14#include <sys/malloc.h> 15#include <sys/uio.h> 16 17#include <uvm/uvm_extern.h> 18 19#include <machine/pio.h> 20 21#include <dev/isa/isareg.h> 22#include <dev/isa/isavar.h> 23#include <dev/isa/isadmavar.h> 24#include <arch/arc/isa/isadmareg.h> /*XXX*/ 25 26struct dma_info { 27 int flags; 28 int active; 29 void *addr; 30 bus_size_t nbytes; 31 struct isadma_seg phys[1]; 32}; 33 34static struct isadma_softc *isadma_sc; /*XXX ugly */ 35static struct dma_info dma_info[8]; 36static uint8_t dma_finished; 37 38/* high byte of address is stored in this port for i-th dma channel */ 39static int dmapageport[2][4] = { 40 {0x87, 0x83, 0x81, 0x82}, 41 {0x8f, 0x8b, 0x89, 0x8a} 42}; 43 44static uint8_t dmamode[4] = { 45 DMA37MD_READ | DMA37MD_SINGLE, 46 DMA37MD_WRITE | DMA37MD_SINGLE, 47 DMA37MD_READ | DMA37MD_LOOP, 48 DMA37MD_WRITE | DMA37MD_LOOP 49}; 50 51static int isadmamatch(device_t, cfdata_t, void *); 52static void isadmaattach(device_t, device_t, void *); 53 54struct isadma_softc { 55 device_t sc_dev; 56 bus_space_tag_t sc_iot; 57 bus_space_handle_t sc_ioh1; 58 bus_space_handle_t sc_ioh2; 59} 60 61CFATTACH_DECL_NEW(isadma, sizeof(struct isadma_softc), 62 isadmamatch, isadmaattach, NULL, NULL); 63 64struct cfdriver isadma_cd = { 65 NULL, "isadma", DV_DULL, 1 66}; 67 68static int 69isadmamatch(device_t parent, cfdata_t cf, void *aux) 70{ 71 struct isa_attach_args *ia = aux; 72 73 /* Sure we exist */ 74 ia->ia_iosize = 0; 75 return 1; 76} 77 78static void 79isadmaattach(device_t parent, device_t self, void *aux) 80{ 81 struct isadma_softc *sc = device_private(self); 82 struct isa_attach_args *ia = aux; 83 bus_space_tag_t iot; 84 bus_space_handle_t ioh; 85 86 sc->sc_dev = self; 87 88 aprint_normal("\n"); 89 90 iot = sc->sc_iot = ia->ia_iot; 91 if (bus_space_map(iot, IO_DMA1, DMA_NREGS, 0, &ioh)) 92 panic("%s: couldn't map I/O ports", __func__); 93 sc->sc_ioh1 = ioh; 94 if (bus_space_map(iot, IO_DMA2, DMA_NREGS*2, 0, &ioh)) 95 panic("%s: couldn't map I/O ports", __func__); 96 sc->sc_ioh2 = ioh; 97 isadma_sc = sc; 98} 99 100/* 101 * isadma_cascade(): program 8237 DMA controller channel to accept 102 * external dma control by a board. 103 */ 104void 105isadma_cascade(int chan) 106{ 107 struct isadma_softc *sc = isadma_sc; 108 bus_space_tag_t iot = sc->sc_iot; 109 110#ifdef ISADMA_DEBUG 111 if (chan < 0 || chan > 7) 112 panic("%s: impossible request", __func__); 113#endif 114 115 /* set dma channel mode, and set dma channel mode */ 116 if ((chan & 4) == 0) { 117 bus_space_write_1(iot, sc->sc_ioh1, DMA1_MODE, 118 chan | DMA37MD_CASCADE); 119 bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK, chan); 120 } else { 121 chan &= 3; 122 123 bus_space_write_1(iot, sc->sc_ioh2, DMA2_MODE, 124 chan | DMA37MD_CASCADE); 125 bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK, chan); 126 } 127} 128 129/* 130 * isadma_start(): program 8237 DMA controller channel, avoid page alignment 131 * problems by using a bounce buffer. 132 */ 133void 134isadma_start(void *addr, bus_size_t nbytes, int chan, int flags) 135{ 136 struct dma_info *di; 137 int waport; 138 int mflags; 139 struct isadma_softc *sc = isadma_sc; 140 bus_space_tag_t iot = sc->sc_iot; 141 bus_space_handle_t ioh; 142 143#ifdef ISADMA_DEBUG 144 if (chan < 0 || chan > 7 || 145 (((flags & DMAMODE_READ) != 0) + ((flags & DMAMODE_WRITE) != 0) + 146 ((flags & DMAMODE_LOOP) != 0) != 1) || 147 ((chan & 4) ? (nbytes >= (1<<17) || nbytes & 1 || (u_int)addr & 1) : 148 (nbytes >= (1<<16)))) 149 panic("%s: impossible request", __func__); 150#endif 151 152 di = dma_info+chan; 153 if (di->active) { 154 log(LOG_ERR,"%s: old request active on %d\n", __func__, chan); 155 isadma_abort(chan); 156 } 157 158 di->flags = flags; 159 di->active = 1; 160 di->addr = addr; 161 di->nbytes = nbytes; 162 163 mflags = ISADMA_MAP_WAITOK | ISADMA_MAP_BOUNCE | ISADMA_MAP_CONTIG; 164 mflags |= (chan & 4) ? ISADMA_MAP_16BIT : ISADMA_MAP_8BIT; 165 166 if (isadma_map(addr, nbytes, di->phys, mflags) != 1) 167 panic("%s: cannot map", __func__); 168 169 /* XXX Will this do what we want with DMAMODE_LOOP? */ 170 if ((flags & DMAMODE_READ) == 0) 171 isadma_copytobuf(addr, nbytes, 1, di->phys); 172 173 dma_finished &= ~(1 << chan); 174 175 if ((chan & 4) == 0) { 176 ioh = sc->sc_ioh1; 177 /* 178 * Program one of DMA channels 0..3. These are 179 * byte mode channels. 180 */ 181 /* set dma channel mode, and reset address ff */ 182 bus_space_write_1(iot, ioh, DMA1_MODE, chan | dmamode[flags]); 183 bus_space_write_1(iot, ioh, DMA1_FFC, 0); 184 185 /* send start address */ 186 waport = DMA1_CHN(chan); 187 outb(dmapageport[0][chan], di->phys[0].addr>>16); 188 outb(waport, di->phys[0].addr); 189 outb(waport, di->phys[0].addr>>8); 190 191 /* send count */ 192 outb(waport + 1, --nbytes); 193 outb(waport + 1, nbytes>>8); 194 195 /* unmask channel */ 196 bus_space_write_1(iot, ioh, DMA1_SMSK, chan | DMA37SM_CLEAR); 197 } else { 198 ioh = sc->sc_ioh2; 199 /* 200 * Program one of DMA channels 4..7. These are 201 * word mode channels. 202 */ 203 /* set dma channel mode, and reset address ff */ 204 bus_space_write_1(iot, ioh, DMA2_MODE, 205 (chan & 3) | dmamode[flags]); 206 bus_space_write_1(iot, ioh, DMA2_FFC, 0); 207 208 /* send start address */ 209 waport = DMA2_CHN(chan & 3); 210 outb(dmapageport[1][chan], di->phys[0].addr >> 16); 211 outb(waport, di->phys[0].addr >> 1); 212 outb(waport, di->phys[0].addr >> 9); 213 214 /* send count */ 215 nbytes >>= 1; 216 outb(waport + 2, --nbytes); 217 outb(waport + 2, nbytes>>8); 218 219 /* unmask channel */ 220 bus_space_write_1(iot, ioh, DMA2_SMSK, 221 (chan & 3) | DMA37SM_CLEAR); 222 } 223} 224 225void 226isadma_abort(int chan) 227{ 228 struct dma_info *di; 229 struct isadma_softc *sc = isadma_sc; 230 bus_space_tag_t iot = sc->sc_iot; 231 232#ifdef ISADMA_DEBUG 233 if (chan < 0 || chan > 7) 234 panic("%s: impossible request", __func__); 235#endif 236 237 di = dma_info+chan; 238 if (! di->active) { 239 log(LOG_ERR,"%s: no request active on %d\n", __func__, chan); 240 return; 241 } 242 243 /* mask channel */ 244 if ((chan & 4) == 0) 245 bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK, 246 DMA37SM_SET | chan); 247 else 248 bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK, 249 DMA37SM_SET | (chan & 3)); 250 251 isadma_unmap(di->addr, di->nbytes, 1, di->phys); 252 di->active = 0; 253} 254 255int 256isadma_finished(int chan) 257{ 258 struct isadma_softc *sc = isadma_sc; 259 bus_space_tag_t iot = sc->sc_iot; 260 261#ifdef ISADMA_DEBUG 262 if (chan < 0 || chan > 7) 263 panic("%s: impossible request", __func__); 264#endif 265 266 /* check that the terminal count was reached */ 267 if ((chan & 4) == 0) 268 dma_finished |= 269 bus_space_read_1(iot, sc->sc_ioh1, DMA1_SR) & 0x0f; 270 else 271 dma_finished |= 272 (bus_space_read_1(iot, sc->sc_ioh2, DMA2_SR) & 0x0f) << 4; 273 274 return (dma_finished & (1 << chan)) != 0; 275} 276 277void 278isadma_done(int chan) 279{ 280 struct dma_info *di; 281 u_char tc; 282 struct isadma_softc *sc = isadma_sc; 283 bus_space_tag_t iot = sc->sc_iot; 284 285#ifdef DIAGNOSTIC 286 if (chan < 0 || chan > 7) 287 panic("%s: impossible request", __func__); 288#endif 289 290 di = dma_info+chan; 291 if (! di->active) { 292 log(LOG_ERR,"%s: no request active on %d\n", __func__, chan); 293 return; 294 } 295 296 /* check that the terminal count was reached */ 297 if ((chan & 4) == 0) 298 tc = bus_space_read_1(iot, sc->sc_ioh1, DMA1_SR) & (1 << chan); 299 else 300 tc = bus_space_read_1(iot, sc->sc_ioh2, DMA2_SR) & 301 (1 << (chan & 3)); 302 if (tc == 0) 303 /* XXX probably should panic or something */ 304 log(LOG_ERR, "dma channel %d not finished\n", chan); 305 306 /* mask channel */ 307 if ((chan & 4) == 0) 308 bus_space_write_1(iot, sc->sc_ioh1, DMA1_SMSK, 309 DMA37SM_SET | chan); 310 else 311 bus_space_write_1(iot, sc->sc_ioh2, DMA2_SMSK, 312 DMA37SM_SET | (chan & 3)); 313 314 /* XXX Will this do what we want with DMAMODE_LOOP? */ 315 if (di->flags & DMAMODE_READ) 316 isadma_copyfrombuf(di->addr, di->nbytes, 1, di->phys); 317 318 isadma_unmap(di->addr, di->nbytes, 1, di->phys); 319 di->active = 0; 320} 321