/*
* 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 <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/sunndi.h>
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/obpdefs.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/ivintr.h>
#include <sys/autoconf.h>
#include <sys/intreg.h>
#include <sys/proc.h>
#include <sys/machsystm.h>
#include <sys/modctl.h>
#include <sys/callb.h>
#include <sys/file.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/fhc.h>
#include <sys/sysctrl.h>
#include <sys/jtag.h>
#include <sys/ac.h>
#include <sys/simmstat.h>
#include <sys/clock.h>
#include <sys/promif.h>
#include <sys/promimpl.h>
#include <sys/cpr.h>
#include <sys/cpuvar.h>
#include <sys/machcpuvar.h>
#include <sys/x_call.h>
#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 *) &reg_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 */