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 2006 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 /* 29 * ISA bus nexus driver 30 */ 31 32 #include <sys/types.h> 33 #include <sys/cmn_err.h> 34 #include <sys/conf.h> 35 #include <sys/modctl.h> 36 #include <sys/autoconf.h> 37 #include <sys/errno.h> 38 #include <sys/debug.h> 39 #include <sys/kmem.h> 40 #include <sys/psm.h> 41 #include <sys/ddidmareq.h> 42 #include <sys/ddi_impldefs.h> 43 #include <sys/dma_engine.h> 44 #include <sys/ddi.h> 45 #include <sys/sunddi.h> 46 #include <sys/sunndi.h> 47 #include <sys/acpi/acpi_enum.h> 48 49 extern int isa_resource_setup(void); 50 static char USED_RESOURCES[] = "used-resources"; 51 static void isa_alloc_nodes(dev_info_t *); 52 static void enumerate_BIOS_serial(dev_info_t *); 53 54 #define BIOS_DATA_AREA 0x400 55 /* 56 * #define ISA_DEBUG 1 57 */ 58 59 /* 60 * Local data 61 */ 62 static ddi_dma_lim_t ISA_dma_limits = { 63 0, /* address low */ 64 0x00ffffff, /* address high */ 65 0, /* counter max */ 66 1, /* burstsize */ 67 DMA_UNIT_8, /* minimum xfer */ 68 0, /* dma speed */ 69 (uint_t)DMALIM_VER0, /* version */ 70 0x0000ffff, /* address register */ 71 0x0000ffff, /* counter register */ 72 1, /* sector size */ 73 0x00000001, /* scatter/gather list length */ 74 (uint_t)0xffffffff /* request size */ 75 }; 76 77 static ddi_dma_attr_t ISA_dma_attr = { 78 DMA_ATTR_V0, 79 (unsigned long long)0, 80 (unsigned long long)0x00ffffff, 81 0x0000ffff, 82 1, 83 1, 84 1, 85 (unsigned long long)0xffffffff, 86 (unsigned long long)0x0000ffff, 87 1, 88 1, 89 0 90 }; 91 92 93 /* 94 * Config information 95 */ 96 97 static int 98 isa_dma_allochdl(dev_info_t *, dev_info_t *, ddi_dma_attr_t *, 99 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *); 100 101 static int 102 isa_dma_mctl(dev_info_t *, dev_info_t *, ddi_dma_handle_t, enum ddi_dma_ctlops, 103 off_t *, size_t *, caddr_t *, uint_t); 104 105 static int 106 isa_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, void *); 107 108 struct bus_ops isa_bus_ops = { 109 BUSO_REV, 110 i_ddi_bus_map, 111 NULL, 112 NULL, 113 NULL, 114 i_ddi_map_fault, 115 ddi_dma_map, 116 isa_dma_allochdl, 117 ddi_dma_freehdl, 118 ddi_dma_bindhdl, 119 ddi_dma_unbindhdl, 120 ddi_dma_flush, 121 ddi_dma_win, 122 isa_dma_mctl, 123 isa_ctlops, 124 ddi_bus_prop_op, 125 NULL, /* (*bus_get_eventcookie)(); */ 126 NULL, /* (*bus_add_eventcall)(); */ 127 NULL, /* (*bus_remove_eventcall)(); */ 128 NULL, /* (*bus_post_event)(); */ 129 NULL, /* (*bus_intr_ctl)(); */ 130 NULL, /* (*bus_config)(); */ 131 NULL, /* (*bus_unconfig)(); */ 132 NULL, /* (*bus_fm_init)(); */ 133 NULL, /* (*bus_fm_fini)(); */ 134 NULL, /* (*bus_fm_access_enter)(); */ 135 NULL, /* (*bus_fm_access_exit)(); */ 136 NULL, /* (*bus_power)(); */ 137 i_ddi_intr_ops /* (*bus_intr_op)(); */ 138 }; 139 140 141 static int isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd); 142 143 /* 144 * Internal isa ctlops support routines 145 */ 146 static int isa_initchild(dev_info_t *child); 147 148 struct dev_ops isa_ops = { 149 DEVO_REV, /* devo_rev, */ 150 0, /* refcnt */ 151 ddi_no_info, /* info */ 152 nulldev, /* identify */ 153 nulldev, /* probe */ 154 isa_attach, /* attach */ 155 nodev, /* detach */ 156 nodev, /* reset */ 157 (struct cb_ops *)0, /* driver operations */ 158 &isa_bus_ops /* bus operations */ 159 160 }; 161 162 /* 163 * Module linkage information for the kernel. 164 */ 165 166 static struct modldrv modldrv = { 167 &mod_driverops, /* Type of module. This is ISA bus driver */ 168 "isa nexus driver for 'ISA' %I%", 169 &isa_ops, /* driver ops */ 170 }; 171 172 static struct modlinkage modlinkage = { 173 MODREV_1, 174 &modldrv, 175 NULL 176 }; 177 178 int 179 _init(void) 180 { 181 return (mod_install(&modlinkage)); 182 } 183 184 int 185 _fini(void) 186 { 187 return (mod_remove(&modlinkage)); 188 } 189 190 int 191 _info(struct modinfo *modinfop) 192 { 193 return (mod_info(&modlinkage, modinfop)); 194 } 195 196 static int 197 isa_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 198 { 199 int rval; 200 201 if (cmd != DDI_ATTACH) 202 return (DDI_FAILURE); 203 204 if ((rval = i_dmae_init(devi)) == DDI_SUCCESS) { 205 ddi_report_dev(devi); 206 /* 207 * Enumerate children -- invoking ACPICA 208 * This is normally in bus_config(), but we need this 209 * to happen earlier to boot. 210 */ 211 isa_alloc_nodes(devi); 212 } 213 return (rval); 214 } 215 216 static int 217 isa_dma_allochdl(dev_info_t *dip, dev_info_t *rdip, ddi_dma_attr_t *dma_attr, 218 int (*waitfp)(caddr_t), caddr_t arg, ddi_dma_handle_t *handlep) 219 { 220 ddi_dma_attr_merge(dma_attr, &ISA_dma_attr); 221 return (ddi_dma_allochdl(dip, rdip, dma_attr, waitfp, arg, handlep)); 222 } 223 224 static int 225 isa_dma_mctl(dev_info_t *dip, dev_info_t *rdip, 226 ddi_dma_handle_t handle, enum ddi_dma_ctlops request, 227 off_t *offp, size_t *lenp, caddr_t *objp, uint_t flags) 228 { 229 int rval; 230 ddi_dma_lim_t defalt; 231 int arg = (int)(uintptr_t)objp; 232 233 switch (request) { 234 235 case DDI_DMA_E_PROG: 236 return (i_dmae_prog(rdip, (struct ddi_dmae_req *)offp, 237 (ddi_dma_cookie_t *)lenp, arg)); 238 239 case DDI_DMA_E_ACQUIRE: 240 return (i_dmae_acquire(rdip, arg, (int(*)(caddr_t))offp, 241 (caddr_t)lenp)); 242 243 case DDI_DMA_E_FREE: 244 return (i_dmae_free(rdip, arg)); 245 246 case DDI_DMA_E_STOP: 247 i_dmae_stop(rdip, arg); 248 return (DDI_SUCCESS); 249 250 case DDI_DMA_E_ENABLE: 251 i_dmae_enable(rdip, arg); 252 return (DDI_SUCCESS); 253 254 case DDI_DMA_E_DISABLE: 255 i_dmae_disable(rdip, arg); 256 return (DDI_SUCCESS); 257 258 case DDI_DMA_E_GETCNT: 259 i_dmae_get_chan_stat(rdip, arg, NULL, (int *)lenp); 260 return (DDI_SUCCESS); 261 262 case DDI_DMA_E_SWSETUP: 263 return (i_dmae_swsetup(rdip, (struct ddi_dmae_req *)offp, 264 (ddi_dma_cookie_t *)lenp, arg)); 265 266 case DDI_DMA_E_SWSTART: 267 i_dmae_swstart(rdip, arg); 268 return (DDI_SUCCESS); 269 270 case DDI_DMA_E_GETLIM: 271 bcopy(&ISA_dma_limits, objp, sizeof (ddi_dma_lim_t)); 272 return (DDI_SUCCESS); 273 274 case DDI_DMA_E_GETATTR: 275 bcopy(&ISA_dma_attr, objp, sizeof (ddi_dma_attr_t)); 276 return (DDI_SUCCESS); 277 278 case DDI_DMA_E_1STPTY: 279 { 280 struct ddi_dmae_req req1stpty = 281 { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; 282 if (arg == 0) { 283 req1stpty.der_command = DMAE_CMD_TRAN; 284 req1stpty.der_trans = DMAE_TRANS_DMND; 285 } else { 286 req1stpty.der_trans = DMAE_TRANS_CSCD; 287 } 288 return (i_dmae_prog(rdip, &req1stpty, NULL, arg)); 289 } 290 291 case DDI_DMA_IOPB_ALLOC: /* get contiguous DMA-able memory */ 292 case DDI_DMA_SMEM_ALLOC: 293 if (!offp) { 294 defalt = ISA_dma_limits; 295 offp = (off_t *)&defalt; 296 } 297 /*FALLTHROUGH*/ 298 default: 299 rval = ddi_dma_mctl(dip, rdip, handle, request, offp, 300 lenp, objp, flags); 301 } 302 return (rval); 303 } 304 305 /* 306 * Check if driver should be treated as an old pre 2.6 driver 307 */ 308 static int 309 old_driver(dev_info_t *dip) 310 { 311 extern int ignore_hardware_nodes; /* force flag from ddi_impl.c */ 312 313 if (ndi_dev_is_persistent_node(dip)) { 314 if (ignore_hardware_nodes) 315 return (1); 316 if (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 317 "ignore-hardware-nodes", -1) != -1) 318 return (1); 319 } 320 return (0); 321 } 322 323 typedef struct { 324 uint32_t phys_hi; 325 uint32_t phys_lo; 326 uint32_t size; 327 } isa_regs_t; 328 329 /* 330 * Return non-zero if device in tree is a PnP isa device 331 */ 332 static int 333 is_pnpisa(dev_info_t *dip) 334 { 335 isa_regs_t *isa_regs; 336 int proplen, pnpisa; 337 338 if (ndi_dev_is_persistent_node(dip) == 0) 339 return (0); 340 if (ddi_getlongprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "reg", 341 (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 342 return (0); 343 } 344 pnpisa = isa_regs[0].phys_hi & 0x80000000; 345 /* 346 * free the memory allocated by ddi_getlongprop(). 347 */ 348 kmem_free(isa_regs, proplen); 349 if (pnpisa) 350 return (1); 351 else 352 return (0); 353 } 354 355 /*ARGSUSED*/ 356 static int 357 isa_ctlops(dev_info_t *dip, dev_info_t *rdip, 358 ddi_ctl_enum_t ctlop, void *arg, void *result) 359 { 360 switch (ctlop) { 361 case DDI_CTLOPS_REPORTDEV: 362 if (rdip == (dev_info_t *)0) 363 return (DDI_FAILURE); 364 cmn_err(CE_CONT, "?ISA-device: %s%d\n", 365 ddi_driver_name(rdip), ddi_get_instance(rdip)); 366 return (DDI_SUCCESS); 367 368 case DDI_CTLOPS_INITCHILD: 369 /* 370 * older drivers aren't expecting the "standard" device 371 * node format used by the hardware nodes. these drivers 372 * only expect their own properties set in their driver.conf 373 * files. so they tell us not to call them with hardware 374 * nodes by setting the property "ignore-hardware-nodes". 375 */ 376 if (old_driver((dev_info_t *)arg)) { 377 return (DDI_NOT_WELL_FORMED); 378 } 379 380 return (isa_initchild((dev_info_t *)arg)); 381 382 case DDI_CTLOPS_UNINITCHILD: 383 impl_ddi_sunbus_removechild((dev_info_t *)arg); 384 return (DDI_SUCCESS); 385 386 case DDI_CTLOPS_SIDDEV: 387 /* 388 * All ISA devices need to do confirming probes 389 * unless they are PnP ISA. 390 */ 391 if (is_pnpisa(dip)) 392 return (DDI_SUCCESS); 393 else 394 return (DDI_FAILURE); 395 396 default: 397 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 398 } 399 } 400 401 static void 402 isa_vendor(uint32_t id, char *vendor) 403 { 404 vendor[0] = '@' + ((id >> 26) & 0x1f); 405 vendor[1] = '@' + ((id >> 21) & 0x1f); 406 vendor[2] = '@' + ((id >> 16) & 0x1f); 407 vendor[3] = 0; 408 } 409 410 /* 411 * Name a child 412 */ 413 static int 414 isa_name_child(dev_info_t *child, char *name, int namelen) 415 { 416 char vendor[8]; 417 int device; 418 uint32_t serial; 419 int func; 420 int bustype; 421 uint32_t base; 422 int proplen; 423 int pnpisa = 0; 424 isa_regs_t *isa_regs; 425 426 void make_ddi_ppd(dev_info_t *, struct ddi_parent_private_data **); 427 428 /* 429 * older drivers aren't expecting the "standard" device 430 * node format used by the hardware nodes. these drivers 431 * only expect their own properties set in their driver.conf 432 * files. so they tell us not to call them with hardware 433 * nodes by setting the property "ignore-hardware-nodes". 434 */ 435 if (old_driver(child)) 436 return (DDI_FAILURE); 437 438 /* 439 * Fill in parent-private data 440 */ 441 if (ddi_get_parent_data(child) == NULL) { 442 struct ddi_parent_private_data *pdptr; 443 make_ddi_ppd(child, &pdptr); 444 ddi_set_parent_data(child, pdptr); 445 } 446 447 if (ndi_dev_is_persistent_node(child) == 0) { 448 /* 449 * For .conf nodes, generate name from parent private data 450 */ 451 name[0] = '\0'; 452 if (sparc_pd_getnreg(child) > 0) { 453 (void) snprintf(name, namelen, "%x,%x", 454 (uint_t)sparc_pd_getreg(child, 0)->regspec_bustype, 455 (uint_t)sparc_pd_getreg(child, 0)->regspec_addr); 456 } 457 return (DDI_SUCCESS); 458 } 459 460 /* 461 * For hw nodes, look up "reg" property 462 */ 463 if (ddi_getlongprop(DDI_DEV_T_ANY, child, DDI_PROP_DONTPASS, "reg", 464 (caddr_t)&isa_regs, &proplen) != DDI_PROP_SUCCESS) { 465 return (DDI_FAILURE); 466 } 467 468 /* 469 * extract the device identifications 470 */ 471 pnpisa = isa_regs[0].phys_hi & 0x80000000; 472 if (pnpisa) { 473 isa_vendor(isa_regs[0].phys_hi, vendor); 474 device = isa_regs[0].phys_hi & 0xffff; 475 serial = isa_regs[0].phys_lo; 476 func = (isa_regs[0].size >> 24) & 0xff; 477 if (func != 0) 478 (void) snprintf(name, namelen, "pnp%s,%04x,%x,%x", 479 vendor, device, serial, func); 480 else 481 (void) snprintf(name, namelen, "pnp%s,%04x,%x", 482 vendor, device, serial); 483 } else { 484 bustype = isa_regs[0].phys_hi; 485 base = isa_regs[0].phys_lo; 486 (void) sprintf(name, "%x,%x", bustype, base); 487 } 488 489 /* 490 * free the memory allocated by ddi_getlongprop(). 491 */ 492 kmem_free(isa_regs, proplen); 493 494 return (DDI_SUCCESS); 495 } 496 497 static int 498 isa_initchild(dev_info_t *child) 499 { 500 char name[80]; 501 502 if (isa_name_child(child, name, 80) != DDI_SUCCESS) 503 return (DDI_FAILURE); 504 ddi_set_name_addr(child, name); 505 506 if (ndi_dev_is_persistent_node(child) != 0) 507 return (DDI_SUCCESS); 508 509 /* 510 * This is a .conf node, try merge properties onto a 511 * hw node with the same name. 512 */ 513 if (ndi_merge_node(child, isa_name_child) == DDI_SUCCESS) { 514 /* 515 * Return failure to remove node 516 */ 517 impl_ddi_sunbus_removechild(child); 518 return (DDI_FAILURE); 519 } 520 /* 521 * Cannot merge node, permit pseudo children 522 */ 523 return (DDI_SUCCESS); 524 } 525 526 /* 527 * called when ACPI enumeration is not used 528 */ 529 static void 530 add_known_used_resources(void) 531 { 532 /* needs to be in increasing order */ 533 int intr[] = {0x1, 0x3, 0x4, 0x6, 0x7, 0xc}; 534 int dma[] = {0x2}; 535 int io[] = {0x60, 0x1, 0x64, 0x1, 0x2f8, 0x8, 0x378, 0x8, 0x3f0, 0x10, 536 0x778, 0x4}; 537 dev_info_t *usedrdip; 538 539 usedrdip = ddi_find_devinfo(USED_RESOURCES, -1, 0); 540 541 if (usedrdip == NULL) { 542 (void) ndi_devi_alloc_sleep(ddi_root_node(), USED_RESOURCES, 543 (pnode_t)DEVI_SID_NODEID, &usedrdip); 544 } 545 546 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 547 "interrupts", (int *)intr, (int)(sizeof (intr) / sizeof (int))); 548 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 549 "io-space", (int *)io, (int)(sizeof (io) / sizeof (int))); 550 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, usedrdip, 551 "dma-channels", (int *)dma, (int)(sizeof (dma) / sizeof (int))); 552 (void) ndi_devi_bind_driver(usedrdip, 0); 553 554 } 555 556 static void 557 isa_alloc_nodes(dev_info_t *isa_dip) 558 { 559 static int alloced = 0; 560 int circ, i; 561 dev_info_t *xdip; 562 563 /* hard coded isa stuff */ 564 struct regspec asy_regs[] = { 565 {1, 0x3f8, 0x8}, 566 {1, 0x2f8, 0x8} 567 }; 568 int asy_intrs[] = {0x4, 0x3}; 569 570 struct regspec i8042_regs[] = { 571 {1, 0x60, 0x1}, 572 {1, 0x64, 0x1} 573 }; 574 int i8042_intrs[] = {0x1, 0xc}; 575 char *acpi_prop; 576 int acpi_enum = 1; /* ACPI is default to be on */ 577 578 if (alloced) 579 return; 580 581 ndi_devi_enter(isa_dip, &circ); 582 if (alloced) { /* just in case we are multi-threaded */ 583 ndi_devi_exit(isa_dip, circ); 584 return; 585 } 586 alloced = 1; 587 588 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, ddi_root_node(), 589 DDI_PROP_DONTPASS, "acpi-enum", &acpi_prop) == DDI_PROP_SUCCESS) { 590 acpi_enum = strcmp("off", acpi_prop); 591 ddi_prop_free(acpi_prop); 592 } 593 594 if (acpi_enum) { 595 if (acpi_isa_device_enum(isa_dip)) { 596 ndi_devi_exit(isa_dip, circ); 597 if (isa_resource_setup() != NDI_SUCCESS) { 598 cmn_err(CE_WARN, "isa nexus: isa " 599 "resource setup failed"); 600 } 601 602 /* serial ports? */ 603 enumerate_BIOS_serial(isa_dip); 604 return; 605 } 606 cmn_err(CE_NOTE, "!Solaris did not detect ACPI BIOS"); 607 } 608 cmn_err(CE_NOTE, "!ACPI is off"); 609 610 /* serial ports */ 611 for (i = 0; i < 2; i++) { 612 ndi_devi_alloc_sleep(isa_dip, "asy", 613 (pnode_t)DEVI_SID_NODEID, &xdip); 614 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 615 "reg", (int *)&asy_regs[i], 3); 616 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 617 "interrupts", asy_intrs[i]); 618 (void) ndi_devi_bind_driver(xdip, 0); 619 } 620 621 /* i8042 node */ 622 ndi_devi_alloc_sleep(isa_dip, "i8042", 623 (pnode_t)DEVI_SID_NODEID, &xdip); 624 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 625 "reg", (int *)i8042_regs, 6); 626 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 627 "interrupts", (int *)i8042_intrs, 2); 628 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 629 "unit-address", "1,60"); 630 (void) ndi_devi_bind_driver(xdip, 0); 631 632 add_known_used_resources(); 633 634 ndi_devi_exit(isa_dip, circ); 635 636 } 637 638 /* 639 * On some machines, serial port 2 isn't listed in the ACPI table. 640 * This function goes through the BIOS data area and makes sure all 641 * the serial ports there are in the dev_info tree. If any are missing, 642 * this function will add them. 643 */ 644 645 static int num_BIOS_serial = 2; /* number of BIOS serial ports to look at */ 646 647 static void 648 enumerate_BIOS_serial(dev_info_t *isa_dip) 649 { 650 ushort_t *bios_data; 651 int i; 652 dev_info_t *xdip; 653 int found; 654 int ret; 655 struct regspec *tmpregs; 656 int tmpregs_len; 657 static struct regspec tmp_asy_regs[] = { 658 {1, 0x3f8, 0x8}, 659 }; 660 static int default_asy_intrs[] = { 4, 3, 4, 3 }; 661 static size_t size = 4; 662 663 /* 664 * The first four 2-byte quantities of the BIOS data area contain 665 * the base I/O addresses of the first four serial ports. 666 */ 667 bios_data = (ushort_t *)psm_map_new((paddr_t)BIOS_DATA_AREA, size, 668 PSM_PROT_READ); 669 for (i = 0; i < num_BIOS_serial; i++) { 670 if (bios_data[i] == 0) { 671 /* no COM[i]: port */ 672 continue; 673 } 674 675 /* Look for it in the dev_info tree */ 676 found = 0; 677 for (xdip = ddi_get_child(isa_dip); xdip != NULL; 678 xdip = ddi_get_next_sibling(xdip)) { 679 if (strncmp(ddi_node_name(xdip), "asy", 3) != 0) { 680 /* skip non asy */ 681 continue; 682 } 683 684 /* Match by addr */ 685 ret = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, xdip, 686 DDI_PROP_DONTPASS, "reg", (int **)&tmpregs, 687 (uint_t *)&tmpregs_len); 688 if (ret != DDI_PROP_SUCCESS) { 689 /* error */ 690 continue; 691 } 692 693 if (tmpregs->regspec_addr == bios_data[i]) 694 found = 1; 695 696 /* 697 * Free the memory allocated by 698 * ddi_prop_lookup_int_array(). 699 */ 700 ddi_prop_free(tmpregs); 701 } 702 703 /* If not found, then add it */ 704 if (!found) { 705 ndi_devi_alloc_sleep(isa_dip, "asy", 706 (pnode_t)DEVI_SID_NODEID, &xdip); 707 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 708 "compatible", "PNP0500"); 709 /* This should be gotten from master file: */ 710 (void) ndi_prop_update_string(DDI_DEV_T_NONE, xdip, 711 "model", "Standard PC COM port"); 712 tmp_asy_regs[0].regspec_addr = bios_data[i]; 713 (void) ndi_prop_update_int_array(DDI_DEV_T_NONE, xdip, 714 "reg", (int *)&tmp_asy_regs[0], 3); 715 (void) ndi_prop_update_int(DDI_DEV_T_NONE, xdip, 716 "interrupts", default_asy_intrs[i]); 717 (void) ndi_devi_bind_driver(xdip, 0); 718 } 719 } 720 721 psm_unmap((caddr_t)bios_data, size); 722 } 723