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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
isadma_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
isadma_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
isadma_check_waiters(isadma_devstate_t * isadmap)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
isadma_dmawait(isadma_devstate_t * isadmap)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
isadma_wakeup(isadma_devstate_t * isadmap)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
isadma_norep_get8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_get16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_get32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_get64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_put8(ddi_acc_impl_t * handle,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_put16(ddi_acc_impl_t * handle,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_put32(ddi_acc_impl_t * handle,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
isadma_norep_put64(ddi_acc_impl_t * handle,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
isadma_get8(ddi_acc_impl_t * hdlp,uint8_t * addr)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
isadma_get16(ddi_acc_impl_t * hdlp,uint16_t * addr)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
isadma_noget32(ddi_acc_impl_t * hdlp,uint32_t * addr)490 isadma_noget32(ddi_acc_impl_t *hdlp, uint32_t *addr)
491 {
492 return (UINT32_MAX);
493 }
494
495 /*ARGSUSED*/
496 uint64_t
isadma_noget64(ddi_acc_impl_t * hdlp,uint64_t * addr)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
isadma_put8(ddi_acc_impl_t * hdlp,uint8_t * addr,uint8_t value)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
isadma_put16(ddi_acc_impl_t * hdlp,uint16_t * addr,uint16_t value)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
isadma_noput32(ddi_acc_impl_t * hdlp,uint32_t * addr,uint32_t value)606 isadma_noput32(ddi_acc_impl_t *hdlp, uint32_t *addr, uint32_t value) {}
607
608 /*ARGSUSED*/
609 void
isadma_noput64(ddi_acc_impl_t * hdlp,uint64_t * addr,uint64_t value)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
isadma_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)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)®p, ®len) != 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