xref: /illumos-gate/usr/src/uts/sun4u/io/isadma.c (revision 657a8c206b913d1ee578fd725f0b25eca5b77253)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 #include <sys/conf.h>
28 #include <sys/sunddi.h>
29 #include <sys/ddi_impldefs.h>
30 #include <sys/kmem.h>
31 #include <sys/dma_i8237A.h>
32 #include <sys/isadma.h>
33 #include <sys/nexusdebug.h>
34 
35 /* Bitfield debugging definitions for this file */
36 #define	ISADMA_MAP_DEBUG	0x1
37 #define	ISADMA_REGACCESS_DEBUG	0x2
38 
39 /*
40  * The isadam nexus serves two functions.  The first is to represent a
41  * a placeholder in the device tree for a shared dma controller register
42  * for the SuperIO floppy and parallel ports.
43  * The second function is to virtualize the shared dma controller register
44  * for those two drivers.  Rather than creating new ddi routines to manage
45  * the shared register, we will use the ddi register mapping functions to
46  * do this.  The two child devices will use ddi_regs_map_setup to map in
47  * their device registers.  The isadma nexus will have an aliased entry in
48  * it's own registers property for the shared dma controller register.  When
49  * the isadma detects the fact that it's children are trying to map the shared
50  * register, it will intercept this mapping and provide it's own register
51  * access routine to be used to access the register when the child devices
52  * use the ddi_{get,put} calls.
53  *
54  * Sigh, the 82C37 has a weird quirk (BUG?) where when DMA is active on the
55  * the bus, PIO's cannot happen.  If they do, they generate bus faults and
56  * cause the system to panic.  On PC's, the Intel processor has special
57  * req/grnt lines that prevent PIO's from occuring while DMA is in flight,
58  * unfortunately, hummingbird doesn't support this special req/grnt pair.
59  * I'm going to try and work around this by implementing a cv to stop PIO's
60  * from occuring while DMA is in flight.  When each child wants to do DMA,
61  * they need to mask out all other channels using the allmask register.
62  * This nexus keys on this access and locks down the hardware using a cv.
63  * Once the driver's interrupt handler is called it needs to clear
64  * the allmask register.  The nexus keys off of this an issues cv wakeups
65  * if necessary.
66  */
67 /*
68  * Function prototypes for busops routines:
69  */
70 static int isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
71     off_t off, off_t len, caddr_t *addrp);
72 
73 /*
74  * function prototypes for dev ops routines:
75  */
76 static int isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
77 static int isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
78 
79 /*
80  * general function prototypes:
81  */
82 
83 /*
84  * bus ops and dev ops structures:
85  */
86 static struct bus_ops isadma_bus_ops = {
87 	BUSO_REV,
88 	isadma_map,
89 	NULL,
90 	NULL,
91 	NULL,
92 	i_ddi_map_fault,
93 	ddi_dma_map,
94 	ddi_dma_allochdl,
95 	ddi_dma_freehdl,
96 	ddi_dma_bindhdl,
97 	ddi_dma_unbindhdl,
98 	ddi_dma_flush,
99 	ddi_dma_win,
100 	ddi_dma_mctl,
101 	ddi_ctlops,
102 	ddi_bus_prop_op,
103 	0,			/* (*bus_get_eventcookie)();	*/
104 	0,			/* (*bus_add_eventcall)();	*/
105 	0,			/* (*bus_remove_eventcall)();	*/
106 	0,			/* (*bus_post_event)();		*/
107 	0,			/* (*bus_intr_control)();	*/
108 	0,			/* (*bus_config)();		*/
109 	0,			/* (*bus_unconfig)();		*/
110 	0,			/* (*bus_fm_init)();		*/
111 	0,			/* (*bus_fm_fini)();		*/
112 	0,			/* (*bus_fm_access_enter)();	*/
113 	0,			/* (*bus_fm_access_exit)();	*/
114 	0,			/* (*bus_power)();		*/
115 	i_ddi_intr_ops		/* (*bus_intr_op();		*/
116 };
117 
118 static struct dev_ops isadma_ops = {
119 	DEVO_REV,
120 	0,
121 	ddi_no_info,
122 	nulldev,
123 	0,
124 	isadma_attach,
125 	isadma_detach,
126 	nodev,
127 	(struct cb_ops *)0,
128 	&isadma_bus_ops,
129 	NULL,
130 	ddi_quiesce_not_needed,		/* quiesce */
131 };
132 
133 /*
134  * module definitions:
135  */
136 #include <sys/modctl.h>
137 
138 static struct modldrv modldrv = {
139 	&mod_driverops, 	/* Type of module.  This one is a driver */
140 	"isadma nexus driver",	/* Name of module. */
141 	&isadma_ops,		/* driver ops */
142 };
143 
144 static struct modlinkage modlinkage = {
145 	MODREV_1, (void *)&modldrv, NULL
146 };
147 
148 /*
149  * driver global data:
150  */
151 static void *per_isadma_state;		/* per-isadma soft state pointer */
152 
153 /* Global debug data */
154 uint64_t isadma_sleep_cnt = 0;
155 uint64_t isadma_wakeup_cnt = 0;
156 #ifdef DEBUG
157 int64_t isadma_max_waiter = 0;
158 int64_t isadma_min_waiter = 0xffffll;
159 uint64_t isadma_punt = 0;
160 uint64_t isadma_setting_wdip = 0;
161 uint64_t isadma_clearing_wdip = 0;
162 #endif
163 
164 int
165 _init(void)
166 {
167 	int e;
168 
169 	/*
170 	 * Initialize per-isadma soft state pointer.
171 	 */
172 	e = ddi_soft_state_init(&per_isadma_state,
173 	    sizeof (isadma_devstate_t), 1);
174 	if (e != 0)
175 		return (e);
176 
177 	/*
178 	 * Install the module.
179 	 */
180 	e = mod_install(&modlinkage);
181 	if (e != 0)
182 		ddi_soft_state_fini(&per_isadma_state);
183 	return (e);
184 }
185 
186 int
187 _fini(void)
188 {
189 	int e;
190 
191 	/*
192 	 * Remove the module.
193 	 */
194 	e = mod_remove(&modlinkage);
195 	if (e != 0)
196 		return (e);
197 
198 	/*
199 	 * Free the soft state info.
200 	 */
201 	ddi_soft_state_fini(&per_isadma_state);
202 	return (e);
203 }
204 
205 int
206 _info(struct modinfo *modinfop)
207 {
208 	return (mod_info(&modlinkage, modinfop));
209 }
210 
211 /* device driver entry points */
212 
213 /*
214  * attach entry point:
215  */
216 static int
217 isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
218 {
219 	isadma_devstate_t *isadmap;	/* per isadma state pointer */
220 	int32_t instance;
221 	int ret = DDI_SUCCESS;
222 
223 #ifdef DEBUG
224 	debug_print_level = 0;
225 	debug_info = 1;
226 #endif
227 	switch (cmd) {
228 	case DDI_ATTACH: {
229 		/*
230 		 * Allocate soft state for this instance.
231 		 */
232 		instance = ddi_get_instance(dip);
233 		if (ddi_soft_state_zalloc(per_isadma_state, instance)
234 		    != DDI_SUCCESS) {
235 			ret = DDI_FAILURE;
236 			goto exit;
237 		}
238 		isadmap = ddi_get_soft_state(per_isadma_state, instance);
239 		isadmap->isadma_dip = dip;
240 
241 		/* Cache our register property */
242 		if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
243 		    "reg", (caddr_t)&isadmap->isadma_regp,
244 		    &isadmap->isadma_reglen) != DDI_SUCCESS) {
245 			ret = DDI_FAILURE;
246 			goto fail_get_prop;
247 		}
248 
249 		/* Initialize our mutex */
250 		mutex_init(&isadmap->isadma_access_lock, NULL, MUTEX_DRIVER,
251 		    NULL);
252 
253 		/* Initialize our condition variable */
254 		cv_init(&isadmap->isadma_access_cv, NULL, CV_DRIVER, NULL);
255 
256 		ddi_report_dev(dip);
257 		goto exit;
258 
259 	}
260 	case DDI_RESUME:
261 	default:
262 		goto exit;
263 	}
264 
265 fail_get_prop:
266 	ddi_soft_state_free(per_isadma_state, instance);
267 
268 exit:
269 	return (ret);
270 }
271 
272 /*
273  * detach entry point:
274  */
275 static int
276 isadma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
277 {
278 	int instance = ddi_get_instance(dip);
279 	isadma_devstate_t *isadmap =
280 	    ddi_get_soft_state(per_isadma_state, instance);
281 
282 	switch (cmd) {
283 	case DDI_DETACH:
284 		cv_destroy(&isadmap->isadma_access_cv);
285 
286 		mutex_destroy(&isadmap->isadma_access_lock);
287 
288 		/* free the cached register property */
289 		kmem_free(isadmap->isadma_regp, isadmap->isadma_reglen);
290 
291 		ddi_soft_state_free(per_isadma_state, instance);
292 		return (DDI_SUCCESS);
293 
294 	case DDI_SUSPEND:
295 		return (DDI_SUCCESS);
296 	}
297 	return (DDI_FAILURE);
298 }
299 
300 
301 #ifdef DEBUG
302 static void
303 isadma_check_waiters(isadma_devstate_t *isadmap)
304 {
305 	if (isadmap->isadma_want > isadma_max_waiter)
306 		isadma_max_waiter = isadmap->isadma_want;
307 
308 	if (isadmap->isadma_want < isadma_min_waiter)
309 		isadma_min_waiter = isadmap->isadma_want;
310 }
311 #endif
312 
313 static void
314 isadma_dmawait(isadma_devstate_t *isadmap)
315 {
316 
317 	ASSERT(mutex_owned(&isadmap->isadma_access_lock));
318 
319 	/* Wait loop, if the locking dip is set, we wait. */
320 	while (isadmap->isadma_ldip != NULL) {
321 
322 		isadmap->isadma_want++;
323 		cv_wait(&isadmap->isadma_access_cv,
324 		    &isadmap->isadma_access_lock);
325 		isadmap->isadma_want--;
326 		isadma_sleep_cnt++;
327 	}
328 }
329 
330 static void
331 isadma_wakeup(isadma_devstate_t *isadmap)
332 {
333 
334 	ASSERT(mutex_owned(&isadmap->isadma_access_lock));
335 
336 	/*
337 	 * If somebody wants register access and the lock dip is not set
338 	 * signal the waiters.
339 	 */
340 	if (isadmap->isadma_want > 0 && isadmap->isadma_ldip == NULL) {
341 		cv_signal(&isadmap->isadma_access_cv);
342 		isadma_wakeup_cnt++;
343 	}
344 
345 }
346 
347 /*
348  * Register access vectors
349  */
350 
351 /*ARGSUSED*/
352 void
353 isadma_norep_get8(ddi_acc_impl_t *handle, uint8_t *host_addr,
354     uint8_t *dev_addr, size_t repcount, uint_t flags)
355 {
356 }
357 
358 /*ARGSUSED*/
359 void
360 isadma_norep_get16(ddi_acc_impl_t *handle, uint16_t *host_addr,
361     uint16_t *dev_addr, size_t repcount, uint_t flags)
362 {
363 }
364 
365 /*ARGSUSED*/
366 void
367 isadma_norep_get32(ddi_acc_impl_t *handle, uint32_t *host_addr,
368     uint32_t *dev_addr, size_t repcount, uint_t flags)
369 {
370 }
371 
372 /*ARGSUSED*/
373 void
374 isadma_norep_get64(ddi_acc_impl_t *handle, uint64_t *host_addr,
375     uint64_t *dev_addr, size_t repcount, uint_t flags)
376 {
377 }
378 
379 /*ARGSUSED*/
380 void
381 isadma_norep_put8(ddi_acc_impl_t *handle, uint8_t *host_addr,
382     uint8_t *dev_addr, size_t repcount, uint_t flags)
383 {
384 }
385 
386 /*ARGSUSED*/
387 void
388 isadma_norep_put16(ddi_acc_impl_t *handle, uint16_t *host_addr,
389     uint16_t *dev_addr, size_t repcount, uint_t flags)
390 {
391 }
392 
393 /*ARGSUSED*/
394 void
395 isadma_norep_put32(ddi_acc_impl_t *handle, uint32_t *host_addr,
396     uint32_t *dev_addr, size_t repcount, uint_t flags)
397 {
398 }
399 
400 /*ARGSUSED*/
401 void
402 isadma_norep_put64(ddi_acc_impl_t *handle, uint64_t *host_addr,
403     uint64_t *dev_addr, size_t repcount, uint_t flags)
404 {
405 }
406 
407 /*ARGSUSED*/
408 uint8_t
409 isadma_get8(ddi_acc_impl_t *hdlp, uint8_t *addr)
410 {
411 	ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
412 	isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
413 	off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
414 	uint8_t ret = 0xff;
415 
416 	if (IN_CHILD_SPACE(offset)) {	/* Pass to parent */
417 #ifdef DEBUG
418 		isadma_punt++;
419 #endif
420 		return (ddi_get8(phdl, addr));
421 	}
422 #ifdef DEBUG
423 	isadma_check_waiters(isadmap);
424 #endif
425 	mutex_enter(&isadmap->isadma_access_lock);
426 	isadma_dmawait(isadmap);	/* wait until on-going dma completes */
427 
428 	/* No 8 bit access to 16 bit address or count registers */
429 	if (IN_16BIT_SPACE(offset))
430 		goto exit;
431 
432 	/* No 8 bit access to first/last flip-flop registers */
433 	if (IS_SEQREG(offset))
434 		goto exit;
435 
436 	ret = ddi_get8(phdl, addr);	/* Pass to parent */
437 exit:
438 	isadma_wakeup(isadmap);
439 	mutex_exit(&isadmap->isadma_access_lock);
440 	return (ret);
441 }
442 
443 /*
444  * Allow child devices to access this shared register set as if it were
445  * a real 16 bit register.  The ISA bridge defines the access to this
446  * 16 bit dma controller & count register by programming an 8 byte register.
447  */
448 /*ARGSUSED*/
449 uint16_t
450 isadma_get16(ddi_acc_impl_t *hdlp, uint16_t *addr)
451 {
452 	ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
453 	isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
454 	off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
455 	uint16_t ret = 0xffff;
456 
457 	if (IN_CHILD_SPACE(offset)) {	/* Pass to parent */
458 #ifdef DEBUG
459 		isadma_punt++;
460 #endif
461 		return (ddi_get16(phdl, addr));
462 	}
463 #ifdef DEBUG
464 	isadma_check_waiters(isadmap);
465 #endif
466 	mutex_enter(&isadmap->isadma_access_lock);
467 	isadma_dmawait(isadmap);	/* wait until on-going dma completes */
468 
469 	/* Only Allow access to the 16 bit count and address registers */
470 	if (!IN_16BIT_SPACE(offset))
471 		goto exit;
472 
473 	/* Set the sequencing register to the low byte */
474 	ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0);
475 
476 	/* Read the low byte, then high byte */
477 	ret = ddi_get8(phdl, (uint8_t *)addr);
478 	ret = (ddi_get8(phdl, (uint8_t *)addr) << 8) | ret;
479 exit:
480 	isadma_wakeup(isadmap);
481 	mutex_exit(&isadmap->isadma_access_lock);
482 	return (ret);
483 }
484 
485 /*ARGSUSED*/
486 uint32_t
487 isadma_noget32(ddi_acc_impl_t *hdlp, uint32_t *addr)
488 {
489 	return (UINT32_MAX);
490 }
491 
492 /*ARGSUSED*/
493 uint64_t
494 isadma_noget64(ddi_acc_impl_t *hdlp, uint64_t *addr)
495 {
496 	return (UINT64_MAX);
497 }
498 
499 /*
500  * Here's where we do our locking magic.  The dma all mask register is an 8
501  * bit register in the dma space, so we look for the access to the
502  * DMAC1_ALLMASK register.  When somebody is masking out the dma channels
503  * we lock down the dma engine from further PIO accesses.  When the driver
504  * calls back into this routine to clear the allmask register, we wakeup
505  * any blocked threads.
506  */
507 /*ARGSUSED*/
508 void
509 isadma_put8(ddi_acc_impl_t *hdlp, uint8_t *addr, uint8_t value)
510 {
511 	ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
512 	isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
513 	off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
514 
515 	if (IN_CHILD_SPACE(offset)) {	/* Pass to parent */
516 #ifdef DEBUG
517 		isadma_punt++;
518 #endif
519 		ddi_put8(phdl, addr, value);
520 		return;
521 	}
522 #ifdef DEBUG
523 	isadma_check_waiters(isadmap);
524 #endif
525 	mutex_enter(&isadmap->isadma_access_lock);
526 
527 	if (isadmap->isadma_ldip == hdlp->ahi_common.ah_dip) { /* owned lock? */
528 		if (END_ISADMA(offset, value)) {
529 			isadmap->isadma_ldip = NULL;	/* reset lock owner */
530 #ifdef DEBUG
531 			isadma_clearing_wdip++;
532 #endif
533 		}
534 	} else	{	/* we don't own the lock */
535 		/* wait until on-going dma completes */
536 		isadma_dmawait(isadmap);
537 
538 		if (BEGIN_ISADMA(offset, value)) {
539 			isadmap->isadma_ldip = hdlp->ahi_common.ah_dip;
540 #ifdef DEBUG
541 			isadma_setting_wdip++;
542 #endif
543 		}
544 	}
545 
546 	/* No 8 bit access to 16 bit address or count registers */
547 	if (IN_16BIT_SPACE(offset))
548 		goto exit;
549 
550 	/* No 8 bit access to first/last flip-flop registers */
551 	if (IS_SEQREG(offset))
552 		goto exit;
553 
554 	ddi_put8(phdl, addr, value);	/* Pass to parent */
555 exit:
556 	isadma_wakeup(isadmap);
557 	mutex_exit(&isadmap->isadma_access_lock);
558 }
559 
560 /*
561  * Allow child devices to access this shared register set as if it were
562  * a real 16 bit register.  The ISA bridge defines the access to this
563  * 16 bit dma controller & count register by programming an 8 byte register.
564  */
565 /*ARGSUSED*/
566 void
567 isadma_put16(ddi_acc_impl_t *hdlp, uint16_t *addr, uint16_t value)
568 {
569 	ddi_acc_handle_t phdl = hdlp->ahi_common.ah_platform_private;
570 	isadma_devstate_t *isadmap = hdlp->ahi_common.ah_bus_private;
571 	off_t offset = (caddr_t)addr - hdlp->ahi_common.ah_addr;
572 
573 	if (IN_CHILD_SPACE(offset)) {	/* Pass to parent */
574 #ifdef DEBUG
575 		isadma_punt++;
576 #endif
577 		ddi_put16(phdl, addr, value);
578 		return;
579 	}
580 #ifdef DEBUG
581 	isadma_check_waiters(isadmap);
582 #endif
583 	mutex_enter(&isadmap->isadma_access_lock);
584 	isadma_dmawait(isadmap);	/* wait until on-going dma completes */
585 
586 	/* Only Allow access to the 16 bit count and address registers */
587 	if (!IN_16BIT_SPACE(offset))
588 		goto exit;
589 
590 	/* Set the sequencing register to the low byte */
591 	ddi_put8(phdl, (uint8_t *)HDL_TO_SEQREG_ADDR(hdlp, offset), 0);
592 
593 	/* Write the low byte, then the high byte */
594 	ddi_put8(phdl, (uint8_t *)addr, value & 0xff);
595 	ddi_put8(phdl, (uint8_t *)addr, (value >> 8) & 0xff);
596 exit:
597 	isadma_wakeup(isadmap);
598 	mutex_exit(&isadmap->isadma_access_lock);
599 }
600 
601 /*ARGSUSED*/
602 void
603 isadma_noput32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) {}
604 
605 /*ARGSUSED*/
606 void
607 isadma_noput64(ddi_acc_impl_t *hdlp, uint64_t *addr, uint64_t value) {}
608 
609 #define	IS_SAME_REG(r1, r2) (((r1)->ebus_addr_hi == (r2)->ebus_addr_hi) && \
610 	((r1)->ebus_addr_low == (r2)->ebus_addr_low))
611 
612 /*
613  * The isadma_map routine determines if it's child is attempting to map a
614  * shared reg.  If it is, it installs it's own vectors and bus private pointer
615  * and stacks those ops that were already defined.
616  */
617 static int
618 isadma_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
619 	off_t off, off_t len, caddr_t *addrp)
620 {
621 	isadma_devstate_t *isadmap = ddi_get_soft_state(per_isadma_state,
622 	    ddi_get_instance(dip));
623 	dev_info_t *pdip = (dev_info_t *)DEVI(dip)->devi_parent;
624 	ebus_regspec_t *child_regp, *regp;
625 	int32_t rnumber = mp->map_obj.rnumber;
626 	int32_t reglen;
627 	int ret;
628 	ddi_acc_impl_t *hp;
629 
630 	/*
631 	 * Get child regspec since the mapping struct may not have it yet
632 	 */
633 	if (ddi_getlongprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
634 	    "reg", (caddr_t)&regp, &reglen) != DDI_SUCCESS) {
635 		return (DDI_FAILURE);
636 	}
637 
638 	child_regp = regp + rnumber;
639 
640 	DPRINTF(ISADMA_MAP_DEBUG, ("isadma_map: child regp %p "
641 	    "parent regp %p Child reg array %p\n", (void *)child_regp,
642 	    (void *)isadmap->isadma_regp, (void *)regp));
643 
644 	/* Figure out if we're mapping or unmapping */
645 	switch (mp->map_op) {
646 	case DDI_MO_MAP_LOCKED:
647 		/* Call up device tree to establish mapping */
648 		ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)
649 		    (pdip, rdip, mp, off, len, addrp);
650 
651 		if ((ret != DDI_SUCCESS) ||
652 		    !IS_SAME_REG(child_regp, isadmap->isadma_regp))
653 			break;
654 
655 		/* Post-process the mapping request. */
656 		hp = kmem_alloc(sizeof (ddi_acc_impl_t), KM_SLEEP);
657 		*hp = *(ddi_acc_impl_t *)mp->map_handlep;
658 		impl_acc_hdl_get((ddi_acc_handle_t)mp->map_handlep)->
659 		    ah_platform_private = hp;
660 		hp = (ddi_acc_impl_t *)mp->map_handlep;
661 		hp->ahi_common.ah_bus_private = isadmap;
662 		hp->ahi_get8 = isadma_get8;
663 		hp->ahi_get16 = isadma_get16;
664 		hp->ahi_get32 = isadma_noget32;
665 		hp->ahi_get64 = isadma_noget64;
666 		hp->ahi_put8 = isadma_put8;
667 		hp->ahi_put16 = isadma_put16;
668 		hp->ahi_put32 = isadma_noput32;
669 		hp->ahi_put64 = isadma_noput64;
670 		hp->ahi_rep_get8 = isadma_norep_get8;
671 		hp->ahi_rep_get16 = isadma_norep_get16;
672 		hp->ahi_rep_get32 = isadma_norep_get32;
673 		hp->ahi_rep_get64 = isadma_norep_get64;
674 		hp->ahi_rep_put8 = isadma_norep_put8;
675 		hp->ahi_rep_put16 = isadma_norep_put16;
676 		hp->ahi_rep_put32 = isadma_norep_put32;
677 		hp->ahi_rep_put64 = isadma_norep_put64;
678 		break;
679 
680 	case DDI_MO_UNMAP:
681 		if (IS_SAME_REG(child_regp, isadmap->isadma_regp)) {
682 			hp = impl_acc_hdl_get(
683 			    (ddi_acc_handle_t)mp->map_handlep)->
684 			    ah_platform_private;
685 			*(ddi_acc_impl_t *)mp->map_handlep = *hp;
686 			kmem_free(hp, sizeof (ddi_acc_impl_t));
687 		}
688 
689 		/* Call up tree to tear down mapping */
690 		ret = (DEVI(pdip)->devi_ops->devo_bus_ops->bus_map)
691 		    (pdip, rdip, mp, off, len, addrp);
692 		break;
693 
694 	default:
695 		ret = DDI_FAILURE;
696 		break;
697 	}
698 
699 	kmem_free(regp, reglen);
700 	return (ret);
701 }
702