1 1.68 andvar /* $NetBSD: isadma.c,v 1.68 2025/06/27 21:36:25 andvar Exp $ */ 2 1.26 thorpej 3 1.26 thorpej /*- 4 1.43 thorpej * Copyright (c) 1997, 1998, 2000 The NetBSD Foundation, Inc. 5 1.26 thorpej * All rights reserved. 6 1.26 thorpej * 7 1.26 thorpej * This code is derived from software contributed to The NetBSD Foundation 8 1.26 thorpej * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, 9 1.26 thorpej * NASA Ames Research Center. 10 1.26 thorpej * 11 1.26 thorpej * Redistribution and use in source and binary forms, with or without 12 1.26 thorpej * modification, are permitted provided that the following conditions 13 1.26 thorpej * are met: 14 1.26 thorpej * 1. Redistributions of source code must retain the above copyright 15 1.26 thorpej * notice, this list of conditions and the following disclaimer. 16 1.26 thorpej * 2. Redistributions in binary form must reproduce the above copyright 17 1.26 thorpej * notice, this list of conditions and the following disclaimer in the 18 1.26 thorpej * documentation and/or other materials provided with the distribution. 19 1.26 thorpej * 20 1.26 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 1.26 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 1.26 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 1.26 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 1.26 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 1.26 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 1.26 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 1.26 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 1.26 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 1.26 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 1.26 thorpej * POSSIBILITY OF SUCH DAMAGE. 31 1.26 thorpej */ 32 1.26 thorpej 33 1.26 thorpej /* 34 1.26 thorpej * Device driver for the ISA on-board DMA controller. 35 1.26 thorpej */ 36 1.49 lukem 37 1.49 lukem #include <sys/cdefs.h> 38 1.68 andvar __KERNEL_RCSID(0, "$NetBSD: isadma.c,v 1.68 2025/06/27 21:36:25 andvar Exp $"); 39 1.8 cgd 40 1.3 mycroft #include <sys/param.h> 41 1.3 mycroft #include <sys/systm.h> 42 1.19 christos #include <sys/proc.h> 43 1.26 thorpej #include <sys/device.h> 44 1.34 thorpej #include <sys/malloc.h> 45 1.5 mycroft 46 1.56 ad #include <sys/bus.h> 47 1.3 mycroft 48 1.12 cgd #include <dev/isa/isareg.h> 49 1.26 thorpej #include <dev/isa/isavar.h> 50 1.12 cgd #include <dev/isa/isadmavar.h> 51 1.12 cgd #include <dev/isa/isadmareg.h> 52 1.1 mycroft 53 1.34 thorpej struct isa_mem *isa_mem_head; 54 1.27 augustss 55 1.26 thorpej /* 56 1.67 jdolecek * DMA Channel to Address Page Register offset mapping 57 1.67 jdolecek * 58 1.67 jdolecek * Offset from IO_DMAPG is stored in this 2D array -- first dimension is 59 1.67 jdolecek * the DMA controller, second dimension is the DMA channel. 60 1.67 jdolecek * 61 1.67 jdolecek * e.g. dmapageport[0][1] gives us the offset for DMA ch 1 on DMA1 62 1.26 thorpej */ 63 1.67 jdolecek static const int dmapageport[2][4] = { 64 1.67 jdolecek {0x6, 0x2, 0x0, 0x1}, 65 1.67 jdolecek {0xe, 0xa, 0x8, 0x9} 66 1.15 mycroft }; 67 1.15 mycroft 68 1.67 jdolecek static const u_int8_t dmamode[] = { 69 1.37 thorpej /* write to device/read from device */ 70 1.17 mycroft DMA37MD_READ | DMA37MD_SINGLE, 71 1.15 mycroft DMA37MD_WRITE | DMA37MD_SINGLE, 72 1.40 mycroft 73 1.40 mycroft /* write to device/read from device */ 74 1.40 mycroft DMA37MD_READ | DMA37MD_DEMAND, 75 1.40 mycroft DMA37MD_WRITE | DMA37MD_DEMAND, 76 1.37 thorpej 77 1.37 thorpej /* write to device/read from device - DMAMODE_LOOP */ 78 1.25 mycroft DMA37MD_READ | DMA37MD_SINGLE | DMA37MD_LOOP, 79 1.37 thorpej DMA37MD_WRITE | DMA37MD_SINGLE | DMA37MD_LOOP, 80 1.37 thorpej 81 1.37 thorpej /* write to device/read from device - DMAMODE_LOOPDEMAND */ 82 1.37 thorpej DMA37MD_READ | DMA37MD_DEMAND | DMA37MD_LOOP, 83 1.37 thorpej DMA37MD_WRITE | DMA37MD_DEMAND | DMA37MD_LOOP, 84 1.15 mycroft }; 85 1.1 mycroft 86 1.53 perry static inline void _isa_dmaunmask(struct isa_dma_state *, int); 87 1.53 perry static inline void _isa_dmamask(struct isa_dma_state *, int); 88 1.19 christos 89 1.26 thorpej static inline void 90 1.60 dsl _isa_dmaunmask(struct isa_dma_state *ids, int chan) 91 1.23 mycroft { 92 1.23 mycroft int ochan = chan & 3; 93 1.23 mycroft 94 1.35 thorpej ISA_DMA_MASK_CLR(ids, chan); 95 1.35 thorpej 96 1.35 thorpej /* 97 1.35 thorpej * If DMA is frozen, don't unmask it now. It will be 98 1.35 thorpej * unmasked when DMA is thawed again. 99 1.35 thorpej */ 100 1.35 thorpej if (ids->ids_frozen) 101 1.35 thorpej return; 102 1.35 thorpej 103 1.23 mycroft /* set dma channel mode, and set dma channel mode */ 104 1.23 mycroft if ((chan & 4) == 0) 105 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 106 1.26 thorpej DMA1_SMSK, ochan | DMA37SM_CLEAR); 107 1.23 mycroft else 108 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 109 1.26 thorpej DMA2_SMSK, ochan | DMA37SM_CLEAR); 110 1.23 mycroft } 111 1.23 mycroft 112 1.26 thorpej static inline void 113 1.60 dsl _isa_dmamask(struct isa_dma_state *ids, int chan) 114 1.23 mycroft { 115 1.23 mycroft int ochan = chan & 3; 116 1.23 mycroft 117 1.35 thorpej ISA_DMA_MASK_SET(ids, chan); 118 1.35 thorpej 119 1.35 thorpej /* 120 1.35 thorpej * XXX Should we avoid masking the channel if DMA is 121 1.35 thorpej * XXX frozen? It seems like what we're doing should 122 1.35 thorpej * XXX be safe, and we do need to reset FFC... 123 1.35 thorpej */ 124 1.35 thorpej 125 1.23 mycroft /* set dma channel mode, and set dma channel mode */ 126 1.23 mycroft if ((chan & 4) == 0) { 127 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 128 1.26 thorpej DMA1_SMSK, ochan | DMA37SM_SET); 129 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 130 1.26 thorpej DMA1_FFC, 0); 131 1.23 mycroft } else { 132 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 133 1.26 thorpej DMA2_SMSK, ochan | DMA37SM_SET); 134 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 135 1.26 thorpej DMA2_FFC, 0); 136 1.23 mycroft } 137 1.23 mycroft } 138 1.23 mycroft 139 1.1 mycroft /* 140 1.34 thorpej * _isa_dmainit(): Initialize the isa_dma_state for this chipset. 141 1.34 thorpej */ 142 1.34 thorpej void 143 1.61 cegger _isa_dmainit(struct isa_dma_state *ids, bus_space_tag_t bst, bus_dma_tag_t dmat, device_t dev) 144 1.34 thorpej { 145 1.43 thorpej int chan; 146 1.34 thorpej 147 1.34 thorpej ids->ids_dev = dev; 148 1.34 thorpej 149 1.34 thorpej if (ids->ids_initialized) { 150 1.34 thorpej /* 151 1.34 thorpej * Some systems may have e.g. `ofisa' (OpenFirmware 152 1.34 thorpej * configuration of ISA bus) and a regular `isa'. 153 1.34 thorpej * We allow both to call the initialization function, 154 1.34 thorpej * and take the device name from the last caller 155 1.34 thorpej * (assuming it will be the indirect ISA bus). Since 156 1.34 thorpej * `ofisa' and `isa' are the same bus with different 157 1.34 thorpej * configuration mechanisms, the space and dma tags 158 1.34 thorpej * must be the same! 159 1.34 thorpej */ 160 1.64 dyoung if (!bus_space_is_equal(ids->ids_bst, bst) || 161 1.64 dyoung ids->ids_dmat != dmat) 162 1.34 thorpej panic("_isa_dmainit: inconsistent ISA tags"); 163 1.34 thorpej } else { 164 1.34 thorpej ids->ids_bst = bst; 165 1.34 thorpej ids->ids_dmat = dmat; 166 1.34 thorpej 167 1.34 thorpej /* 168 1.34 thorpej * Map the registers used by the ISA DMA controller. 169 1.34 thorpej */ 170 1.34 thorpej if (bus_space_map(ids->ids_bst, IO_DMA1, DMA1_IOSIZE, 0, 171 1.34 thorpej &ids->ids_dma1h)) 172 1.34 thorpej panic("_isa_dmainit: unable to map DMA controller #1"); 173 1.34 thorpej if (bus_space_map(ids->ids_bst, IO_DMA2, DMA2_IOSIZE, 0, 174 1.34 thorpej &ids->ids_dma2h)) 175 1.34 thorpej panic("_isa_dmainit: unable to map DMA controller #2"); 176 1.67 jdolecek if (bus_space_map(ids->ids_bst, IO_DMAPG, DMAPG_IOSIZE, 0, 177 1.34 thorpej &ids->ids_dmapgh)) 178 1.34 thorpej panic("_isa_dmainit: unable to map DMA page registers"); 179 1.34 thorpej 180 1.35 thorpej /* 181 1.35 thorpej * All 8 DMA channels start out "masked". 182 1.35 thorpej */ 183 1.35 thorpej ids->ids_masked = 0xff; 184 1.35 thorpej 185 1.43 thorpej /* 186 1.43 thorpej * Initialize the max transfer size for each channel, if 187 1.43 thorpej * it is not initialized already (i.e. by a bus-dependent 188 1.43 thorpej * front-end). 189 1.43 thorpej */ 190 1.43 thorpej for (chan = 0; chan < 8; chan++) { 191 1.43 thorpej if (ids->ids_maxsize[chan] == 0) 192 1.43 thorpej ids->ids_maxsize[chan] = 193 1.43 thorpej ISA_DMA_MAXSIZE_DEFAULT(chan); 194 1.43 thorpej } 195 1.43 thorpej 196 1.34 thorpej ids->ids_initialized = 1; 197 1.38 thorpej 198 1.38 thorpej /* 199 1.38 thorpej * DRQ 4 is used to chain the two 8237s together; make 200 1.38 thorpej * sure it's always cascaded, and that it will be unmasked 201 1.38 thorpej * when DMA is thawed. 202 1.38 thorpej */ 203 1.38 thorpej _isa_dmacascade(ids, 4); 204 1.34 thorpej } 205 1.34 thorpej } 206 1.34 thorpej 207 1.62 dyoung void 208 1.62 dyoung _isa_dmadestroy(struct isa_dma_state *ids) 209 1.62 dyoung { 210 1.62 dyoung if (!ids->ids_initialized) 211 1.62 dyoung return; 212 1.62 dyoung 213 1.62 dyoung _isa_dmacascade_stop(ids, 4); 214 1.62 dyoung 215 1.62 dyoung /* 216 1.62 dyoung * Unmap the registers used by the ISA DMA controller. 217 1.62 dyoung */ 218 1.67 jdolecek bus_space_unmap(ids->ids_bst, ids->ids_dmapgh, DMAPG_IOSIZE); 219 1.62 dyoung bus_space_unmap(ids->ids_bst, ids->ids_dma2h, DMA2_IOSIZE); 220 1.62 dyoung bus_space_unmap(ids->ids_bst, ids->ids_dma1h, DMA1_IOSIZE); 221 1.62 dyoung 222 1.62 dyoung ids->ids_initialized = 0; 223 1.62 dyoung } 224 1.62 dyoung 225 1.34 thorpej /* 226 1.34 thorpej * _isa_dmacascade(): program 8237 DMA controller channel to accept 227 1.1 mycroft * external dma control by a board. 228 1.1 mycroft */ 229 1.36 thorpej int 230 1.60 dsl _isa_dmacascade(struct isa_dma_state *ids, int chan) 231 1.1 mycroft { 232 1.23 mycroft int ochan = chan & 3; 233 1.4 mycroft 234 1.26 thorpej if (chan < 0 || chan > 7) { 235 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 236 1.36 thorpej return (EINVAL); 237 1.26 thorpej } 238 1.26 thorpej 239 1.62 dyoung if (!ISA_DMA_DRQ_ISFREE(ids, chan)) { 240 1.57 cegger printf("%s: DRQ %d is not free\n", device_xname(ids->ids_dev), 241 1.34 thorpej chan); 242 1.36 thorpej return (EAGAIN); 243 1.26 thorpej } 244 1.26 thorpej 245 1.34 thorpej ISA_DMA_DRQ_ALLOC(ids, chan); 246 1.1 mycroft 247 1.1 mycroft /* set dma channel mode, and set dma channel mode */ 248 1.23 mycroft if ((chan & 4) == 0) 249 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 250 1.26 thorpej DMA1_MODE, ochan | DMA37MD_CASCADE); 251 1.26 thorpej else 252 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 253 1.26 thorpej DMA2_MODE, ochan | DMA37MD_CASCADE); 254 1.26 thorpej 255 1.34 thorpej _isa_dmaunmask(ids, chan); 256 1.36 thorpej return (0); 257 1.26 thorpej } 258 1.26 thorpej 259 1.62 dyoung /* 260 1.62 dyoung * _isa_dmacascade_stop(): turn off cascading on the 8237 DMA controller channel 261 1.62 dyoung * external dma control by a board. 262 1.62 dyoung */ 263 1.62 dyoung int 264 1.62 dyoung _isa_dmacascade_stop(struct isa_dma_state *ids, int chan) 265 1.62 dyoung { 266 1.62 dyoung if (chan < 0 || chan > 7) { 267 1.62 dyoung printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 268 1.62 dyoung return EINVAL; 269 1.62 dyoung } 270 1.62 dyoung 271 1.62 dyoung if (ISA_DMA_DRQ_ISFREE(ids, chan)) 272 1.62 dyoung return 0; 273 1.62 dyoung 274 1.62 dyoung _isa_dmamask(ids, chan); 275 1.62 dyoung 276 1.62 dyoung ISA_DMA_DRQ_FREE(ids, chan); 277 1.62 dyoung 278 1.62 dyoung return 0; 279 1.62 dyoung } 280 1.62 dyoung 281 1.52 fvdl int 282 1.60 dsl _isa_drq_alloc(struct isa_dma_state *ids, int chan) 283 1.52 fvdl { 284 1.62 dyoung if (!ISA_DMA_DRQ_ISFREE(ids, chan)) 285 1.52 fvdl return EBUSY; 286 1.52 fvdl ISA_DMA_DRQ_ALLOC(ids, chan); 287 1.52 fvdl return 0; 288 1.52 fvdl } 289 1.52 fvdl 290 1.52 fvdl int 291 1.60 dsl _isa_drq_free(struct isa_dma_state *ids, int chan) 292 1.52 fvdl { 293 1.52 fvdl if (ISA_DMA_DRQ_ISFREE(ids, chan)) 294 1.52 fvdl return EINVAL; 295 1.52 fvdl ISA_DMA_DRQ_FREE(ids, chan); 296 1.52 fvdl return 0; 297 1.52 fvdl } 298 1.52 fvdl 299 1.43 thorpej bus_size_t 300 1.60 dsl _isa_dmamaxsize(struct isa_dma_state *ids, int chan) 301 1.43 thorpej { 302 1.43 thorpej 303 1.43 thorpej if (chan < 0 || chan > 7) { 304 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 305 1.43 thorpej return (0); 306 1.43 thorpej } 307 1.43 thorpej 308 1.43 thorpej return (ids->ids_maxsize[chan]); 309 1.43 thorpej } 310 1.43 thorpej 311 1.26 thorpej int 312 1.60 dsl _isa_dmamap_create(struct isa_dma_state *ids, int chan, bus_size_t size, int flags) 313 1.26 thorpej { 314 1.42 mycroft int error; 315 1.26 thorpej 316 1.26 thorpej if (chan < 0 || chan > 7) { 317 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 318 1.36 thorpej return (EINVAL); 319 1.26 thorpej } 320 1.26 thorpej 321 1.43 thorpej if (size > ids->ids_maxsize[chan]) 322 1.26 thorpej return (EINVAL); 323 1.26 thorpej 324 1.43 thorpej error = bus_dmamap_create(ids->ids_dmat, size, 1, size, 325 1.43 thorpej ids->ids_maxsize[chan], flags, &ids->ids_dmamaps[chan]); 326 1.42 mycroft 327 1.42 mycroft return (error); 328 1.26 thorpej } 329 1.26 thorpej 330 1.26 thorpej void 331 1.60 dsl _isa_dmamap_destroy(struct isa_dma_state *ids, int chan) 332 1.26 thorpej { 333 1.26 thorpej 334 1.26 thorpej if (chan < 0 || chan > 7) { 335 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 336 1.26 thorpej goto lose; 337 1.26 thorpej } 338 1.26 thorpej 339 1.34 thorpej bus_dmamap_destroy(ids->ids_dmat, ids->ids_dmamaps[chan]); 340 1.26 thorpej return; 341 1.26 thorpej 342 1.26 thorpej lose: 343 1.34 thorpej panic("_isa_dmamap_destroy"); 344 1.1 mycroft } 345 1.1 mycroft 346 1.1 mycroft /* 347 1.34 thorpej * _isa_dmastart(): program 8237 DMA controller channel and set it 348 1.26 thorpej * in motion. 349 1.1 mycroft */ 350 1.26 thorpej int 351 1.60 dsl _isa_dmastart(struct isa_dma_state *ids, int chan, void *addr, bus_size_t nbytes, struct proc *p, int flags, int busdmaflags) 352 1.1 mycroft { 353 1.26 thorpej bus_dmamap_t dmam; 354 1.26 thorpej bus_addr_t dmaaddr; 355 1.1 mycroft int waport; 356 1.23 mycroft int ochan = chan & 3; 357 1.26 thorpej int error; 358 1.26 thorpej 359 1.26 thorpej if (chan < 0 || chan > 7) { 360 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 361 1.26 thorpej goto lose; 362 1.26 thorpej } 363 1.26 thorpej 364 1.26 thorpej #ifdef ISADMA_DEBUG 365 1.34 thorpej printf("_isa_dmastart: drq %d, addr %p, nbytes 0x%lx, p %p, " 366 1.26 thorpej "flags 0x%x, dmaflags 0x%x\n", 367 1.59 bouyer chan, addr, (u_long)nbytes, p, flags, busdmaflags); 368 1.26 thorpej #endif 369 1.52 fvdl 370 1.52 fvdl if (ISA_DMA_DRQ_ISFREE(ids, chan)) { 371 1.52 fvdl printf("%s: dma start on free channel %d\n", 372 1.57 cegger device_xname(ids->ids_dev), chan); 373 1.52 fvdl goto lose; 374 1.52 fvdl } 375 1.26 thorpej 376 1.26 thorpej if (chan & 4) { 377 1.26 thorpej if (nbytes > (1 << 17) || nbytes & 1 || (u_long)addr & 1) { 378 1.26 thorpej printf("%s: drq %d, nbytes 0x%lx, addr %p\n", 379 1.57 cegger device_xname(ids->ids_dev), chan, 380 1.47 briggs (unsigned long) nbytes, addr); 381 1.26 thorpej goto lose; 382 1.26 thorpej } 383 1.26 thorpej } else { 384 1.26 thorpej if (nbytes > (1 << 16)) { 385 1.26 thorpej printf("%s: drq %d, nbytes 0x%lx\n", 386 1.57 cegger device_xname(ids->ids_dev), chan, 387 1.47 briggs (unsigned long) nbytes); 388 1.26 thorpej goto lose; 389 1.26 thorpej } 390 1.26 thorpej } 391 1.26 thorpej 392 1.34 thorpej dmam = ids->ids_dmamaps[chan]; 393 1.31 augustss if (dmam == NULL) 394 1.50 provos panic("_isa_dmastart: no DMA map for chan %d", chan); 395 1.26 thorpej 396 1.34 thorpej error = bus_dmamap_load(ids->ids_dmat, dmam, addr, nbytes, 397 1.48 thorpej p, busdmaflags | 398 1.48 thorpej ((flags & DMAMODE_READ) ? BUS_DMA_READ : BUS_DMA_WRITE)); 399 1.26 thorpej if (error) 400 1.26 thorpej return (error); 401 1.1 mycroft 402 1.13 mycroft #ifdef ISADMA_DEBUG 403 1.26 thorpej __asm(".globl isa_dmastart_afterload ; isa_dmastart_afterload:"); 404 1.4 mycroft #endif 405 1.1 mycroft 406 1.26 thorpej if (flags & DMAMODE_READ) { 407 1.34 thorpej bus_dmamap_sync(ids->ids_dmat, dmam, 0, dmam->dm_mapsize, 408 1.33 thorpej BUS_DMASYNC_PREREAD); 409 1.34 thorpej ids->ids_dmareads |= (1 << chan); 410 1.26 thorpej } else { 411 1.34 thorpej bus_dmamap_sync(ids->ids_dmat, dmam, 0, dmam->dm_mapsize, 412 1.33 thorpej BUS_DMASYNC_PREWRITE); 413 1.34 thorpej ids->ids_dmareads &= ~(1 << chan); 414 1.1 mycroft } 415 1.1 mycroft 416 1.26 thorpej dmaaddr = dmam->dm_segs[0].ds_addr; 417 1.26 thorpej 418 1.26 thorpej #ifdef ISADMA_DEBUG 419 1.65 jym printf(" dmaaddr %#" PRIxPADDR "\n", dmaaddr); 420 1.26 thorpej 421 1.26 thorpej __asm(".globl isa_dmastart_aftersync ; isa_dmastart_aftersync:"); 422 1.26 thorpej #endif 423 1.24 mycroft 424 1.34 thorpej ids->ids_dmalength[chan] = nbytes; 425 1.1 mycroft 426 1.34 thorpej _isa_dmamask(ids, chan); 427 1.34 thorpej ids->ids_dmafinished &= ~(1 << chan); 428 1.14 mycroft 429 1.1 mycroft if ((chan & 4) == 0) { 430 1.23 mycroft /* set dma channel mode */ 431 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, DMA1_MODE, 432 1.26 thorpej ochan | dmamode[flags]); 433 1.1 mycroft 434 1.1 mycroft /* send start address */ 435 1.23 mycroft waport = DMA1_CHN(ochan); 436 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dmapgh, 437 1.26 thorpej dmapageport[0][ochan], (dmaaddr >> 16) & 0xff); 438 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport, 439 1.26 thorpej dmaaddr & 0xff); 440 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport, 441 1.26 thorpej (dmaaddr >> 8) & 0xff); 442 1.1 mycroft 443 1.1 mycroft /* send count */ 444 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport + 1, 445 1.26 thorpej (--nbytes) & 0xff); 446 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, waport + 1, 447 1.26 thorpej (nbytes >> 8) & 0xff); 448 1.1 mycroft } else { 449 1.23 mycroft /* set dma channel mode */ 450 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, DMA2_MODE, 451 1.26 thorpej ochan | dmamode[flags]); 452 1.1 mycroft 453 1.1 mycroft /* send start address */ 454 1.23 mycroft waport = DMA2_CHN(ochan); 455 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dmapgh, 456 1.26 thorpej dmapageport[1][ochan], (dmaaddr >> 16) & 0xff); 457 1.26 thorpej dmaaddr >>= 1; 458 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, waport, 459 1.26 thorpej dmaaddr & 0xff); 460 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, waport, 461 1.26 thorpej (dmaaddr >> 8) & 0xff); 462 1.1 mycroft 463 1.1 mycroft /* send count */ 464 1.1 mycroft nbytes >>= 1; 465 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, waport + 2, 466 1.26 thorpej (--nbytes) & 0xff); 467 1.34 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, waport + 2, 468 1.26 thorpej (nbytes >> 8) & 0xff); 469 1.23 mycroft } 470 1.1 mycroft 471 1.34 thorpej _isa_dmaunmask(ids, chan); 472 1.26 thorpej return (0); 473 1.26 thorpej 474 1.26 thorpej lose: 475 1.34 thorpej panic("_isa_dmastart"); 476 1.1 mycroft } 477 1.1 mycroft 478 1.1 mycroft void 479 1.60 dsl _isa_dmaabort(struct isa_dma_state *ids, int chan) 480 1.1 mycroft { 481 1.1 mycroft 482 1.26 thorpej if (chan < 0 || chan > 7) { 483 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 484 1.34 thorpej panic("_isa_dmaabort"); 485 1.26 thorpej } 486 1.1 mycroft 487 1.34 thorpej _isa_dmamask(ids, chan); 488 1.34 thorpej bus_dmamap_unload(ids->ids_dmat, ids->ids_dmamaps[chan]); 489 1.34 thorpej ids->ids_dmareads &= ~(1 << chan); 490 1.22 mycroft } 491 1.22 mycroft 492 1.26 thorpej bus_size_t 493 1.60 dsl _isa_dmacount(struct isa_dma_state *ids, int chan) 494 1.22 mycroft { 495 1.22 mycroft int waport; 496 1.26 thorpej bus_size_t nbytes; 497 1.23 mycroft int ochan = chan & 3; 498 1.22 mycroft 499 1.26 thorpej if (chan < 0 || chan > 7) { 500 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 501 1.26 thorpej panic("isa_dmacount"); 502 1.26 thorpej } 503 1.22 mycroft 504 1.34 thorpej _isa_dmamask(ids, chan); 505 1.23 mycroft 506 1.24 mycroft /* 507 1.24 mycroft * We have to shift the byte count by 1. If we're in auto-initialize 508 1.24 mycroft * mode, the count may have wrapped around to the initial value. We 509 1.24 mycroft * can't use the TC bit to check for this case, so instead we compare 510 1.24 mycroft * against the original byte count. 511 1.24 mycroft * If we're not in auto-initialize mode, then the count will wrap to 512 1.24 mycroft * -1, so we also handle that case. 513 1.24 mycroft */ 514 1.24 mycroft if ((chan & 4) == 0) { 515 1.24 mycroft waport = DMA1_CHN(ochan); 516 1.34 thorpej nbytes = bus_space_read_1(ids->ids_bst, ids->ids_dma1h, 517 1.26 thorpej waport + 1) + 1; 518 1.34 thorpej nbytes += bus_space_read_1(ids->ids_bst, ids->ids_dma1h, 519 1.26 thorpej waport + 1) << 8; 520 1.24 mycroft nbytes &= 0xffff; 521 1.24 mycroft } else { 522 1.24 mycroft waport = DMA2_CHN(ochan); 523 1.34 thorpej nbytes = bus_space_read_1(ids->ids_bst, ids->ids_dma2h, 524 1.26 thorpej waport + 2) + 1; 525 1.34 thorpej nbytes += bus_space_read_1(ids->ids_bst, ids->ids_dma2h, 526 1.26 thorpej waport + 2) << 8; 527 1.24 mycroft nbytes <<= 1; 528 1.24 mycroft nbytes &= 0x1ffff; 529 1.24 mycroft } 530 1.24 mycroft 531 1.34 thorpej if (nbytes == ids->ids_dmalength[chan]) 532 1.23 mycroft nbytes = 0; 533 1.22 mycroft 534 1.34 thorpej _isa_dmaunmask(ids, chan); 535 1.22 mycroft return (nbytes); 536 1.1 mycroft } 537 1.1 mycroft 538 1.13 mycroft int 539 1.60 dsl _isa_dmafinished(struct isa_dma_state *ids, int chan) 540 1.1 mycroft { 541 1.1 mycroft 542 1.26 thorpej if (chan < 0 || chan > 7) { 543 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 544 1.34 thorpej panic("_isa_dmafinished"); 545 1.26 thorpej } 546 1.1 mycroft 547 1.1 mycroft /* check that the terminal count was reached */ 548 1.1 mycroft if ((chan & 4) == 0) 549 1.34 thorpej ids->ids_dmafinished |= bus_space_read_1(ids->ids_bst, 550 1.34 thorpej ids->ids_dma1h, DMA1_SR) & 0x0f; 551 1.1 mycroft else 552 1.34 thorpej ids->ids_dmafinished |= (bus_space_read_1(ids->ids_bst, 553 1.34 thorpej ids->ids_dma2h, DMA2_SR) & 0x0f) << 4; 554 1.14 mycroft 555 1.34 thorpej return ((ids->ids_dmafinished & (1 << chan)) != 0); 556 1.13 mycroft } 557 1.13 mycroft 558 1.13 mycroft void 559 1.60 dsl _isa_dmadone(struct isa_dma_state *ids, int chan) 560 1.13 mycroft { 561 1.26 thorpej bus_dmamap_t dmam; 562 1.13 mycroft 563 1.26 thorpej if (chan < 0 || chan > 7) { 564 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 565 1.34 thorpej panic("_isa_dmadone"); 566 1.26 thorpej } 567 1.26 thorpej 568 1.34 thorpej dmam = ids->ids_dmamaps[chan]; 569 1.26 thorpej 570 1.34 thorpej _isa_dmamask(ids, chan); 571 1.14 mycroft 572 1.34 thorpej if (_isa_dmafinished(ids, chan) == 0) 573 1.34 thorpej printf("%s: _isa_dmadone: channel %d not finished\n", 574 1.57 cegger device_xname(ids->ids_dev), chan); 575 1.23 mycroft 576 1.34 thorpej bus_dmamap_sync(ids->ids_dmat, dmam, 0, dmam->dm_mapsize, 577 1.34 thorpej (ids->ids_dmareads & (1 << chan)) ? BUS_DMASYNC_POSTREAD : 578 1.26 thorpej BUS_DMASYNC_POSTWRITE); 579 1.9 mycroft 580 1.34 thorpej bus_dmamap_unload(ids->ids_dmat, dmam); 581 1.34 thorpej ids->ids_dmareads &= ~(1 << chan); 582 1.35 thorpej } 583 1.35 thorpej 584 1.35 thorpej void 585 1.60 dsl _isa_dmafreeze(struct isa_dma_state *ids) 586 1.35 thorpej { 587 1.35 thorpej int s; 588 1.35 thorpej 589 1.35 thorpej s = splhigh(); 590 1.35 thorpej 591 1.35 thorpej if (ids->ids_frozen == 0) { 592 1.35 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 593 1.35 thorpej DMA1_MASK, 0x0f); 594 1.35 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 595 1.35 thorpej DMA2_MASK, 0x0f); 596 1.35 thorpej } 597 1.35 thorpej 598 1.35 thorpej ids->ids_frozen++; 599 1.35 thorpej if (ids->ids_frozen < 1) 600 1.35 thorpej panic("_isa_dmafreeze: overflow"); 601 1.35 thorpej 602 1.35 thorpej splx(s); 603 1.35 thorpej } 604 1.35 thorpej 605 1.35 thorpej void 606 1.60 dsl _isa_dmathaw(struct isa_dma_state *ids) 607 1.35 thorpej { 608 1.35 thorpej int s; 609 1.35 thorpej 610 1.35 thorpej s = splhigh(); 611 1.35 thorpej 612 1.35 thorpej ids->ids_frozen--; 613 1.35 thorpej if (ids->ids_frozen < 0) 614 1.35 thorpej panic("_isa_dmathaw: underflow"); 615 1.35 thorpej 616 1.35 thorpej if (ids->ids_frozen == 0) { 617 1.35 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma1h, 618 1.35 thorpej DMA1_MASK, ids->ids_masked & 0x0f); 619 1.35 thorpej bus_space_write_1(ids->ids_bst, ids->ids_dma2h, 620 1.35 thorpej DMA2_MASK, (ids->ids_masked >> 4) & 0x0f); 621 1.35 thorpej } 622 1.35 thorpej 623 1.35 thorpej splx(s); 624 1.1 mycroft } 625 1.1 mycroft 626 1.1 mycroft int 627 1.60 dsl _isa_dmamem_alloc(struct isa_dma_state *ids, int chan, bus_size_t size, bus_addr_t *addrp, int flags) 628 1.26 thorpej { 629 1.26 thorpej bus_dma_segment_t seg; 630 1.26 thorpej int error, boundary, rsegs; 631 1.26 thorpej 632 1.26 thorpej if (chan < 0 || chan > 7) { 633 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 634 1.34 thorpej panic("_isa_dmamem_alloc"); 635 1.1 mycroft } 636 1.26 thorpej 637 1.26 thorpej boundary = (chan & 4) ? (1 << 17) : (1 << 16); 638 1.26 thorpej 639 1.26 thorpej size = round_page(size); 640 1.26 thorpej 641 1.46 thorpej error = bus_dmamem_alloc(ids->ids_dmat, size, PAGE_SIZE, boundary, 642 1.26 thorpej &seg, 1, &rsegs, flags); 643 1.26 thorpej if (error) 644 1.26 thorpej return (error); 645 1.26 thorpej 646 1.26 thorpej *addrp = seg.ds_addr; 647 1.26 thorpej return (0); 648 1.5 mycroft } 649 1.5 mycroft 650 1.26 thorpej void 651 1.60 dsl _isa_dmamem_free(struct isa_dma_state *ids, int chan, bus_addr_t addr, bus_size_t size) 652 1.26 thorpej { 653 1.26 thorpej bus_dma_segment_t seg; 654 1.26 thorpej 655 1.26 thorpej if (chan < 0 || chan > 7) { 656 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 657 1.34 thorpej panic("_isa_dmamem_free"); 658 1.26 thorpej } 659 1.26 thorpej 660 1.26 thorpej seg.ds_addr = addr; 661 1.26 thorpej seg.ds_len = size; 662 1.5 mycroft 663 1.34 thorpej bus_dmamem_free(ids->ids_dmat, &seg, 1); 664 1.26 thorpej } 665 1.5 mycroft 666 1.26 thorpej int 667 1.60 dsl _isa_dmamem_map(struct isa_dma_state *ids, int chan, bus_addr_t addr, bus_size_t size, void **kvap, int flags) 668 1.26 thorpej { 669 1.26 thorpej bus_dma_segment_t seg; 670 1.26 thorpej 671 1.26 thorpej if (chan < 0 || chan > 7) { 672 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 673 1.34 thorpej panic("_isa_dmamem_map"); 674 1.5 mycroft } 675 1.5 mycroft 676 1.26 thorpej seg.ds_addr = addr; 677 1.26 thorpej seg.ds_len = size; 678 1.26 thorpej 679 1.34 thorpej return (bus_dmamem_map(ids->ids_dmat, &seg, 1, size, kvap, flags)); 680 1.5 mycroft } 681 1.5 mycroft 682 1.5 mycroft void 683 1.60 dsl _isa_dmamem_unmap(struct isa_dma_state *ids, int chan, void *kva, size_t size) 684 1.19 christos { 685 1.5 mycroft 686 1.26 thorpej if (chan < 0 || chan > 7) { 687 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 688 1.34 thorpej panic("_isa_dmamem_unmap"); 689 1.5 mycroft } 690 1.26 thorpej 691 1.34 thorpej bus_dmamem_unmap(ids->ids_dmat, kva, size); 692 1.26 thorpej } 693 1.26 thorpej 694 1.44 simonb paddr_t 695 1.60 dsl _isa_dmamem_mmap(struct isa_dma_state *ids, int chan, bus_addr_t addr, bus_size_t size, off_t off, int prot, int flags) 696 1.26 thorpej { 697 1.26 thorpej bus_dma_segment_t seg; 698 1.26 thorpej 699 1.26 thorpej if (chan < 0 || chan > 7) { 700 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 701 1.34 thorpej panic("_isa_dmamem_mmap"); 702 1.26 thorpej } 703 1.39 mrg 704 1.39 mrg if (off < 0) 705 1.39 mrg return (-1); 706 1.26 thorpej 707 1.26 thorpej seg.ds_addr = addr; 708 1.26 thorpej seg.ds_len = size; 709 1.26 thorpej 710 1.34 thorpej return (bus_dmamem_mmap(ids->ids_dmat, &seg, 1, off, prot, flags)); 711 1.30 augustss } 712 1.30 augustss 713 1.30 augustss int 714 1.60 dsl _isa_drq_isfree(struct isa_dma_state *ids, int chan) 715 1.30 augustss { 716 1.34 thorpej 717 1.30 augustss if (chan < 0 || chan > 7) { 718 1.57 cegger printf("%s: bogus drq %d\n", device_xname(ids->ids_dev), chan); 719 1.34 thorpej panic("_isa_drq_isfree"); 720 1.30 augustss } 721 1.34 thorpej 722 1.34 thorpej return ISA_DMA_DRQ_ISFREE(ids, chan); 723 1.27 augustss } 724 1.27 augustss 725 1.27 augustss void * 726 1.60 dsl _isa_malloc(struct isa_dma_state *ids, int chan, size_t size, struct malloc_type *pool, int flags) 727 1.27 augustss { 728 1.27 augustss bus_addr_t addr; 729 1.55 christos void *kva; 730 1.27 augustss int bflags; 731 1.27 augustss struct isa_mem *m; 732 1.27 augustss 733 1.27 augustss bflags = flags & M_WAITOK ? BUS_DMA_WAITOK : BUS_DMA_NOWAIT; 734 1.27 augustss 735 1.34 thorpej if (_isa_dmamem_alloc(ids, chan, size, &addr, bflags)) 736 1.27 augustss return 0; 737 1.34 thorpej if (_isa_dmamem_map(ids, chan, addr, size, &kva, bflags)) { 738 1.34 thorpej _isa_dmamem_free(ids, chan, addr, size); 739 1.27 augustss return 0; 740 1.27 augustss } 741 1.27 augustss m = malloc(sizeof(*m), pool, flags); 742 1.27 augustss if (m == 0) { 743 1.34 thorpej _isa_dmamem_unmap(ids, chan, kva, size); 744 1.34 thorpej _isa_dmamem_free(ids, chan, addr, size); 745 1.27 augustss return 0; 746 1.27 augustss } 747 1.34 thorpej m->ids = ids; 748 1.27 augustss m->chan = chan; 749 1.27 augustss m->size = size; 750 1.27 augustss m->addr = addr; 751 1.27 augustss m->kva = kva; 752 1.27 augustss m->next = isa_mem_head; 753 1.27 augustss isa_mem_head = m; 754 1.27 augustss return (void *)kva; 755 1.27 augustss } 756 1.27 augustss 757 1.27 augustss void 758 1.60 dsl _isa_free(void *addr, struct malloc_type *pool) 759 1.27 augustss { 760 1.27 augustss struct isa_mem **mp, *m; 761 1.55 christos void *kva = (void *)addr; 762 1.27 augustss 763 1.34 thorpej for(mp = &isa_mem_head; *mp && (*mp)->kva != kva; 764 1.34 thorpej mp = &(*mp)->next) 765 1.27 augustss ; 766 1.27 augustss m = *mp; 767 1.27 augustss if (!m) { 768 1.68 andvar printf("_isa_free: freeing unallocated memory\n"); 769 1.27 augustss return; 770 1.27 augustss } 771 1.27 augustss *mp = m->next; 772 1.34 thorpej _isa_dmamem_unmap(m->ids, m->chan, kva, m->size); 773 1.34 thorpej _isa_dmamem_free(m->ids, m->chan, m->addr, m->size); 774 1.27 augustss free(m, pool); 775 1.28 augustss } 776 1.28 augustss 777 1.44 simonb paddr_t 778 1.60 dsl _isa_mappage(void *mem, off_t off, int prot) 779 1.28 augustss { 780 1.28 augustss struct isa_mem *m; 781 1.28 augustss 782 1.55 christos for(m = isa_mem_head; m && m->kva != (void *)mem; m = m->next) 783 1.28 augustss ; 784 1.28 augustss if (!m) { 785 1.68 andvar printf("_isa_mappage: mapping unallocated memory\n"); 786 1.28 augustss return -1; 787 1.28 augustss } 788 1.34 thorpej return _isa_dmamem_mmap(m->ids, m->chan, m->addr, 789 1.34 thorpej m->size, off, prot, BUS_DMA_WAITOK); 790 1.1 mycroft } 791