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 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * Schizo/PCI Functions to the Safari Configurator 31 * 32 */ 33 34 #include <sys/types.h> 35 #include <sys/cred.h> 36 #include <sys/mman.h> 37 #include <sys/kmem.h> 38 #include <sys/conf.h> 39 #include <sys/ddi.h> 40 #include <sys/sunddi.h> 41 #include <sys/sunndi.h> 42 #include <sys/modctl.h> 43 #include <sys/stat.h> 44 #include <sys/param.h> 45 #include <sys/autoconf.h> 46 #include <sys/ksynch.h> 47 #include <sys/promif.h> 48 #include <sys/ndi_impldefs.h> 49 #include <sys/ddi_impldefs.h> 50 #include <sys/machsystm.h> 51 #include <sys/gp2cfg.h> 52 #include <sys/gptwo_pci.h> 53 54 #ifdef DEBUG 55 int gptwo_pci_debug = 0; 56 57 static void debug(char *, uintptr_t, uintptr_t, 58 uintptr_t, uintptr_t, uintptr_t); 59 60 #define GPTWO_DEBUG0(level, flag, s) if (gptwo_pci_debug >= level) \ 61 cmn_err(flag, s) 62 #define GPTWO_DEBUG1(level, flag, fmt, a1) if (gptwo_pci_debug >= level) \ 63 debug(fmt, (uintptr_t)(a1), 0, 0, 0, 0); 64 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) if (gptwo_pci_debug >= level) \ 65 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), 0, 0, 0); 66 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) \ 67 if (gptwo_pci_debug >= level) \ 68 debug(fmt, (uintptr_t)(a1), (uintptr_t)(a2), (uintptr_t)(a3), 0, 0); 69 #else 70 #define GPTWO_DEBUG0(level, flag, s) 71 #define GPTWO_DEBUG1(level, flag, fmt, a1) 72 #define GPTWO_DEBUG2(level, flag, fmt, a1, a2) 73 #define GPTWO_DEBUG3(level, flag, fmt, a1, a2, a3) 74 #endif 75 76 void gptwocfg_devi_attach_to_parent(dev_info_t *); 77 static char *gptwo_get_probe_string(spcd_t *, int); 78 static void gptwo_find_nodes(dev_info_t *, int, gptwo_new_nodes_t *); 79 80 extern caddr_t efcode_vaddr; 81 extern int efcode_size; 82 83 /* 84 * Module linkage information for the kernel. 85 */ 86 87 extern struct mod_ops mod_miscops; 88 89 static struct modlmisc modlmisc = { 90 &mod_miscops, /* Type of module */ 91 "gptwo->pci configurator %I%", 92 }; 93 94 static struct modlinkage modlinkage = { 95 MODREV_1, (void *)&modlmisc, NULL 96 }; 97 98 int 99 _init(void) 100 { 101 int err = 0; 102 103 /* 104 * Create a resource map for the contigous memory allocated 105 * at start-of-day in startup.c 106 */ 107 if (ndi_ra_map_setup(ddi_root_node(), "gptwo-contigousmem") 108 == NDI_FAILURE) { 109 GPTWO_DEBUG0(1, CE_WARN, 110 "Can not setup resource map - gptwo-contigousmem\n"); 111 return (1); 112 } 113 114 /* 115 * Put the allocated memory into the pool. 116 */ 117 (void) ndi_ra_free(ddi_root_node(), (uint64_t)efcode_vaddr, 118 (uint64_t)efcode_size, "gptwo-contigousmem", 0); 119 120 /* register devices with the configurator */ 121 gptwocfg_register_ops(SAFPTYPE_sPCI, gptwo_configure_pci, 122 gptwo_unconfigure_pci); 123 gptwocfg_register_ops(SAFPTYPE_cPCI, gptwo_configure_pci, 124 gptwo_unconfigure_pci); 125 gptwocfg_register_ops(SAFPTYPE_PCIX, gptwo_configure_pci, 126 gptwo_unconfigure_pci); 127 128 if ((err = mod_install(&modlinkage)) != 0) { 129 GPTWO_DEBUG1(1, CE_WARN, "gptwo_pci (PCI Functions) " 130 "failed to load, error=%d\n", err); 131 gptwocfg_unregister_ops(SAFPTYPE_sPCI); 132 gptwocfg_unregister_ops(SAFPTYPE_cPCI); 133 gptwocfg_unregister_ops(SAFPTYPE_PCIX); 134 } else { 135 GPTWO_DEBUG0(1, CE_WARN, "gptwo_pci (PCI Functions) " 136 "has been loaded.\n"); 137 } 138 return (err); 139 } 140 141 int 142 _fini(void) 143 { 144 gptwocfg_unregister_ops(SAFPTYPE_sPCI); 145 gptwocfg_unregister_ops(SAFPTYPE_cPCI); 146 gptwocfg_unregister_ops(SAFPTYPE_PCIX); 147 return (mod_remove(&modlinkage)); 148 } 149 150 int 151 _info(modinfop) 152 struct modinfo *modinfop; 153 { 154 return (mod_info(&modlinkage, modinfop)); 155 } 156 157 /*ARGSUSED*/ 158 static int 159 set_name_prop(dev_info_t *dip, void *arg, uint_t flags) 160 { 161 if (ndi_prop_update_string(DDI_DEV_T_NONE, dip, 162 "name", "pci") != DDI_SUCCESS) { 163 return (DDI_WALK_ERROR); 164 } 165 166 return (DDI_WALK_TERMINATE); 167 } 168 169 /*ARGSUSED*/ 170 static void 171 get_new_child(dev_info_t *rdip, void *arg, uint_t flags) 172 { 173 dev_info_t **dipp = (dev_info_t **)arg; 174 175 ASSERT(dipp && (*dipp == NULL)); 176 177 *dipp = rdip; 178 } 179 180 gptwo_new_nodes_t * 181 gptwo_configure_pci(dev_info_t *ap, spcd_t *pcd, uint_t id) 182 { 183 fco_handle_t fco_handle; 184 int error, i, circ, freq; 185 dev_info_t *new_child; 186 char unit_address[64]; 187 gptwo_new_nodes_t *new_nodes; 188 char *probe_string; 189 devi_branch_t b = {0}; 190 191 GPTWO_DEBUG2(1, CE_CONT, "gptwo_configure_pci: id=%x pcd=%lx\n", 192 id, pcd); 193 194 new_nodes = gptwocfg_allocate_node_list(IOBUS_PER_PORT); 195 196 i = IOBUS_PER_PORT; 197 198 while (i) { 199 i--; 200 201 if (pcd->spcd_iobus_rsv[i] != SPCD_RSV_PASS) { 202 203 cmn_err(CE_WARN, "gptwo_configure_pci: saf id=0x%x " 204 "leaf %d - Can not be probed\n", id, i); 205 206 continue; 207 } 208 209 /* 210 * Ideally, fcode would be run from the "sid_branch_create" 211 * callback (that is the primary purpose of that callback). 212 * However, the fcode interpreter was written with the 213 * assumption that the "new_child" was linked into the 214 * device tree. The callback is invoked with the devinfo node 215 * in the DS_PROTO state. More investigation is needed before 216 * we can invoke the interpreter from the callback. For now, 217 * we create the "new_child" in the BOUND state, invoke the 218 * fcode interpreter and then rebind the dip to use any 219 * compatible properties created by fcode. 220 */ 221 222 new_child = NULL; 223 224 b.arg = &new_child; 225 b.type = DEVI_BRANCH_SID; 226 b.create.sid_branch_create = set_name_prop; 227 b.devi_branch_callback = get_new_child; 228 229 /* 230 * Prevent any changes to new_child 231 * until we have bound it to the correct driver. 232 */ 233 ndi_devi_enter(ap, &circ); 234 if (e_ddi_branch_create(ap, &b, NULL, 0)) { 235 ASSERT(new_child == NULL); 236 237 if (new_nodes->gptwo_nodes[0] == NULL) { 238 GPTWO_DEBUG0(1, CE_CONT, "gptwo_configure_pci: " 239 "No nodes configured - " 240 "removing new_nodes\n"); 241 gptwocfg_free_node_list(new_nodes); 242 new_nodes = NULL; 243 } 244 245 ndi_devi_exit(ap, circ); 246 247 return (new_nodes); 248 } 249 250 /* 251 * The platform DR interfaces created the dip in 252 * bound state. Bring devinfo node down to linked 253 * state and hold it there until compatible 254 * properties are created. 255 */ 256 e_ddi_branch_rele(new_child); 257 (void) i_ndi_unconfig_node(new_child, DS_LINKED, 0); 258 ASSERT(i_ddi_node_state(new_child) == DS_LINKED); 259 e_ddi_branch_hold(new_child); 260 261 mutex_enter(&DEVI(new_child)->devi_lock); 262 DEVI(new_child)->devi_flags |= DEVI_NO_BIND; 263 mutex_exit(&DEVI(new_child)->devi_lock); 264 265 /* 266 * Drop the busy-hold on parent before calling 267 * fcode_interpreter to prevent potential deadlocks 268 */ 269 ndi_devi_exit(ap, circ); 270 271 (void) sprintf(unit_address, "%x", id); 272 273 /* 274 * Build the probe string from the PCD that will be passed 275 * in to the interpreter as my-args. This will tell the 276 * fcode what pci devices to probe after the pci node has 277 * been probed. 278 */ 279 probe_string = gptwo_get_probe_string(pcd, i); 280 281 GPTWO_DEBUG3(1, CE_CONT, "gptwo_configure_pci: args to " 282 "interpreter ap=%lx new_child=%lx unit_address=%s\n", 283 ap, new_child, unit_address); 284 285 if (probe_string) 286 GPTWO_DEBUG1(1, CE_CONT, "gptwo_configure_pci: " 287 "probe string=%s\n", probe_string); 288 289 fco_handle = gp2_fc_ops_alloc_handle(ap, new_child, NULL, NULL, 290 unit_address, probe_string); 291 292 GPTWO_DEBUG0(1, CE_CONT, 293 "gptwocfg: Calling Fcode Interpeter...\n"); 294 295 error = fcode_interpreter(ap, &gp2_fc_ops, fco_handle); 296 297 GPTWO_DEBUG1(1, CE_CONT, 298 "gptwo_configure_pci: fcode_interpreter " 299 " returned %x\n", error); 300 301 if (error) { 302 cmn_err(CE_WARN, "gptwo_pci: Unable to probe pci leaf " 303 "%s\n", unit_address); 304 305 gp2_fc_ops_free_handle(fco_handle); 306 307 (void) e_ddi_branch_destroy(new_child, NULL, 0); 308 } else { 309 gptwocfg_save_handle(new_child, fco_handle); 310 311 /* 312 * Compatible properties (if any) have been created, 313 * so bind driver. 314 */ 315 ndi_devi_enter(ap, &circ); 316 ASSERT(i_ddi_node_state(new_child) <= DS_LINKED); 317 318 mutex_enter(&DEVI(new_child)->devi_lock); 319 DEVI(new_child)->devi_flags &= ~DEVI_NO_BIND; 320 mutex_exit(&DEVI(new_child)->devi_lock); 321 322 ndi_devi_exit(ap, circ); 323 324 if (ndi_devi_bind_driver(new_child, 0) != 325 DDI_SUCCESS) { 326 cmn_err(CE_WARN, "gptwo_pci: Unable to bind" 327 " new pci child at dip=0x%p\n", 328 new_child); 329 } 330 331 /* 332 * If POST provided a frequency, the clock-frequency 333 * property needs to be updated. 334 */ 335 if (pcd->spcd_afreq) { 336 337 /* 338 * The upper byte is for leaf B and the lower 339 * byte is for leaf A. 340 */ 341 if (i) 342 freq = pcd->spcd_afreq >> 8; 343 else 344 freq = pcd->spcd_afreq & 0x00ff; 345 346 (void) ndi_prop_update_int(DDI_DEV_T_NONE, 347 new_child, "clock-frequency", 348 (freq * 1000 * 1000)); 349 } 350 } 351 } 352 353 gptwo_find_nodes(ap, id, new_nodes); 354 355 if (new_nodes->gptwo_nodes[0] == NULL) { 356 GPTWO_DEBUG0(1, CE_CONT, "gptwo_configure_pci: " 357 "No nodes configured - removing new_nodes\n"); 358 gptwocfg_free_node_list(new_nodes); 359 new_nodes = NULL; 360 } 361 362 GPTWO_DEBUG1(1, CE_CONT, "gptwo_configure_pci: " 363 "Returning new_nodes=%p\n", new_nodes); 364 365 return (new_nodes); 366 } 367 368 dev_info_t * 369 gptwo_unconfigure_pci(dev_info_t *dip) 370 { 371 fco_handle_t fco_handle; 372 373 fco_handle = gptwocfg_get_handle(dip); 374 375 if (fco_handle != NULL) { 376 /* 377 * If there is a handle, there may be resources 378 * that need to be freed from when the 379 * devices's fcode ran. 380 */ 381 GPTWO_DEBUG1(1, CE_CONT, "fco_handle=%lx\n", fco_handle); 382 gp2_fc_ops_free_handle(fco_handle); 383 } 384 return (NULL); 385 } 386 387 static void 388 gptwo_find_nodes(dev_info_t *ap, int id, gptwo_new_nodes_t *new_nodes) 389 { 390 dev_info_t *saf_dev; 391 int found, j, circ; 392 int i = 0; 393 394 GPTWO_DEBUG1(1, CE_CONT, "gptwo_find_nodes - id=%x\n", id); 395 396 /* 397 * We are walking child list of ap, so hold it busy 398 */ 399 ndi_devi_enter(ap, &circ); 400 401 saf_dev = ddi_get_child(ap); 402 while (saf_dev != NULL) { 403 if (ddi_getprop(DDI_DEV_T_ANY, saf_dev, 404 DDI_PROP_DONTPASS, "portid", -1) == id) { 405 if (i < IOBUS_PER_PORT) { 406 407 GPTWO_DEBUG2(1, CE_CONT, 408 "gptwo_find_nodes - " 409 "Found %d %p\n", i, saf_dev); 410 411 found = 0; 412 for (j = 0; j < IOBUS_PER_PORT; j++) { 413 if (new_nodes->gptwo_nodes[j] == 414 saf_dev) { 415 found = 1; 416 } 417 } 418 if (!found) { 419 /* 420 * Branch rooted at saf-dev was 421 * held earlier. 422 */ 423 ASSERT(e_ddi_branch_held(saf_dev)); 424 new_nodes->gptwo_nodes[i] = saf_dev; 425 i++; 426 } 427 } else { 428 GPTWO_DEBUG0(1, CE_CONT, 429 "gptwo_find_nodes - " 430 "No room in new_nodes\n"); 431 } 432 } 433 saf_dev = ddi_get_next_sibling(saf_dev); 434 } 435 436 ndi_devi_exit(ap, circ); 437 } 438 439 static char * 440 gptwo_get_probe_string(spcd_t *pcd, int bus_number) 441 { 442 int i, str_size; 443 char temp[64]; 444 char num[8]; 445 char *probe; 446 447 GPTWO_DEBUG2(1, CE_CONT, "gptwo_get_probe_string - %p %x\n", pcd, 448 bus_number); 449 450 temp[0] = NULL; 451 452 for (i = 0; i < IOCARD_PER_BUS; i++) { 453 454 GPTWO_DEBUG2(1, CE_CONT, "gptwo_get_probe_string - " 455 "card status %x %x\n", 456 i, pcd->spcd_iocard_rsv[bus_number][i]); 457 458 if (pcd->spcd_iocard_rsv[bus_number][i] == SPCD_RSV_PASS) { 459 numtos(i, num); 460 if (temp[0] == NULL) 461 (void) sprintf(temp, "%s", num); 462 else 463 (void) sprintf(temp, "%s,%s", temp, num); 464 } 465 } 466 467 if (bus_number == 0) 468 (void) sprintf(temp, "%sa", temp); /* Append a 'a' for leaf A */ 469 else 470 (void) sprintf(temp, "%sb", temp); /* Append a 'b' for leaf B */ 471 472 str_size = strlen(temp); 473 474 if (str_size == 0) 475 return (NULL); 476 477 probe = kmem_zalloc(str_size + 1, KM_SLEEP); 478 479 (void) strcpy(probe, temp); 480 481 GPTWO_DEBUG1(1, CE_CONT, "gptwo_get_probe_string - Returning %s\n", 482 probe); 483 484 return (probe); 485 } 486 487 #ifdef DEBUG 488 static void 489 debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3, 490 uintptr_t a4, uintptr_t a5) 491 { 492 cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5); 493 } 494 #endif 495