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