xref: /netbsd/sys/arch/i386/mca/mca_machdep.c (revision c4a72b64)
1 /*	$NetBSD: mca_machdep.c,v 1.18 2002/11/22 15:23:51 fvdl Exp $	*/
2 
3 /*-
4  * Copyright (c) 2000, 2001 The NetBSD Foundation, Inc.
5  * Copyright (c) 1996-1999 Scott D. Telford.
6  * All rights reserved.
7  *
8  * This code is derived from software contributed to The NetBSD Foundation
9  * by Scott Telford <s.telford@ed.ac.uk> and Jaromir Dolecek
10  * <jdolecek@NetBSD.org>.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the NetBSD
23  *	Foundation, Inc. and its contributors.
24  * 4. Neither the name of The NetBSD Foundation nor the names of its
25  *    contributors may be used to endorse or promote products derived
26  *    from this software without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
30  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
31  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
32  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
33  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
34  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
35  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
36  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38  * POSSIBILITY OF SUCH DAMAGE.
39  */
40 
41 /*
42  * Machine-specific functions for MCA autoconfiguration.
43  */
44 
45 #include <sys/cdefs.h>
46 __KERNEL_RCSID(0, "$NetBSD: mca_machdep.c,v 1.18 2002/11/22 15:23:51 fvdl Exp $");
47 
48 #include <sys/types.h>
49 #include <sys/param.h>
50 #include <sys/device.h>
51 #include <sys/malloc.h>
52 #include <sys/systm.h>
53 #include <sys/syslog.h>
54 #include <sys/time.h>
55 #include <sys/kernel.h>
56 
57 #include <machine/bioscall.h>
58 #include <machine/psl.h>
59 
60 #define _I386_BUS_DMA_PRIVATE
61 #include <machine/bus.h>
62 
63 #include <dev/isa/isavar.h>
64 #include <dev/isa/isareg.h>
65 #include <dev/mca/mcavar.h>
66 #include <dev/mca/mcareg.h>
67 
68 #include "isa.h"
69 #include "opt_mcaverbose.h"
70 
71 /* System Configuration Block - this info is returned by the BIOS call */
72 struct bios_config {
73 	u_int16_t	count;
74 	u_int8_t	model;
75 	u_int8_t	submodel;
76 	u_int8_t	bios_rev;
77 	u_int8_t	feature1;
78 #define FEATURE_MCAISA	0x01	/* Machine contains both MCA and ISA bus */
79 #define FEATURE_MCABUS	0x02	/* Machine has MCA bus instead of ISA	*/
80 #define FEATURE_EBDA	0x04	/* Extended BIOS data area allocated	*/
81 #define FEATURE_WAITEV	0x08	/* Wait for external event is supported	*/
82 #define FEATURE_KBDINT	0x10	/* Keyboard intercept called by Int 09h	*/
83 #define FEATURE_RTC	0x20	/* Real-time clock present		*/
84 #define FEATURE_IC2	0x40	/* Second interrupt chip present	*/
85 #define FEATURE_DMA3	0x80	/* DMA channel 3 used by hard disk BIOS	*/
86 	u_int8_t	feature2;
87 	u_int8_t	pad[9];
88 } __attribute__ ((packed));
89 
90 /*
91  * Used to encode DMA channel into ISA DMA cookie. We use upper 4 bits of
92  * ISA DMA cookie id_flags, it's unused.
93  */
94 struct i386_isa_dma_cookie {
95 	int id_flags;
96 	/* We don't care about rest */
97 };
98 
99 /* ISA DMA stuff - see i386/isa/isa_machdep.c */
100 int	_isa_bus_dmamap_create __P((bus_dma_tag_t, bus_size_t, int,
101 	    bus_size_t, bus_size_t, int, bus_dmamap_t *));
102 void	_isa_bus_dmamap_destroy __P((bus_dma_tag_t, bus_dmamap_t));
103 int	_isa_bus_dmamap_load __P((bus_dma_tag_t, bus_dmamap_t, void *,
104 	    bus_size_t, struct proc *, int));
105 void	_isa_bus_dmamap_unload __P((bus_dma_tag_t, bus_dmamap_t));
106 void	_isa_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t,
107 	    bus_addr_t, bus_size_t, int));
108 
109 int	_isa_bus_dmamem_alloc __P((bus_dma_tag_t, bus_size_t, bus_size_t,
110 	    bus_size_t, bus_dma_segment_t *, int, int *, int));
111 
112 static void	_mca_bus_dmamap_sync __P((bus_dma_tag_t, bus_dmamap_t,
113 		    bus_addr_t, bus_size_t, int));
114 static int	_mca_bus_dmamap_load_mbuf __P((bus_dma_tag_t, bus_dmamap_t,
115 		    struct mbuf *, int));
116 static int	_mca_bus_dmamap_load_uio __P((bus_dma_tag_t, bus_dmamap_t,
117 		    struct uio *, int));
118 static int	_mca_bus_dmamap_load_raw __P((bus_dma_tag_t, bus_dmamap_t,
119 		    bus_dma_segment_t *, int, bus_size_t, int));
120 
121 /*
122  * For now, we use MCA DMA to 0-16M always. Some IBM PS/2 have 32bit MCA bus,
123  * but majority of them have 24bit only.
124  */
125 #define	MCA_DMA_BOUNCE_THRESHOLD	(16 * 1024 * 1024)
126 
127 struct i386_bus_dma_tag mca_bus_dma_tag = {
128 	MCA_DMA_BOUNCE_THRESHOLD,		/* _bounce_thresh */
129 	_isa_bus_dmamap_create,
130 	_isa_bus_dmamap_destroy,
131 	_isa_bus_dmamap_load,
132 	_mca_bus_dmamap_load_mbuf,
133 	_mca_bus_dmamap_load_uio,
134 	_mca_bus_dmamap_load_raw,
135 	_isa_bus_dmamap_unload,
136 	_mca_bus_dmamap_sync,
137 	_isa_bus_dmamem_alloc,
138 	_bus_dmamem_free,
139 	_bus_dmamem_map,
140 	_bus_dmamem_unmap,
141 	_bus_dmamem_mmap,
142 };
143 
144 /* Updated in mca_busprobe() if appropriate. */
145 int MCA_system = 0;
146 
147 /* Used to kick MCA DMA controller */
148 #define DMA_CMD		0x18		/* command the controller */
149 #define DMA_EXEC	0x1A		/* tell controller how to do things */
150 static bus_space_handle_t dmaiot, dmacmdh, dmaexech;
151 
152 /*
153  * MCA DMA controller commands. The exact sense of individual bits
154  * are from Tymm Twillman <tymm@computer.org>, who worked on Linux MCA DMA
155  * support.
156  */
157 #define DMACMD_SET_IO		0x00	/* set port (16bit) for i/o transfer */
158 #define DMACMD_SET_ADDR		0x20	/* set addr (24bit) for i/o transfer */
159 #define DMACMD_GET_ADDR		0x30	/* get addr (24bit) for i/o transfer */
160 #define	DMACMD_SET_CNT		0x40	/* set memory size for DMA (16b) */
161 #define DMACMD_GET_CNT		0x50	/* get count of remaining bytes in DMA*/
162 #define DMACMD_GET_STATUS	0x60	/* ?? */
163 #define DMACMD_SET_MODE		0x70	/* set DMA mode */
164 # define DMACMD_MODE_XFER	0x04	/* do transfer, read by default */
165 # define DMACMD_MODE_READ	0x08	/* read transfer */
166 # define DMACMD_MODE_WRITE	0x00	/* write transfer */
167 # define DMACMD_MODE_IOPORT	0x01	/* DMA from/to IO register */
168 # define DMACMD_MODE_16BIT	0x40	/* 16bit transfers (default 8bit) */
169 #define DMACMD_SET_ARBUS	0x80	/* ?? */
170 #define DMACMD_MASK		0x90	/* command mask */
171 #define DMACMD_RESET_MASK	0xA0	/* reset */
172 #define DMACMD_MASTER_CLEAR	0xD0	/* ?? */
173 
174 /*
175  * Map the MCA DMA controller registers.
176  */
177 void
178 mca_attach_hook(parent, self, mba)
179 	struct device *parent, *self;
180 	struct mcabus_attach_args *mba;
181 {
182 	dmaiot = mba->mba_iot;
183 
184 	if (bus_space_map(dmaiot, DMA_CMD, 1, 0, &dmacmdh)
185 	    || bus_space_map(dmaiot, DMA_EXEC, 1, 0, &dmaexech))
186 		panic("%s: couldn't map DMA registers",
187 			mba->mba_busname);
188 }
189 
190 /*
191  * Read value of MCA POS register "reg" in slot "slot".
192  */
193 
194 int
195 mca_conf_read(mc, slot, reg)
196 	mca_chipset_tag_t mc;
197 	int slot, reg;
198 {
199 	int	data;
200 
201 	slot &= 7;	/* slot must be in range 0-7 */
202 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
203 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
204 	data = inb(MCA_POS_REG(reg));
205 	outb(MCA_ADAP_SETUP_REG, 0);
206 	return data;
207 }
208 
209 
210 /*
211  * Write "data" to MCA POS register "reg" in slot "slot".
212  */
213 
214 void
215 mca_conf_write(mc, slot, reg, data)
216 	mca_chipset_tag_t mc;
217 	int slot, reg, data;
218 {
219 	slot&=7;	/* slot must be in range 0-7 */
220 	outb(MCA_MB_SETUP_REG, 0xff); /* ensure m/board setup is disabled */
221 	outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
222 	outb(MCA_POS_REG(reg), data);
223 	outb(MCA_ADAP_SETUP_REG, 0);
224 }
225 
226 #if NISA <= 0
227 #error mca_intr_(dis)establish: needs ISA to be configured into kernel
228 #endif
229 
230 #if 0
231 const struct evcnt *
232 mca_intr_establish(mca_chipset_tag_t mc, mca_intr_handle_t ih)
233 {
234 
235 	/* XXX for now, no evcnt parent reported */
236 	return NULL;
237 }
238 #endif
239 
240 void *
241 mca_intr_establish(mc, ih, level, func, arg)
242 	mca_chipset_tag_t mc;
243 	mca_intr_handle_t ih;
244 	int level, (*func) __P((void *));
245 	void *arg;
246 {
247 	if (ih == 0 || ih >= NUM_LEGACY_IRQS || ih == 2)
248 		panic("mca_intr_establish: bogus handle 0x%x", ih);
249 
250 	/* MCA interrupts are always level-triggered */
251 	return isa_intr_establish(NULL, ih, IST_LEVEL, level, func, arg);
252 }
253 
254 void
255 mca_intr_disestablish(mc, cookie)
256 	mca_chipset_tag_t mc;
257 	void *cookie;
258 {
259 	isa_intr_disestablish(NULL, cookie);
260 }
261 
262 
263 /*
264  * Handle a NMI.
265  * return true to panic system, false to ignore.
266  */
267 int
268 mca_nmi()
269 {
270 	/*
271 	* PS/2 MCA devices can generate NMIs - we can find out which
272 	* slot generated it from the POS registers.
273 	*/
274 
275 	int 	slot, mcanmi=0;
276 
277 	/* if there is no MCA bus, call i386_nmi() */
278 	if (!MCA_system)
279 		goto out;
280 
281 	/* ensure motherboard setup is disabled */
282 	outb(MCA_MB_SETUP_REG, 0xff);
283 
284 	/* find if an MCA slot has the CHCK bit asserted (low) in POS 5 */
285 	for(slot=0; slot<MCA_MAX_SLOTS; slot++) {
286 		outb(MCA_ADAP_SETUP_REG, slot | MCA_ADAP_SET);
287 		if ((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK) == 0) {
288 			mcanmi = 1;
289 			/* find if CHCK status is available in POS 6/7 */
290 			if((inb(MCA_POS_REG(5)) & MCA_POS5_CHCK_STAT) == 0)
291 				log(LOG_CRIT, "MCA NMI: slot %d, POS6=0x%02x, POS7=0x%02x\n",
292 					slot+1, inb(MCA_POS_REG(6)),
293 						inb(MCA_POS_REG(7)));
294 			else
295 				log(LOG_CRIT, "MCA NMI: slot %d\n", slot+1);
296 		}
297 	}
298 	outb(MCA_ADAP_SETUP_REG, 0);
299 
300    out:
301 	if (!mcanmi) {
302 		/* no CHCK bits asserted, assume ISA NMI */
303 		return (i386_nmi());
304 	} else
305 		return(0);
306 }
307 
308 /*
309  * We can obtain the information about MCA bus presence via
310  * GET CONFIGURATION BIOS call - int 0x15, function 0xc0.
311  * The call returns a pointer to memory place with the configuration block
312  * in es:bx (on AT-compatible, e.g. all we care about, computers).
313  *
314  * Configuration block contains block length (2 bytes), model
315  * number (1 byte), submodel number (1 byte), BIOS revision
316  * (1 byte) and up to 5 feature bytes. We only care about
317  * first feature byte.
318  */
319 void
320 mca_busprobe()
321 {
322 	struct bioscallregs regs;
323 	struct bios_config *scp;
324 	paddr_t             paddr;
325 	char buf[50];
326 
327 	memset(&regs, 0, sizeof(regs));
328 	regs.AH = 0xc0;
329 	bioscall(0x15, &regs);
330 
331 	if ((regs.EFLAGS & PSL_C) || regs.AH != 0) {
332 #ifdef DEBUG
333 		printf("BIOS CFG: Not supported. Not AT-compatible?\n");
334 #endif
335 		return;
336 	}
337 
338 	paddr = (regs.ES << 4) + regs.BX;
339 	scp = (struct bios_config *)ISA_HOLE_VADDR(paddr);
340 
341 #if 1 /* MCAVERBOSE */
342 	bitmask_snprintf(((scp->feature2 & 1)<< 8) | scp->feature1,
343 		"\20"
344 		"\01MCA+ISA"
345 		"\02MCA"
346 		"\03EBDA"
347 		"\04WAITEV"
348 		"\05KBDINT"
349 		"\06RTC"
350 		"\07IC2"
351 		"\010DMA3B"
352 		"\011DMA32\n",
353 		buf, sizeof(buf));
354 
355 	printf("BIOS CFG: Model-SubM-Rev: %02x-%02x-%02x, 0x%s\n",
356 		scp->model, scp->submodel, scp->bios_rev, buf);
357 #endif
358 
359 	MCA_system = (scp->feature1 & FEATURE_MCABUS) ? 1 : 0;
360 }
361 
362 #define PORT_DISKLED	0x92
363 #define DISKLED_ON	0x40
364 
365 /*
366  * Light disk busy LED on IBM PS/2.
367  */
368 void
369 mca_disk_busy(void)
370 {
371 	outb(PORT_DISKLED, inb(PORT_DISKLED) | DISKLED_ON);
372 }
373 
374 /*
375  * Turn off disk LED on IBM PS/2.
376  */
377 void
378 mca_disk_unbusy(void)
379 {
380 	outb(PORT_DISKLED, inb(PORT_DISKLED) & ~DISKLED_ON);
381 }
382 
383 /*
384  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
385  * MCA DMA specific stuff. We use ISA routines for bulk of the work,
386  * since MCA shares much of the charasteristics with it. We just hook
387  * the DMA channel initialization and kick MCA DMA controller appropriately.
388  * -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
389  */
390 
391 /*
392  * Like _mca_bus_dmamap_load(), but for mbufs.
393  */
394 static int
395 _mca_bus_dmamap_load_mbuf(t, map, m0, flags)
396 	bus_dma_tag_t t;
397 	bus_dmamap_t map;
398 	struct mbuf *m0;
399 	int flags;
400 {
401 
402 	panic("_mca_bus_dmamap_load_mbuf: not implemented");
403 }
404 
405 /*
406  * Like _mca_bus_dmamap_load(), but for uios.
407  */
408 static int
409 _mca_bus_dmamap_load_uio(t, map, uio, flags)
410 	bus_dma_tag_t t;
411 	bus_dmamap_t map;
412 	struct uio *uio;
413 	int flags;
414 {
415 
416 	panic("_mca_bus_dmamap_load_uio: not implemented");
417 }
418 
419 /*
420  * Like _mca_bus_dmamap_load(), but for raw memory allocated with
421  * bus_dmamem_alloc().
422  */
423 static int
424 _mca_bus_dmamap_load_raw(t, map, segs, nsegs, size, flags)
425 	bus_dma_tag_t t;
426 	bus_dmamap_t map;
427 	bus_dma_segment_t *segs;
428 	int nsegs;
429 	bus_size_t size;
430 	int flags;
431 {
432 
433 	panic("_mca_bus_dmamap_load_raw: not implemented");
434 }
435 
436 /*
437  * Synchronize a MCA DMA map.
438  */
439 static void
440 _mca_bus_dmamap_sync(t, map, offset, len, ops)
441 	bus_dma_tag_t t;
442 	bus_dmamap_t map;
443 	bus_addr_t offset;
444 	bus_size_t len;
445 	int ops;
446 {
447 	struct i386_isa_dma_cookie *cookie;
448 	bus_addr_t phys;
449 	bus_size_t cnt;
450 	int dmach, mode;
451 
452 	_isa_bus_dmamap_sync(t, map, offset, len, ops);
453 
454 	/*
455 	 * Don't do anything if not using the DMA controller.
456 	 */
457 	if ((map->_dm_flags & _MCABUS_DMA_USEDMACTRL) == 0)
458 		return;
459 
460 	/*
461 	 * Don't do anything if not PRE* operation, allow only
462 	 * one of PREREAD and PREWRITE.
463 	 */
464 	if (ops != BUS_DMASYNC_PREREAD && ops != BUS_DMASYNC_PREWRITE)
465 		return;
466 
467 	cookie = (struct i386_isa_dma_cookie *)map->_dm_cookie;
468 	dmach = (cookie->id_flags & 0xf0) >> 4;
469 
470 	phys = map->dm_segs[0].ds_addr;
471 	cnt = map->dm_segs[0].ds_len;
472 
473 	mode = DMACMD_MODE_XFER;
474 	mode |= (ops == BUS_DMASYNC_PREREAD)
475 			? DMACMD_MODE_READ : DMACMD_MODE_WRITE;
476 	if (map->_dm_flags & MCABUS_DMA_IOPORT)
477 		mode |= DMACMD_MODE_IOPORT;
478 
479 	/* Use 16bit DMA if requested */
480 	if (map->_dm_flags & MCABUS_DMA_16BIT) {
481 #ifdef DIAGNOSTIC
482 		if ((cnt % 2) != 0) {
483 			panic("_mca_bus_dmamap_sync: 16bit DMA and cnt %lu odd",
484 				cnt);
485 		}
486 #endif
487 		mode |= DMACMD_MODE_16BIT;
488 		cnt /= 2;
489 	}
490 
491 	/*
492 	 * Initialize the MCA DMA controller appropriately. The exact
493 	 * sequence to setup the controller is taken from Minix.
494 	 */
495 
496 	/* Disable access to dma channel. */
497 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dmach);
498 
499 	/* Set the transfer mode. */
500 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_MODE | dmach);
501 	bus_space_write_1(dmaiot, dmaexech, 0, mode);
502 
503 	/* Set the address byte pointer. */
504 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_ADDR | dmach);
505 	/* address bits 0..7   */
506 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 0) & 0xff);
507 	/* address bits 8..15  */
508 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 8) & 0xff);
509 	/* address bits 16..23  */
510 	bus_space_write_1(dmaiot, dmaexech, 0, (phys >> 16) & 0xff);
511 
512 	/* Set the count byte pointer */
513 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_CNT | dmach);
514 	/* count bits 0..7     */
515 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 0) & 0xff);
516 	/* count bits 8..15    */
517 	bus_space_write_1(dmaiot, dmaexech, 0, ((cnt - 1) >> 8) & 0xff);
518 
519 	/* Enable access to dma channel. */
520 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dmach);
521 }
522 
523 /*
524  * Allocate a dma map, and set up dma channel.
525  */
526 int
527 mca_dmamap_create(t, size, flags, dmamp, dmach)
528 	bus_dma_tag_t t;
529 	bus_size_t size;
530 	int flags;
531 	bus_dmamap_t *dmamp;
532 	int dmach;
533 {
534 	int error;
535 	struct i386_isa_dma_cookie *cookie;
536 
537 #ifdef DEBUG
538 	/* Sanity check */
539 	if (dmach < 0 || dmach >= 16) {
540 		printf("mcadma_create: invalid DMA channel %d\n",
541 			dmach);
542 		return (EINVAL);
543 	}
544 
545 	if (size > 65536) {
546 		panic("mca_dmamap_create: dmamap sz %ld > 65536",
547 		    (long) size);
548 	}
549 #endif
550 
551 	/*
552 	 * MCA DMA transfer can be maximum 65536 bytes long and must
553 	 * be in one chunk. No specific boundary constraints are present.
554 	 */
555 	if ((error = bus_dmamap_create(t, size, 1, 65536, 0, flags, dmamp)))
556 		return (error);
557 
558 	/* Encode DMA channel */
559 	cookie = (struct i386_isa_dma_cookie *) (*dmamp)->_dm_cookie;
560 	cookie->id_flags &= 0x0f;
561 	cookie->id_flags |= dmach << 4;
562 
563 	/* Mark the dmamap as using DMA controller. Some devices
564 	 * drive DMA themselves, and don't need the MCA DMA controller.
565 	 * To distinguish the two, use a flag for dmamaps which use the DMA
566 	 * controller.
567  	 */
568 	(*dmamp)->_dm_flags |= _MCABUS_DMA_USEDMACTRL;
569 
570 	return (0);
571 }
572 
573 /*
574  * Set I/O port for DMA. Implemented separately from _mca_bus_dmamap_sync()
575  * so that it's available for one-shot setup.
576  */
577 void
578 mca_dma_set_ioport(dma, port)
579 	int dma;
580 	u_int16_t port;
581 {
582 	/* Disable access to dma channel. */
583 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_MASK | dma);
584 
585 	/* Set I/O port to use for DMA */
586 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_SET_IO | dma);
587 	bus_space_write_1(dmaiot, dmaexech, 0, port & 0xff);
588 	bus_space_write_1(dmaiot, dmaexech, 0, (port >> 8) & 0xff);
589 
590 	/* Enable access to dma channel. */
591 	bus_space_write_1(dmaiot, dmacmdh, 0, DMACMD_RESET_MASK | dma);
592 }
593