/* * 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. */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG struct regs_data { caddr_t msg; u_longlong_t physaddr; uint_t pre_dsct; uint_t post_dsct; uint_t eflag; uint_t oflag; }; static struct regs_data reg_tmpl[] = { "AC Control and Status reg = 0x", AC_BCSR(0), 0, 0, 0, 0, "FHC Control and Status reg = 0x", FHC_CTRL(0), 0, 0, 0, 0, "JTAG Control reg = 0x", FHC_JTAG_CTRL(0), 0, 0, 0, 0, "Interrupt Group Number reg = 0x", FHC_IGN(0), 0, 0, 0, 0, "System Interrupt Mapping reg = 0x", FHC_SIM(0), 0, 0, 0, 0, "System Interrupt State reg = 0x", FHC_SSM(0), 0, 0, 0, 0, "UART Interrupt Mapping reg = 0x", FHC_UIM(0), 0, 0, 0, 0, "UART Interrupt State reg = 0x", FHC_USM(0), 0, 0, 0, 0 }; #define NUM_REG (sizeof (reg_tmpl)/sizeof (reg_tmpl[0])) static struct regs_data reg_dt[MAX_BOARDS][NUM_REG]; int sysctrl_enable_regdump = 0; static void precache_regdump(int board); static void display_regdump(void); static void boardstat_regdump(void); #endif /* DEBUG */ extern void bd_remove_poll(struct sysctrl_soft_state *); extern int sysctrl_getsystem_freq(void); extern enum power_state compute_power_state(struct sysctrl_soft_state *, int); extern enum temp_state fhc_env_temp_state(int); extern int sysctrl_hotplug_disabled; /* Let user disable Sunfire Dynamic Reconfiguration */ int enable_dynamic_reconfiguration = 1; int enable_redist = 1; static void sysc_dr_err_decode(sysc_dr_handle_t *, dev_info_t *, int); static uint_t sysc_policy_enough_cooling(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held); static uint_t sysc_policy_enough_precharge(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat); static uint_t sysc_policy_enough_power(struct sysctrl_soft_state *softsp, int plus_load, uint_t ps_mutex_is_held); static uint_t sysc_policy_hardware_compatible(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, sysc_cfga_pkt_t *pkt); static void sysc_policy_empty_condition( struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t failure, uint_t ps_mutex_is_held); static void sysc_policy_disconnected_condition( struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t failure, uint_t ps_mutex_is_held); static void sysc_policy_connected_condition(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held); static void sysc_policy_set_condition(void *sp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held); static void sysc_policy_audit_messages(sysc_audit_evt_t event, sysc_cfga_stat_t *sysc_stat); static void sysctrl_post_config_change(struct sysctrl_soft_state *softsp); static int sysc_bd_connect(int, sysc_cfga_pkt_t *); static int sysc_bd_disconnect(int, sysc_cfga_pkt_t *); static int sysc_bd_configure(int, sysc_cfga_pkt_t *); static int sysc_bd_unconfigure(int, sysc_cfga_pkt_t *); static void sysc_dr_init(sysc_dr_handle_t *handle); static void sysc_dr_uninit(sysc_dr_handle_t *handle); static int sysc_dr_attach(sysc_dr_handle_t *handle, int board); static int sysc_dr_detach(sysc_dr_handle_t *handle, int board); static int sysc_prom_select(pnode_t pnode, void *arg, uint_t flag); static void sysc_branch_callback(dev_info_t *rdip, void *arg, uint_t flags); static int find_and_setup_cpu(int); static int sysc_board_connect_supported(enum board_type); static int find_and_setup_cpu_start(void *cpuid_arg, int has_changed); /* * This function will basically do a prediction on the power state * based on adding one additional load to the equation implemented * by the function compute_power_state. */ /*ARGSUSED*/ static uint_t sysc_policy_enough_power(struct sysctrl_soft_state *softsp, int plus_load, uint_t ps_mutex_is_held) { int retval = 0; ASSERT(softsp); if (!ps_mutex_is_held) { mutex_enter(&softsp->ps_fail_lock); } /* * note that we add one more load * to the equation in compute_power_state * and the answer better be REDUNDANT or * MINIMUM before proceeding. */ switch (compute_power_state(softsp, plus_load)) { case REDUNDANT: case MINIMUM: retval = 1; break; case BELOW_MINIMUM: default: break; } if (!ps_mutex_is_held) { mutex_exit(&softsp->ps_fail_lock); } return (retval); } /* * This function gropes through the shadow registers in the sysctrl soft_state * for the core power supply status, since fan status for them are ORed into * the same status bit, and all other remaining fans. */ static uint_t sysc_policy_enough_cooling(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held) { int retval = 0; if (!ps_mutex_is_held) { mutex_enter(&softsp->ps_fail_lock); } /* * check the power supply in the slot in question * for fans then check all the common fans. */ retval = ((softsp->ps_stats[FHC_BOARD2PS(sysc_stat->board)].pshadow == PRES_IN) && (softsp->ps_stats[FHC_BOARD2PS(sysc_stat->board)].dcshadow == PS_OK)); if (!ps_mutex_is_held) { mutex_exit(&softsp->ps_fail_lock); } return (retval); } /* * This function will check all precharge voltage status. */ /*ARGSUSED*/ static uint_t sysc_policy_enough_precharge(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat) { int retval = 0; int ppsval = 0; mutex_enter(&softsp->ps_fail_lock); /* * note that we always have to explicitly check * the peripheral power supply for precharge since it * supplies all of the precharge voltages. */ ppsval = (softsp->ps_stats[SYS_PPS0_INDEX].pshadow == PRES_IN) && (softsp->ps_stats[SYS_PPS0_INDEX].dcshadow == PS_OK); /* * check all the precharge status */ retval = ((softsp->ps_stats[SYS_V3_PCH_INDEX].pshadow == PRES_IN) && (softsp->ps_stats[SYS_V3_PCH_INDEX].dcshadow == PS_OK) && (softsp->ps_stats[SYS_V5_PCH_INDEX].pshadow == PRES_IN) && (softsp->ps_stats[SYS_V5_PCH_INDEX].dcshadow == PS_OK)); mutex_exit(&softsp->ps_fail_lock); return (retval&&ppsval); } static int Fsys; /* * This function should only be called once as we may * zero the clock board registers to indicate a configuration change. * The code to calculate the bus frequency has been removed and we * read the eeprom property instead. Another static Fmod (module * frequency may be needed later but so far it is commented out. */ void set_clockbrd_info(void) { uint_t clock_freq = 0; pnode_t root = prom_nextnode((pnode_t)0); (void) prom_getprop(root, "clock-frequency", (caddr_t)&clock_freq); Fsys = clock_freq / 1000000; } #define abs(x) ((x) < 0 ? -(x) : (x)) /*ARGSUSED*/ static uint_t sysc_policy_hardware_compatible(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, sysc_cfga_pkt_t *pkt) { int status; ASSERT(Fsys > 0); /* Only allow DR operations on supported hardware */ switch (sysc_stat->type) { case CPU_BOARD: { #ifdef RFE_4174486 int i; int cpu_freq; int sram_mode; ASSERT(Fmod > 0); cpu_freq = CPU->cpu_type_info.pi_clock; if (abs(cpu_freq - Fmod) < 8) sram_mode = 1; else sram_mode = 2; status = TRUE; for (i = 0; i < 2; i++) { /* * XXX: Add jtag code which rescans disabled boards. * For the time being disabled boards are not * checked for compatibility when cpu_speed is 0. */ if (sysc_stat->bd.cpu[i].cpu_speed == 0) continue; if (sysc_stat->bd.cpu[i].cpu_speed < cpu_freq) { cmn_err(CE_WARN, "board %d, cpu module %c " "rated at %d Mhz, system freq %d Mhz", sysc_stat->board, (i == 0) ? 'A' : 'B', sysc_stat->bd.cpu[i].cpu_speed, cpu_freq); status = FALSE; } if (sram_mode != sysc_stat->bd.cpu[i].cpu_sram_mode) { cmn_err(CE_WARN, "board %d, cpu module %c " "incompatible sram mode of %dx, " "system is %dx", sysc_stat->board, (i == 0) ? 'A' : 'B', sysc_stat->bd.cpu[i].cpu_sram_mode, sram_mode); status = FALSE; } } break; #endif /* RFE_4174486 */ } case MEM_BOARD: case IO_2SBUS_BOARD: case IO_SBUS_FFB_BOARD: case IO_PCI_BOARD: case IO_2SBUS_SOCPLUS_BOARD: case IO_SBUS_FFB_SOCPLUS_BOARD: status = TRUE; break; case CLOCK_BOARD: case DISK_BOARD: default: status = FALSE; /* default is not supported */ break; } if (status == FALSE) return (status); /* Check for Sunfire boards in a Sunfire+ system */ if (status == TRUE && Fsys > 84 && !fhc_bd_is_plus(sysc_stat->board)) { (void) snprintf(pkt->errbuf, SYSC_OUTPUT_LEN, "not 100 MHz capable "); cmn_err(CE_WARN, "board %d, is not capable of running at " "current system clock (%dMhz)", sysc_stat->board, Fsys); status = FALSE; } return (status); } /* * This function is called to check the policy for a request to transition * to the connected state from the disconnected state. The generic policy * is to do sanity checks again before going live. */ /*ARGSUSED*/ int sysc_policy_connect(struct sysctrl_soft_state *softsp, sysc_cfga_pkt_t *pkt, sysc_cfga_stat_t *sysc_stat) { int retval; ASSERT(fhc_bdlist_locked()); DPRINTF(SYSC_DEBUG, ("Previous RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Previous OState: %d\n", sysc_stat->ostate)); switch (sysc_stat->rstate) { case SYSC_CFGA_RSTATE_DISCONNECTED: /* * Safety policy: only allow connect if board is UNKNOWN cond. * cold start board will be demoted to UNKNOWN cond when * disconnected */ if (sysc_stat->condition != SYSC_CFGA_COND_UNKNOWN) { SYSC_ERR_SET(pkt, SYSC_ERR_COND); return (EINVAL); } if (!enable_dynamic_reconfiguration) { SYSC_ERR_SET(pkt, SYSC_ERR_NON_DR_PROM); return (ENOTSUP); } if (sysctrl_hotplug_disabled) { SYSC_ERR_SET(pkt, SYSC_ERR_HOTPLUG); return (ENOTSUP); } /* Check PROM support. */ if (!sysc_board_connect_supported(sysc_stat->type)) { cmn_err(CE_WARN, "%s board %d connect" " is not supported by firmware.", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); SYSC_ERR_SET(pkt, SYSC_ERR_HW_COMPAT); return (ENOTSUP); } if (!sysc_policy_enough_power(softsp, TRUE, FALSE)) { SYSC_ERR_SET(pkt, SYSC_ERR_POWER); return (EAGAIN); } if (!sysc_policy_enough_precharge(softsp, sysc_stat)) { SYSC_ERR_SET(pkt, SYSC_ERR_PRECHARGE); return (EAGAIN); } if (!sysc_policy_enough_cooling(softsp, sysc_stat, FALSE)) { SYSC_ERR_SET(pkt, SYSC_ERR_COOLING); return (EAGAIN); } if (!sysc_policy_hardware_compatible(softsp, sysc_stat, pkt)) { SYSC_ERR_SET(pkt, SYSC_ERR_HW_COMPAT); return (ENOTSUP); } sysc_policy_audit_messages(SYSC_AUDIT_RSTATE_CONNECT, sysc_stat); retval = sysc_bd_connect(sysc_stat->board, pkt); if (!retval) { sysc_stat->rstate = SYSC_CFGA_RSTATE_CONNECTED; sysc_policy_connected_condition(softsp, sysc_stat, FALSE); sysc_policy_audit_messages(SYSC_AUDIT_RSTATE_SUCCEEDED, sysc_stat); } else { uint_t prom_failure; prom_failure = (retval == EIO && pkt->cmd_cfga.errtype == SYSC_ERR_PROM) ? TRUE : FALSE; sysc_policy_disconnected_condition(softsp, sysc_stat, prom_failure, FALSE); sysc_policy_audit_messages( SYSC_AUDIT_RSTATE_CONNECT_FAILED, sysc_stat); } break; case SYSC_CFGA_RSTATE_EMPTY: case SYSC_CFGA_RSTATE_CONNECTED: default: SYSC_ERR_SET(pkt, SYSC_ERR_RSTATE); retval = EINVAL; break; } DPRINTF(SYSC_DEBUG, ("Current RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Current OState: %d\n", sysc_stat->ostate)); DPRINTF(SYSC_DEBUG, ("Current Condition: %d\n", sysc_stat->condition)); return (retval); } /* * This function is called to check the policy for a request to transition * to the disconnected state from the connected/unconfigured state only. * All other requests are invalid. */ /*ARGSUSED*/ int sysc_policy_disconnect(struct sysctrl_soft_state *softsp, sysc_cfga_pkt_t *pkt, sysc_cfga_stat_t *sysc_stat) { int retval; ASSERT(fhc_bdlist_locked()); DPRINTF(SYSC_DEBUG, ("Previous RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Previous OState: %d\n", sysc_stat->ostate)); switch (sysc_stat->rstate) { case SYSC_CFGA_RSTATE_CONNECTED: switch (sysc_stat->ostate) { case SYSC_CFGA_OSTATE_UNCONFIGURED: if (!enable_dynamic_reconfiguration) { SYSC_ERR_SET(pkt, SYSC_ERR_NON_DR_PROM); return (ENOTSUP); } /* Check PROM support. */ if (!sysc_board_connect_supported(sysc_stat->type)) { cmn_err(CE_WARN, "%s board %d disconnect" " is not supported by firmware.", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); SYSC_ERR_SET(pkt, SYSC_ERR_HW_COMPAT); return (ENOTSUP); } if (!sysc_policy_hardware_compatible(softsp, sysc_stat, pkt)) { cmn_err(CE_WARN, "%s board %d disconnect" " is not yet supported.", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); SYSC_ERR_SET(pkt, SYSC_ERR_HW_COMPAT); return (ENOTSUP); } if (fhc_bd_is_jtag_master(sysc_stat->board)) { sysc_policy_update(softsp, sysc_stat, SYSC_EVT_BD_CORE_RESOURCE_DISCONNECT); SYSC_ERR_SET(pkt, SYSC_ERR_CORE_RESOURCE); return (EINVAL); } sysc_policy_audit_messages(SYSC_AUDIT_RSTATE_DISCONNECT, sysc_stat); retval = sysc_bd_disconnect(sysc_stat->board, pkt); if (!retval) { sysc_stat->rstate = SYSC_CFGA_RSTATE_DISCONNECTED; DPRINTF(SYSCTRL_ATTACH_DEBUG, ("disconnect starting bd_remove_poll()")); bd_remove_poll(softsp); sysc_policy_disconnected_condition( softsp, sysc_stat, FALSE, FALSE); sysc_policy_audit_messages( SYSC_AUDIT_RSTATE_SUCCEEDED, sysc_stat); cmn_err(CE_NOTE, "board %d is ready to remove", sysc_stat->board); } else { sysc_policy_connected_condition( softsp, sysc_stat, FALSE); sysc_policy_audit_messages( SYSC_AUDIT_RSTATE_DISCONNECT_FAILED, sysc_stat); } break; case SYSC_CFGA_OSTATE_CONFIGURED: default: SYSC_ERR_SET(pkt, SYSC_ERR_OSTATE); retval = EINVAL; break; } break; case SYSC_CFGA_RSTATE_EMPTY: case SYSC_CFGA_RSTATE_DISCONNECTED: default: SYSC_ERR_SET(pkt, SYSC_ERR_RSTATE); retval = EINVAL; break; } DPRINTF(SYSC_DEBUG, ("Current RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Current OState: %d\n", sysc_stat->ostate)); DPRINTF(SYSC_DEBUG, ("Current Condition: %d\n", sysc_stat->condition)); return (retval); } /* * This function is called to check the policy for a request to transition * from the connected/configured state to the connected/unconfigured state only. * All other requests are invalid. */ /*ARGSUSED*/ int sysc_policy_unconfigure(struct sysctrl_soft_state *softsp, sysc_cfga_pkt_t *pkt, sysc_cfga_stat_t *sysc_stat) { int retval; ASSERT(fhc_bdlist_locked()); DPRINTF(SYSC_DEBUG, ("Previous RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Previous OState: %d\n", sysc_stat->ostate)); switch (sysc_stat->ostate) { case SYSC_CFGA_OSTATE_CONFIGURED: if (!enable_dynamic_reconfiguration) { SYSC_ERR_SET(pkt, SYSC_ERR_NON_DR_PROM); return (ENOTSUP); } if (!sysc_policy_hardware_compatible(softsp, sysc_stat, pkt)) { cmn_err(CE_WARN, "%s board %d unconfigure" " is not yet supported.", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); SYSC_ERR_SET(pkt, SYSC_ERR_HW_COMPAT); return (ENOTSUP); } sysc_policy_audit_messages(SYSC_AUDIT_OSTATE_UNCONFIGURE, sysc_stat); retval = sysc_bd_unconfigure(sysc_stat->board, pkt); if (!retval) { sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_policy_audit_messages( SYSC_AUDIT_OSTATE_SUCCEEDED, sysc_stat); } else { sysc_policy_audit_messages( SYSC_AUDIT_OSTATE_UNCONFIGURE_FAILED, sysc_stat); } sysc_policy_connected_condition(softsp, sysc_stat, FALSE); break; case SYSC_CFGA_OSTATE_UNCONFIGURED: default: SYSC_ERR_SET(pkt, SYSC_ERR_OSTATE); retval = EINVAL; break; } DPRINTF(SYSC_DEBUG, ("Current RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Current OState: %d\n", sysc_stat->ostate)); DPRINTF(SYSC_DEBUG, ("Current Condition: %d\n", sysc_stat->condition)); return (retval); } /* * This function is called to check the policy for a requested transition * from either the connected/unconfigured state or the connected/configured * state to the connected/configured state. The redundant state transition * is permitted for partially configured set of devices. Basically, we * retry the configure. */ /*ARGSUSED*/ int sysc_policy_configure(struct sysctrl_soft_state *softsp, sysc_cfga_pkt_t *pkt, sysc_cfga_stat_t *sysc_stat) { int retval; ASSERT(fhc_bdlist_locked()); DPRINTF(SYSC_DEBUG, ("Previous RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Previous OState: %d\n", sysc_stat->ostate)); switch (sysc_stat->rstate) { case SYSC_CFGA_RSTATE_CONNECTED: switch (sysc_stat->ostate) { case SYSC_CFGA_OSTATE_UNCONFIGURED: if (sysc_stat->condition != SYSC_CFGA_COND_OK) { SYSC_ERR_SET(pkt, SYSC_ERR_COND); return (EINVAL); } sysc_policy_audit_messages(SYSC_AUDIT_OSTATE_CONFIGURE, sysc_stat); retval = sysc_bd_configure(sysc_stat->board, pkt); sysc_stat->ostate = SYSC_CFGA_OSTATE_CONFIGURED; sysc_policy_connected_condition(softsp, sysc_stat, FALSE); if (!retval) { sysc_policy_audit_messages( SYSC_AUDIT_OSTATE_SUCCEEDED, sysc_stat); } else { sysc_policy_audit_messages( SYSC_AUDIT_OSTATE_CONFIGURE_FAILED, sysc_stat); } break; case SYSC_CFGA_OSTATE_CONFIGURED: SYSC_ERR_SET(pkt, SYSC_ERR_OSTATE); retval = ENOTSUP; break; default: SYSC_ERR_SET(pkt, SYSC_ERR_OSTATE); retval = EINVAL; break; } break; case SYSC_CFGA_RSTATE_EMPTY: case SYSC_CFGA_RSTATE_DISCONNECTED: default: SYSC_ERR_SET(pkt, SYSC_ERR_RSTATE); retval = EINVAL; break; } DPRINTF(SYSC_DEBUG, ("Current RState: %d\n", sysc_stat->rstate)); DPRINTF(SYSC_DEBUG, ("Current OState: %d\n", sysc_stat->ostate)); DPRINTF(SYSC_DEBUG, ("Current Condition: %d\n", sysc_stat->condition)); return (retval); } /*ARGSUSED*/ static void sysc_policy_empty_condition(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t failure, uint_t ps_mutex_is_held) { ASSERT(fhc_bdlist_locked()); switch (sysc_stat->condition) { case SYSC_CFGA_COND_UNKNOWN: case SYSC_CFGA_COND_OK: case SYSC_CFGA_COND_FAILING: case SYSC_CFGA_COND_FAILED: /* nothing in the slot so just check power supplies */ case SYSC_CFGA_COND_UNUSABLE: if (sysc_policy_enough_cooling(softsp, sysc_stat, ps_mutex_is_held) && sysc_policy_enough_power(softsp, FALSE, ps_mutex_is_held)) { sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; } else { sysc_stat->condition = SYSC_CFGA_COND_UNUSABLE; } sysc_stat->last_change = gethrestime_sec(); break; default: ASSERT(FALSE); break; } } /*ARGSUSED*/ static void sysc_policy_disconnected_condition(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t failure, uint_t ps_mutex_is_held) { ASSERT(fhc_bdlist_locked()); if (failure) { sysc_stat->condition = SYSC_CFGA_COND_FAILED; sysc_stat->last_change = gethrestime_sec(); return; } switch (sysc_stat->condition) { /* * if unknown, we have come from hotplug case so do a quick * reevaluation. */ case SYSC_CFGA_COND_UNKNOWN: /* * if ok, we have come from connected to disconnected and we stay * ok until removed or reevaluate when reconnect. We might have * experienced a ps fail so reevaluate the condition. */ case SYSC_CFGA_COND_OK: /* * if unsuable, either power supply was missing or * hardware was not compatible. Check to see if * this is still true. */ case SYSC_CFGA_COND_UNUSABLE: /* * failing must transition in the disconnected state * to either unusable or unknown. We may have come here * from cfgadm -f -c disconnect after a power supply failure * in an attempt to protect the board. */ case SYSC_CFGA_COND_FAILING: if (sysc_policy_enough_cooling(softsp, sysc_stat, ps_mutex_is_held) && sysc_policy_enough_power(softsp, FALSE, ps_mutex_is_held)) { sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; } else { sysc_stat->condition = SYSC_CFGA_COND_UNUSABLE; } sysc_stat->last_change = gethrestime_sec(); break; /* * if failed, we have failed POST and must stay in this * condition until the board has been removed * before ever coming back into another condition */ case SYSC_CFGA_COND_FAILED: break; default: ASSERT(FALSE); break; } } /*ARGSUSED*/ static void sysc_policy_connected_condition(struct sysctrl_soft_state *softsp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held) { ASSERT(fhc_bdlist_locked()); switch (sysc_stat->condition) { case SYSC_CFGA_COND_UNKNOWN: case SYSC_CFGA_COND_OK: case SYSC_CFGA_COND_FAILING: case SYSC_CFGA_COND_UNUSABLE: if (sysc_policy_enough_cooling(softsp, sysc_stat, ps_mutex_is_held) && sysc_policy_enough_power(softsp, FALSE, ps_mutex_is_held) && (fhc_env_temp_state(sysc_stat->board) == TEMP_OK)) { sysc_stat->condition = SYSC_CFGA_COND_OK; } else { sysc_stat->condition = SYSC_CFGA_COND_FAILING; } sysc_stat->last_change = gethrestime_sec(); break; case SYSC_CFGA_COND_FAILED: break; default: ASSERT(FALSE); break; } } static void sysc_policy_set_condition(void *sp, sysc_cfga_stat_t *sysc_stat, uint_t ps_mutex_is_held) { struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)sp; ASSERT(fhc_bdlist_locked()); switch (sysc_stat->rstate) { case SYSC_CFGA_RSTATE_EMPTY: sysc_policy_empty_condition(softsp, sysc_stat, FALSE, ps_mutex_is_held); break; case SYSC_CFGA_RSTATE_DISCONNECTED: sysc_policy_disconnected_condition(softsp, sysc_stat, FALSE, ps_mutex_is_held); break; case SYSC_CFGA_RSTATE_CONNECTED: sysc_policy_connected_condition(softsp, sysc_stat, ps_mutex_is_held); break; default: ASSERT(FALSE); break; } } void sysc_policy_update(void *softsp, sysc_cfga_stat_t *sysc_stat, sysc_evt_t event) { fhc_bd_t *list; ASSERT(event == SYSC_EVT_BD_HP_DISABLED || fhc_bdlist_locked()); switch (event) { case SYSC_EVT_BD_EMPTY: sysc_stat->rstate = SYSC_CFGA_RSTATE_EMPTY; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; sysc_policy_empty_condition(softsp, sysc_stat, FALSE, FALSE); break; case SYSC_EVT_BD_PRESENT: if (sysc_stat->type == DISK_BOARD) { sysc_stat->rstate = SYSC_CFGA_RSTATE_DISCONNECTED; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; } else { sysc_stat->rstate = SYSC_CFGA_RSTATE_CONNECTED; sysc_stat->ostate = SYSC_CFGA_OSTATE_CONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_OK; } sysc_stat->last_change = gethrestime_sec(); break; case SYSC_EVT_BD_DISABLED: sysc_stat->rstate = SYSC_CFGA_RSTATE_DISCONNECTED; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; sysc_policy_disconnected_condition(softsp, sysc_stat, FALSE, FALSE); cmn_err(CE_NOTE, "disabled %s board in slot %d", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_EVT_BD_FAILED: sysc_stat->rstate = SYSC_CFGA_RSTATE_DISCONNECTED; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_UNUSABLE; sysc_policy_disconnected_condition(softsp, sysc_stat, TRUE, FALSE); cmn_err(CE_WARN, "failed %s board in slot %d", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_EVT_BD_OVERTEMP: case SYSC_EVT_BD_TEMP_OK: sysc_policy_set_condition((void *)softsp, sysc_stat, FALSE); break; case SYSC_EVT_BD_PS_CHANGE: for (list = fhc_bd_first(); list; list = fhc_bd_next(list)) { sysc_stat = &(list->sc); sysc_policy_set_condition((void *)softsp, sysc_stat, TRUE); } break; case SYSC_EVT_BD_INS_FAILED: cmn_err(CE_WARN, "powerdown of board %d failed", sysc_stat->board); break; case SYSC_EVT_BD_INSERTED: sysc_stat->rstate = SYSC_CFGA_RSTATE_DISCONNECTED; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysctrl_post_config_change(softsp); sysc_policy_disconnected_condition(softsp, sysc_stat, FALSE, FALSE); cmn_err(CE_NOTE, "%s board has been inserted into slot %d", fhc_bd_typestr(sysc_stat->type), sysc_stat->board); cmn_err(CE_NOTE, "board %d can be removed", sysc_stat->board); break; case SYSC_EVT_BD_REMOVED: sysc_stat->rstate = SYSC_CFGA_RSTATE_EMPTY; sysc_stat->ostate = SYSC_CFGA_OSTATE_UNCONFIGURED; sysc_stat->condition = SYSC_CFGA_COND_UNKNOWN; /* now it is ok to free the ac pa memory database */ fhc_del_memloc(sysc_stat->board); /* reinitialize sysc_cfga_stat structure */ sysc_stat->type = UNKNOWN_BOARD; sysc_stat->fhc_compid = 0; sysc_stat->ac_compid = 0; (void) bzero(&(sysc_stat->prom_rev), sizeof (sysc_stat->prom_rev)); (void) bzero(&(sysc_stat->bd), sizeof (union bd_un)); sysc_stat->no_detach = sysc_stat->plus_board = 0; sysc_policy_disconnected_condition(softsp, sysc_stat, FALSE, FALSE); cmn_err(CE_NOTE, "board %d has been removed", sysc_stat->board); break; case SYSC_EVT_BD_HP_DISABLED: cmn_err(CE_NOTE, "Hot Plug not supported in this system"); break; case SYSC_EVT_BD_CORE_RESOURCE_DISCONNECT: cmn_err(CE_WARN, "board %d cannot be disconnected because it" " is a core system resource", sysc_stat->board); break; default: ASSERT(FALSE); break; } } /* * signal to POST that the system has been reconfigured and that * the system configuration status information should be invalidated * the next time through POST */ static void sysctrl_post_config_change(struct sysctrl_soft_state *softsp) { /* * We are heading into a configuration change! * Tell post to invalidate its notion of the system configuration. * This is done by clearing the clock registers... */ *softsp->clk_freq1 = 0; *softsp->clk_freq2 &= ~(CLOCK_FREQ_8 | CLOCK_DIV_1 | CLOCK_RANGE | CLOCK_DIV_0); } static int sysc_attach_board(void *arg) { int board = *(int *)arg; return (prom_sunfire_attach_board((uint_t)board)); } static int sysc_bd_connect(int board, sysc_cfga_pkt_t *pkt) { int error = 0; fhc_bd_t *bdp; sysc_dr_handle_t *sh; uint64_t mempa; int del_kstat = 0; ASSERT(fhc_bd_busy(board)); bdp = fhc_bd(board); /* find gap for largest supported simm in advance */ #define MAX_BANK_SIZE_MB (2 * 1024) #define BANKS_PER_BOARD 2 mempa = fhc_find_memloc_gap(BANKS_PER_BOARD * MAX_BANK_SIZE_MB); fhc_bdlist_unlock(); /* TODO: Is mempa vulnerable to re-use here? */ sysctrl_suspend_prepare(); if ((error = sysctrl_suspend(pkt)) == DDI_SUCCESS) { /* ASSERT(jtag not held) */ error = prom_tree_update(sysc_attach_board, &board); if (error) { error = EIO; SYSC_ERR_SET(pkt, SYSC_ERR_PROM); } else { /* attempt to program the memory while frozen */ fhc_program_memory(board, mempa); } sysctrl_resume(pkt); } if (error) { goto done; } /* * Must not delete kstat used by prtdiag until the PROM * has successfully connected to board. */ del_kstat = 1; sh = &bdp->sh[SYSC_DR_HANDLE_FHC]; sh->flags |= SYSC_DR_FHC; sh->errstr = pkt->errbuf; sysc_dr_init(sh); error = sysc_dr_attach(sh, board); if (error) SYSC_ERR_SET(pkt, SYSC_ERR_NDI_ATTACH); sysc_dr_uninit(sh); if (enable_redist) { mutex_enter(&cpu_lock); intr_redist_all_cpus(); mutex_exit(&cpu_lock); } done: if (del_kstat && bdp->ksp) { kstat_delete(bdp->ksp); bdp->ksp = NULL; } (void) fhc_bdlist_lock(-1); return (error); } static int sysc_detach_board(void * arg) { int rt; cpuset_t xcset; struct jt_mstr *jtm; int board = *(int *)arg; (void) fhc_bdlist_lock(-1); #ifdef DEBUG /* it is important to have fhc_bdlist_lock() earlier */ if (sysctrl_enable_regdump) precache_regdump(board); #endif /* DEBUG */ jtm = jtag_master_lock(); CPUSET_ALL(xcset); promsafe_xc_attention(xcset); #ifdef DEBUG if (sysctrl_enable_regdump) boardstat_regdump(); #endif /* DEBUG */ rt = prom_sunfire_detach_board((uint_t)board); #ifdef DEBUG if (sysctrl_enable_regdump) display_regdump(); #endif /* DEBUG */ xc_dismissed(xcset); jtag_master_unlock(jtm); fhc_bdlist_unlock(); return (rt); } static int sysc_bd_disconnect(int board, sysc_cfga_pkt_t *pkt) { int error; fhc_bd_t *bdp; sysc_dr_handle_t *sh; void fhc_bd_ks_alloc(fhc_bd_t *); ASSERT(fhc_bd_busy(board)); ASSERT(!fhc_bd_is_jtag_master(board)); bdp = fhc_bd(board); bdp->flags |= BDF_DETACH; fhc_bdlist_unlock(); sh = &bdp->sh[SYSC_DR_HANDLE_FHC]; sh->errstr = pkt->errbuf; ASSERT(sh->dip_list == NULL); sh->flags |= SYSC_DR_FHC; sysc_dr_init(sh); error = sysc_dr_detach(sh, board); sh->flags &= ~SYSC_DR_REMOVE; sysc_dr_uninit(sh); if (error) { SYSC_ERR_SET(pkt, SYSC_ERR_NDI_DETACH); goto done; } error = prom_tree_update(sysc_detach_board, &board); if (error) { error = EIO; SYSC_ERR_SET(pkt, SYSC_ERR_PROM); goto done; } if (enable_redist) { mutex_enter(&cpu_lock); intr_redist_all_cpus(); mutex_exit(&cpu_lock); } fhc_bd_ks_alloc(bdp); done: (void) fhc_bdlist_lock(-1); return (error); } static int sysc_bd_configure(int board, sysc_cfga_pkt_t *pkt) { int error = 0; fhc_bd_t *bdp; sysc_dr_handle_t *sh; ASSERT(fhc_bd_busy(board)); bdp = fhc_bd(board); fhc_bdlist_unlock(); sh = &bdp->sh[SYSC_DR_HANDLE_DEVS]; sh->errstr = pkt->errbuf; ASSERT(sh->dip_list == NULL); sysc_dr_init(sh); sh->flags |= SYSC_DR_DEVS; error = sysc_dr_attach(sh, board); if (error) { SYSC_ERR_SET(pkt, SYSC_ERR_NDI_ATTACH); sysc_dr_uninit(sh); goto done; } sysc_dr_uninit(sh); if (enable_redist) { mutex_enter(&cpu_lock); intr_redist_all_cpus(); mutex_exit(&cpu_lock); } done: if (bdp->sc.type == CPU_BOARD) { /* * Value of error gets lost for CPU boards. */ mutex_enter(&cpu_lock); error = find_and_setup_cpu(FHC_BOARD2CPU_A(board)); if ((error == 0) || (error == ENODEV)) { int retval_b; retval_b = find_and_setup_cpu(FHC_BOARD2CPU_B(board)); if (retval_b != ENODEV) error = retval_b; } mutex_exit(&cpu_lock); } (void) fhc_bdlist_lock(-1); return (error); } static int sysc_bd_unconfigure(int board, sysc_cfga_pkt_t *pkt) { int error; fhc_bd_t *bdp; sysc_dr_handle_t *sh; ASSERT(fhc_bdlist_locked()); ASSERT(fhc_bd_busy(board)); bdp = fhc_bd(board); if (bdp->sc.type == CPU_BOARD || bdp->sc.type == MEM_BOARD) { struct ac_soft_state *acsp; /* * Check that any memory on board is not in use. * This must be done while the board list lock is held * as memory state can change while fhc_bd_busy() is true * even though a memory operation cannot be started * if fhc_bd_busy() is true. */ if ((acsp = (struct ac_soft_state *)bdp->ac_softsp) != NULL) { if (acsp->bank[Bank0].busy != 0 || acsp->bank[Bank0].ostate == SYSC_CFGA_OSTATE_CONFIGURED) { cmn_err(CE_WARN, "memory bank %d in " "slot %d is in use.", Bank0, board); (void) snprintf(pkt->errbuf, SYSC_OUTPUT_LEN, "memory bank %d in use", Bank0); return (EBUSY); } if (acsp->bank[Bank1].busy != 0 || acsp->bank[Bank1].ostate == SYSC_CFGA_OSTATE_CONFIGURED) { cmn_err(CE_WARN, "memory bank %d in " "slot %d is in use.", Bank1, board); (void) snprintf(pkt->errbuf, SYSC_OUTPUT_LEN, "memory bank %d in use", Bank1); return (EBUSY); } /* * Nothing more to do here. The memory interface * will not make any transitions while * fhc_bd_busy() is true. Once the ostate * becomes unconfigured, the memory becomes * invisible. */ } error = 0; if (bdp->sc.type == CPU_BOARD) { struct cpu *cpua, *cpub; int cpu_flags = 0; if (pkt->cmd_cfga.force) cpu_flags = CPU_FORCED; fhc_bdlist_unlock(); mutex_enter(&cpu_lock); /* protects CPU states */ error = fhc_board_poweroffcpus(board, pkt->errbuf, cpu_flags); cpua = cpu_get(FHC_BOARD2CPU_A(board)); cpub = cpu_get(FHC_BOARD2CPU_B(board)); if ((error == 0) && (cpua != NULL)) { error = cpu_unconfigure(cpua->cpu_id); if (error != 0) { (void) snprintf(pkt->errbuf, SYSC_OUTPUT_LEN, "processor %d unconfigure failed", cpua->cpu_id); } } if ((error == 0) && (cpub != NULL)) { error = cpu_unconfigure(cpub->cpu_id); if (error != 0) { (void) snprintf(pkt->errbuf, SYSC_OUTPUT_LEN, "processor %d unconfigure failed", cpub->cpu_id); } } mutex_exit(&cpu_lock); (void) fhc_bdlist_lock(-1); } if (error != 0) return (error); } fhc_bdlist_unlock(); sh = &bdp->sh[SYSC_DR_HANDLE_DEVS]; sh->errstr = pkt->errbuf; ASSERT(sh->dip_list == NULL); sysc_dr_init(sh); sh->flags |= SYSC_DR_DEVS; error = sysc_dr_detach(sh, board); sh->flags &= ~SYSC_DR_REMOVE; if (error) { SYSC_ERR_SET(pkt, SYSC_ERR_NDI_DETACH); sysc_dr_uninit(sh); goto done; } sysc_dr_uninit(sh); if (enable_redist) { mutex_enter(&cpu_lock); intr_redist_all_cpus(); mutex_exit(&cpu_lock); } done: (void) fhc_bdlist_lock(-1); return (error); } typedef struct sysc_prom { sysc_dr_handle_t *handle; /* DR handle */ int board; /* board id */ dev_info_t **dipp; /* next slot for storing dip */ } sysc_prom_t; /* * Attaching devices on a board. */ static int sysc_dr_attach(sysc_dr_handle_t *handle, int board) { int i; int err; sysc_prom_t arg; devi_branch_t b = {0}; arg.handle = handle; arg.board = board; arg.dipp = handle->dip_list; b.arg = &arg; b.type = DEVI_BRANCH_PROM; b.create.prom_branch_select = sysc_prom_select; b.devi_branch_callback = sysc_branch_callback; handle->error = e_ddi_branch_create(ddi_root_node(), &b, NULL, DEVI_BRANCH_CHILD); if (handle->error) return (handle->error); for (i = 0, arg.dipp = handle->dip_list; i < handle->dip_list_len; i++, arg.dipp++) { err = e_ddi_branch_configure(*arg.dipp, NULL, 0); /* * Error only if we fail for fhc dips */ if (err && (handle->flags & SYSC_DR_FHC)) { handle->error = err; sysc_dr_err_decode(handle, *arg.dipp, TRUE); return (handle->error); } } return (0); } /*ARGSUSED*/ static int sysc_make_list(void *arg, int has_changed) { dev_info_t *rdip; sysc_prom_t *wp = (sysc_prom_t *)arg; pnode_t nid = prom_childnode(prom_rootnode()); if (wp == NULL) return (EINVAL); for (; nid != OBP_NONODE && nid != OBP_BADNODE; nid = prom_nextnode(nid)) { if (sysc_prom_select(nid, arg, 0) != DDI_SUCCESS) continue; if (wp->handle->dip_list_len < SYSC_DR_MAX_NODE) { rdip = wp->handle->dip_list[wp->handle->dip_list_len] = e_ddi_nodeid_to_dip(nid); if (rdip != NULL) { wp->handle->dip_list_len++; /* * Branch rooted at dip already held, so * release hold acquired in e_ddi_nodeid_to_dip */ ddi_release_devi(rdip); ASSERT(e_ddi_branch_held(rdip)); #ifdef DEBUG } else { DPRINTF(SYSC_DEBUG, ("sysc_make_list:" " e_ddi_nodeid_to_dip() failed for" " nodeid: %d\n", nid)); #endif } } else { #ifdef DEBUG cmn_err(CE_WARN, "sysc_make_list: list overflow\n"); #endif return (EFAULT); } } return (0); } /* * Detaching devices on a board. */ static int sysc_dr_detach(sysc_dr_handle_t *handle, int board) { int i; uint_t flags; sysc_prom_t arg; ASSERT(handle->dip_list); ASSERT(handle->dip_list_len == 0); ASSERT(*handle->dip_list == NULL); arg.handle = handle; arg.board = board; arg.dipp = NULL; handle->error = prom_tree_access(sysc_make_list, &arg, NULL); if (handle->error) return (handle->error); flags = DEVI_BRANCH_DESTROY | DEVI_BRANCH_EVENT; for (i = handle->dip_list_len; i > 0; i--) { ASSERT(e_ddi_branch_held(handle->dip_list[i - 1])); handle->error = e_ddi_branch_unconfigure( handle->dip_list[i - 1], NULL, flags); if (handle->error) return (handle->error); } return (0); } static void sysc_dr_init(sysc_dr_handle_t *handle) { handle->dip_list = kmem_zalloc(sizeof (dev_info_t *) * SYSC_DR_MAX_NODE, KM_SLEEP); handle->dip_list_len = 0; } /*ARGSUSED2*/ static int sysc_prom_select(pnode_t pnode, void *arg, uint_t flag) { int bd_id; char name[OBP_MAXDRVNAME]; int len; int *regp; sysc_prom_t *wp = (sysc_prom_t *)arg; bd_id = -1; len = prom_getproplen(pnode, OBP_REG); if (len > 0) { regp = kmem_alloc(len, KM_SLEEP); (void) prom_getprop(pnode, OBP_REG, (caddr_t)regp); /* * Get board id for EXXXX platforms where * 0x1c0 is EXXXX platform specific data to * acquire board id. */ bd_id = (*regp - 0x1c0) >> 2; kmem_free(regp, len); } (void) prom_getprop(pnode, OBP_NAME, (caddr_t)name); if ((bd_id == wp->board) && ((wp->handle->flags & SYSC_DR_FHC) ? (strcmp(name, "fhc") == 0): (strcmp(name, "fhc") != 0)) && (strcmp(name, "central") != 0)) { return (DDI_SUCCESS); } return (DDI_FAILURE); } /*ARGSUSED*/ static void sysc_branch_callback(dev_info_t *rdip, void *arg, uint_t flags) { sysc_prom_t *wp = (sysc_prom_t *)arg; ASSERT(wp->dipp != NULL); ASSERT(*wp->dipp == NULL); ASSERT((wp->handle->flags & SYSC_DR_REMOVE) == 0); if (wp->handle->dip_list_len < SYSC_DR_MAX_NODE) { *wp->dipp = rdip; wp->dipp++; wp->handle->dip_list_len++; } else { cmn_err(CE_PANIC, "sysc_branch_callback: list overflow"); } } /* * Uninitialize devices for the state of a board. */ static void sysc_dr_uninit(sysc_dr_handle_t *handle) { kmem_free(handle->dip_list, sizeof (dev_info_t *) * SYSC_DR_MAX_NODE); handle->dip_list = NULL; handle->dip_list_len = 0; } static void sysc_dr_err_decode(sysc_dr_handle_t *handle, dev_info_t *dip, int attach) { char *p; ASSERT(handle->error != 0); switch (handle->error) { case ENOMEM: break; case EBUSY: (void) ddi_pathname(dip, handle->errstr); break; default: handle->error = EFAULT; if (attach) (void) ddi_pathname(ddi_get_parent(dip), handle->errstr); else (void) ddi_pathname(dip, handle->errstr); if (attach) { p = "/"; (void) strcat(handle->errstr, p); (void) strcat(handle->errstr, ddi_node_name(dip)); } break; } } static char * sysc_rstate_typestr(sysc_cfga_rstate_t rstate, sysc_audit_evt_t event) { char *type_str; switch (rstate) { case SYSC_CFGA_RSTATE_EMPTY: switch (event) { case SYSC_AUDIT_RSTATE_EMPTY: type_str = "emptying"; break; case SYSC_AUDIT_RSTATE_SUCCEEDED: type_str = "emptied"; break; case SYSC_AUDIT_RSTATE_EMPTY_FAILED: type_str = "empty"; break; default: type_str = "empty?"; break; } break; case SYSC_CFGA_RSTATE_DISCONNECTED: switch (event) { case SYSC_AUDIT_RSTATE_DISCONNECT: type_str = "disconnecting"; break; case SYSC_AUDIT_RSTATE_SUCCEEDED: type_str = "disconnected"; break; case SYSC_AUDIT_RSTATE_DISCONNECT_FAILED: type_str = "disconnect"; break; default: type_str = "disconnect?"; break; } break; case SYSC_CFGA_RSTATE_CONNECTED: switch (event) { case SYSC_AUDIT_RSTATE_CONNECT: type_str = "connecting"; break; case SYSC_AUDIT_RSTATE_SUCCEEDED: type_str = "connected"; break; case SYSC_AUDIT_RSTATE_CONNECT_FAILED: type_str = "connect"; break; default: type_str = "connect?"; break; } break; default: type_str = "undefined receptacle state"; break; } return (type_str); } static char * sysc_ostate_typestr(sysc_cfga_ostate_t ostate, sysc_audit_evt_t event) { char *type_str; switch (ostate) { case SYSC_CFGA_OSTATE_UNCONFIGURED: switch (event) { case SYSC_AUDIT_OSTATE_UNCONFIGURE: type_str = "unconfiguring"; break; case SYSC_AUDIT_OSTATE_SUCCEEDED: case SYSC_AUDIT_OSTATE_UNCONFIGURE_FAILED: type_str = "unconfigured"; break; default: type_str = "unconfigure?"; break; } break; case SYSC_CFGA_OSTATE_CONFIGURED: switch (event) { case SYSC_AUDIT_OSTATE_CONFIGURE: type_str = "configuring"; break; case SYSC_AUDIT_OSTATE_SUCCEEDED: case SYSC_AUDIT_OSTATE_CONFIGURE_FAILED: type_str = "configured"; break; default: type_str = "configure?"; break; } break; default: type_str = "undefined occupant state"; break; } return (type_str); } static void sysc_policy_audit_messages(sysc_audit_evt_t event, sysc_cfga_stat_t *sysc_stat) { switch (event) { case SYSC_AUDIT_RSTATE_CONNECT: cmn_err(CE_NOTE, "%s %s board in slot %d", sysc_rstate_typestr(SYSC_CFGA_RSTATE_CONNECTED, event), fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_AUDIT_RSTATE_DISCONNECT: cmn_err(CE_NOTE, "%s %s board in slot %d", sysc_rstate_typestr( SYSC_CFGA_RSTATE_DISCONNECTED, event), fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_AUDIT_RSTATE_SUCCEEDED: cmn_err(CE_NOTE, "%s board in slot %d is %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_rstate_typestr(sysc_stat->rstate, event)); break; case SYSC_AUDIT_RSTATE_CONNECT_FAILED: cmn_err(CE_NOTE, "%s board in slot %d failed to %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_rstate_typestr(SYSC_CFGA_RSTATE_CONNECTED, event)); break; case SYSC_AUDIT_RSTATE_DISCONNECT_FAILED: cmn_err(CE_NOTE, "%s board in slot %d failed to %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_rstate_typestr( SYSC_CFGA_RSTATE_DISCONNECTED, event)); break; case SYSC_AUDIT_OSTATE_CONFIGURE: cmn_err(CE_NOTE, "%s %s board in slot %d", sysc_ostate_typestr(SYSC_CFGA_OSTATE_CONFIGURED, event), fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_AUDIT_OSTATE_UNCONFIGURE: cmn_err(CE_NOTE, "%s %s board in slot %d", sysc_ostate_typestr( SYSC_CFGA_OSTATE_UNCONFIGURED, event), fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; case SYSC_AUDIT_OSTATE_SUCCEEDED: cmn_err(CE_NOTE, "%s board in slot %d is %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_ostate_typestr(sysc_stat->ostate, event)); break; case SYSC_AUDIT_OSTATE_CONFIGURE_FAILED: cmn_err(CE_NOTE, "%s board in slot %d partially %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_ostate_typestr( SYSC_CFGA_OSTATE_CONFIGURED, event)); break; case SYSC_AUDIT_OSTATE_UNCONFIGURE_FAILED: cmn_err(CE_NOTE, "%s board in slot %d partially %s", fhc_bd_typestr(sysc_stat->type), sysc_stat->board, sysc_ostate_typestr( SYSC_CFGA_OSTATE_UNCONFIGURED, event)); break; default: cmn_err(CE_NOTE, "unknown audit of a %s %s board in" " slot %d", sysc_rstate_typestr(sysc_stat->rstate, event), fhc_bd_typestr(sysc_stat->type), sysc_stat->board); break; } } #define MAX_PROP_LEN 33 /* must be > strlen("cpu") */ static int find_and_setup_cpu(int cpuid) { return (prom_tree_access(find_and_setup_cpu_start, &cpuid, NULL)); } /* ARGSUSED */ static int find_and_setup_cpu_start(void *cpuid_arg, int has_changed) { pnode_t nodeid; int upaid; char type[MAX_PROP_LEN]; int cpuid = *(int *)cpuid_arg; nodeid = prom_childnode(prom_rootnode()); while (nodeid != OBP_NONODE) { if (prom_getproplen(nodeid, "device_type") < MAX_PROP_LEN) (void) prom_getprop(nodeid, "device_type", (caddr_t)type); else type[0] = '\0'; (void) prom_getprop(nodeid, "upa-portid", (caddr_t)&upaid); if ((strcmp(type, "cpu") == 0) && (upaid == cpuid)) { return (cpu_configure(cpuid)); } nodeid = prom_nextnode(nodeid); } return (ENODEV); } #define MAX_BOARD_TYPE IO_SBUS_FFB_SOCPLUS_BOARD static char sysc_supp_conn[MAX_BOARD_TYPE + 1]; static int sysc_board_connect_supported(enum board_type type) { if (type > MAX_BOARD_TYPE) return (0); return (sysc_supp_conn[type]); } void sysc_board_connect_supported_init(void) { pnode_t openprom_node; char sup_list[16]; int proplen; int i; char tstr[3 * 5 + 1]; /* Check the firmware for Dynamic Reconfiguration support */ if (prom_test("SUNW,Ultra-Enterprise,rm-brd") != 0) { /* The message was printed in platmod:set_platform_defaults */ enable_dynamic_reconfiguration = 0; } openprom_node = prom_finddevice("/openprom"); if (openprom_node != OBP_BADNODE) { proplen = prom_bounded_getprop(openprom_node, "add-brd-supported-types", sup_list, sizeof (sup_list) - 1); } else { proplen = -1; } if (proplen < 0) { /* * This is an old prom which may cause a fatal reset, * so don't allow any DR operations. * If enable_dynamic_reconfiguration is 0 * we have already printed a similar message. */ if (enable_dynamic_reconfiguration) { cmn_err(CE_WARN, "Firmware does not support" " Dynamic Reconfiguration"); enable_dynamic_reconfiguration = 0; } return; } for (i = 0; i < proplen; i++) { switch (sup_list[i]) { case '0': sysc_supp_conn[CPU_BOARD] = 1; sysc_supp_conn[MEM_BOARD] = 1; break; case '1': sysc_supp_conn[IO_2SBUS_BOARD] = 1; break; case '2': sysc_supp_conn[IO_SBUS_FFB_BOARD] = 1; break; case '3': sysc_supp_conn[IO_PCI_BOARD] = 1; break; case '4': sysc_supp_conn[IO_2SBUS_SOCPLUS_BOARD] = 1; break; case '5': sysc_supp_conn[IO_SBUS_FFB_SOCPLUS_BOARD] = 1; break; default: /* Ignore other characters. */ break; } } if (sysc_supp_conn[CPU_BOARD]) { cmn_err(CE_NOTE, "!Firmware supports Dynamic Reconfiguration" " of CPU/Memory boards."); } else { cmn_err(CE_NOTE, "Firmware does not support Dynamic" " Reconfiguration of CPU/Memory boards."); } tstr[0] = '\0'; if (sysc_supp_conn[IO_2SBUS_BOARD]) (void) strcat(tstr, ", 1"); if (sysc_supp_conn[IO_SBUS_FFB_BOARD]) (void) strcat(tstr, ", 2"); if (sysc_supp_conn[IO_PCI_BOARD]) (void) strcat(tstr, ", 3"); if (sysc_supp_conn[IO_2SBUS_SOCPLUS_BOARD]) (void) strcat(tstr, ", 4"); if (sysc_supp_conn[IO_SBUS_FFB_SOCPLUS_BOARD]) (void) strcat(tstr, ", 5"); if (tstr[0] != '\0') { /* Skip leading ", " using &tstr[2]. */ cmn_err(CE_NOTE, "!Firmware supports Dynamic Reconfiguration" " of I/O board types %s.", &tstr[2]); } else { cmn_err(CE_NOTE, "Firmware does not support Dynamic" " Reconfiguration of I/O boards."); } } #ifdef DEBUG static void precache_regdump(int board) { fhc_bd_t *curr_bdp; int bd_idx; int reg_idx; for (bd_idx = 0; bd_idx < fhc_max_boards(); bd_idx++) { bcopy((void *) reg_tmpl, (void *) ®_dt[bd_idx][0], (sizeof (struct regs_data))*NUM_REG); curr_bdp = fhc_bd(bd_idx); if (curr_bdp->sc.rstate == SYSC_CFGA_RSTATE_CONNECTED) { for (reg_idx = 0; reg_idx < NUM_REG; reg_idx++) { reg_dt[bd_idx][reg_idx].eflag = TRUE; if (bd_idx != board) reg_dt[bd_idx][reg_idx].oflag = TRUE; reg_dt[bd_idx][reg_idx].physaddr += (FHC_BOARD_SPAN*2*bd_idx); reg_dt[bd_idx][reg_idx].pre_dsct = ldphysio(reg_dt[bd_idx][reg_idx].physaddr); } } } } static void boardstat_regdump(void) { int bd_idx; prom_printf("\nBoard status before disconnect.\n"); for (bd_idx = 0; bd_idx < fhc_max_boards(); bd_idx++) { if (reg_dt[bd_idx][0].eflag == 0) { prom_printf("Board #%d is idle.\n", bd_idx); } else { prom_printf("Board #%d is on.\n", bd_idx); } } for (bd_idx = 0; bd_idx < fhc_max_boards(); bd_idx++) { if (reg_dt[bd_idx][0].eflag) { prom_printf("\nRegisters for Board #%d", bd_idx); prom_printf(" (before disconnect).\n"); prom_printf("AC_BCSR FHC_CTRL JTAG IGN SIM" " SISM UIM USM\n"); prom_printf("%08x %08x %08x %04x" " %08x %04x %08x %04x\n", reg_dt[bd_idx][0].pre_dsct, reg_dt[bd_idx][1].pre_dsct, reg_dt[bd_idx][2].pre_dsct, reg_dt[bd_idx][3].pre_dsct, reg_dt[bd_idx][4].pre_dsct, reg_dt[bd_idx][5].pre_dsct, reg_dt[bd_idx][6].pre_dsct, reg_dt[bd_idx][7].pre_dsct); } } } static void display_regdump(void) { int bd_idx; int reg_idx; prom_printf("Board status after disconnect.\n"); for (bd_idx = 0; bd_idx < fhc_max_boards(); bd_idx++) { if (reg_dt[bd_idx][0].oflag == 0) { prom_printf("Board #%d is idle.\n", bd_idx); } else { prom_printf("Board #%d is on.\n", bd_idx); for (reg_idx = 0; reg_idx < NUM_REG; reg_idx++) reg_dt[bd_idx][reg_idx].post_dsct = ldphysio(reg_dt[bd_idx][reg_idx].physaddr); } } for (bd_idx = 0; bd_idx < fhc_max_boards(); bd_idx++) { if (reg_dt[bd_idx][0].eflag) { prom_printf("\nRegisters for Board #%d", bd_idx); prom_printf(" (before and after disconnect).\n"); prom_printf("AC_BCSR FHC_CTRL JTAG IGN SIM" " SISM UIM USM\n"); prom_printf("%08x %08x %08x %04x" " %08x %04x %08x %04x\n", reg_dt[bd_idx][0].pre_dsct, reg_dt[bd_idx][1].pre_dsct, reg_dt[bd_idx][2].pre_dsct, reg_dt[bd_idx][3].pre_dsct, reg_dt[bd_idx][4].pre_dsct, reg_dt[bd_idx][5].pre_dsct, reg_dt[bd_idx][6].pre_dsct, reg_dt[bd_idx][7].pre_dsct); if (reg_dt[bd_idx][0].oflag) { prom_printf("%08x %08x %08x %04x" " %08x %04x %08x %04x\n", reg_dt[bd_idx][0].post_dsct, reg_dt[bd_idx][1].post_dsct, reg_dt[bd_idx][2].post_dsct, reg_dt[bd_idx][3].post_dsct, reg_dt[bd_idx][4].post_dsct, reg_dt[bd_idx][5].post_dsct, reg_dt[bd_idx][6].post_dsct, reg_dt[bd_idx][7].post_dsct); } else { prom_printf("no data (board got" " disconnected)-------------------" "---------------\n"); } } } } #endif /* DEBUG */