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)®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