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 #include <sys/types.h> 28 #include <sys/conf.h> 29 #include <sys/ddi.h> 30 #include <sys/sunddi.h> 31 #include <sys/ddi_impldefs.h> 32 #include <sys/obpdefs.h> 33 #include <sys/promif.h> 34 #include <sys/cmn_err.h> 35 #include <sys/errno.h> 36 #include <sys/kmem.h> 37 #include <sys/kstat.h> 38 #include <sys/debug.h> 39 #include <sys/fhc.h> 40 #include <sys/jtag.h> 41 #include <sys/sysctrl.h> 42 43 static fhc_bd_resizable_t boards; /* booted and hotplugged boards */ 44 static fhc_bd_resizable_t clocks; /* clocks under central. */ 45 46 static int fhc_bdmax; 47 /* 48 * !! IMPORTANT !! fhc_bdlist_rwlock is implemented as a single 49 * RW_WRITER lock with *no* RW_READERs -- and it should stay that 50 * way. The fhc_bdlist_rwlock should never be used with RW_READER. 51 * 52 * The lock was originally a mutex, but was changed to a 53 * single-writer, zero-reader rwlock to force requesting threads 54 * to block (sleep, not spin) when the RW_WRITER lock is already 55 * held by a thread currently running. 56 */ 57 static krwlock_t fhc_bdlist_rwlock; 58 static sysc_evt_handle_t fhc_bd_evt; 59 static sysc_evt_handle_t *fbe = &fhc_bd_evt; 60 61 #define fhc_bd_sc_evt(s, e) (*fbe->update)(fbe->soft, s, e) 62 #define FHC_INCREMENT 4 63 #define FHC_B_SEARCH(in_array, board) \ 64 fhc_b_search(in_array.boards, board, 0, in_array.last); 65 66 static int fhc_bd_disabled(int); 67 static void fhc_check_array(int); 68 static void fhc_shell_sort(fhc_bd_t **, int, int); 69 static int fhc_b_search(fhc_bd_t **, int, int, int); 70 static void fhc_check_size(fhc_bd_resizable_t *); 71 static void fhc_resize(fhc_bd_t ***, int, int); 72 73 74 /* 75 * fhc_bdmax gets set in fhc_bdlist_prime() and does not 76 * change thereafter. 77 */ 78 int 79 fhc_max_boards() 80 { 81 return (fhc_bdmax + 1); 82 } 83 84 static int 85 fhc_bd_disabled(int board) 86 { 87 int index; 88 89 ASSERT(boards.sorted); 90 index = FHC_B_SEARCH(boards, board); 91 ASSERT(index != -1); 92 return (boards.boards[index]->flags & BDF_DISABLED); 93 } 94 95 static void 96 fhc_check_array(int btype) 97 { 98 if (btype == FHC_BOARDS) { 99 ASSERT(fhc_bdlist_locked()); 100 if (!boards.sorted) { 101 fhc_shell_sort(boards.boards, 0, boards.last); 102 boards.sorted = TRUE; 103 } 104 } else { 105 ASSERT(fhc_bdlist_locked()); 106 if (!clocks.sorted) { 107 fhc_shell_sort(clocks.boards, 0, clocks.last); 108 clocks.sorted = TRUE; 109 } 110 } 111 } 112 113 static void 114 fhc_shell_sort(fhc_bd_t *a[], int lb, int ub) 115 { 116 int n, h, i, j; 117 fhc_bd_t *t; 118 119 /* sort array a[lb..ub] */ 120 121 /* compute largest increment */ 122 n = ub - lb + 1; 123 h = 1; 124 if (n < 14) 125 h = 1; 126 else { 127 while (h < n) 128 h = 3 * h + 1; 129 h /= 3; 130 h /= 3; 131 } 132 133 while (h > 0) { 134 /* sort-by-insertion in increments of h */ 135 for (i = lb + h; i <= ub; i++) { 136 t = a[i]; 137 for (j = i - h; 138 j >= lb && a[j]->sc.board > t->sc.board; 139 j -= h) { 140 a[j+h] = a[j]; 141 } 142 a[j+h] = t; 143 } 144 145 /* compute next increment */ 146 h /= 3; 147 } 148 } 149 150 static int 151 fhc_b_search(fhc_bd_t *in_array[], int board, int first, int last) 152 { 153 int mid; 154 155 /* Array of length 0 case. */ 156 if (in_array == NULL) 157 return (-1); 158 159 /* Array of length > 0 case. */ 160 while (first < last) { 161 mid = (first + last) / 2; 162 if (in_array[mid]->sc.board < board) 163 first = mid + 1; 164 else 165 last = mid; 166 } 167 168 if (in_array[first]->sc.board == board) { 169 return (first); 170 } else { 171 return (-1); 172 } 173 174 } 175 176 static void 177 fhc_check_size(fhc_bd_resizable_t *resizable) 178 { 179 int oldsize; 180 int newsize; 181 182 ASSERT(fhc_bdlist_locked()); 183 184 if (resizable->size == resizable->last + 1) { 185 oldsize = sizeof (fhc_bd_t *) * resizable->size; 186 resizable->size += FHC_INCREMENT; 187 newsize = sizeof (fhc_bd_t *) * resizable->size; 188 fhc_resize(&(resizable->boards), oldsize, newsize); 189 } 190 } 191 192 int 193 fhc_bdlist_locked() 194 { 195 if (panicstr) 196 return (1); 197 198 return (rw_owner(&fhc_bdlist_rwlock) == curthread); 199 } 200 201 int 202 fhc_bd_busy(int board) 203 { 204 int index; 205 206 ASSERT(boards.sorted); 207 index = FHC_B_SEARCH(boards, board); 208 ASSERT(index != -1); 209 return (boards.boards[index]->sc.in_transition); 210 } 211 212 int 213 fhc_bd_is_jtag_master(int board) 214 { 215 int index; 216 217 ASSERT(boards.sorted); 218 index = FHC_B_SEARCH(boards, board); 219 ASSERT(index != -1); 220 if (boards.boards[index]->softsp == NULL) 221 return (FALSE); 222 else 223 return ((boards.boards[index]->softsp)->jt_master.is_master); 224 } 225 226 int 227 fhc_bd_is_plus(int board) 228 { 229 int index; 230 231 ASSERT(boards.sorted); 232 index = FHC_B_SEARCH(boards, board); 233 ASSERT(index != -1); 234 if (boards.boards[index]->sc.plus_board) 235 return (boards.boards[index]->sc.plus_board); 236 else 237 return (FALSE); 238 } 239 240 void 241 fhc_bdlist_init() 242 { 243 ASSERT(!fhc_bdmax); 244 rw_init(&fhc_bdlist_rwlock, NULL, RW_DEFAULT, NULL); 245 boards.boards = NULL; 246 boards.size = 0; 247 boards.last = -1; 248 boards.sorted = TRUE; /* Array of 0 elements is sorted. */ 249 250 clocks.boards = NULL; 251 clocks.size = 0; 252 clocks.last = -1; 253 clocks.sorted = TRUE; /* Array of 0 elements is sorted. */ 254 } 255 256 void 257 fhc_bdlist_fini() 258 { 259 rw_destroy(&fhc_bdlist_rwlock); 260 } 261 262 fhc_bd_t * 263 fhc_bdlist_lock(int board) 264 { 265 int index; 266 267 ASSERT(!fhc_bdlist_locked()); 268 269 /* RW_WRITER *ONLY*. Never use RW_READER! */ 270 rw_enter(&fhc_bdlist_rwlock, RW_WRITER); 271 272 if (board == -1) 273 return (NULL); 274 else { 275 ASSERT(boards.sorted); 276 index = FHC_B_SEARCH(boards, board); 277 ASSERT(index != -1); 278 return (boards.boards[index]); 279 } 280 } 281 282 void 283 fhc_bdlist_unlock() 284 { 285 ASSERT(fhc_bdlist_locked()); 286 287 rw_exit(&fhc_bdlist_rwlock); 288 } 289 290 static void 291 fhc_resize(fhc_bd_t ***in_array, int oldsize, int newsize) 292 { 293 fhc_bd_t **temp; 294 295 /* This function only grows arrays. */ 296 ASSERT(newsize > oldsize); 297 298 /* Allocate new array. */ 299 temp = kmem_alloc(newsize, KM_SLEEP); 300 301 /* Bcopy old array and free it. */ 302 if (*in_array != NULL) { 303 ASSERT(oldsize > 0); 304 bcopy(*in_array, temp, oldsize); 305 kmem_free(*in_array, oldsize); 306 } 307 *in_array = temp; 308 } 309 310 void 311 fhc_bd_init(struct fhc_soft_state *softsp, int board, enum board_type type) 312 { 313 fhc_bd_t *bdp; 314 int index; 315 316 (void) fhc_bdlist_lock(-1); 317 318 /* See if board already exists. */ 319 ASSERT(boards.sorted); 320 ASSERT(clocks.sorted); 321 if (softsp->is_central) { 322 index = FHC_B_SEARCH(clocks, board); 323 } else { 324 index = FHC_B_SEARCH(boards, board); 325 } 326 327 /* If index == -1 board does not exist. */ 328 if (index != -1) { 329 if (softsp->is_central) { 330 bdp = clocks.boards[index]; 331 } else { 332 bdp = boards.boards[index]; 333 } 334 } else { 335 if (softsp->is_central) { 336 fhc_check_size(&clocks); 337 clocks.boards[clocks.last + 1] = 338 kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); 339 bdp = clocks.boards[clocks.last + 1]; 340 clocks.last++; 341 clocks.sorted = FALSE; 342 } else { 343 fhc_check_size(&boards); 344 boards.boards[boards.last + 1] = 345 kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); 346 bdp = boards.boards[boards.last + 1]; 347 boards.last++; 348 boards.sorted = FALSE; 349 } 350 } 351 352 softsp->list = bdp; 353 bdp->flags |= BDF_VALID; 354 bdp->softsp = softsp; 355 bdp->sc.type = type; 356 bdp->sc.board = board; 357 bdp->sc.plus_board = ISPLUSBRD(*softsp->bsr); 358 359 /* Keep arrays sorted. */ 360 fhc_check_array(FHC_BOARDS); 361 fhc_check_array(FHC_CLOCKS); 362 363 fhc_bdlist_unlock(); 364 } 365 366 fhc_bd_t * 367 fhc_bd(int board) 368 { 369 int index; 370 371 if (fhc_bdmax) { 372 ASSERT(fhc_bdlist_locked()); 373 } 374 ASSERT(boards.sorted); 375 index = FHC_B_SEARCH(boards, board); 376 ASSERT(index != -1); 377 return (boards.boards[index]); 378 } 379 380 fhc_bd_t * 381 fhc_bd_clock(void) 382 { 383 ASSERT(fhc_bdlist_locked()); 384 ASSERT(clocks.size != 0); 385 386 return (clocks.boards[0]); 387 } 388 389 fhc_bd_t * 390 fhc_bd_first() 391 { 392 ASSERT(fhc_bdlist_locked()); 393 if (boards.boards != NULL) 394 return (boards.boards[0]); 395 else 396 return (NULL); 397 } 398 399 fhc_bd_t * 400 fhc_bd_next(fhc_bd_t *bdp) 401 { 402 int index; 403 404 ASSERT(boards.sorted); 405 index = FHC_B_SEARCH(boards, bdp->sc.board); 406 ASSERT(index != -1); 407 if (index < boards.last) 408 return (boards.boards[index + 1]); 409 else 410 return (NULL); 411 } 412 413 int 414 fhc_bd_valid(int bd) 415 { 416 int index; 417 418 ASSERT(bd >= 0); 419 /* Untill fhc_bdlist_prime runs anything is valid. */ 420 if (!fhc_bdmax) 421 return (TRUE); 422 423 ASSERT(boards.sorted); 424 index = FHC_B_SEARCH(boards, bd); 425 if (index == -1) 426 return (FALSE); 427 else 428 return (TRUE); 429 } 430 431 enum board_type 432 fhc_bd_type(int board) 433 { 434 int index; 435 436 ASSERT(boards.sorted); 437 index = FHC_B_SEARCH(boards, board); 438 if (index == -1) 439 return (-1); 440 441 return (boards.boards[index]->sc.type); 442 } 443 444 char * 445 fhc_bd_typestr(enum board_type type) 446 { 447 char *type_str; 448 449 switch (type) { 450 case MEM_BOARD: 451 type_str = MEM_BD_NAME; 452 break; 453 454 case CPU_BOARD: 455 type_str = CPU_BD_NAME; 456 break; 457 458 case IO_2SBUS_BOARD: 459 type_str = IO_2SBUS_BD_NAME; 460 break; 461 462 case IO_SBUS_FFB_BOARD: 463 type_str = IO_SBUS_FFB_BD_NAME; 464 break; 465 466 case IO_2SBUS_SOCPLUS_BOARD: 467 type_str = IO_2SBUS_SOCPLUS_BD_NAME; 468 break; 469 470 case IO_SBUS_FFB_SOCPLUS_BOARD: 471 type_str = IO_SBUS_FFB_SOCPLUS_BD_NAME; 472 break; 473 474 case IO_PCI_BOARD: 475 type_str = IO_PCI_BD_NAME; 476 break; 477 478 case DISK_BOARD: 479 type_str = DISK_BD_NAME; 480 break; 481 482 case UNKNOWN_BOARD: 483 default: 484 type_str = "unknown"; 485 break; 486 } 487 488 return (type_str); 489 } 490 491 void 492 fhc_bd_env_set(int board, void *env) 493 { 494 fhc_bd_t *bdp; 495 496 bdp = fhc_bd(board); 497 bdp->dev_softsp = env; 498 } 499 500 static void 501 fhc_bd_dlist_init() 502 { 503 int i; 504 int len; 505 int board; 506 pnode_t node; 507 char *dlist; 508 int index; 509 510 /* 511 * Find the disabled board list property if present. 512 * 513 * The disabled board list is in the options node under root; 514 * it is a null terminated list of boards in a string. 515 * Each char represents a board. The driver must 516 * reject illegal chars in case a user places them in the 517 * property. 518 */ 519 if (((node = prom_finddevice("/options")) == OBP_BADNODE) || 520 ((len = prom_getproplen(node, "disabled-board-list")) == -1)) 521 return; 522 523 dlist = kmem_alloc(len, KM_SLEEP); 524 (void) prom_getprop(node, "disabled-board-list", dlist); 525 526 /* 527 * now loop thru the string, and create disabled board list 528 * entries for all legal boards in the list. 529 */ 530 for (i = 0; (i < len) && (dlist[i] != 0); i++) { 531 char ch = dlist[i]; 532 533 if (ch >= '0' && ch <= '9') 534 board = ch - '0'; 535 else if (ch >= 'A' && ch <= 'F') 536 board = ch - 'A' + 10; 537 else if (ch >= 'a' && ch <= 'f') 538 board = ch - 'a' + 10; 539 else 540 /* junk entry */ 541 continue; 542 543 index = FHC_B_SEARCH(boards, board); 544 if (index != -1) { 545 boards.boards[index]->flags |= BDF_DISABLED; 546 } 547 } 548 kmem_free(dlist, len); 549 } 550 551 static struct bd_info fhc_bd_info; 552 553 static int 554 fhc_bd_ks_update(kstat_t *ksp, int rw) 555 { 556 fhc_bd_t *bdp; 557 sysc_cfga_stat_t *sc; 558 struct bd_info *uip; 559 enum board_state state; 560 561 if (rw == KSTAT_WRITE) 562 return (EACCES); 563 564 bdp = (fhc_bd_t *)ksp->ks_private; 565 uip = &fhc_bd_info; 566 sc = &bdp->sc; 567 568 ASSERT(fhc_bd_valid(sc->board)); 569 570 uip->board = sc->board; 571 uip->type = sc->type; 572 uip->fhc_compid = sc->fhc_compid; 573 uip->ac_compid = sc->ac_compid; 574 bcopy((caddr_t)sc->prom_rev, uip->prom_rev, sizeof (uip->prom_rev)); 575 bcopy((caddr_t)&sc->bd, &uip->bd, sizeof (union bd_un)); 576 577 switch (sc->rstate) { 578 case SYSC_CFGA_RSTATE_DISCONNECTED: 579 switch (sc->condition) { 580 case SYSC_CFGA_COND_OK: 581 case SYSC_CFGA_COND_UNKNOWN: 582 state = DISABLED_STATE; 583 break; 584 case SYSC_CFGA_COND_FAILING: 585 case SYSC_CFGA_COND_FAILED: 586 case SYSC_CFGA_COND_UNUSABLE: 587 state = FAILED_STATE; 588 break; 589 default: 590 state = UNKNOWN_STATE; 591 break; 592 } 593 break; 594 default: 595 state = UNKNOWN_STATE; 596 break; 597 } 598 599 uip->state = state; 600 601 return (0); 602 } 603 604 void 605 fhc_bd_ks_alloc(fhc_bd_t *bdp) 606 { 607 ASSERT(!bdp->ksp); 608 609 bdp->ksp = kstat_create("unix", bdp->sc.board, 610 BDLIST_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, 611 sizeof (struct bd_info), KSTAT_FLAG_VIRTUAL); 612 613 if (bdp->ksp != NULL) { 614 bdp->ksp->ks_data = &fhc_bd_info; 615 bdp->ksp->ks_update = fhc_bd_ks_update; 616 bdp->ksp->ks_private = (void *)bdp; 617 kstat_install(bdp->ksp); 618 } 619 } 620 621 static void 622 fhc_bdlist_dk_init() 623 { 624 dev_info_t *dnode; 625 626 /* 627 * Search the children of root to see if there are any 628 * disk boards in the tree. 629 */ 630 for (dnode = ddi_get_child(ddi_root_node()); 631 dnode != NULL; dnode = ddi_get_next_sibling(dnode)) { 632 if (strcmp(ddi_node_name(dnode), "disk-board") == 0) { 633 int id; 634 int board; 635 fhc_bd_t *bdp; 636 sysc_cfga_stat_t *sc; 637 638 /* 639 * Get the board number property. 640 */ 641 if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, 642 DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { 643 cmn_err(CE_WARN, 644 "Could not find board number"); 645 continue; 646 } 647 bdp = fhc_bd(board); 648 sc = &bdp->sc; 649 650 if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, 651 DDI_PROP_DONTPASS, "disk0-scsi-id", -1)) != -1) { 652 sc->bd.dsk.disk_pres[0] = 1; 653 sc->bd.dsk.disk_id[0] = id; 654 } else { 655 sc->bd.dsk.disk_pres[0] = 0; 656 } 657 658 if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, 659 DDI_PROP_DONTPASS, "disk1-scsi-id", -1)) != -1) { 660 sc->bd.dsk.disk_pres[1] = 1; 661 sc->bd.dsk.disk_id[1] = id; 662 } else { 663 sc->bd.dsk.disk_pres[1] = 0; 664 } 665 666 } 667 } 668 669 } 670 671 struct jt_mstr * 672 jtag_master_lock(void) 673 { 674 fhc_bd_t *bdp; 675 struct jt_mstr *master = NULL; 676 677 ASSERT(fhc_bdlist_locked()); 678 679 /* 680 * Now search for the JTAG master and place the addresses for 681 * command into the fhc soft state structure. 682 * Disk board do not have softsp set. 683 */ 684 for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) 685 if (bdp->softsp && (bdp->softsp->jt_master.is_master == 1)) { 686 master = &bdp->softsp->jt_master; 687 mutex_enter(&master->lock); 688 break; 689 } 690 691 return (master); 692 } 693 694 void 695 jtag_master_unlock(struct jt_mstr *mstr) 696 { 697 ASSERT(fhc_bdlist_locked()); 698 ASSERT(mutex_owned(&mstr->lock)); 699 700 mutex_exit(&mstr->lock); 701 } 702 703 void 704 fhc_bdlist_prime(int first, int count, int incr) 705 { 706 int board; 707 fhc_bd_t *bdp; 708 sysc_evt_t se; 709 sysc_cfga_stat_t *sc; 710 struct jt_mstr *jtm; 711 int index; 712 int nadded; 713 714 ASSERT(fbe->update); 715 716 (void) fhc_bdlist_lock(-1); 717 nadded = 0; 718 for (board = first; board < count; board += incr) { 719 /* 720 * Search only subset of array. We hold mutex so 721 * noone can add new elements to it. 722 */ 723 index = fhc_b_search(boards.boards, board, 0, 724 boards.last - nadded); 725 if (index == -1) { 726 fhc_check_size(&boards); 727 boards.boards[boards.last + 1] = 728 kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); 729 boards.boards[boards.last + 1]->sc.type = UNKNOWN_BOARD; 730 boards.boards[boards.last + 1]->sc.board = board; 731 boards.boards[boards.last + 1]->softsp = NULL; 732 boards.last++; 733 nadded++; 734 boards.sorted = FALSE; 735 } 736 } 737 fhc_check_array(FHC_BOARDS); 738 fhc_bdlist_unlock(); 739 740 fhc_bdmax = count - 1; 741 742 /* 743 * Initialize our copy of the disabled board list. 744 */ 745 fhc_bd_dlist_init(); 746 747 (void) fhc_bdlist_lock(-1); 748 749 if ((jtm = jtag_master_lock()) == NULL) 750 cmn_err(CE_PANIC, "fhc_bdlist_prime: no jtag master"); 751 752 /* 753 * Go through the board list, skipping illegal slots 754 * and initialize each slot. 755 */ 756 for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { 757 sc = &bdp->sc; 758 board = sc->board; 759 760 se = SYSC_EVT_BD_PRESENT; 761 762 if (sc->type == UNKNOWN_BOARD) { 763 uint_t fhc_csr; 764 uint_t fhc_bsr; 765 enum board_type type; 766 767 type = jtag_get_board_type(jtm->jtag_cmd, sc); 768 switch (type) { 769 case EMPTY_BOARD: 770 fhc_bd_sc_evt(sc, SYSC_EVT_BD_EMPTY); 771 continue; 772 case DISK_BOARD: 773 /* 774 * Disk boards are handled differently 775 * in that they don't fail POST and have 776 * no fhc attached. 777 */ 778 sc->type = DISK_BOARD; 779 (void) jtag_init_disk_board(jtm->jtag_cmd, 780 board, 781 &fhc_csr, &fhc_bsr); 782 fhc_bd_ks_alloc(bdp); 783 break; 784 default: 785 /* 786 * Set the condition to FAILED if POST has 787 * failed. A failed board is physically 788 * present, is not on the disabled list and 789 * is of type UNKNOWN. 790 * NOTE: a non-present board which is 791 * (potentially) on the disabled board 792 * list has been ignored in the empty 793 * slot case. 794 */ 795 if (fhc_bd_disabled(board)) { 796 fhc_bd_ks_alloc(bdp); 797 se = SYSC_EVT_BD_DISABLED; 798 } else 799 se = SYSC_EVT_BD_FAILED; 800 801 sc->type = type; 802 break; 803 } 804 } 805 806 fhc_bd_sc_evt(sc, se); 807 } 808 809 /* 810 * Do the disk specific initialization. This routine scans 811 * for all disk boards, so we call it only once. 812 */ 813 fhc_bdlist_dk_init(); 814 815 jtag_master_unlock(jtm); 816 817 fhc_bdlist_unlock(); 818 } 819 820 struct cpu_speed { 821 int cpu_freq; 822 int sram_mode; 823 int system_div; 824 int system_dvd; 825 }; 826 827 struct cpu_speed ultraI_speed_table[] = { 828 { 0, 0, 0, 0}, 829 { 143, 1, 2, 1}, 830 { 154, 1, 2, 1}, 831 { 168, 1, 2, 1}, 832 { 182, 1, 3, 1}, 833 { 200, 1, 3, 1}, 834 { 222, 1, 3, 1}, 835 { 250, 1, 3, 1} 836 }; 837 838 struct cpu_speed ultraII_speed_table[] = { 839 { 0, 0, 0, 0}, 840 { 360, 2, 2, 1}, 841 { 400, 2, 4, 1}, 842 { 400, 2, 5, 2}, 843 { 248, 2, 3, 2}, 844 { 496, 2, 5, 2}, 845 { 296, 2, 2, 1}, 846 { 336, 2, 2, 1} 847 }; 848 849 /* 850 * set_cpu_info 851 * 852 * This routine extracts CPU module information used later for 853 * determining hotplug compatibility. 854 */ 855 static void 856 set_cpu_info(sysc_cfga_stat_t *sc, uint_t fhc_bsr) 857 { 858 int i; 859 int speed_pins; 860 struct cpu_speed *table; 861 862 for (i = 0; i < 2; i++) { 863 sc->bd.cpu[i].cpu_speed = 0; 864 sc->bd.cpu[i].cpu_sram_mode = 0; 865 866 if (!sc->bd.cpu[i].cpu_detected) 867 continue; 868 869 speed_pins = (i == 0) ? CPU_0_PINS(fhc_bsr) : 870 CPU_1_PINS(fhc_bsr); 871 872 switch (sc->bd.cpu[i].cpu_compid & CID_REV_MASK) { 873 case ULTRAI_COMPID: 874 table = ultraI_speed_table; 875 break; 876 case ULTRAII_COMPID: 877 table = ultraII_speed_table; 878 break; 879 default: 880 cmn_err(CE_WARN, "board %d, cpu module %c " 881 "unknown type", sc->board, 882 (i == 0) ? 'A' : 'B'); 883 sc->bd.cpu[i].cpu_speed = -1; 884 continue; 885 } 886 887 sc->bd.cpu[i].cpu_speed = table[speed_pins].cpu_freq; 888 sc->bd.cpu[i].cpu_sram_mode = table[speed_pins].sram_mode; 889 } 890 } 891 892 int 893 fhc_bdlist_scan(sysc_cfga_rstate_t rstate, struct jt_mstr *jtm) 894 { 895 int board; 896 int error; 897 int found = 0; 898 uint_t fhc_csr; 899 uint_t fhc_bsr; 900 fhc_bd_t *bdp; 901 sysc_cfga_stat_t *sc; 902 enum board_type type; 903 904 for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { 905 906 sc = &bdp->sc; 907 board = sc->board; 908 909 /* 910 * Check the boards in EMPTY and DISCONNECTED 911 * states. We need to check a board in the 912 * DISCONNECTED state in case it had been replugged. 913 */ 914 if (sc->in_transition || sc->rstate != rstate) 915 continue; 916 else if (sc->rstate == SYSC_CFGA_RSTATE_EMPTY) { 917 type = jtag_get_board_type(jtm->jtag_cmd, sc); 918 if (type == -1) 919 continue; /* no board present */ 920 sc->type = type; 921 } else 922 type = sc->type; 923 924 if (type != UNKNOWN_BOARD) 925 (void) jtag_get_board_info(jtm->jtag_cmd, sc); 926 927 error = 0; 928 929 if (type == DISK_BOARD) 930 /* 931 * Scan the FHC to turn off the board insert 932 * interrupt and modify LEDs based on hotplug 933 * status. 934 */ 935 (void) jtag_init_disk_board(jtm->jtag_cmd, board, 936 &fhc_csr, &fhc_bsr); 937 else 938 error = jtag_powerdown_board(jtm->jtag_cmd, 939 board, type, &fhc_csr, &fhc_bsr, FALSE); 940 941 if (error) { 942 fhc_bd_sc_evt(sc, SYSC_EVT_BD_INS_FAILED); 943 continue; 944 } 945 946 if (fhc_csr & FHC_NOT_BRD_PRES) 947 continue; 948 949 if (type == CPU_BOARD) { 950 set_cpu_info(sc, fhc_bsr); 951 } 952 953 fhc_bd_sc_evt(sc, SYSC_EVT_BD_INSERTED); 954 955 /* 956 * A replugged board will still have its kstat info. 957 */ 958 if (!bdp->ksp) 959 fhc_bd_ks_alloc(bdp); 960 961 found++; 962 break; 963 } 964 965 return (found); 966 } 967 968 int 969 fhc_bd_insert_scan() 970 { 971 struct jt_mstr *jtm; 972 int found; 973 974 ASSERT(fhc_bdlist_locked()); 975 976 if ((jtm = jtag_master_lock()) == NULL) 977 cmn_err(CE_PANIC, "fhc_bd_insert_scan: no jtag master"); 978 979 /* first check empty then disconnected */ 980 found = fhc_bdlist_scan(SYSC_CFGA_RSTATE_EMPTY, jtm); 981 if (!found) 982 found |= fhc_bdlist_scan(SYSC_CFGA_RSTATE_DISCONNECTED, jtm); 983 if (!found) 984 cmn_err(CE_WARN, "Could not find hotplugged core system board"); 985 986 jtag_master_unlock(jtm); 987 988 return (found); 989 } 990 991 int 992 fhc_bd_remove_scan() 993 { 994 int poll = 0; 995 fhc_bd_t *bdp; 996 struct jt_mstr *jtm; 997 sysc_cfga_stat_t *sc; 998 999 ASSERT(fhc_bdlist_locked()); 1000 1001 if ((jtm = jtag_master_lock()) == NULL) 1002 cmn_err(CE_PANIC, "fhc_bd_remove_scan: no jtag master"); 1003 1004 for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { 1005 sc = &bdp->sc; 1006 1007 if (sc->rstate != SYSC_CFGA_RSTATE_DISCONNECTED) 1008 continue; 1009 /* 1010 * While there is a board in the disconnected state 1011 * continue polling. When the last board is removed, 1012 * we will get one last scan. 1013 */ 1014 poll++; 1015 1016 if (sc->in_transition) 1017 continue; 1018 1019 /* 1020 * Scan to see if the board is still in. 1021 */ 1022 if (jtag_get_board_type(jtm->jtag_cmd, sc) == -1) { 1023 if (bdp->ksp) { 1024 kstat_delete(bdp->ksp); 1025 bdp->ksp = NULL; 1026 } 1027 fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED); 1028 } 1029 } 1030 1031 jtag_master_unlock(jtm); 1032 1033 return (poll); 1034 } 1035 1036 int 1037 fhc_bd_detachable(int board) 1038 { 1039 fhc_bd_t *bdp = fhc_bd(board); 1040 1041 if (bdp->softsp != NULL) 1042 return (bdp->flags & BDF_DETACH); 1043 else 1044 return (FALSE); 1045 } 1046 1047 void 1048 fhc_bd_sc_register(void (*f)(void *, sysc_cfga_stat_t *, sysc_evt_t), void *sp) 1049 { 1050 fhc_bd_evt.update = f; 1051 fhc_bd_evt.soft = sp; 1052 } 1053 1054 void 1055 fhc_bd_update(int board, sysc_evt_t evt) 1056 { 1057 fhc_bd_t *bdp; 1058 1059 ASSERT(fhc_bd_valid(board)); 1060 1061 /* 1062 * There is a window where this routine might be called 1063 * as a result of the environ thread before sysctrl has 1064 * attached and registered the callback. 1065 */ 1066 if (!(fbe->update)) 1067 return; 1068 1069 bdp = fhc_bdlist_lock(board); 1070 1071 fhc_bd_sc_evt(&bdp->sc, evt); 1072 1073 fhc_bdlist_unlock(); 1074 } 1075 1076 /* ARGSUSED */ 1077 int 1078 fhc_bd_test(int board, sysc_cfga_pkt_t *pkt) 1079 { 1080 uint_t fhc_csr, fhc_bsr; 1081 fhc_bd_t *bdp; 1082 struct jt_mstr *jtm; 1083 sysc_cfga_stat_t *sc; 1084 1085 ASSERT(fhc_bdlist_locked()); 1086 ASSERT(fhc_bd_busy(board)); 1087 1088 bdp = fhc_bd(board); 1089 sc = &bdp->sc; 1090 1091 switch (sc->rstate) { 1092 case SYSC_CFGA_RSTATE_EMPTY: 1093 cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d insertion", 1094 board); 1095 1096 jtm = jtag_master_lock(); 1097 ASSERT(jtm); 1098 jtag_master_unlock(jtm); 1099 1100 (void) jtag_powerdown_board(jtm->jtag_cmd, board, 1101 sc->type, &fhc_csr, &fhc_bsr, TRUE); 1102 break; 1103 case SYSC_CFGA_RSTATE_DISCONNECTED: 1104 cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d removal", 1105 board); 1106 1107 if (bdp->ksp) { 1108 kstat_delete(bdp->ksp); 1109 bdp->ksp = NULL; 1110 } 1111 fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED); 1112 break; 1113 default: 1114 cmn_err(CE_NOTE, 1115 "fhc_bd_test: invalid board state: %d", board); 1116 break; 1117 } 1118 1119 return (0); 1120 } 1121 1122 /* 1123 * force a board condition for test purpose 1124 */ 1125 /* ARGSUSED */ 1126 int 1127 fhc_bd_test_set_cond(int board, sysc_cfga_pkt_t *sysc_pkt) 1128 { 1129 fhc_bd_t *bdp; 1130 sysc_cfga_stat_t *sc; 1131 sysc_cfga_cond_t cond; 1132 1133 ASSERT(fhc_bdlist_locked()); 1134 ASSERT(fhc_bd_busy(board)); 1135 1136 bdp = fhc_bd(board); 1137 sc = &bdp->sc; 1138 1139 cond = (sysc_cfga_cond_t)sysc_pkt->cmd_cfga.arg; 1140 1141 switch (cond) { 1142 case SYSC_CFGA_COND_UNKNOWN: 1143 case SYSC_CFGA_COND_OK: 1144 case SYSC_CFGA_COND_FAILING: 1145 case SYSC_CFGA_COND_FAILED: 1146 case SYSC_CFGA_COND_UNUSABLE: 1147 sc->condition = cond; 1148 return (0); 1149 default: 1150 return (EINVAL); 1151 } 1152 } 1153