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 */
73static 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 */
79static int isadma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
80static 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 */
89static 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
121static 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
141static 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
147static struct modlinkage modlinkage = {
148	MODREV_1, (void *)&modldrv, NULL
149};
150
151/*
152 * driver global data:
153 */
154static void *per_isadma_state;		/* per-isadma soft state pointer */
155
156/* Global debug data */
157uint64_t isadma_sleep_cnt = 0;
158uint64_t isadma_wakeup_cnt = 0;
159#ifdef DEBUG
160int64_t isadma_max_waiter = 0;
161int64_t isadma_min_waiter = 0xffffll;
162uint64_t isadma_punt = 0;
163uint64_t isadma_setting_wdip = 0;
164uint64_t isadma_clearing_wdip = 0;
165#endif
166
167int
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
189int
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
208int
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 */
219static int
220isadma_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
268fail_get_prop:
269	ddi_soft_state_free(per_isadma_state, instance);
270
271exit:
272	return (ret);
273}
274
275/*
276 * detach entry point:
277 */
278static int
279isadma_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
305static void
306isadma_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
316static void
317isadma_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
333static void
334isadma_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*/
355void
356isadma_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*/
362void
363isadma_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*/
369void
370isadma_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*/
376void
377isadma_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*/
383void
384isadma_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*/
390void
391isadma_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*/
397void
398isadma_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*/
404void
405isadma_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*/
411uint8_t
412isadma_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 */
440exit:
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*/
452uint16_t
453isadma_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;
482exit:
483	isadma_wakeup(isadmap);
484	mutex_exit(&isadmap->isadma_access_lock);
485	return (ret);
486}
487
488/*ARGSUSED*/
489uint32_t
490isadma_noget32(ddi_acc_impl_t *hdlp, uint32_t *addr)
491{
492	return (UINT32_MAX);
493}
494
495/*ARGSUSED*/
496uint64_t
497isadma_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*/
511void
512isadma_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 */
558exit:
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*/
569void
570isadma_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);
599exit:
600	isadma_wakeup(isadmap);
601	mutex_exit(&isadmap->isadma_access_lock);
602}
603
604/*ARGSUSED*/
605void
606isadma_noput32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) {}
607
608/*ARGSUSED*/
609void
610isadma_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 */
620static int
621isadma_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