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