/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static fhc_bd_resizable_t boards; /* booted and hotplugged boards */ static fhc_bd_resizable_t clocks; /* clocks under central. */ static int fhc_bdmax; /* * !! IMPORTANT !! fhc_bdlist_rwlock is implemented as a single * RW_WRITER lock with *no* RW_READERs -- and it should stay that * way. The fhc_bdlist_rwlock should never be used with RW_READER. * * The lock was originally a mutex, but was changed to a * single-writer, zero-reader rwlock to force requesting threads * to block (sleep, not spin) when the RW_WRITER lock is already * held by a thread currently running. */ static krwlock_t fhc_bdlist_rwlock; static sysc_evt_handle_t fhc_bd_evt; static sysc_evt_handle_t *fbe = &fhc_bd_evt; #define fhc_bd_sc_evt(s, e) (*fbe->update)(fbe->soft, s, e) #define FHC_INCREMENT 4 #define FHC_B_SEARCH(in_array, board) \ fhc_b_search(in_array.boards, board, 0, in_array.last); static int fhc_bd_disabled(int); static void fhc_check_array(int); static void fhc_shell_sort(fhc_bd_t **, int, int); static int fhc_b_search(fhc_bd_t **, int, int, int); static void fhc_check_size(fhc_bd_resizable_t *); static void fhc_resize(fhc_bd_t ***, int, int); /* * fhc_bdmax gets set in fhc_bdlist_prime() and does not * change thereafter. */ int fhc_max_boards() { return (fhc_bdmax + 1); } static int fhc_bd_disabled(int board) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); return (boards.boards[index]->flags & BDF_DISABLED); } static void fhc_check_array(int btype) { if (btype == FHC_BOARDS) { ASSERT(fhc_bdlist_locked()); if (!boards.sorted) { fhc_shell_sort(boards.boards, 0, boards.last); boards.sorted = TRUE; } } else { ASSERT(fhc_bdlist_locked()); if (!clocks.sorted) { fhc_shell_sort(clocks.boards, 0, clocks.last); clocks.sorted = TRUE; } } } static void fhc_shell_sort(fhc_bd_t *a[], int lb, int ub) { int n, h, i, j; fhc_bd_t *t; /* sort array a[lb..ub] */ /* compute largest increment */ n = ub - lb + 1; h = 1; if (n < 14) h = 1; else { while (h < n) h = 3 * h + 1; h /= 3; h /= 3; } while (h > 0) { /* sort-by-insertion in increments of h */ for (i = lb + h; i <= ub; i++) { t = a[i]; for (j = i - h; j >= lb && a[j]->sc.board > t->sc.board; j -= h) { a[j+h] = a[j]; } a[j+h] = t; } /* compute next increment */ h /= 3; } } static int fhc_b_search(fhc_bd_t *in_array[], int board, int first, int last) { int mid; /* Array of length 0 case. */ if (in_array == NULL) return (-1); /* Array of length > 0 case. */ while (first < last) { mid = (first + last) / 2; if (in_array[mid]->sc.board < board) first = mid + 1; else last = mid; } if (in_array[first]->sc.board == board) { return (first); } else { return (-1); } } static void fhc_check_size(fhc_bd_resizable_t *resizable) { int oldsize; int newsize; ASSERT(fhc_bdlist_locked()); if (resizable->size == resizable->last + 1) { oldsize = sizeof (fhc_bd_t *) * resizable->size; resizable->size += FHC_INCREMENT; newsize = sizeof (fhc_bd_t *) * resizable->size; fhc_resize(&(resizable->boards), oldsize, newsize); } } int fhc_bdlist_locked() { if (panicstr) return (1); return (rw_owner(&fhc_bdlist_rwlock) == curthread); } int fhc_bd_busy(int board) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); return (boards.boards[index]->sc.in_transition); } int fhc_bd_is_jtag_master(int board) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); if (boards.boards[index]->softsp == NULL) return (FALSE); else return ((boards.boards[index]->softsp)->jt_master.is_master); } int fhc_bd_is_plus(int board) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); if (boards.boards[index]->sc.plus_board) return (boards.boards[index]->sc.plus_board); else return (FALSE); } void fhc_bdlist_init() { ASSERT(!fhc_bdmax); rw_init(&fhc_bdlist_rwlock, NULL, RW_DEFAULT, NULL); boards.boards = NULL; boards.size = 0; boards.last = -1; boards.sorted = TRUE; /* Array of 0 elements is sorted. */ clocks.boards = NULL; clocks.size = 0; clocks.last = -1; clocks.sorted = TRUE; /* Array of 0 elements is sorted. */ } void fhc_bdlist_fini() { rw_destroy(&fhc_bdlist_rwlock); } fhc_bd_t * fhc_bdlist_lock(int board) { int index; ASSERT(!fhc_bdlist_locked()); /* RW_WRITER *ONLY*. Never use RW_READER! */ rw_enter(&fhc_bdlist_rwlock, RW_WRITER); if (board == -1) return (NULL); else { ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); return (boards.boards[index]); } } void fhc_bdlist_unlock() { ASSERT(fhc_bdlist_locked()); rw_exit(&fhc_bdlist_rwlock); } static void fhc_resize(fhc_bd_t ***in_array, int oldsize, int newsize) { fhc_bd_t **temp; /* This function only grows arrays. */ ASSERT(newsize > oldsize); /* Allocate new array. */ temp = kmem_alloc(newsize, KM_SLEEP); /* Bcopy old array and free it. */ if (*in_array != NULL) { ASSERT(oldsize > 0); bcopy(*in_array, temp, oldsize); kmem_free(*in_array, oldsize); } *in_array = temp; } void fhc_bd_init(struct fhc_soft_state *softsp, int board, enum board_type type) { fhc_bd_t *bdp; int index; (void) fhc_bdlist_lock(-1); /* See if board already exists. */ ASSERT(boards.sorted); ASSERT(clocks.sorted); if (softsp->is_central) { index = FHC_B_SEARCH(clocks, board); } else { index = FHC_B_SEARCH(boards, board); } /* If index == -1 board does not exist. */ if (index != -1) { if (softsp->is_central) { bdp = clocks.boards[index]; } else { bdp = boards.boards[index]; } } else { if (softsp->is_central) { fhc_check_size(&clocks); clocks.boards[clocks.last + 1] = kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); bdp = clocks.boards[clocks.last + 1]; clocks.last++; clocks.sorted = FALSE; } else { fhc_check_size(&boards); boards.boards[boards.last + 1] = kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); bdp = boards.boards[boards.last + 1]; boards.last++; boards.sorted = FALSE; } } softsp->list = bdp; bdp->flags |= BDF_VALID; bdp->softsp = softsp; bdp->sc.type = type; bdp->sc.board = board; bdp->sc.plus_board = ISPLUSBRD(*softsp->bsr); /* Keep arrays sorted. */ fhc_check_array(FHC_BOARDS); fhc_check_array(FHC_CLOCKS); fhc_bdlist_unlock(); } fhc_bd_t * fhc_bd(int board) { int index; if (fhc_bdmax) { ASSERT(fhc_bdlist_locked()); } ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); ASSERT(index != -1); return (boards.boards[index]); } fhc_bd_t * fhc_bd_clock(void) { ASSERT(fhc_bdlist_locked()); ASSERT(clocks.size != 0); return (clocks.boards[0]); } fhc_bd_t * fhc_bd_first() { ASSERT(fhc_bdlist_locked()); if (boards.boards != NULL) return (boards.boards[0]); else return (NULL); } fhc_bd_t * fhc_bd_next(fhc_bd_t *bdp) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, bdp->sc.board); ASSERT(index != -1); if (index < boards.last) return (boards.boards[index + 1]); else return (NULL); } int fhc_bd_valid(int bd) { int index; ASSERT(bd >= 0); /* Untill fhc_bdlist_prime runs anything is valid. */ if (!fhc_bdmax) return (TRUE); ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, bd); if (index == -1) return (FALSE); else return (TRUE); } enum board_type fhc_bd_type(int board) { int index; ASSERT(boards.sorted); index = FHC_B_SEARCH(boards, board); if (index == -1) return (-1); return (boards.boards[index]->sc.type); } char * fhc_bd_typestr(enum board_type type) { char *type_str; switch (type) { case MEM_BOARD: type_str = MEM_BD_NAME; break; case CPU_BOARD: type_str = CPU_BD_NAME; break; case IO_2SBUS_BOARD: type_str = IO_2SBUS_BD_NAME; break; case IO_SBUS_FFB_BOARD: type_str = IO_SBUS_FFB_BD_NAME; break; case IO_2SBUS_SOCPLUS_BOARD: type_str = IO_2SBUS_SOCPLUS_BD_NAME; break; case IO_SBUS_FFB_SOCPLUS_BOARD: type_str = IO_SBUS_FFB_SOCPLUS_BD_NAME; break; case IO_PCI_BOARD: type_str = IO_PCI_BD_NAME; break; case DISK_BOARD: type_str = DISK_BD_NAME; break; case UNKNOWN_BOARD: default: type_str = "unknown"; break; } return (type_str); } void fhc_bd_env_set(int board, void *env) { fhc_bd_t *bdp; bdp = fhc_bd(board); bdp->dev_softsp = env; } static void fhc_bd_dlist_init() { int i; int len; int board; pnode_t node; char *dlist; int index; /* * Find the disabled board list property if present. * * The disabled board list is in the options node under root; * it is a null terminated list of boards in a string. * Each char represents a board. The driver must * reject illegal chars in case a user places them in the * property. */ if (((node = prom_finddevice("/options")) == OBP_BADNODE) || ((len = prom_getproplen(node, "disabled-board-list")) == -1)) return; dlist = kmem_alloc(len, KM_SLEEP); (void) prom_getprop(node, "disabled-board-list", dlist); /* * now loop thru the string, and create disabled board list * entries for all legal boards in the list. */ for (i = 0; (i < len) && (dlist[i] != 0); i++) { char ch = dlist[i]; if (ch >= '0' && ch <= '9') board = ch - '0'; else if (ch >= 'A' && ch <= 'F') board = ch - 'A' + 10; else if (ch >= 'a' && ch <= 'f') board = ch - 'a' + 10; else /* junk entry */ continue; index = FHC_B_SEARCH(boards, board); if (index != -1) { boards.boards[index]->flags |= BDF_DISABLED; } } kmem_free(dlist, len); } static struct bd_info fhc_bd_info; static int fhc_bd_ks_update(kstat_t *ksp, int rw) { fhc_bd_t *bdp; sysc_cfga_stat_t *sc; struct bd_info *uip; enum board_state state; if (rw == KSTAT_WRITE) return (EACCES); bdp = (fhc_bd_t *)ksp->ks_private; uip = &fhc_bd_info; sc = &bdp->sc; ASSERT(fhc_bd_valid(sc->board)); uip->board = sc->board; uip->type = sc->type; uip->fhc_compid = sc->fhc_compid; uip->ac_compid = sc->ac_compid; bcopy((caddr_t)sc->prom_rev, uip->prom_rev, sizeof (uip->prom_rev)); bcopy((caddr_t)&sc->bd, &uip->bd, sizeof (union bd_un)); switch (sc->rstate) { case SYSC_CFGA_RSTATE_DISCONNECTED: switch (sc->condition) { case SYSC_CFGA_COND_OK: case SYSC_CFGA_COND_UNKNOWN: state = DISABLED_STATE; break; case SYSC_CFGA_COND_FAILING: case SYSC_CFGA_COND_FAILED: case SYSC_CFGA_COND_UNUSABLE: state = FAILED_STATE; break; default: state = UNKNOWN_STATE; break; } break; default: state = UNKNOWN_STATE; break; } uip->state = state; return (0); } void fhc_bd_ks_alloc(fhc_bd_t *bdp) { ASSERT(!bdp->ksp); bdp->ksp = kstat_create("unix", bdp->sc.board, BDLIST_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, sizeof (struct bd_info), KSTAT_FLAG_VIRTUAL); if (bdp->ksp != NULL) { bdp->ksp->ks_data = &fhc_bd_info; bdp->ksp->ks_update = fhc_bd_ks_update; bdp->ksp->ks_private = (void *)bdp; kstat_install(bdp->ksp); } } static void fhc_bdlist_dk_init() { dev_info_t *dnode; /* * Search the children of root to see if there are any * disk boards in the tree. */ for (dnode = ddi_get_child(ddi_root_node()); dnode != NULL; dnode = ddi_get_next_sibling(dnode)) { if (strcmp(ddi_node_name(dnode), "disk-board") == 0) { int id; int board; fhc_bd_t *bdp; sysc_cfga_stat_t *sc; /* * Get the board number property. */ if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) { cmn_err(CE_WARN, "Could not find board number"); continue; } bdp = fhc_bd(board); sc = &bdp->sc; if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, DDI_PROP_DONTPASS, "disk0-scsi-id", -1)) != -1) { sc->bd.dsk.disk_pres[0] = 1; sc->bd.dsk.disk_id[0] = id; } else { sc->bd.dsk.disk_pres[0] = 0; } if ((id = (int)ddi_getprop(DDI_DEV_T_ANY, dnode, DDI_PROP_DONTPASS, "disk1-scsi-id", -1)) != -1) { sc->bd.dsk.disk_pres[1] = 1; sc->bd.dsk.disk_id[1] = id; } else { sc->bd.dsk.disk_pres[1] = 0; } } } } struct jt_mstr * jtag_master_lock(void) { fhc_bd_t *bdp; struct jt_mstr *master = NULL; ASSERT(fhc_bdlist_locked()); /* * Now search for the JTAG master and place the addresses for * command into the fhc soft state structure. * Disk board do not have softsp set. */ for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) if (bdp->softsp && (bdp->softsp->jt_master.is_master == 1)) { master = &bdp->softsp->jt_master; mutex_enter(&master->lock); break; } return (master); } void jtag_master_unlock(struct jt_mstr *mstr) { ASSERT(fhc_bdlist_locked()); ASSERT(mutex_owned(&mstr->lock)); mutex_exit(&mstr->lock); } void fhc_bdlist_prime(int first, int count, int incr) { int board; fhc_bd_t *bdp; sysc_evt_t se; sysc_cfga_stat_t *sc; struct jt_mstr *jtm; int index; int nadded; ASSERT(fbe->update); (void) fhc_bdlist_lock(-1); nadded = 0; for (board = first; board < count; board += incr) { /* * Search only subset of array. We hold mutex so * noone can add new elements to it. */ index = fhc_b_search(boards.boards, board, 0, boards.last - nadded); if (index == -1) { fhc_check_size(&boards); boards.boards[boards.last + 1] = kmem_zalloc(sizeof (fhc_bd_t), KM_SLEEP); boards.boards[boards.last + 1]->sc.type = UNKNOWN_BOARD; boards.boards[boards.last + 1]->sc.board = board; boards.boards[boards.last + 1]->softsp = NULL; boards.last++; nadded++; boards.sorted = FALSE; } } fhc_check_array(FHC_BOARDS); fhc_bdlist_unlock(); fhc_bdmax = count - 1; /* * Initialize our copy of the disabled board list. */ fhc_bd_dlist_init(); (void) fhc_bdlist_lock(-1); if ((jtm = jtag_master_lock()) == NULL) cmn_err(CE_PANIC, "fhc_bdlist_prime: no jtag master"); /* * Go through the board list, skipping illegal slots * and initialize each slot. */ for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { sc = &bdp->sc; board = sc->board; se = SYSC_EVT_BD_PRESENT; if (sc->type == UNKNOWN_BOARD) { uint_t fhc_csr; uint_t fhc_bsr; enum board_type type; type = jtag_get_board_type(jtm->jtag_cmd, sc); switch (type) { case EMPTY_BOARD: fhc_bd_sc_evt(sc, SYSC_EVT_BD_EMPTY); continue; case DISK_BOARD: /* * Disk boards are handled differently * in that they don't fail POST and have * no fhc attached. */ sc->type = DISK_BOARD; (void) jtag_init_disk_board(jtm->jtag_cmd, board, &fhc_csr, &fhc_bsr); fhc_bd_ks_alloc(bdp); break; default: /* * Set the condition to FAILED if POST has * failed. A failed board is physically * present, is not on the disabled list and * is of type UNKNOWN. * NOTE: a non-present board which is * (potentially) on the disabled board * list has been ignored in the empty * slot case. */ if (fhc_bd_disabled(board)) { fhc_bd_ks_alloc(bdp); se = SYSC_EVT_BD_DISABLED; } else se = SYSC_EVT_BD_FAILED; sc->type = type; break; } } fhc_bd_sc_evt(sc, se); } /* * Do the disk specific initialization. This routine scans * for all disk boards, so we call it only once. */ fhc_bdlist_dk_init(); jtag_master_unlock(jtm); fhc_bdlist_unlock(); } struct cpu_speed { int cpu_freq; int sram_mode; int system_div; int system_dvd; }; struct cpu_speed ultraI_speed_table[] = { { 0, 0, 0, 0}, { 143, 1, 2, 1}, { 154, 1, 2, 1}, { 168, 1, 2, 1}, { 182, 1, 3, 1}, { 200, 1, 3, 1}, { 222, 1, 3, 1}, { 250, 1, 3, 1} }; struct cpu_speed ultraII_speed_table[] = { { 0, 0, 0, 0}, { 360, 2, 2, 1}, { 400, 2, 4, 1}, { 400, 2, 5, 2}, { 248, 2, 3, 2}, { 496, 2, 5, 2}, { 296, 2, 2, 1}, { 336, 2, 2, 1} }; /* * set_cpu_info * * This routine extracts CPU module information used later for * determining hotplug compatibility. */ static void set_cpu_info(sysc_cfga_stat_t *sc, uint_t fhc_bsr) { int i; int speed_pins; struct cpu_speed *table; for (i = 0; i < 2; i++) { sc->bd.cpu[i].cpu_speed = 0; sc->bd.cpu[i].cpu_sram_mode = 0; if (!sc->bd.cpu[i].cpu_detected) continue; speed_pins = (i == 0) ? CPU_0_PINS(fhc_bsr) : CPU_1_PINS(fhc_bsr); switch (sc->bd.cpu[i].cpu_compid & CID_REV_MASK) { case ULTRAI_COMPID: table = ultraI_speed_table; break; case ULTRAII_COMPID: table = ultraII_speed_table; break; default: cmn_err(CE_WARN, "board %d, cpu module %c " "unknown type", sc->board, (i == 0) ? 'A' : 'B'); sc->bd.cpu[i].cpu_speed = -1; continue; } sc->bd.cpu[i].cpu_speed = table[speed_pins].cpu_freq; sc->bd.cpu[i].cpu_sram_mode = table[speed_pins].sram_mode; } } int fhc_bdlist_scan(sysc_cfga_rstate_t rstate, struct jt_mstr *jtm) { int board; int error; int found = 0; uint_t fhc_csr; uint_t fhc_bsr; fhc_bd_t *bdp; sysc_cfga_stat_t *sc; enum board_type type; for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { sc = &bdp->sc; board = sc->board; /* * Check the boards in EMPTY and DISCONNECTED * states. We need to check a board in the * DISCONNECTED state in case it had been replugged. */ if (sc->in_transition || sc->rstate != rstate) continue; else if (sc->rstate == SYSC_CFGA_RSTATE_EMPTY) { type = jtag_get_board_type(jtm->jtag_cmd, sc); if (type == -1) continue; /* no board present */ sc->type = type; } else type = sc->type; if (type != UNKNOWN_BOARD) (void) jtag_get_board_info(jtm->jtag_cmd, sc); error = 0; if (type == DISK_BOARD) /* * Scan the FHC to turn off the board insert * interrupt and modify LEDs based on hotplug * status. */ (void) jtag_init_disk_board(jtm->jtag_cmd, board, &fhc_csr, &fhc_bsr); else error = jtag_powerdown_board(jtm->jtag_cmd, board, type, &fhc_csr, &fhc_bsr, FALSE); if (error) { fhc_bd_sc_evt(sc, SYSC_EVT_BD_INS_FAILED); continue; } if (fhc_csr & FHC_NOT_BRD_PRES) continue; if (type == CPU_BOARD) { set_cpu_info(sc, fhc_bsr); } fhc_bd_sc_evt(sc, SYSC_EVT_BD_INSERTED); /* * A replugged board will still have its kstat info. */ if (!bdp->ksp) fhc_bd_ks_alloc(bdp); found++; break; } return (found); } int fhc_bd_insert_scan() { struct jt_mstr *jtm; int found; ASSERT(fhc_bdlist_locked()); if ((jtm = jtag_master_lock()) == NULL) cmn_err(CE_PANIC, "fhc_bd_insert_scan: no jtag master"); /* first check empty then disconnected */ found = fhc_bdlist_scan(SYSC_CFGA_RSTATE_EMPTY, jtm); if (!found) found |= fhc_bdlist_scan(SYSC_CFGA_RSTATE_DISCONNECTED, jtm); if (!found) cmn_err(CE_WARN, "Could not find hotplugged core system board"); jtag_master_unlock(jtm); return (found); } int fhc_bd_remove_scan() { int poll = 0; fhc_bd_t *bdp; struct jt_mstr *jtm; sysc_cfga_stat_t *sc; ASSERT(fhc_bdlist_locked()); if ((jtm = jtag_master_lock()) == NULL) cmn_err(CE_PANIC, "fhc_bd_remove_scan: no jtag master"); for (bdp = fhc_bd_first(); bdp; bdp = fhc_bd_next(bdp)) { sc = &bdp->sc; if (sc->rstate != SYSC_CFGA_RSTATE_DISCONNECTED) continue; /* * While there is a board in the disconnected state * continue polling. When the last board is removed, * we will get one last scan. */ poll++; if (sc->in_transition) continue; /* * Scan to see if the board is still in. */ if (jtag_get_board_type(jtm->jtag_cmd, sc) == -1) { if (bdp->ksp) { kstat_delete(bdp->ksp); bdp->ksp = NULL; } fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED); } } jtag_master_unlock(jtm); return (poll); } int fhc_bd_detachable(int board) { fhc_bd_t *bdp = fhc_bd(board); if (bdp->softsp != NULL) return (bdp->flags & BDF_DETACH); else return (FALSE); } void fhc_bd_sc_register(void (*f)(void *, sysc_cfga_stat_t *, sysc_evt_t), void *sp) { fhc_bd_evt.update = f; fhc_bd_evt.soft = sp; } void fhc_bd_update(int board, sysc_evt_t evt) { fhc_bd_t *bdp; ASSERT(fhc_bd_valid(board)); /* * There is a window where this routine might be called * as a result of the environ thread before sysctrl has * attached and registered the callback. */ if (!(fbe->update)) return; bdp = fhc_bdlist_lock(board); fhc_bd_sc_evt(&bdp->sc, evt); fhc_bdlist_unlock(); } /* ARGSUSED */ int fhc_bd_test(int board, sysc_cfga_pkt_t *pkt) { uint_t fhc_csr, fhc_bsr; fhc_bd_t *bdp; struct jt_mstr *jtm; sysc_cfga_stat_t *sc; ASSERT(fhc_bdlist_locked()); ASSERT(fhc_bd_busy(board)); bdp = fhc_bd(board); sc = &bdp->sc; switch (sc->rstate) { case SYSC_CFGA_RSTATE_EMPTY: cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d insertion", board); jtm = jtag_master_lock(); ASSERT(jtm); jtag_master_unlock(jtm); (void) jtag_powerdown_board(jtm->jtag_cmd, board, sc->type, &fhc_csr, &fhc_bsr, TRUE); break; case SYSC_CFGA_RSTATE_DISCONNECTED: cmn_err(CE_NOTE, "fhc_bd_test: simulate board %d removal", board); if (bdp->ksp) { kstat_delete(bdp->ksp); bdp->ksp = NULL; } fhc_bd_sc_evt(sc, SYSC_EVT_BD_REMOVED); break; default: cmn_err(CE_NOTE, "fhc_bd_test: invalid board state: %d", board); break; } return (0); } /* * force a board condition for test purpose */ /* ARGSUSED */ int fhc_bd_test_set_cond(int board, sysc_cfga_pkt_t *sysc_pkt) { fhc_bd_t *bdp; sysc_cfga_stat_t *sc; sysc_cfga_cond_t cond; ASSERT(fhc_bdlist_locked()); ASSERT(fhc_bd_busy(board)); bdp = fhc_bd(board); sc = &bdp->sc; cond = (sysc_cfga_cond_t)sysc_pkt->cmd_cfga.arg; switch (cond) { case SYSC_CFGA_COND_UNKNOWN: case SYSC_CFGA_COND_OK: case SYSC_CFGA_COND_FAILING: case SYSC_CFGA_COND_FAILED: case SYSC_CFGA_COND_UNUSABLE: sc->condition = cond; return (0); default: return (EINVAL); } }