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