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 /* 27 * Safari Configurator (gptwocfg) 28 * 29 */ 30 31 #include <sys/types.h> 32 #include <sys/cred.h> 33 #include <sys/mman.h> 34 #include <sys/kmem.h> 35 #include <sys/conf.h> 36 #include <sys/cmn_err.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/modctl.h> 41 #include <sys/stat.h> 42 #include <sys/param.h> 43 #include <sys/autoconf.h> 44 #include <sys/ksynch.h> 45 #include <sys/promif.h> 46 #include <sys/ndi_impldefs.h> 47 #include <sys/ddi_impldefs.h> 48 #include <sys/gp2cfg.h> 49 #include <sys/machsystm.h> 50 #include <sys/platform_module.h> 51 #pragma weak starcat_dr_name 52 53 #ifdef DEBUG 54 int gptwocfg_debug = 0; 55 56 static void debug(char *, uintptr_t, uintptr_t, 57 uintptr_t, uintptr_t, uintptr_t); 58 59 #define GPTWO_DEBUG0(level, flag, s) if (gptwocfg_debug >= level) \ 60 cmn_err(flag, s) 61 #define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwocfg_debug >= level) \ 62 debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); 63 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwocfg_debug >= level) \ 64 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); 65 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \ 66 if (gptwocfg_debug >= level) \ 67 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); 68 #else 69 #define GPTWO_DEBUG0(level, flag, s) 70 #define GPTWO_DEBUG1(level, flag, fmt, a1) 71 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) 72 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) 73 #endif 74 75 kmutex_t gptwo_handle_list_lock; 76 gptwocfg_handle_list_t *gptwocfg_handle_list; 77 78 static kmutex_t gptwo_config_list_lock; 79 static gptwocfg_config_t *gptwo_config_list; 80 81 static gptwo_new_nodes_t * 82 gptwocfg_get_obp_created_nodes(dev_info_t *, uint_t); 83 84 void (*gptwocfg_unclaim_address)(uint_t); 85 86 extern caddr_t efcode_vaddr; 87 extern int efcode_size; 88 89 #define GPTWO_NUMBER_OF_DEVICE_TYPES 6 90 91 static kmutex_t gptwocfg_ops_table_lock; 92 gptwocfg_ops_t *gptwocfg_ops_table[GPTWO_NUMBER_OF_DEVICE_TYPES]; 93 94 /* 95 * Module linkage information for the kernel. 96 */ 97 98 extern struct mod_ops mod_miscops; 99 100 static struct modlmisc modlmisc = { 101 &mod_miscops, /* Type of module */ 102 "gptwo configurator", 103 }; 104 105 static struct modlinkage modlinkage = { 106 MODREV_1, (void *)&modlmisc, NULL 107 }; 108 109 int 110 _init(void) 111 { 112 unsigned int i; 113 114 GPTWO_DEBUG0(1, CE_WARN, "gptwocfg (Safari Configurator) " 115 "has been loaded\n"); 116 117 mutex_init(&gptwo_config_list_lock, NULL, MUTEX_DRIVER, NULL); 118 mutex_init(&gptwocfg_ops_table_lock, NULL, MUTEX_DRIVER, NULL); 119 gptwo_config_list = NULL; 120 121 mutex_init(&gptwo_handle_list_lock, NULL, MUTEX_DRIVER, NULL); 122 gptwocfg_handle_list = NULL; 123 124 for (i = 0; i < GPTWO_NUMBER_OF_DEVICE_TYPES; i++) 125 gptwocfg_ops_table[i] = NULL; 126 127 return (mod_install(&modlinkage)); 128 } 129 130 int 131 _fini(void) 132 { 133 int error; 134 135 error = mod_remove(&modlinkage); 136 if (error != 0) { 137 return (error); 138 } 139 mutex_destroy(&gptwo_config_list_lock); 140 mutex_destroy(&gptwocfg_ops_table_lock); 141 mutex_destroy(&gptwo_handle_list_lock); 142 143 return (0); 144 } 145 146 int 147 _info(modinfop) 148 struct modinfo *modinfop; 149 { 150 return (mod_info(&modlinkage, modinfop)); 151 } 152 153 gptwo_new_nodes_t * 154 gptwocfg_allocate_node_list(int number_of_nodes) 155 { 156 gptwo_new_nodes_t *gptwo_new_nodes; 157 int size; 158 159 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- %d nodes", 160 number_of_nodes); 161 162 size = sizeof (gptwo_new_nodes_t) + 163 ((number_of_nodes -1) * sizeof (dev_info_t *)); 164 165 gptwo_new_nodes = kmem_zalloc(size, KM_SLEEP); 166 167 gptwo_new_nodes->gptwo_number_of_nodes = number_of_nodes; 168 gptwo_new_nodes->gptwo_version = GP2_VERSION; 169 170 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_allocate_node_list- returned %p\n", 171 gptwo_new_nodes); 172 173 return (gptwo_new_nodes); 174 } 175 176 void 177 gptwocfg_free_node_list(gptwo_new_nodes_t *gptwo_new_nodes) 178 { 179 int size; 180 181 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_free_node_list- %p %d nodes", 182 gptwo_new_nodes, gptwo_new_nodes->gptwo_number_of_nodes); 183 184 size = sizeof (gptwo_new_nodes_t) + 185 ((gptwo_new_nodes->gptwo_number_of_nodes - 1) * 186 sizeof (dev_info_t *)); 187 188 kmem_free(gptwo_new_nodes, size); 189 } 190 191 void 192 gptwocfg_register_ops(uint_t type, gptwo_cfgfunc_t *cfg_func, 193 gptwo_uncfgfunc_t *uncfg_func) 194 { 195 /* KM_SLEEP guarantees success */ 196 gptwocfg_ops_t *ops = kmem_zalloc(sizeof (gptwocfg_ops_t), KM_SLEEP); 197 198 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_register_ops: type=%x ops=%lx\n", 199 type, ops); 200 ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES); 201 ops->gptwocfg_type = type; 202 ops->gptwocfg_version = GPTWOCFG_OPS_VERSION; 203 ops->gptwocfg_configure = cfg_func; 204 ops->gptwocfg_unconfigure = uncfg_func; 205 206 mutex_enter(&gptwocfg_ops_table_lock); 207 gptwocfg_ops_table[type] = ops; 208 mutex_exit(&gptwocfg_ops_table_lock); 209 } 210 211 212 213 void 214 gptwocfg_unregister_ops(uint_t type) 215 { 216 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unregister_ops: type=%x\n", type); 217 218 ASSERT(type < GPTWO_NUMBER_OF_DEVICE_TYPES); 219 220 mutex_enter(&gptwocfg_ops_table_lock); 221 kmem_free(gptwocfg_ops_table[type], sizeof (gptwocfg_ops_t)); 222 gptwocfg_ops_table[type] = NULL; 223 mutex_exit(&gptwocfg_ops_table_lock); 224 } 225 226 gptwocfg_cookie_t 227 gptwocfg_configure(dev_info_t *ap, spcd_t *pcd, gptwo_aid_t id) 228 { 229 gptwo_new_nodes_t *new_nodes = NULL; 230 gptwocfg_config_t *config; 231 gptwocfg_ops_t *ops; 232 233 GPTWO_DEBUG3(1, CE_CONT, "gptwocfg_configure: ap=0x%p pcd=%p id=%x\n", 234 ap, pcd, id); 235 236 /* 237 * Look to see if the port is already configured. 238 */ 239 mutex_enter(&gptwo_config_list_lock); 240 config = gptwo_config_list; 241 while (config != NULL) { 242 if (&starcat_dr_name) { 243 if (starcat_dr_name(ddi_node_name(ap)) < 0) { 244 config = config->gptwo_next; 245 continue; 246 } 247 } 248 if (config->gptwo_portid == id) { 249 cmn_err(CE_WARN, "gptwocfg: gptwocfg_configure: " 250 "0x%x Port already configured\n", id); 251 mutex_exit(&gptwo_config_list_lock); 252 return (NULL); 253 } 254 config = config->gptwo_next; 255 } 256 mutex_exit(&gptwo_config_list_lock); 257 258 if (pcd == NULL) { 259 GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_configure: pcd=NULL\n"); 260 return (NULL); 261 } 262 263 if ((pcd->spcd_magic != PCD_MAGIC) || 264 (pcd->spcd_version != PCD_VERSION)) { 265 cmn_err(CE_WARN, "gptwocfg: Invalid Port " 266 "Configuration Descriptor\n"); 267 return (NULL); 268 } 269 270 if (pcd->spcd_ptype >= GPTWO_NUMBER_OF_DEVICE_TYPES) { 271 cmn_err(CE_WARN, 272 "gptwocfg: Invalid device type %x", pcd->spcd_ptype); 273 return (NULL); 274 } 275 276 if (pcd->spcd_prsv != SPCD_RSV_PASS) { 277 cmn_err(CE_WARN, 278 "gptwocfg: Agent at ID %x has not passed test(s)\n", id); 279 return (NULL); 280 } 281 282 mutex_enter(&gptwocfg_ops_table_lock); 283 284 ops = gptwocfg_ops_table[pcd->spcd_ptype]; 285 286 if (ops == NULL) { 287 cmn_err(CE_WARN, "gptwocfg: Ops for type %x have not been " 288 "registered\n", pcd->spcd_ptype); 289 mutex_exit(&gptwocfg_ops_table_lock); 290 return (NULL); 291 } 292 293 if (ops->gptwocfg_configure == NULL) { 294 cmn_err(CE_WARN, "gptwocfg: no configure routine registered " 295 "for sfaari type %x\n", pcd->spcd_ptype); 296 mutex_exit(&gptwocfg_ops_table_lock); 297 return (NULL); 298 } 299 300 new_nodes = ops->gptwocfg_configure(ap, pcd, id); 301 302 mutex_exit(&gptwocfg_ops_table_lock); 303 304 if (new_nodes != NULL) { 305 config = kmem_zalloc(sizeof (gptwocfg_config_t), KM_SLEEP); 306 config->gptwo_version = GP2_VERSION; 307 config->gptwo_ap = ap; 308 config->gptwo_portid = id; 309 config->gptwo_nodes = new_nodes; 310 config->gptwo_ops = ops; 311 312 /* 313 * put config on config list 314 */ 315 mutex_enter(&gptwo_config_list_lock); 316 config->gptwo_next = gptwo_config_list; 317 gptwo_config_list = config; 318 mutex_exit(&gptwo_config_list_lock); 319 } else { 320 config = NULL; 321 } 322 323 return ((gptwocfg_cookie_t)config); 324 } 325 326 gptwocfg_cookie_t 327 gptwocfg_unconfigure(dev_info_t *ap, gptwo_aid_t id) 328 { 329 int i, circ; 330 int failure = 0; 331 dev_info_t *saf_dip; 332 gptwocfg_config_t *config, *temp; 333 gptwo_new_nodes_t *obp_nodes; 334 gptwocfg_ops_t *ops; 335 336 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_unconfigure: ap=0x%p id=0x%lx\n", 337 ap, id); 338 339 mutex_enter(&gptwo_config_list_lock); 340 config = gptwo_config_list; 341 while (config != NULL) { 342 if (config->gptwo_portid == id) { 343 break; 344 } 345 config = config->gptwo_next; 346 } 347 mutex_exit(&gptwo_config_list_lock); 348 349 if (config == NULL) { 350 /* 351 * There is no config structure associated with this agent id 352 * so it was probably built by firmware at start of day. We 353 * need to create a config structure before we can continue. 354 */ 355 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: id=0x%lx " 356 "No config structure - Need to build one\n", id); 357 358 obp_nodes = gptwocfg_get_obp_created_nodes(ap, id); 359 360 if (obp_nodes != NULL) { 361 config = kmem_zalloc(sizeof (gptwocfg_config_t), 362 KM_SLEEP); 363 config->gptwo_version = GP2_VERSION; 364 config->gptwo_ap = ap; 365 config->gptwo_portid = id; 366 config->gptwo_nodes = obp_nodes; 367 368 /* 369 * put config on config list 370 */ 371 mutex_enter(&gptwo_config_list_lock); 372 config->gptwo_next = gptwo_config_list; 373 gptwo_config_list = config; 374 mutex_exit(&gptwo_config_list_lock); 375 } else { 376 cmn_err(CE_WARN, "gptwocfg: gptwocfg_unconfigure: " 377 "No OBP created nodes for ap=0x%lx agent id=0x%x", 378 (long)ap, id); 379 return (NULL); 380 } 381 } 382 383 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure config=0x%lx\n", 384 config); 385 386 ops = config->gptwo_ops; 387 388 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure: ops=%lx\n", ops); 389 390 ndi_devi_enter(ap, &circ); 391 392 for (i = 0; i < config->gptwo_nodes->gptwo_number_of_nodes; i++) { 393 dev_info_t *fdip = NULL; 394 395 saf_dip = config->gptwo_nodes->gptwo_nodes[i]; 396 397 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure saf_dip=0x%lx\n", 398 saf_dip); 399 400 if (saf_dip == NULL) { 401 GPTWO_DEBUG0(1, CE_CONT, "gptwocfg_unconfigure: " 402 "skipping NULLL saf device\n"); 403 404 continue; 405 } 406 407 config->gptwo_nodes->gptwo_nodes[i] = NULL; 408 409 if (ops) { 410 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_configure " 411 "ops->gptwocfg_configure=%lx\n", 412 ops->gptwocfg_configure); 413 414 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_unconfigure " 415 "ops->gptwocfg_unconfigure=%lx\n", 416 ops->gptwocfg_unconfigure); 417 418 if (ops->gptwocfg_unconfigure != NULL) { 419 config->gptwo_nodes->gptwo_nodes[i] = 420 ops->gptwocfg_unconfigure(saf_dip); 421 422 } 423 } 424 425 GPTWO_DEBUG1(1, CE_CONT, "e_ddi_branch_destroy <%s>\n", 426 ddi_get_name(saf_dip)); 427 428 ASSERT(e_ddi_branch_held(saf_dip)); 429 430 /* 431 * Don't hold parent busy when calling 432 * e_ddi_branch_unconfigure/destroy/referenced() 433 */ 434 ndi_devi_exit(ap, circ); 435 if (e_ddi_branch_destroy(saf_dip, &fdip, 0)) { 436 char *path = kmem_alloc(MAXPATHLEN, KM_SLEEP); 437 438 /* 439 * If non-NULL, fdip is held and must be released. 440 */ 441 if (fdip != NULL) { 442 (void) ddi_pathname(fdip, path); 443 ddi_release_devi(fdip); 444 } else { 445 (void) ddi_pathname(saf_dip, path); 446 } 447 448 cmn_err(CE_WARN, "saf node removal failed: %s (%p)", 449 path, fdip ? (void *)fdip : (void *)saf_dip); 450 451 kmem_free(path, MAXPATHLEN); 452 453 config->gptwo_nodes->gptwo_nodes[i] = saf_dip; 454 failure = 1; 455 } 456 ndi_devi_enter(ap, &circ); 457 } 458 459 ndi_devi_exit(ap, circ); 460 461 if (!failure) { 462 gptwocfg_free_node_list(config->gptwo_nodes); 463 464 mutex_enter(&gptwo_config_list_lock); 465 if (gptwo_config_list == config) { 466 gptwo_config_list = config->gptwo_next; 467 } else { 468 temp = gptwo_config_list; 469 while (temp->gptwo_next != config) { 470 temp = temp->gptwo_next; 471 } 472 temp->gptwo_next = config->gptwo_next; 473 } 474 mutex_exit(&gptwo_config_list_lock); 475 476 kmem_free(config, sizeof (gptwocfg_config_t)); 477 config = NULL; 478 } 479 480 return (config); 481 } 482 483 int 484 gptwocfg_next_node(gptwocfg_cookie_t c, dev_info_t *previous, dev_info_t **next) 485 { 486 gptwocfg_config_t *cookie; 487 int i, j; 488 489 GPTWO_DEBUG3(1, CE_WARN, "gptwocfg_next_node" 490 "(c=0x%lx, previous=0x%lx, next=0x%lx)\n", c, previous, next); 491 492 cookie = (gptwocfg_config_t *)c; 493 494 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) { 495 GPTWO_DEBUG1(1, CE_WARN, "0x%lx\n", 496 cookie->gptwo_nodes->gptwo_nodes[i]); 497 } 498 499 if (previous == NULL) { 500 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; 501 i++) { 502 if (cookie->gptwo_nodes->gptwo_nodes[i]) { 503 *next = cookie->gptwo_nodes->gptwo_nodes[i]; 504 GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n", 505 *next); 506 return (1); 507 } 508 } 509 return (0); 510 } 511 512 for (i = 0; i < cookie->gptwo_nodes->gptwo_number_of_nodes; i++) { 513 if (cookie->gptwo_nodes->gptwo_nodes[i] == previous) { 514 for (j = i + 1; 515 j < cookie->gptwo_nodes->gptwo_number_of_nodes; 516 j++) { 517 if (cookie->gptwo_nodes->gptwo_nodes[j]) { 518 *next = 519 cookie->gptwo_nodes->gptwo_nodes[j]; 520 GPTWO_DEBUG1(1, CE_WARN, 521 "returned 0x%lx\n", *next); 522 return (1); 523 } 524 } 525 *next = NULL; 526 GPTWO_DEBUG1(1, CE_WARN, "returned 0x%lx\n", 527 *next); 528 return (1); 529 } 530 } 531 532 /* 533 * previous is probably an invalid dev_info. 534 */ 535 return (0); 536 } 537 538 static gptwo_new_nodes_t * 539 gptwocfg_get_obp_created_nodes(dev_info_t *ap, uint_t id) 540 { 541 gptwo_new_nodes_t *obp_nodes; 542 dev_info_t *saf_dev; 543 int i = 0, nodes = 0; 544 int circular_count; 545 546 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_obp_created_nodes - ap=0x%lx " 547 "id=0x%x\n", ap, id); 548 549 ndi_devi_enter(ap, &circular_count); 550 551 /* 552 * First go through all the children of the attachment point 553 * to count matching safari agent ids 554 */ 555 saf_dev = ddi_get_child(ap); 556 while (saf_dev != NULL) { 557 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS, 558 "portid", -1) == id) { 559 if (&starcat_dr_name) { 560 if (starcat_dr_name(ddi_node_name(saf_dev)) 561 < 0) { 562 saf_dev = ddi_get_next_sibling(saf_dev); 563 continue; 564 } 565 } 566 nodes++; 567 } 568 saf_dev = ddi_get_next_sibling(saf_dev); 569 } 570 571 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - %d nodes " 572 "found\n", nodes); 573 574 obp_nodes = gptwocfg_allocate_node_list(nodes); 575 576 /* 577 * Then fill in the nodes structure. 578 */ 579 saf_dev = ddi_get_child(ap); 580 while ((saf_dev != NULL) && (i < nodes)) { 581 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, DDI_PROP_DONTPASS, 582 "portid", -1) == id) { 583 if (&starcat_dr_name) { 584 if (starcat_dr_name(ddi_node_name(saf_dev)) 585 < 0) { 586 saf_dev = ddi_get_next_sibling(saf_dev); 587 continue; 588 } 589 } 590 /* 591 * Branch rooted at this dip must have been 592 * held by the DR driver. 593 */ 594 ASSERT(e_ddi_branch_held(saf_dev)); 595 obp_nodes->gptwo_nodes[i++] = saf_dev; 596 } 597 saf_dev = ddi_get_next_sibling(saf_dev); 598 } 599 600 ndi_devi_exit(ap, circular_count); 601 602 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_obp_created_nodes - " 603 "Returning 0x%lx\n", obp_nodes); 604 605 return (obp_nodes); 606 } 607 608 void 609 gptwocfg_save_handle(dev_info_t *dip, fco_handle_t fco_handle) 610 { 611 gptwocfg_handle_list_t *h; 612 613 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_save_handle - " 614 "dip=%lx fco_handle=%lx\n", dip, fco_handle); 615 616 h = kmem_zalloc(sizeof (gptwocfg_handle_list_t), KM_SLEEP); 617 618 mutex_enter(&gptwo_handle_list_lock); 619 620 h->next = gptwocfg_handle_list; 621 h->dip = dip; 622 h->fco_handle = fco_handle; 623 gptwocfg_handle_list = h; 624 625 mutex_exit(&gptwo_handle_list_lock); 626 } 627 628 fco_handle_t 629 gptwocfg_get_handle(dev_info_t *dip) 630 { 631 gptwocfg_handle_list_t *h, *last; 632 fco_handle_t fco_handle; 633 634 mutex_enter(&gptwo_handle_list_lock); 635 636 h = last = gptwocfg_handle_list; 637 638 while (h != NULL) { 639 if (h->dip == dip) { 640 if (h == gptwocfg_handle_list) 641 gptwocfg_handle_list = h->next; 642 else 643 last->next = h->next; 644 645 mutex_exit(&gptwo_handle_list_lock); 646 647 fco_handle = h->fco_handle; 648 649 kmem_free(h, sizeof (gptwocfg_handle_list_t)); 650 651 GPTWO_DEBUG2(1, CE_CONT, "gptwocfg_get_handle - " 652 "dip=%lx fco_handle=%lx\n", dip, fco_handle); 653 654 return (fco_handle); 655 } 656 last = h; 657 h = h->next; 658 } 659 660 mutex_exit(&gptwo_handle_list_lock); 661 662 GPTWO_DEBUG1(1, CE_CONT, "gptwocfg_get_handle - dip=%lx NO HANDLE\n", 663 dip); 664 665 return (0); 666 } 667 668 void 669 gptwocfg_devi_attach_to_parent(dev_info_t *dip) 670 { 671 (void) i_ndi_config_node(dip, DS_LINKED, 0); 672 } 673 674 #ifdef DEBUG 675 static void 676 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, 677 uintptr_t a4, uintptr_t a5) 678 { 679 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 680 } 681 #endif 682