sysctrl.c revision 193974072f41a843678abf5f61979c748687e66b
/*
* 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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include <sys/sysmacros.h>
#include <sys/autoconf.h>
#include <sys/simmstat.h>
#include <sys/promimpl.h>
#include <sys/machsystm.h>
/* Useful debugging Stuff */
#ifdef DEBUG
int sysc_debug_info = 1;
int sysc_debug_print_level = 0;
#endif
/*
* Function prototypes
*/
void **result);
static void spur_retry(void *);
static void spur_long_timeout(void *);
static void ac_fail_retry(void *);
int plus_load);
static void ps_fail_retry(void *);
static void pps_fanfail_retry(void *);
static void pps_fan_poll(void *);
static void bd_insert_timeout(void *);
static void bd_remove_timeout(void *);
static int psstat_kstat_update(kstat_t *, int);
static void init_remote_console_uart(struct sysctrl_soft_state *);
static void blink_led_timeout(void *);
static void sysctrl_thread_wakeup(void *type);
static void sysctrl_overtemp_poll(void);
static void sysctrl_keyswitch_poll(void);
static void update_key_state(struct sysctrl_soft_state *);
static void sysctrl_abort_seq_handler(char *msg);
static void toggle_board_green_leds(int);
void bd_remove_poll(struct sysctrl_soft_state *);
extern void sysc_board_connect_supported_init(void);
/*
* Configuration data structures
*/
static struct cb_ops sysctrl_cb_ops = {
sysctrl_open, /* open */
sysctrl_close, /* close */
nulldev, /* strategy */
nulldev, /* print */
nulldev, /* dump */
nulldev, /* read */
nulldev, /* write */
sysctrl_ioctl, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
0, /* streamtab */
CB_REV, /* rev */
nodev, /* cb_aread */
nodev /* cb_awrite */
};
static struct dev_ops sysctrl_ops = {
DEVO_REV, /* devo_rev */
0, /* refcnt */
sysctrl_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
sysctrl_attach, /* attach */
sysctrl_detach, /* detach */
nulldev, /* reset */
&sysctrl_cb_ops, /* cb_ops */
(struct bus_ops *)0, /* bus_ops */
nulldev, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
void *sysctrlp; /* sysctrl soft state hook */
/* # of ticks to silence spurious interrupts */
static clock_t spur_timeout_hz;
/* # of ticks to count spurious interrupts to print message */
static clock_t spur_long_timeout_hz;
/* # of ticks between AC failure polling */
static clock_t ac_timeout_hz;
/* # of ticks between Power Supply Failure polling */
static clock_t ps_fail_timeout_hz;
/*
* # of ticks between Peripheral Power Supply failure polling
* (used both for interrupt retry timeout and polling function)
*/
static clock_t pps_fan_timeout_hz;
/* # of ticks delay after board insert interrupt */
static clock_t bd_insert_delay_hz;
/* # of secs to wait before restarting poll if we cannot clear interrupts */
static clock_t bd_insert_retry_hz;
/* # of secs between Board Removal polling */
static clock_t bd_remove_timeout_hz;
/* # of secs between toggle of OS LED */
static clock_t blink_led_timeout_hz;
/* overtemp polling routine timeout delay */
static clock_t overtemp_timeout_hz;
/* key switch polling routine timeout delay */
static clock_t keyswitch_timeout_hz;
/* Specify which system interrupt condition to monitor */
/* Should the overtemp_poll thread be running? */
static int sysctrl_do_overtemp_thread = 1;
/* Should the keyswitch_poll thread be running? */
static int sysctrl_do_keyswitch_thread = 1;
/*
* This timeout ID is for board remove polling routine. It is
* protected by the fhc_bdlist mutex.
* XXX - This will not work for wildfire. A different scheme must be
* used since there will be multiple sysctrl nodes, each with its
* own list of hotplugged boards to scan.
*/
static timeout_id_t bd_remove_to_id = 0;
/*
* If this is set, the system will not shutdown when insufficient power
* condition persists.
*/
int disable_insufficient_power_reboot = 0;
/*
*/
int sysctrl_enable_detach_suspend = 0;
/*
* Set this to reflect the OBP initialized HOTPLUG_DISABLED_PROPERTY and
* during dynamic detection
*/
int sysctrl_hotplug_disabled = FALSE;
/* Indicates whether or not the overtemp thread has been started */
static int sysctrl_overtemp_thread_started = 0;
/* Indicates whether or not the key switch thread has been started */
static int sysctrl_keyswitch_thread_started = 0;
/* *Mutex used to protect the soft state list */
static kmutex_t sslist_mutex;
/* The CV is used to wakeup the overtemp thread when needed. */
static kcondvar_t overtemp_cv;
/* The CV is used to wakeup the key switch thread when needed. */
static kcondvar_t keyswitch_cv;
/* This mutex is used to protect the sysctrl_ddi_branch_init variable */
static kmutex_t sysctrl_branch_mutex;
/*
* This variable is set after all existing branches in the system have
* been discovered and held via e_ddi_branch_hold(). This happens on
* first open() of any sysctrl minor node.
*/
static int sysctrl_ddi_branch_init;
/*
* Linked list of all syctrl soft state structures.
* Used for polling sysctrl state changes, i.e. temperature.
*/
extern struct mod_ops mod_driverops;
&mod_driverops, /* Type of module. This one is a driver */
"Clock Board", /* name of module */
&sysctrl_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, /* rev */
(void *)&modldrv,
};
#ifndef lint
char _depends_on[] = "drv/fhc";
#endif /* lint */
/*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
sizeof (struct sysctrl_soft_state), 1)) != 0)
return (error);
if (error != 0) {
return (error);
}
return (0);
}
int
_fini(void)
{
int error;
return (error);
return (0);
}
int
{
}
/* ARGSUSED */
static int
{
int instance;
if (infocmd == DDI_INFO_DEVT2INSTANCE) {
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
{
struct sysctrl_soft_state *softsp;
int instance;
char *propval;
int proplen;
int slot_num;
int start; /* start index for scan loop */
int limit; /* board number limit for scan loop */
int incr; /* amount to incr each pass thru loop */
void set_clockbrd_info(void);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
/* XXX see sysctrl:DDI_SUSPEND for special h/w treatment */
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
return (DDI_FAILURE);
/* Set the dip in the soft state */
/* Set up the parent dip */
/* First set all of the timeout values */
/*
* Map in the registers sets that OBP hands us. According
* to the sun4u device tree spec., the register sets are as
* follows:
*
* 0 Clock Frequency Registers (contains the bit
* for enabling the remote console reset)
* 1 Misc (has all the registers that we need
* 2 Clock Version Register
*/
"registers", instance);
goto bad0;
}
"registers", instance);
goto bad1;
}
/*
* There is a new register for newer vintage clock board nodes,
* OBP register set 2 in the clock board node.
*
*/
/*
* Fill in the virtual addresses of the registers in the
* sysctrl_soft_state structure. We do not want to calculate
* them on the fly. This way we waste a little memory, but
* avoid bugs down the road.
*/
/*
* Enable the hardware watchdog gate on the clock board if
* map_wellknown has detected that watchdog timer is available
* and user wants it to be enabled.
*/
if (watchdog_available && watchdog_enable)
else
/* Check for inherited faults from the PROM. */
}
/*
* calculate and cache the number of slots on this system
*/
case SYS_16_SLOT:
break;
case SYS_8_SLOT:
break;
case SYS_4_SLOT:
/* check the clk_version register - if the ptr is valid */
} else {
}
break;
case SYS_TESTBED:
default:
break;
}
/* create the fault list kstat */
/*
* Do a priming read on the ADC, and throw away the first value
* read. This is a feature of the ADC hardware. After a power cycle
* it does not contains valid data until a read occurs.
*/
/* Wait 30 usec for ADC hardware to stabilize. */
DELAY(30);
/* shut off all interrupt sources */
#ifdef lint
#endif
/*
* Now register our high interrupt with the system.
*/
goto bad2;
goto bad3;
goto bad4;
(void *)softsp->spur_int_c);
goto bad5;
goto bad6;
/*
* Now register low-level ac fail handler
*/
goto bad7;
goto bad8;
/*
* Now register low-level ps fail handler
*/
goto bad9;
goto bad10;
/*
* Now register low-level pps fan fail handler
*/
goto bad11;
goto bad12;
/*
* Based upon a check for a current share backplane, advise
* that system does not support hot plug
*
*/
}
/*
* If the trigger circuit is busted or the NOT_BRD_PRES line
* is stuck then OBP will publish this property stating that
* hot plug is not available. If this happens we will complain
* to the console and register a system fault. We will also
* not enable the board insert interrupt for this session.
*/
}
/* Prime the board list. */
/*
* Set up a board remove timeout call.
*/
(void) fhc_bdlist_lock(-1);
("attach: start bd_remove_poll()..."));
/*
* Now register low-level board insert handler
*/
goto bad13;
goto bad14;
/*
* Now register led blink handler (interrupt level)
*/
goto bad15;
/* initialize the bit field for all pps fans to assumed good */
/* prime the power supply state machines */
/* kick off the OS led blinker */
/* Now enable selected interrupt sources */
#ifdef lint
#endif
/* Initialize the temperature */
/*
* initialize key switch shadow state
*/
/*
* Now add this soft state structure to the front of the linked list
* of soft state structures.
*/
}
/* Setup the kstats for this device */
/* kick off the PPS fan poll routine */
if (sysctrl_overtemp_thread_started == 0) {
/*
* set up the overtemp condition variable before
* starting the thread.
*/
/*
* start up the overtemp polling thread
*/
}
if (sysctrl_keyswitch_thread_started == 0) {
extern void (*abort_seq_handler)();
/*
* interpose sysctrl's abort sequence handler
*/
/*
* set up the key switch condition variable before
* starting the thread
*/
/*
* start up the key switch polling thread
*/
(void) thread_create(NULL, 0,
}
/*
* perform initialization to allow setting of powerfail-time
*/
else
("sysctrl: Creating devices start:%d, limit:%d, incr:%d\n",
/*
* Create minor node for each system attachment points
*/
char name[30];
DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
"ddi_create_minor_node failed",
goto bad16;
}
}
/*
* Remote console is inherited from POST
*/
} else
return (DDI_SUCCESS);
bad9:
bad8:
bad7:
bad6:
bad5:
bad4:
bad3:
bad2:
0, 0);
bad1:
bad0:
"sysctrl%d: Initialization failure. Some system level events,"
" {AC Fail, Fan Failure, PS Failure} not detected", instance);
return (DDI_FAILURE);
}
struct sysc_hold {
int start;
int limit;
int incr;
int hold;
};
static int
{
/*
* For Sunfire, top nodes on board are always children of root dip
*/
/*
* Skip non-PROM and "central" nodes
*/
if (!ndi_dev_is_prom_node(dip) ||
return (DDI_WALK_PRUNECHILD);
/*
* Extract board # from reg property.
*/
!= DDI_SUCCESS) {
return (DDI_WALK_PRUNECHILD);
}
if (i == slot)
break;
}
(void *)dip));
return (DDI_WALK_PRUNECHILD);
}
} else {
}
return (DDI_WALK_PRUNECHILD);
}
/* ARGSUSED */
static int
{
#ifdef SYSCTRL_SUPPORTS_DETACH
struct sysctrl_soft_state *softsp;
#endif /* SYSCTRL_SUPPORTS_DETACH */
if (sysctrl_enable_detach_suspend == FALSE)
return (DDI_FAILURE);
switch (cmd) {
case DDI_SUSPEND:
/*
* XXX we don't presently save the state of the remote
* console because it is a constant function of POST.
* XXX we don't deal with the hardware watchdog here
* either. It should be handled in hardclk.
*/
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
#ifdef SYSCTRL_SUPPORTS_DETACH
/*
* XXX If sysctrl ever supports detach, this code should be enabled
* This is only the portion of the detach code dealing with
* the DDI branch routines. Other parts of detach will need
* to be added.
*/
/*
* Walk immediate children of root devinfo node, releasing holds
* on branches acquired in first sysctrl_open().
*/
return (DDI_FAILURE);
}
rdip = ddi_root_node();
return (DDI_SUCCESS);
#endif /* SYSCTRL_SUPPORTS_DETACH */
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
{
int instance;
int slot;
int circ;
struct sysctrl_soft_state *softsp;
/*
* We checked against the instance softstate structure since there
* will only be one instance of sysctrl (clock board) in UEXX00
*
* Since we only create minor devices for existing slots on a
* particular system, we don't need to worry about non-exist slot.
*/
/* Is the instance attached? */
return (ENXIO);
}
/* verify that otyp is appropriate */
return (EINVAL);
}
if (!fhc_bd_valid(slot))
return (ENXIO);
/*
* On first open of a sysctrl minor walk immediate children of the
* devinfo root node and hold all branches of interest.
*/
if (!sysctrl_ddi_branch_init) {
rdip = ddi_root_node();
&arg);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
return (DDI_SUCCESS);
}
/*
* This function will acquire the lock and set the in_transition
* bit for the specified slot. If the slot is being used,
* we return FALSE; else set in_transition and return TRUE.
*/
static int
{
/* mutex lock the structure */
return (FALSE);
}
glist = fhc_bd_clock();
if (slot == -1)
/* change the in_transition bit */
return (FALSE);
} else {
return (TRUE);
}
}
/*
* This function will release the lock and clear the in_transition
* bit for the specified slot.
*/
static void
sysc_exit_transition(int slot)
{
if (slot == -1)
list = fhc_bd_clock();
else
}
static int
{
#ifdef _MULTI_DATAMODEL
sizeof (sysc_cfga_cmd32_t), flag) != 0) {
return (EFAULT);
}
} else
#endif /* _MULTI_DATAMODEL */
sizeof (sysc_cfga_cmd_t), flag) != 0) {
return (EFAULT);
}
return (0);
}
static int
{
#ifdef _MULTI_DATAMODEL
sizeof (sysc_err_t), flag) != 0) {
}
} else
#endif
sizeof (sysc_err_t), flag) != 0) {
}
SYSC_OUTPUT_LEN, flag) != 0))) {
}
return (ret);
}
/* ARGSUSED */
static int
int *rval_p)
{
struct sysctrl_soft_state *softsp;
int instance;
int slot;
int retval = 0;
int i;
"sysctrl_ioctl(%d): NULL softstate ptr!\n",
return (ENXIO);
}
/*
* First switch is to do correct locking and do ddi_copyin()
*/
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
/* mutex lock the whole list */
goto cleanup_exit;
}
/* allocate the memory before acquiring mutex */
KM_SLEEP);
fhc_max_boards(), KM_SLEEP);
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
goto cleanup_exit;
case SYSC_CFGA_CMD_CONNECT:
case SYSC_CFGA_CMD_DISCONNECT:
case SYSC_CFGA_CMD_CONFIGURE:
case SYSC_CFGA_CMD_TEST:
/* ioctls allowed if caller has write permission */
goto cleanup_exit;
}
if (retval != 0)
goto cleanup_exit;
/* grasp lock and set in_transition bit */
goto cleanup_copyout;
}
/* get the status structure for the slot */
break;
/* POSIX definition: return ENOTTY if unsupported command */
default:
goto cleanup_exit;
}
/*
* Second switch is to call the underlayer workhorse.
*/
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
for (i = 0; i < fhc_max_boards(); i++) {
if (fhc_bd_valid(i)) {
if (fhc_bd_is_jtag_master(i))
else
&sc_list[i], sizeof (sysc_cfga_stat_t));
} else {
}
}
sysc_exit_transition(-1);
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
goto cleanup_exit;
case SYSC_CFGA_CMD_CONNECT:
break;
case SYSC_CFGA_CMD_DISCONNECT:
break;
break;
case SYSC_CFGA_CMD_CONFIGURE:
break;
case SYSC_CFGA_CMD_TEST:
break;
break;
} else {
}
(void) fhc_bdlist_lock(-1);
sysc_exit_transition(-1);
break;
default:
goto cleanup_exit;
}
/*
* 3rd switch is to do appropriate copyout and reset locks
*/
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
sizeof (sysc_cfga_stat_t) * fhc_max_boards(),
flag) != 0) {
}
/* cleanup memory */
fhc_max_boards());
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
break;
case SYSC_CFGA_CMD_CONNECT:
case SYSC_CFGA_CMD_DISCONNECT:
case SYSC_CFGA_CMD_CONFIGURE:
case SYSC_CFGA_CMD_TEST:
return (EFAULT);
break;
default:
break;
}
return (retval);
}
/*
* system_high_handler()
* This routine handles system interrupts.
*
* This routine goes through all the interrupt sources and masks
* off the enable bit if interrupting. Because of the special
* nature of the pps fan source bits, we also cache the state
* of the fan bits for that special case.
*
* The rest of the work is done in the low level handlers
*/
static uint_t
{
int serviced = 0;
/* read in the hardware registers */
if (csr & SYS_AC_PWR_FAIL_EN) {
if (status2 & SYS_AC_FAIL) {
/* save the powerfail state in nvram */
/* disable this interrupt source */
csr &= ~SYS_AC_PWR_FAIL_EN;
serviced++;
}
}
if (csr & SYS_PS_FAIL_EN) {
SYS_CLK_50_OK)) ||
/* disable this interrupt source */
csr &= ~SYS_PS_FAIL_EN;
serviced++;
}
}
if (csr & SYS_PPS_FAN_FAIL_EN) {
if (status2 & SYS_RACK_FANFAIL ||
!(status2 & SYS_AC_FAN_OK) ||
!(status2 & SYS_KEYSW_FAN_OK)) {
/*
* we must cache the fan status because it goes
* away when we disable interrupts !?!?!
*/
/* disable this interrupt source */
csr &= ~SYS_PPS_FAN_FAIL_EN;
serviced++;
}
}
if (csr & SYS_SBRD_PRES_EN) {
/* disable this interrupt source */
csr &= ~SYS_SBRD_PRES_EN;
serviced++;
}
}
if (!serviced) {
/*
* if we get here than it is likely that contact bounce
* is messing with us. so, we need to shut this interrupt
* up for a while to let the contacts settle down.
* Then we will re-enable the interrupts that are enabled
* right now. The trick is to disable the appropriate
* interrupts and then to re-enable them correctly, even
* though intervening handlers might have been working.
*/
/* remember all interrupts that could have caused it */
/* and then turn them off */
/* and then bump the counter */
softsp->spur_count++;
/* and kick off the timeout */
}
/* update the real csr */
#ifdef lint
#endif
return (DDI_INTR_CLAIMED);
}
/*
* we've detected a spurious interrupt.
* determine if we should log a message and if we need another timeout
*/
static uint_t
{
/* do we need to complain? */
/* NOTE: this is == because we want one message per long timeout */
char buf[128];
/* print out the candidates known at this time */
/* XXX not perfect because of re-entrant nature but close */
buf[0] = '\0';
/*
* This is a high level mutex, therefore it needs to be
* dropped before calling cmn_err.
*/
" possible sources [%s].",
} else
/* do we need to start the short timeout? */
if (softsp->spur_timeout_id == 0) {
}
/* do we need to start the long timeout? */
if (softsp->spur_long_timeout_id == 0) {
}
return (DDI_INTR_CLAIMED);
}
/*
* spur_retry
*
* this routine simply triggers the interrupt which will re-enable
* the interrupts disabled by the spurious int detection.
*/
static void
spur_retry(void *arg)
{
softsp->spur_timeout_id = 0;
}
/*
* spur_reenable
*
* OK, we've been slient for a while. Go ahead and re-enable the
* interrupts that were enabled at the time of the spurious detection.
*/
static uint_t
{
/* reenable those who were spurious candidates */
#ifdef lint
#endif
/* clear out the saved state */
softsp->saved_en_state = 0;
return (DDI_INTR_CLAIMED);
}
/*
* spur_long_timeout
*
* this routine merely resets the spurious interrupt counter thus ending
* the interval of interest. of course this is done by triggering a
* softint because the counter is protected by an interrupt mutex.
*/
static void
spur_long_timeout(void *arg)
{
softsp->spur_long_timeout_id = 0;
}
/*
* spur_clear_count
*
* simply clear out the spurious interrupt counter.
*
* softint level only
*/
static uint_t
{
softsp->spur_count = 0;
return (DDI_INTR_CLAIMED);
}
/*
* ac_fail_handler
*
* This routine polls the AC power failure bit in the system status2
* register. If we get to this routine, then we sensed an ac fail
* condition. Note the fact and check again in a few.
*
* Called as softint from high interrupt.
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* The timeout from ac_fail_handler() that checks to see if the
* condition persists.
*/
static void
ac_fail_retry(void *arg)
{
} else {
}
}
/*
* The interrupt routine that we use to re-enable the interrupt.
* Called from ddi_trigger_softint() in the ac_fail_retry() when
* the AC is better.
*/
static uint_t
{
#ifdef lint
#endif
return (DDI_INTR_CLAIMED);
}
/*
* ps_fail_int_handler
*
* Handle power supply failure interrupt.
*
* This wrapper is called as softint from hardware interrupt routine.
*/
static uint_t
{
}
/*
* ps_fail_poll_handler
*
* Handle power supply failure interrupt.
*
* This wrapper is called as softint from power supply poll routine.
*/
static uint_t
{
}
/*
* ps_fail_handler
*
* This routine checks all eight of the board power supplies that are
* installed plus the Peripheral power supply and the two DC OK. Since the
* hardware bits are not enough to indicate Power Supply failure
* vs. being turned off via software, the driver must maintain a
* shadow state for the Power Supply status and monitor all changes.
*
* Called as a softint only.
*/
static uint_t
{
int i;
int poll_needed = 0;
/* pre-read the hardware state */
(void) fhc_bdlist_lock(-1);
i++, pstatp++) {
int temp_psok;
int temp_pres;
int is_precharge = FALSE;
int is_fan_assy = FALSE;
/*
* pre-compute the presence and ok bits for this
* power supply from the hardware registers.
* NOTE: 4-slot pps1 is the same as core ps 7...
*/
switch (i) {
/* the core power supplies */
case 0: case 1: case 2: case 3:
case 4: case 5: case 6: case 7:
break;
/* the first peripheral power supply */
case SYS_PPS0_INDEX:
break;
/* shared 3.3v clock power */
case SYS_CLK_33_INDEX:
break;
/* shared 5.0v clock power */
case SYS_CLK_50_INDEX:
break;
/* peripheral 5v */
case SYS_V5_P_INDEX:
!(ps_pres & SYS_NOT_PPS1_PRES));
break;
/* peripheral 12v */
case SYS_V12_P_INDEX:
!(ps_pres & SYS_NOT_PPS1_PRES));
break;
/* aux 5v */
case SYS_V5_AUX_INDEX:
break;
/* peripheral 5v precharge */
case SYS_V5_P_PCH_INDEX:
is_precharge = TRUE;
break;
/* peripheral 12v precharge */
case SYS_V12_P_PCH_INDEX:
is_precharge = TRUE;
break;
/* 3.3v precharge */
case SYS_V3_PCH_INDEX:
is_precharge = TRUE;
break;
/* 5v precharge */
case SYS_V5_PCH_INDEX:
is_precharge = TRUE;
break;
/* peripheral fan assy */
case SYS_P_FAN_INDEX:
!(status1 & SYS_NOT_P_FAN_PRES);
is_fan_assy = TRUE;
break;
}
/* *** Phase 1 -- power supply presence tests *** */
/* do we know the presence status for this power supply? */
} else {
/* has the ps presence state changed? */
} else {
/* a change! are we counting? */
PS_UNKNOWN : PS_OUT;
/*
* Now we know the state has
* changed, so we should log it.
*/
i, temp_pres);
}
}
}
/* *** Phase 2 -- power supply status tests *** */
/* check if the Power Supply is removed or same as before */
} else {
/* OK, a change, do we start the timer? */
case PS_BOOT:
break;
case PS_UNKNOWN:
break;
case PS_OK:
break;
case PS_FAIL:
break;
default:
panic("sysctrl%d: Unknown Power "
}
}
/* has the ticker expired? */
/* we'll skip OK messages during boot */
temp_psok)) {
i, temp_psok);
}
/*
* remote console interface has to be
* reinitialized on the rising edge V5_AUX
* when it is NOT boot. At the boot time an
* an error condition exists if it was not
* enabled before.
*/
if ((i == SYS_V5_AUX_INDEX) &&
(softsp->enable_rcons_atboot)) {
if (temp_psok)
else
/* disable rconsole */
#ifdef lint
#endif
}
/* regardless, update the shadow state */
/* always update board condition */
}
}
/*
* We will need to continue polling for three reasons:
* - a failing power supply is detected and we haven't yet
* determined the power supplies existence.
* - the power supply is just installed and we're waiting
* to give it a change to power up,
* - a failed power supply state is recognized
*
* NOTE: PS_FAIL shadow state is not the same as !temp_psok
* because of the persistence of PS_FAIL->PS_OK.
*/
if (!temp_psok ||
poll_needed++;
}
}
/*
* Now, get the current power state for this instance.
* If the current state is different than what was known, complain.
*/
switch (current_power_state) {
case BELOW_MINIMUM:
"Insufficient power available to system");
}
break;
case MINIMUM:
/* If we came from REDUNDANT, complain */
/* If we came from BELOW_MINIMUM, hurrah! */
}
break;
case REDUNDANT:
/* If we aren't from boot, spread the good news */
}
break;
default:
break;
}
}
/*
* Are we in insufficient powerstate?
* If so, is it time to take action?
*/
"Insufficient power. System Reboot Started...");
fhc_reboot();
}
/*
* If we don't have ps problems that need to be polled for, then
* enable interrupts.
*/
if (!poll_needed) {
#ifdef lint
#endif
}
/*
* Only the polling loop re-triggers the polling loop timeout
*/
if (!fromint) {
}
return (DDI_INTR_CLAIMED);
}
/*
* Compute the current power configuration for this system.
* Disk boards and Clock boards are not counted.
*
* This function must be called with the ps_fail_lock held.
*/
enum power_state
{
int i;
int ok_supply_count = 0;
int load_count = 0;
int minimum_power_count;
int pps_ok;
/*
* Walk down the interesting power supplies and
* count the operational power units
*/
for (i = 0; i < 8; i++) {
/*
* power supply id 7 on a 4 or 5 slot system is PPS1.
* don't include it in the redundant core power calculation.
*/
if (i == 7 &&
continue;
}
/* Note the state of the PPS... */
/*
* Dynamically compute the load count in the system.
* Don't count disk boards or boards in low power state.
*/
load_count++;
}
}
load_count += plus_load;
/*
* If we are 8 slot and we have 7 or 8 boards, then the PPS
* can count as a power supply...
*/
/*
* This is to cover the corner case of a UE3500 having 5
* boards installed and still giving it N+1 power status.
*/
/*
* Determine our power situation. This is a simple step
* function right now:
*
* minimum power count = min(7, floor((board count + 1) / 2))
*/
if (minimum_power_count > 7)
minimum_power_count = 7;
return (REDUNDANT);
else if (ok_supply_count == minimum_power_count)
return (MINIMUM);
else
return (BELOW_MINIMUM);
}
/*
* log the change of power supply presence
*/
static void
{
switch (index) {
/* the core power supplies (except for 7) */
case 0: case 1: case 2: case 3:
case 4: case 5: case 6:
trans);
if (!present) {
}
break;
/* power supply 7 / pps 1 */
case 7:
trans);
if (!present) {
}
} else {
if (!present) {
}
}
break;
/* the peripheral power supply 0 */
case SYS_PPS0_INDEX:
if (!present) {
}
break;
/* the peripheral rack fan assy */
case SYS_P_FAN_INDEX:
if (!present) {
}
break;
/* we don't mention a change of presence state for any other power */
}
}
/*
* log the change of power supply status
*/
static void
{
switch (index) {
/* the core power supplies (except 7) */
case 0: case 1: case 2: case 3:
case 4: case 5: case 6:
if (ps_ok) {
} else {
}
break;
/* power supply 7 / pps 1 */
case 7:
if (ps_ok) {
} else {
}
} else {
index, s);
if (ps_ok) {
} else {
}
}
break;
/* the peripheral power supply */
case SYS_PPS0_INDEX:
if (ps_ok) {
} else {
}
break;
/* shared 3.3v clock power */
case SYS_CLK_33_INDEX:
if (ps_ok) {
} else {
}
break;
/* shared 5.0v clock power */
case SYS_CLK_50_INDEX:
if (ps_ok) {
} else {
}
break;
/* peripheral 5v */
case SYS_V5_P_INDEX:
if (ps_ok) {
} else {
}
break;
/* peripheral 12v */
case SYS_V12_P_INDEX:
if (ps_ok) {
} else {
}
break;
/* aux 5v */
case SYS_V5_AUX_INDEX:
if (ps_ok) {
} else {
}
break;
/* peripheral 5v precharge */
case SYS_V5_P_PCH_INDEX:
if (ps_ok) {
} else {
}
break;
/* peripheral 12v precharge */
case SYS_V12_P_PCH_INDEX:
if (ps_ok) {
} else {
}
break;
/* 3.3v precharge */
case SYS_V3_PCH_INDEX:
if (ps_ok) {
} else {
}
break;
/* 5v precharge */
case SYS_V5_PCH_INDEX:
if (ps_ok) {
} else {
}
break;
/* peripheral power supply fans */
case SYS_P_FAN_INDEX:
if (ps_ok) {
} else {
}
break;
}
}
/*
* The timeout from ps_fail_handler() that simply re-triggers a check
* of the ps condition.
*/
static void
ps_fail_retry(void *arg)
{
}
/*
* pps_fanfail_handler
*
* This routine is called from the high level handler.
*/
static uint_t
{
/* always check again in a bit by re-enabling the fan interrupt */
return (DDI_INTR_CLAIMED);
}
/*
* After a bit of waiting, we simply re-enable the interrupt to
* see if we get another one. The softintr triggered routine does
* the dirty work for us since it runs in the interrupt context.
*/
static void
pps_fanfail_retry(void *arg)
{
}
/*
* The other half of the retry handler run from the interrupt context
*/
static uint_t
{
/*
* re-initialize the bit field for all pps fans to assumed good.
* If the fans are still bad, we're going to get an immediate system
* interrupt which will put the correct state back anyway.
*
* NOTE: the polling routines that use this state understand the
* pulse resulting from above...
*/
#ifdef lint
#endif
return (DDI_INTR_CLAIMED);
}
/*
*
* Poll the hardware shadow state to determine the pps fan status.
* The shadow state is maintained by the system_high handler and its
* associated pps_* functions (above).
*
* There is a short time interval where the shadow state is pulsed to
* the OK state even when the fans are bad. However, this polling
* routine has some built in hysteresis to filter out those _normal_
* events.
*/
static void
pps_fan_poll(void *arg)
{
int i;
for (i = 0; i < SYS_PPS_FAN_COUNT; i++) {
/* determine fan status */
switch (i) {
case RACK:
break;
case AC:
/*
* Don't bother polling the AC fan on 4 and 5 slot
* systems.
* Rather, it is handled by the power supply loop.
*/
break;
case KEYSW:
/*
* This signal is not usable if aux5v is missing
* so we will synthesize a failed fan when aux5v
* fails or when pps0 is out.
* The 4 and 5 slot systems behave the same.
*/
PS_OK)) ||
break;
}
/* is the fan bad? */
if (fanfail) {
/* is this condition different than we know? */
if (softsp->pps_fan_state_count[i] == 0) {
/* log the change to failed */
}
/* always restart the fan OK counter */
} else {
/* do we currently know the fan is bad? */
if (softsp->pps_fan_state_count[i]) {
/* yes, but has it been stable? */
if (--softsp->pps_fan_state_count[i] == 0) {
/* log the change to OK */
}
}
}
}
/* always check again in a bit by re-enabling the fan interrupt */
}
/*
* pps_fan_state_change()
*
* Log the changed fan condition and update the external status.
*/
static void
{
char *fan_type;
switch (index) {
case RACK:
/* 4 and 5 slot systems behave the same */
"Disk Drive" : "Rack Exhaust";
if (fan_ok) {
} else {
}
break;
case AC:
fan_type = "AC Box";
if (fan_ok) {
} else {
}
break;
case KEYSW:
fan_type = "Keyswitch";
if (fan_ok) {
} else {
}
break;
default:
fan_type = "[invalid fan id]";
break;
}
/* now log the state change */
}
static uint_t
{
return (DDI_INTR_CLAIMED);
}
void
{
if (!bd_remove_to_id) {
} else {
("bd_remove_poll ignoring start request"));
}
}
/*
* bd_insert_timeout()
*
* This routine handles the board insert interrupt. It is called from a
* timeout so that it does not run at interrupt level. The main job
* of this routine is to find hotplugged boards and de-assert the
* board insert interrupt coming from the board. For hotplug phase I,
* the routine also powers down the board.
* JTAG scan is used to find boards which have been inserted.
* All other control of the boards is also done by JTAG scan.
*/
static void
bd_insert_timeout(void *arg)
{
int found;
if (sysctrl_hotplug_disabled) {
} else {
/*
* Lock the board list mutex. Keep it locked until all work
* is done.
*/
(void) fhc_bdlist_lock(-1);
found = fhc_bd_insert_scan();
if (found) {
("bd_insert_timeout starting bd_remove_poll()"));
}
}
/*
* Enable interrupts.
*/
}
static void
bd_remove_timeout(void *arg)
{
int keep_polling;
/*
* Lock the board list mutex. Keep it locked until all work
* is done.
*/
(void) fhc_bdlist_lock(-1);
bd_remove_to_id = 0; /* delete our timeout ID */
if (keep_polling) {
} else {
}
}
static uint_t
{
/* has the condition been removed? */
/* XXX add deglitch state machine here */
/* check again in a few */
} else {
/* Turn on the enable bit for this interrupt */
/* flush the hardware store buffer */
#ifdef lint
#endif
}
return (DDI_INTR_CLAIMED);
}
/*
* blink LED handler.
*
* The actual bit manipulation needs to occur at interrupt level
* because we need access to the CSR with its CSR mutex
*/
static uint_t
{
/*
* XXX - The lock for the sys_led is not held here. If more
* complicated tasks are done with the System LED, then
* locking should be done here.
*/
/* read the hardware register. */
/* Only turn on the OS System LED bit if the softsp state is on. */
tmp_reg |= SYS_LED_RIGHT;
} else {
tmp_reg &= ~SYS_LED_RIGHT;
}
/* Turn on the yellow LED if system fault status is set. */
tmp_reg |= SYS_LED_MID;
} else {
tmp_reg &= ~SYS_LED_MID;
}
/* write to the hardware register */
/* flush the hardware store buffer */
#ifdef lint
#endif
return (DDI_INTR_CLAIMED);
}
/*
* simply re-trigger the interrupt handler on led timeout
*/
static void
blink_led_timeout(void *arg)
{
int led_state;
/*
* Process the system fault list here. This is where the driver
* must decide what yellow LEDs to turn on if any. The fault
* list is walked and each fhc_list entry is updated with it's
* yellow LED status. This info is used later by the routine
* toggle_board_green_leds().
*
* The variable system_fault is non-zero if any non-
* suppressed faults are found in the system.
*/
/* blink the system board OS LED */
}
void
{
(void) fhc_bdlist_lock(-1);
continue;
value |= FHC_LED_RIGHT;
value |= FHC_LED_MID;
else
value &= ~FHC_LED_MID;
}
}
/*
* timestamp an AC power failure in nvram
*/
static void
{
char buf[80];
int len = 0;
if (softsp->options_nodeid) {
}
if (len <= 0) {
}
}
void
{
sizeof (struct sysctrl_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
} else {
struct sysctrl_kstat *sysksp;
/* now init the named kstats */
}
} else {
}
} else {
}
} else {
}
}
static int
{
struct sysctrl_kstat *sysksp;
struct sysctrl_soft_state *softsp;
/* this is a read-only kstat. Exit on a write */
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
/*
* copy the current state of the hardware into the
* kstat structure.
*/
/*
* non-existence of the clock version register returns the
* value 0xff when the hardware register location is read
*/
else
}
return (0);
}
static int
{
struct sysctrl_soft_state *softsp;
int ps;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
}
}
return (0);
}
static void
sysctrl_thread_wakeup(void *arg)
{
/*
* grab mutex to guarantee that our wakeup call
* arrives after we go to sleep -- so we can't sleep forever.
*/
switch (type) {
case OVERTEMP_POLL:
break;
case KEYSWITCH_POLL:
break;
default:
break;
}
}
static void
sysctrl_overtemp_poll(void)
{
struct sysctrl_soft_state *list;
/* The overtemp data structures are protected by a mutex. */
while (sysctrl_do_overtemp_thread) {
}
}
/* now have this thread sleep for a while */
}
thread_exit();
/* NOTREACHED */
}
static void
sysctrl_keyswitch_poll(void)
{
struct sysctrl_soft_state *list;
/* The keyswitch data strcutures are protected by a mutex. */
while (sysctrl_do_keyswitch_thread) {
}
/* now have this thread sleep for a while */
}
thread_exit();
/* NOTREACHED */
}
/*
* check the key switch position for state changes
*/
static void
{
enum keyswitch_state key;
/*
* snapshot current hardware key position
*/
else
key = KEY_SECURE;
/*
* check for state transition
*/
/*
* handle state transition
*/
switch (list->key_shadow) {
case KEY_BOOT:
break;
case KEY_SECURE:
case KEY_NOT_SECURE:
" to the %s position",
break;
default:
"?sysctrl%d: Key switch is in an unknown position,"
"treated as being in the %s position\n",
"secure" : "not-secure");
break;
}
}
}
/*
* consider key switch position when handling an abort sequence
*/
static void
sysctrl_abort_seq_handler(char *msg)
{
struct sysctrl_soft_state *list;
/*
* if any of the key switch positions are secure,
*/
buf[0] = (char)0;
if (secure++)
/*
* XXX: later, replace instance number with nodeid
*/
}
}
if (secure) {
"!sysctrl(%s): ignoring debug enter sequence\n", buf);
} else {
}
}
#define TABLE_END 0xFF
struct uart_cmd {
};
/*
* Time constant defined by this formula:
* ((4915200/32)/(baud) -2)
*/
struct uart_cmd uart_table[] = {
{ 0x09, 0xc0 }, /* Force hardware reset */
{ 0x09, 0x02 }, /* No vector returned on interrupt */
{ 0x0b, 0x55 }, /* Rx Clock = Tx Clock = BR generator = ~TRxC OUT */
{ 0x0c, 0x0e }, /* Time Constant = 0x000e for 9600 baud */
{ 0x0d, 0x00 }, /* High byte of time constant */
{ 0x0e, 0x02 }, /* BR generator comes from Z-SCC's PCLK input */
{ 0x0e, 0x03 }, /* BR comes from PCLK, BR generator is enabled */
{ 0x00, 0x30 }, /* Error reset */
{ 0x00, 0x30 }, /* Error reset */
{ 0x00, 0x10 }, /* external status reset */
{ TABLE_END, 0x0 }
};
static void
{
int i = 0;
/*
* Serial chip expects software to write to the control
* register first with the desired register number. Then
* write to the control register with the desired data.
* the serial port chip.
*/
i++;
}
}
/*
* return the slot information of the system
*
* function take a sysctrl_soft_state, so it's ready for sunfire+
* change which requires 2 registers to decide the system type.
*/
static void
{
switch (nslots) {
case 8:
*start = 0;
*limit = 8;
*incr = 1;
break;
case 5:
*start = 1;
*limit = 10;
*incr = 2;
break;
case 4:
*start = 1;
*limit = 8;
*incr = 2;
break;
case 0:
case 16:
default:
*start = 0;
*limit = 16;
*incr = 1;
break;
}
}
/*
* reinitialize the Remote Console on the clock board
*
* with V5_AUX power outage the Remote Console ends up in
* unknown state and has to be reinitilized if it was enabled
* initially.
*/
static void
{
/*
* There is no OBP register set for the remote console UART,
* so offset from the last register set, the misc register
* set, in order to map in the remote console UART.
*/
"remote console.");
return;
}
/* Disable the remote console reset control bits. */
/* flush the hardware buffers */
/*
* Program the UART to watch ttya console.
*/
/* Now enable the remote console reset control bits. */
/* flush the hardware buffers */
/* print some info for user to watch */
#ifdef lint
#endif
}