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
* 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 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/sunndi.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/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/sunndi.h>
#include <sys/machsystm.h>
/* Useful debugging Stuff */
#ifdef DEBUG
int sysc_debug_info = 1;
int sysc_debug_print_level = 0;
#endif
/*
* Function prototypes
*/
static int sysctrl_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
void **result);
static int sysctrl_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int sysctrl_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
static int sysctrl_open(dev_t *, int, int, cred_t *);
static int sysctrl_close(dev_t, int, int, cred_t *);
static int sysctrl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
static uint_t system_high_handler(caddr_t arg);
static uint_t spur_delay(caddr_t arg);
static void spur_retry(void *);
static uint_t spur_reenable(caddr_t arg);
static void spur_long_timeout(void *);
static uint_t spur_clear_count(caddr_t arg);
static uint_t ac_fail_handler(caddr_t arg);
static void ac_fail_retry(void *);
static uint_t ac_fail_reenable(caddr_t arg);
static uint_t ps_fail_int_handler(caddr_t arg);
static uint_t ps_fail_poll_handler(caddr_t arg);
static uint_t ps_fail_handler(struct sysctrl_soft_state *softsp, int fromint);
enum power_state compute_power_state(struct sysctrl_soft_state *softsp,
int plus_load);
static void ps_log_state_change(struct sysctrl_soft_state *softsp,
int index, int present);
static void ps_log_pres_change(struct sysctrl_soft_state *softsp,
int index, int present);
static void ps_fail_retry(void *);
static uint_t pps_fanfail_handler(caddr_t arg);
static void pps_fanfail_retry(void *);
static uint_t pps_fanfail_reenable(caddr_t arg);
static void pps_fan_poll(void *);
static void pps_fan_state_change(struct sysctrl_soft_state *softsp,
int index, int fan_ok);
static uint_t bd_insert_handler(caddr_t arg);
static void bd_insert_timeout(void *);
static void bd_remove_timeout(void *);
static uint_t bd_insert_normal(caddr_t arg);
static void sysctrl_add_kstats(struct sysctrl_soft_state *softsp);
static int sysctrl_kstat_update(kstat_t *ksp, int rw);
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 uint_t blink_led_handler(caddr_t arg);
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 nvram_update_powerfail(struct sysctrl_soft_state *softsp);
static void toggle_board_green_leds(int);
void bd_remove_poll(struct sysctrl_soft_state *);
static void sysc_slot_info(int nslots, int *start, int *limit, int *incr);
extern void sysc_board_connect_supported_init(void);
static void rcons_reinit(struct sysctrl_soft_state *softsp);
/*
* 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 */
D_MP|D_NEW, /* Driver compatibility flag */
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 */
int enable_sys_interrupt = SYS_AC_PWR_FAIL_EN | SYS_PPS_FAN_FAIL_EN |
SYS_PS_FAIL_EN | SYS_SBRD_PRES_EN;
/* 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;
/*
* Set this to enable suspend/resume
*/
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.
*/
struct sysctrl_soft_state *sys_list = NULL;
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&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,
NULL
};
#ifndef lint
char _depends_on[] = "drv/fhc";
#endif /* lint */
/*
* These are the module initialization routines.
*/
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&sysctrlp,
sizeof (struct sysctrl_soft_state), 1)) != 0)
return (error);
error = mod_install(&modlinkage);
if (error != 0) {
ddi_soft_state_fini(&sysctrlp);
return (error);
}
mutex_init(&sysctrl_branch_mutex, NULL, MUTEX_DRIVER, NULL);
return (0);
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) != 0)
return (error);
ddi_soft_state_fini(&sysctrlp);
mutex_destroy(&sysctrl_branch_mutex);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/* ARGSUSED */
static int
sysctrl_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
dev_t dev;
int instance;
if (infocmd == DDI_INFO_DEVT2INSTANCE) {
dev = (dev_t)arg;
instance = GETINSTANCE(dev);
*result = (void *)(uintptr_t)instance;
return (DDI_SUCCESS);
}
return (DDI_FAILURE);
}
static int
sysctrl_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
struct sysctrl_soft_state *softsp;
int instance;
uchar_t tmp_reg;
dev_info_t *dip;
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);
}
instance = ddi_get_instance(devi);
if (ddi_soft_state_zalloc(sysctrlp, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
softsp = GETSOFTC(instance);
/* Set the dip in the soft state */
softsp->dip = devi;
/* Set up the parent dip */
softsp->pdip = ddi_get_parent(softsp->dip);
DPRINTF(SYSCTRL_ATTACH_DEBUG, ("sysctrl: devi= 0x%p\n, softsp=0x%p\n",
devi, softsp));
/* First set all of the timeout values */
spur_timeout_hz = drv_usectohz(SPUR_TIMEOUT_USEC);
spur_long_timeout_hz = drv_usectohz(SPUR_LONG_TIMEOUT_USEC);
ac_timeout_hz = drv_usectohz(AC_TIMEOUT_USEC);
ps_fail_timeout_hz = drv_usectohz(PS_FAIL_TIMEOUT_USEC);
pps_fan_timeout_hz = drv_usectohz(PPS_FAN_TIMEOUT_USEC);
bd_insert_delay_hz = drv_usectohz(BRD_INSERT_DELAY_USEC);
bd_insert_retry_hz = drv_usectohz(BRD_INSERT_RETRY_USEC);
bd_remove_timeout_hz = drv_usectohz(BRD_REMOVE_TIMEOUT_USEC);
blink_led_timeout_hz = drv_usectohz(BLINK_LED_TIMEOUT_USEC);
overtemp_timeout_hz = drv_usectohz(OVERTEMP_TIMEOUT_SEC * MICROSEC);
keyswitch_timeout_hz = drv_usectohz(KEYSWITCH_TIMEOUT_USEC);
/*
* 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
*/
if (ddi_map_regs(softsp->dip, 0,
(caddr_t *)&softsp->clk_freq1, 0, 0)) {
cmn_err(CE_WARN, "sysctrl%d: unable to map clock frequency "
"registers", instance);
goto bad0;
}
if (ddi_map_regs(softsp->dip, 1,
(caddr_t *)&softsp->csr, 0, 0)) {
cmn_err(CE_WARN, "sysctrl%d: unable to map internal"
"registers", instance);
goto bad1;
}
/*
* There is a new register for newer vintage clock board nodes,
* OBP register set 2 in the clock board node.
*
*/
(void) ddi_map_regs(softsp->dip, 2, (caddr_t *)&softsp->clk_ver, 0, 0);
/*
* 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.
*/
softsp->clk_freq2 = (uchar_t *)((caddr_t)softsp->clk_freq1 +
SYS_OFF_CLK_FREQ2);
softsp->status1 = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_STAT1);
softsp->status2 = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_STAT2);
softsp->ps_stat = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_PSSTAT);
softsp->ps_pres = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_PSPRES);
softsp->pppsr = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_PPPSR);
softsp->temp_reg = (uchar_t *)((caddr_t)softsp->csr +
SYS_OFF_TEMP);
set_clockbrd_info();
/*
* 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)
*(softsp->clk_freq2) |= TOD_RESET_EN;
else
*(softsp->clk_freq2) &= ~TOD_RESET_EN;
/* Check for inherited faults from the PROM. */
if (*softsp->csr & SYS_LED_MID) {
reg_fault(0, FT_PROM, FT_SYSTEM);
}
/*
* calculate and cache the number of slots on this system
*/
switch (SYS_TYPE(*softsp->status1)) {
case SYS_16_SLOT:
softsp->nslots = 16;
break;
case SYS_8_SLOT:
softsp->nslots = 8;
break;
case SYS_4_SLOT:
/* check the clk_version register - if the ptr is valid */
if ((softsp->clk_ver != NULL) &&
(SYS_TYPE2(*softsp->clk_ver) == SYS_PLUS_SYSTEM)) {
softsp->nslots = 5;
} else {
softsp->nslots = 4;
}
break;
case SYS_TESTBED:
default:
softsp->nslots = 0;
break;
}
/* create the fault list kstat */
create_ft_kstats(instance);
/*
* 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.
*/
tmp_reg = *(softsp->temp_reg);
/* Wait 30 usec for ADC hardware to stabilize. */
DELAY(30);
/* shut off all interrupt sources */
*(softsp->csr) &= ~(SYS_PPS_FAN_FAIL_EN | SYS_PS_FAIL_EN |
SYS_AC_PWR_FAIL_EN | SYS_SBRD_PRES_EN);
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
/*
* Now register our high interrupt with the system.
*/
if (ddi_add_intr(devi, 0, &softsp->iblock,
&softsp->idevice, (uint_t (*)(caddr_t))nulldev, NULL) !=
DDI_SUCCESS)
goto bad2;
mutex_init(&softsp->csr_mutex, NULL, MUTEX_DRIVER,
(void *)softsp->iblock);
ddi_remove_intr(devi, 0, softsp->iblock);
if (ddi_add_intr(devi, 0, &softsp->iblock,
&softsp->idevice, system_high_handler, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad3;
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_id,
&softsp->spur_int_c, NULL, spur_delay, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad4;
mutex_init(&softsp->spur_int_lock, NULL, MUTEX_DRIVER,
(void *)softsp->spur_int_c);
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_high_id,
NULL, NULL, spur_reenable, (caddr_t)softsp) != DDI_SUCCESS)
goto bad5;
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_long_to_id,
NULL, NULL, spur_clear_count, (caddr_t)softsp) != DDI_SUCCESS)
goto bad6;
/*
* Now register low-level ac fail handler
*/
if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &softsp->ac_fail_id,
NULL, NULL, ac_fail_handler, (caddr_t)softsp) != DDI_SUCCESS)
goto bad7;
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->ac_fail_high_id,
NULL, NULL, ac_fail_reenable, (caddr_t)softsp) != DDI_SUCCESS)
goto bad8;
/*
* Now register low-level ps fail handler
*/
if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &softsp->ps_fail_int_id,
&softsp->ps_fail_c, NULL, ps_fail_int_handler, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad9;
mutex_init(&softsp->ps_fail_lock, NULL, MUTEX_DRIVER,
(void *)softsp->ps_fail_c);
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->ps_fail_poll_id,
NULL, NULL, ps_fail_poll_handler, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad10;
/*
* Now register low-level pps fan fail handler
*/
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->pps_fan_id,
NULL, NULL, pps_fanfail_handler, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad11;
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->pps_fan_high_id,
NULL, NULL, pps_fanfail_reenable, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad12;
/*
* Based upon a check for a current share backplane, advise
* that system does not support hot plug
*
*/
if ((*(softsp->pppsr) & SYS_NOT_CURRENT_S) != 0) {
cmn_err(CE_NOTE, "Hot Plug not supported in this system");
sysctrl_hotplug_disabled = TRUE;
}
/*
* 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.
*/
if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC,
DDI_PROP_DONTPASS, HOTPLUG_DISABLED_PROPERTY,
(caddr_t)&propval, &proplen) == DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "Hot Plug Unavailable [%s]", propval);
reg_fault(0, FT_HOT_PLUG, FT_SYSTEM);
sysctrl_hotplug_disabled = TRUE;
enable_sys_interrupt &= ~SYS_SBRD_PRES_EN;
kmem_free(propval, proplen);
}
sysc_board_connect_supported_init();
fhc_bd_sc_register(sysc_policy_update, softsp);
sysc_slot_info(softsp->nslots, &start, &limit, &incr);
/* Prime the board list. */
fhc_bdlist_prime(start, limit, incr);
/*
* Set up a board remove timeout call.
*/
(void) fhc_bdlist_lock(-1);
DPRINTF(SYSCTRL_ATTACH_DEBUG,
("attach: start bd_remove_poll()..."));
bd_remove_poll(softsp);
fhc_bdlist_unlock();
/*
* Now register low-level board insert handler
*/
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->sbrd_pres_id,
NULL, NULL, bd_insert_handler, (caddr_t)softsp) != DDI_SUCCESS)
goto bad13;
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->sbrd_gone_id,
NULL, NULL, bd_insert_normal, (caddr_t)softsp) != DDI_SUCCESS)
goto bad14;
/*
* Now register led blink handler (interrupt level)
*/
if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->blink_led_id,
&softsp->sys_led_c, NULL, blink_led_handler, (caddr_t)softsp) !=
DDI_SUCCESS)
goto bad15;
mutex_init(&softsp->sys_led_lock, NULL, MUTEX_DRIVER,
(void *)softsp->sys_led_c);
/* initialize the bit field for all pps fans to assumed good */
softsp->pps_fan_saved = softsp->pps_fan_external_state =
SYS_AC_FAN_OK | SYS_KEYSW_FAN_OK;
/* prime the power supply state machines */
if (enable_sys_interrupt & SYS_PS_FAIL_EN)
ddi_trigger_softintr(softsp->ps_fail_poll_id);
/* kick off the OS led blinker */
softsp->sys_led = FALSE;
ddi_trigger_softintr(softsp->blink_led_id);
/* Now enable selected interrupt sources */
mutex_enter(&softsp->csr_mutex);
*(softsp->csr) |= enable_sys_interrupt &
(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
/* Initialize the temperature */
init_temp_arrays(&softsp->tempstat);
/*
* initialize key switch shadow state
*/
softsp->key_shadow = KEY_BOOT;
/*
* Now add this soft state structure to the front of the linked list
* of soft state structures.
*/
if (sys_list == (struct sysctrl_soft_state *)NULL) {
mutex_init(&sslist_mutex, NULL, MUTEX_DEFAULT, NULL);
}
mutex_enter(&sslist_mutex);
softsp->next = sys_list;
sys_list = softsp;
mutex_exit(&sslist_mutex);
/* Setup the kstats for this device */
sysctrl_add_kstats(softsp);
/* kick off the PPS fan poll routine */
pps_fan_poll(softsp);
if (sysctrl_overtemp_thread_started == 0) {
/*
* set up the overtemp condition variable before
* starting the thread.
*/
cv_init(&overtemp_cv, NULL, CV_DRIVER, NULL);
/*
* start up the overtemp polling thread
*/
(void) thread_create(NULL, 0, (void (*)())sysctrl_overtemp_poll,
NULL, 0, &p0, TS_RUN, minclsyspri);
sysctrl_overtemp_thread_started++;
}
if (sysctrl_keyswitch_thread_started == 0) {
extern void (*abort_seq_handler)();
/*
* interpose sysctrl's abort sequence handler
*/
abort_seq_handler = sysctrl_abort_seq_handler;
/*
* set up the key switch condition variable before
* starting the thread
*/
cv_init(&keyswitch_cv, NULL, CV_DRIVER, NULL);
/*
* start up the key switch polling thread
*/
(void) thread_create(NULL, 0,
(void (*)())sysctrl_keyswitch_poll, NULL, 0, &p0,
TS_RUN, minclsyspri);
sysctrl_keyswitch_thread_started++;
}
/*
* perform initialization to allow setting of powerfail-time
*/
if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL)
softsp->options_nodeid = (pnode_t)NULL;
else
softsp->options_nodeid = (pnode_t)ddi_get_nodeid(dip);
DPRINTF(SYSCTRL_ATTACH_DEBUG,
("sysctrl: Creating devices start:%d, limit:%d, incr:%d\n",
start, limit, incr));
/*
* Create minor node for each system attachment points
*/
for (slot_num = start; slot_num < limit; slot_num = slot_num + incr) {
char name[30];
(void) sprintf(name, "slot%d", slot_num);
if (ddi_create_minor_node(devi, name, S_IFCHR,
(PUTINSTANCE(instance) | slot_num),
DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
cmn_err(CE_WARN, "sysctrl%d: \"%s\" "
"ddi_create_minor_node failed",
instance, name);
goto bad16;
}
}
ddi_report_dev(devi);
/*
* Remote console is inherited from POST
*/
if ((*(softsp->clk_freq2) & RCONS_UART_EN) == 0) {
softsp->enable_rcons_atboot = FALSE;
cmn_err(CE_WARN, "Remote console not active");
} else
softsp->enable_rcons_atboot = TRUE;
return (DDI_SUCCESS);
bad16:
cv_destroy(&keyswitch_cv);
cv_destroy(&overtemp_cv);
mutex_destroy(&sslist_mutex);
mutex_destroy(&softsp->sys_led_lock);
ddi_remove_softintr(softsp->blink_led_id);
bad15:
ddi_remove_softintr(softsp->sbrd_gone_id);
bad14:
ddi_remove_softintr(softsp->sbrd_pres_id);
bad13:
ddi_remove_softintr(softsp->pps_fan_high_id);
bad12:
ddi_remove_softintr(softsp->pps_fan_id);
bad11:
ddi_remove_softintr(softsp->ps_fail_poll_id);
bad10:
mutex_destroy(&softsp->ps_fail_lock);
ddi_remove_softintr(softsp->ps_fail_int_id);
bad9:
ddi_remove_softintr(softsp->ac_fail_high_id);
bad8:
ddi_remove_softintr(softsp->ac_fail_id);
bad7:
ddi_remove_softintr(softsp->spur_long_to_id);
bad6:
ddi_remove_softintr(softsp->spur_high_id);
bad5:
mutex_destroy(&softsp->spur_int_lock);
ddi_remove_softintr(softsp->spur_id);
bad4:
ddi_remove_intr(devi, 0, softsp->iblock);
bad3:
mutex_destroy(&softsp->csr_mutex);
bad2:
ddi_unmap_regs(softsp->dip, 1, (caddr_t *)&softsp->csr, 0, 0);
if (softsp->clk_ver != NULL)
ddi_unmap_regs(softsp->dip, 2, (caddr_t *)&softsp->clk_ver,
0, 0);
bad1:
ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->clk_freq1, 0, 0);
bad0:
ddi_soft_state_free(sysctrlp, instance);
ddi_remove_minor_node(dip, NULL);
cmn_err(CE_WARN,
"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
sysctrl_hold_rele_branches(dev_info_t *dip, void *arg)
{
int *rp, len, slot, i;
struct sysc_hold *ap = (struct sysc_hold *)arg;
/*
* For Sunfire, top nodes on board are always children of root dip
*/
ASSERT(ddi_get_parent(dip) == ddi_root_node());
/*
* Skip non-PROM and "central" nodes
*/
if (!ndi_dev_is_prom_node(dip) ||
strcmp(ddi_node_name(dip), "central") == 0)
return (DDI_WALK_PRUNECHILD);
/*
* Extract board # from reg property.
*/
if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&rp, &len)
!= DDI_SUCCESS) {
DPRINTF(SYSC_DEBUG, ("devinfo node %s(%p) has no reg"
" property\n", ddi_node_name(dip), (void *)dip));
return (DDI_WALK_PRUNECHILD);
}
slot = (*rp - 0x1c0) >> 2;
kmem_free(rp, len);
ASSERT(ap->start >= 0 && ap->start < ap->limit);
for (i = ap->start; i < ap->limit; i = i + ap->incr) {
if (i == slot)
break;
}
if (i >= ap->limit) {
DPRINTF(SYSC_DEBUG, ("sysctrl_hold_rele: Invalid board # (%d)"
" for node %s(%p)\n", slot, ddi_node_name(dip),
(void *)dip));
return (DDI_WALK_PRUNECHILD);
}
if (ap->hold) {
ASSERT(!e_ddi_branch_held(dip));
e_ddi_branch_hold(dip);
} else {
ASSERT(e_ddi_branch_held(dip));
e_ddi_branch_rele(dip);
}
return (DDI_WALK_PRUNECHILD);
}
/* ARGSUSED */
static int
sysctrl_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
#ifdef SYSCTRL_SUPPORTS_DETACH
dev_info_t *rdip;
struct sysc_hold arg = {0};
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().
*/
instance = ddi_get_instance(dip);
softsp = GETSOFTC(instance);
if (softsp == NULL) {
cmn_err(CE_WARN, "sysctrl%d device not attached", instance);
return (DDI_FAILURE);
}
sysc_slot_info(softsp->nslots, &arg.start, &arg.limit, &arg.incr);
arg.hold = 0;
rdip = ddi_root_node();
ndi_devi_enter(rdip, &circ);
ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches, &arg);
ndi_devi_exit(rdip, circ);
sysctrl_ddi_branch_init = 0;
return (DDI_SUCCESS);
#endif /* SYSCTRL_SUPPORTS_DETACH */
return (DDI_FAILURE);
}
/* ARGSUSED */
static int
sysctrl_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
int instance;
int slot;
dev_t dev;
int circ;
dev_info_t *rdip;
struct sysc_hold arg = {0};
struct sysctrl_soft_state *softsp;
dev = *devp;
/*
* 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.
*/
instance = GETINSTANCE(dev);
slot = GETSLOT(dev);
/* Is the instance attached? */
if ((softsp = GETSOFTC(instance)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d device not attached", instance);
return (ENXIO);
}
/* verify that otyp is appropriate */
if (otyp != OTYP_CHR) {
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.
*/
mutex_enter(&sysctrl_branch_mutex);
if (!sysctrl_ddi_branch_init) {
sysctrl_ddi_branch_init = 1;
sysc_slot_info(softsp->nslots, &arg.start, &arg.limit,
&arg.incr);
arg.hold = 1;
rdip = ddi_root_node();
ndi_devi_enter(rdip, &circ);
ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches,
&arg);
ndi_devi_exit(rdip, circ);
}
mutex_exit(&sysctrl_branch_mutex);
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
sysctrl_close(dev_t devp, int flag, int otyp, cred_t *credp)
{
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
sysc_enter_transition(int slot)
{
fhc_bd_t *list;
sysc_cfga_stat_t *sysc_stat_lk;
fhc_bd_t *glist;
sysc_cfga_stat_t *sysc_stat_gk;
/* mutex lock the structure */
list = fhc_bdlist_lock(slot);
if ((slot != -1) && (list == NULL)) {
fhc_bdlist_unlock();
return (FALSE);
}
glist = fhc_bd_clock();
if (slot == -1)
list = glist;
/* change the in_transition bit */
sysc_stat_lk = &list->sc;
sysc_stat_gk = &glist->sc;
if ((sysc_stat_lk->in_transition == TRUE) ||
(sysc_stat_gk->in_transition == TRUE)) {
fhc_bdlist_unlock();
return (FALSE);
} else {
sysc_stat_lk->in_transition = TRUE;
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)
{
fhc_bd_t *list;
sysc_cfga_stat_t *sysc_stat_lk;
ASSERT(fhc_bdlist_locked());
if (slot == -1)
list = fhc_bd_clock();
else
list = fhc_bd(slot);
sysc_stat_lk = &list->sc;
ASSERT(sysc_stat_lk->in_transition == TRUE);
sysc_stat_lk->in_transition = FALSE;
fhc_bdlist_unlock();
}
static int
sysc_pkt_init(sysc_cfga_pkt_t *pkt, intptr_t arg, int flag)
{
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
sysc_cfga_cmd32_t sysc_cmd32;
if (ddi_copyin((void *)arg, &sysc_cmd32,
sizeof (sysc_cfga_cmd32_t), flag) != 0) {
return (EFAULT);
}
pkt->cmd_cfga.force = sysc_cmd32.force;
pkt->cmd_cfga.test = sysc_cmd32.test;
pkt->cmd_cfga.arg = sysc_cmd32.arg;
pkt->cmd_cfga.errtype = sysc_cmd32.errtype;
pkt->cmd_cfga.outputstr =
(char *)(uintptr_t)sysc_cmd32.outputstr;
} else
#endif /* _MULTI_DATAMODEL */
if (ddi_copyin((void *)arg, &(pkt->cmd_cfga),
sizeof (sysc_cfga_cmd_t), flag) != 0) {
return (EFAULT);
}
pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP);
return (0);
}
static int
sysc_pkt_fini(sysc_cfga_pkt_t *pkt, intptr_t arg, int flag)
{
int ret = TRUE;
#ifdef _MULTI_DATAMODEL
if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
if (ddi_copyout(&(pkt->cmd_cfga.errtype),
(void *)&(((sysc_cfga_cmd32_t *)arg)->errtype),
sizeof (sysc_err_t), flag) != 0) {
ret = FALSE;
}
} else
#endif
if (ddi_copyout(&(pkt->cmd_cfga.errtype),
(void *)&(((sysc_cfga_cmd_t *)arg)->errtype),
sizeof (sysc_err_t), flag) != 0) {
ret = FALSE;
}
if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) &&
(ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr,
SYSC_OUTPUT_LEN, flag) != 0))) {
ret = FALSE;
}
kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN);
return (ret);
}
/* ARGSUSED */
static int
sysctrl_ioctl(dev_t devt, int cmd, intptr_t arg, int flag, cred_t *cred_p,
int *rval_p)
{
struct sysctrl_soft_state *softsp;
sysc_cfga_pkt_t sysc_pkt;
fhc_bd_t *fhc_list = NULL;
sysc_cfga_stat_t *sc_list = NULL;
fhc_bd_t *bdp;
sysc_cfga_stat_t *sc = NULL;
int instance;
int slot;
int retval = 0;
int i;
instance = GETINSTANCE(devt);
softsp = GETSOFTC(instance);
if (softsp == NULL) {
cmn_err(CE_CONT,
"sysctrl_ioctl(%d): NULL softstate ptr!\n",
(int)GETSLOT(devt));
return (ENXIO);
}
slot = GETSLOT(devt);
/*
* First switch is to do correct locking and do ddi_copyin()
*/
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
/* mutex lock the whole list */
if (sysc_enter_transition(-1) != TRUE) {
retval = EBUSY;
goto cleanup_exit;
}
/* allocate the memory before acquiring mutex */
fhc_list = kmem_zalloc(sizeof (fhc_bd_t) * fhc_max_boards(),
KM_SLEEP);
sc_list = kmem_zalloc(sizeof (sysc_cfga_stat_t) *
fhc_max_boards(), KM_SLEEP);
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
retval = ENOTSUP;
goto cleanup_exit;
case SYSC_CFGA_CMD_CONNECT:
case SYSC_CFGA_CMD_DISCONNECT:
case SYSC_CFGA_CMD_UNCONFIGURE:
case SYSC_CFGA_CMD_CONFIGURE:
case SYSC_CFGA_CMD_TEST:
case SYSC_CFGA_CMD_TEST_SET_COND:
case SYSC_CFGA_CMD_QUIESCE_TEST:
/* ioctls allowed if caller has write permission */
if (!(flag & FWRITE)) {
retval = EPERM;
goto cleanup_exit;
}
retval = sysc_pkt_init(&sysc_pkt, arg, flag);
if (retval != 0)
goto cleanup_exit;
/* grasp lock and set in_transition bit */
if (sysc_enter_transition(cmd == SYSC_CFGA_CMD_QUIESCE_TEST
? -1 : slot) != TRUE) {
retval = EBUSY;
SYSC_ERR_SET(&sysc_pkt, SYSC_ERR_INTRANS);
goto cleanup_copyout;
}
/* get the status structure for the slot */
bdp = fhc_bd(slot);
sc = &bdp->sc;
break;
/* POSIX definition: return ENOTTY if unsupported command */
default:
retval = ENOTTY;
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)) {
bdp = fhc_bd(i);
if (fhc_bd_is_jtag_master(i))
bdp->sc.no_detach = 1;
else
bdp->sc.no_detach = 0;
bcopy((caddr_t)&bdp->sc,
&sc_list[i], sizeof (sysc_cfga_stat_t));
} else {
sc_list[i].board = -1;
sc_list[i].rstate = SYSC_CFGA_RSTATE_EMPTY;
}
}
sysc_exit_transition(-1);
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
retval = ENOTSUP;
goto cleanup_exit;
case SYSC_CFGA_CMD_CONNECT:
retval = sysc_policy_connect(softsp, &sysc_pkt, sc);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_DISCONNECT:
retval = sysc_policy_disconnect(softsp, &sysc_pkt, sc);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_UNCONFIGURE:
retval = sysc_policy_unconfigure(softsp, &sysc_pkt, sc);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_CONFIGURE:
retval = sysc_policy_configure(softsp, &sysc_pkt, sc);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_TEST:
retval = fhc_bd_test(slot, &sysc_pkt);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_TEST_SET_COND:
retval = fhc_bd_test_set_cond(slot, &sysc_pkt);
sysc_exit_transition(slot);
break;
case SYSC_CFGA_CMD_QUIESCE_TEST:
sysctrl_suspend_prepare();
fhc_bdlist_unlock();
if (sysctrl_suspend(&sysc_pkt) == DDI_SUCCESS) {
sysctrl_resume(&sysc_pkt);
} else {
retval = EBUSY;
}
(void) fhc_bdlist_lock(-1);
sysc_exit_transition(-1);
break;
default:
retval = ENOTTY;
goto cleanup_exit;
}
cleanup_copyout:
/*
* 3rd switch is to do appropriate copyout and reset locks
*/
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
if (ddi_copyout(sc_list, (void *)arg,
sizeof (sysc_cfga_stat_t) * fhc_max_boards(),
flag) != 0) {
retval = EFAULT;
}
/* cleanup memory */
kmem_free(fhc_list, sizeof (fhc_bd_t) * fhc_max_boards());
kmem_free(sc_list, sizeof (sysc_cfga_stat_t) *
fhc_max_boards());
break;
case SYSC_CFGA_CMD_EJECT:
case SYSC_CFGA_CMD_INSERT:
retval = ENOTSUP;
break;
case SYSC_CFGA_CMD_CONNECT:
case SYSC_CFGA_CMD_DISCONNECT:
case SYSC_CFGA_CMD_UNCONFIGURE:
case SYSC_CFGA_CMD_CONFIGURE:
case SYSC_CFGA_CMD_TEST:
case SYSC_CFGA_CMD_TEST_SET_COND:
case SYSC_CFGA_CMD_QUIESCE_TEST:
if (sysc_pkt_fini(&sysc_pkt, arg, flag) != TRUE)
return (EFAULT);
break;
default:
retval = ENOTTY;
break;
}
cleanup_exit:
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
system_high_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t csr;
uchar_t status2;
uchar_t tmp_reg;
int serviced = 0;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
/* read in the hardware registers */
csr = *(softsp->csr);
status2 = *(softsp->status2);
if (csr & SYS_AC_PWR_FAIL_EN) {
if (status2 & SYS_AC_FAIL) {
/* save the powerfail state in nvram */
nvram_update_powerfail(softsp);
/* disable this interrupt source */
csr &= ~SYS_AC_PWR_FAIL_EN;
ddi_trigger_softintr(softsp->ac_fail_id);
serviced++;
}
}
if (csr & SYS_PS_FAIL_EN) {
if ((*(softsp->ps_stat) != 0xff) ||
((~status2) & (SYS_PPS0_OK | SYS_CLK_33_OK |
SYS_CLK_50_OK)) ||
(~(*(softsp->pppsr)) & SYS_PPPSR_BITS)) {
/* disable this interrupt source */
csr &= ~SYS_PS_FAIL_EN;
ddi_trigger_softintr(softsp->ps_fail_int_id);
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 !?!?!
*/
softsp->pps_fan_saved = status2;
/* disable this interrupt source */
csr &= ~SYS_PPS_FAN_FAIL_EN;
ddi_trigger_softintr(softsp->pps_fan_id);
serviced++;
}
}
if (csr & SYS_SBRD_PRES_EN) {
if (!(*(softsp->status1) & SYS_NOT_BRD_PRES)) {
/* disable this interrupt source */
csr &= ~SYS_SBRD_PRES_EN;
ddi_trigger_softintr(softsp->sbrd_pres_id);
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 */
softsp->saved_en_state |= csr &
(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
/* and then turn them off */
csr &= ~(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
/* and then bump the counter */
softsp->spur_count++;
/* and kick off the timeout */
ddi_trigger_softintr(softsp->spur_id);
}
/* update the real csr */
*(softsp->csr) = csr;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
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
spur_delay(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
/* do we need to complain? */
mutex_enter(&softsp->csr_mutex);
/* NOTE: this is == because we want one message per long timeout */
if (softsp->spur_count == MAX_SPUR_COUNT) {
char buf[128];
/* print out the candidates known at this time */
/* XXX not perfect because of re-entrant nature but close */
buf[0] = '\0';
if (softsp->saved_en_state & SYS_AC_PWR_FAIL_EN)
(void) strcat(buf, "AC FAIL");
if (softsp->saved_en_state & SYS_PPS_FAN_FAIL_EN)
(void) strcat(buf, buf[0] ? "|PPS FANS" : "PPS FANS");
if (softsp->saved_en_state & SYS_PS_FAIL_EN)
(void) strcat(buf, buf[0] ? "|PS FAIL" : "PS FAIL");
if (softsp->saved_en_state & SYS_SBRD_PRES_EN)
(void) strcat(buf,
buf[0] ? "|BOARD INSERT" : "BOARD INSERT");
/*
* This is a high level mutex, therefore it needs to be
* dropped before calling cmn_err.
*/
mutex_exit(&softsp->csr_mutex);
cmn_err(CE_WARN, "sysctrl%d: unserviced interrupt."
" possible sources [%s].",
ddi_get_instance(softsp->dip), buf);
} else
mutex_exit(&softsp->csr_mutex);
mutex_enter(&softsp->spur_int_lock);
/* do we need to start the short timeout? */
if (softsp->spur_timeout_id == 0) {
softsp->spur_timeout_id = timeout(spur_retry, softsp,
spur_timeout_hz);
}
/* do we need to start the long timeout? */
if (softsp->spur_long_timeout_id == 0) {
softsp->spur_long_timeout_id = timeout(spur_long_timeout,
softsp, spur_long_timeout_hz);
}
mutex_exit(&softsp->spur_int_lock);
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)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->spur_high_id);
mutex_enter(&softsp->spur_int_lock);
softsp->spur_timeout_id = 0;
mutex_exit(&softsp->spur_int_lock);
}
/*
* 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
spur_reenable(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t tmp_reg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
/* reenable those who were spurious candidates */
*(softsp->csr) |= softsp->saved_en_state &
(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
/* clear out the saved state */
softsp->saved_en_state = 0;
mutex_exit(&softsp->csr_mutex);
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)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->spur_long_to_id);
mutex_enter(&softsp->spur_int_lock);
softsp->spur_long_timeout_id = 0;
mutex_exit(&softsp->spur_int_lock);
}
/*
* spur_clear_count
*
* simply clear out the spurious interrupt counter.
*
* softint level only
*/
static uint_t
spur_clear_count(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
softsp->spur_count = 0;
mutex_exit(&softsp->csr_mutex);
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
ac_fail_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
cmn_err(CE_WARN, "%s failure detected", ft_str_table[FT_AC_PWR]);
reg_fault(0, FT_AC_PWR, FT_SYSTEM);
(void) timeout(ac_fail_retry, softsp, ac_timeout_hz);
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)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
if (*softsp->status2 & SYS_AC_FAIL) { /* still bad? */
(void) timeout(ac_fail_retry, softsp, ac_timeout_hz);
} else {
cmn_err(CE_NOTE, "%s failure no longer detected",
ft_str_table[FT_AC_PWR]);
clear_fault(0, FT_AC_PWR, FT_SYSTEM);
ddi_trigger_softintr(softsp->ac_fail_high_id);
}
}
/*
* 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
ac_fail_reenable(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t tmp_reg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
*(softsp->csr) |= SYS_AC_PWR_FAIL_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
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_int_handler(caddr_t arg)
{
return (ps_fail_handler((struct sysctrl_soft_state *)arg, 1));
}
/*
* 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_poll_handler(caddr_t arg)
{
return (ps_fail_handler((struct sysctrl_soft_state *)arg, 0));
}
/*
* 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
ps_fail_handler(struct sysctrl_soft_state *softsp, int fromint)
{
int i;
struct ps_state *pstatp;
int poll_needed = 0;
uchar_t ps_stat, ps_pres, status1, status2, pppsr;
uchar_t tmp_reg;
enum power_state current_power_state;
ASSERT(softsp);
/* pre-read the hardware state */
ps_stat = *softsp->ps_stat;
ps_pres = *softsp->ps_pres;
status1 = *softsp->status1;
status2 = *softsp->status2;
pppsr = *softsp->pppsr;
(void) fhc_bdlist_lock(-1);
mutex_enter(&softsp->ps_fail_lock);
for (i = 0, pstatp = &softsp->ps_stats[0]; i < SYS_PS_COUNT;
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:
temp_pres = !((ps_pres >> i) & 0x1);
temp_psok = (ps_stat >> i) & 0x1;
break;
/* the first peripheral power supply */
case SYS_PPS0_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = status2 & SYS_PPS0_OK;
break;
/* shared 3.3v clock power */
case SYS_CLK_33_INDEX:
temp_pres = TRUE;
temp_psok = status2 & SYS_CLK_33_OK;
break;
/* shared 5.0v clock power */
case SYS_CLK_50_INDEX:
temp_pres = TRUE;
temp_psok = status2 & SYS_CLK_50_OK;
break;
/* peripheral 5v */
case SYS_V5_P_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES) ||
((IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
!(ps_pres & SYS_NOT_PPS1_PRES));
temp_psok = pppsr & SYS_V5_P_OK;
break;
/* peripheral 12v */
case SYS_V12_P_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES) ||
((IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
!(ps_pres & SYS_NOT_PPS1_PRES));
temp_psok = pppsr & SYS_V12_P_OK;
break;
/* aux 5v */
case SYS_V5_AUX_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V5_AUX_OK;
break;
/* peripheral 5v precharge */
case SYS_V5_P_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V5_P_PCH_OK;
is_precharge = TRUE;
break;
/* peripheral 12v precharge */
case SYS_V12_P_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V12_P_PCH_OK;
is_precharge = TRUE;
break;
/* 3.3v precharge */
case SYS_V3_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V3_PCH_OK;
is_precharge = TRUE;
break;
/* 5v precharge */
case SYS_V5_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V5_PCH_OK;
is_precharge = TRUE;
break;
/* peripheral fan assy */
case SYS_P_FAN_INDEX:
temp_pres = (IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
!(status1 & SYS_NOT_P_FAN_PRES);
temp_psok = softsp->pps_fan_saved &
SYS_AC_FAN_OK;
is_fan_assy = TRUE;
break;
}
/* *** Phase 1 -- power supply presence tests *** */
/* do we know the presence status for this power supply? */
if (pstatp->pshadow == PRES_UNKNOWN) {
pstatp->pshadow = temp_pres ? PRES_IN : PRES_OUT;
pstatp->dcshadow = temp_pres ? PS_BOOT : PS_OUT;
} else {
/* has the ps presence state changed? */
if (!temp_pres ^ (pstatp->pshadow == PRES_IN)) {
pstatp->pctr = 0;
} else {
/* a change! are we counting? */
if (pstatp->pctr == 0) {
pstatp->pctr = PS_PRES_CHANGE_TICKS;
} else if (--pstatp->pctr == 0) {
pstatp->pshadow = temp_pres ?
PRES_IN : PRES_OUT;
pstatp->dcshadow = temp_pres ?
PS_UNKNOWN : PS_OUT;
/*
* Now we know the state has
* changed, so we should log it.
*/
ps_log_pres_change(softsp,
i, temp_pres);
}
}
}
/* *** Phase 2 -- power supply status tests *** */
/* check if the Power Supply is removed or same as before */
if ((pstatp->dcshadow == PS_OUT) ||
((pstatp->dcshadow == PS_OK) && temp_psok) ||
((pstatp->dcshadow == PS_FAIL) && !temp_psok)) {
pstatp->dcctr = 0;
} else {
/* OK, a change, do we start the timer? */
if (pstatp->dcctr == 0) {
switch (pstatp->dcshadow) {
case PS_BOOT:
pstatp->dcctr = PS_FROM_BOOT_TICKS;
break;
case PS_UNKNOWN:
pstatp->dcctr = is_fan_assy ?
PS_P_FAN_FROM_UNKNOWN_TICKS :
PS_FROM_UNKNOWN_TICKS;
break;
case PS_OK:
pstatp->dcctr = is_precharge ?
PS_PCH_FROM_OK_TICKS :
PS_FROM_OK_TICKS;
break;
case PS_FAIL:
pstatp->dcctr = PS_FROM_FAIL_TICKS;
break;
default:
panic("sysctrl%d: Unknown Power "
"Supply State %d", pstatp->dcshadow,
ddi_get_instance(softsp->dip));
}
}
/* has the ticker expired? */
if (--pstatp->dcctr == 0) {
/* we'll skip OK messages during boot */
if (!((pstatp->dcshadow == PS_BOOT) &&
temp_psok)) {
ps_log_state_change(softsp,
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) &&
(pstatp->dcshadow != PS_BOOT) &&
(softsp->enable_rcons_atboot)) {
if (temp_psok)
rcons_reinit(softsp);
else
/* disable rconsole */
*(softsp->clk_freq2) &=
~RCONS_UART_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
/* regardless, update the shadow state */
pstatp->dcshadow = temp_psok ? PS_OK : PS_FAIL;
/* always update board condition */
sysc_policy_update(softsp, NULL,
SYSC_EVT_BD_PS_CHANGE);
}
}
/*
* 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 ||
(pstatp->dcshadow == PS_UNKNOWN) ||
(pstatp->dcshadow == PS_FAIL)) {
poll_needed++;
}
}
/*
* Now, get the current power state for this instance.
* If the current state is different than what was known, complain.
*/
current_power_state = compute_power_state(softsp, 0);
if (softsp->power_state != current_power_state) {
switch (current_power_state) {
case BELOW_MINIMUM:
cmn_err(CE_WARN,
"Insufficient power available to system");
if (!disable_insufficient_power_reboot) {
cmn_err(CE_WARN, "System reboot in %d seconds",
PS_INSUFFICIENT_COUNTDOWN_SEC);
}
reg_fault(1, FT_INSUFFICIENT_POWER, FT_SYSTEM);
softsp->power_countdown = PS_POWER_COUNTDOWN_TICKS;
break;
case MINIMUM:
/* If we came from REDUNDANT, complain */
if (softsp->power_state == REDUNDANT) {
cmn_err(CE_WARN, "Redundant power lost");
/* If we came from BELOW_MINIMUM, hurrah! */
} else if (softsp->power_state == BELOW_MINIMUM) {
cmn_err(CE_NOTE, "Minimum power available");
clear_fault(1, FT_INSUFFICIENT_POWER,
FT_SYSTEM);
}
break;
case REDUNDANT:
/* If we aren't from boot, spread the good news */
if (softsp->power_state != BOOT) {
cmn_err(CE_NOTE, "Redundant power available");
clear_fault(1, FT_INSUFFICIENT_POWER,
FT_SYSTEM);
}
break;
default:
break;
}
softsp->power_state = current_power_state;
sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
}
mutex_exit(&softsp->ps_fail_lock);
fhc_bdlist_unlock();
/*
* Are we in insufficient powerstate?
* If so, is it time to take action?
*/
if (softsp->power_state == BELOW_MINIMUM &&
softsp->power_countdown > 0 && --(softsp->power_countdown) == 0 &&
!disable_insufficient_power_reboot) {
cmn_err(CE_WARN,
"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) {
mutex_enter(&softsp->csr_mutex);
*(softsp->csr) |= SYS_PS_FAIL_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
}
/*
* Only the polling loop re-triggers the polling loop timeout
*/
if (!fromint) {
(void) timeout(ps_fail_retry, softsp, ps_fail_timeout_hz);
}
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
compute_power_state(struct sysctrl_soft_state *softsp, int plus_load)
{
int i;
int ok_supply_count = 0;
int load_count = 0;
int minimum_power_count;
int pps_ok;
fhc_bd_t *list;
ASSERT(mutex_owned(&softsp->ps_fail_lock));
/*
* 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 &&
(IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)))
continue;
if (softsp->ps_stats[i].dcshadow == PS_OK)
ok_supply_count++;
}
/* Note the state of the PPS... */
pps_ok = (softsp->ps_stats[SYS_PPS0_INDEX].dcshadow == PS_OK);
/*
* Dynamically compute the load count in the system.
* Don't count disk boards or boards in low power state.
*/
for (list = fhc_bd_first(); list; list = fhc_bd_next(list)) {
ASSERT(list->sc.type != CLOCK_BOARD);
if (list->sc.rstate == SYSC_CFGA_RSTATE_CONNECTED) {
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...
*/
if (IS8SLOT(softsp->nslots) && load_count >= 7 && pps_ok)
ok_supply_count++;
/*
* This is to cover the corner case of a UE3500 having 5
* boards installed and still giving it N+1 power status.
*/
if (IS5SLOT(softsp->nslots) && (load_count >= 5))
ok_supply_count++;
/*
* Determine our power situation. This is a simple step
* function right now:
*
* minimum power count = min(7, floor((board count + 1) / 2))
*/
minimum_power_count = (load_count + 1) / 2;
if (minimum_power_count > 7)
minimum_power_count = 7;
if (ok_supply_count > minimum_power_count)
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
ps_log_pres_change(struct sysctrl_soft_state *softsp, int index, int present)
{
char *trans = present ? "Installed" : "Removed";
switch (index) {
/* the core power supplies (except for 7) */
case 0: case 1: case 2: case 3:
case 4: case 5: case 6:
cmn_err(CE_NOTE, "%s %d %s", ft_str_table[FT_CORE_PS], index,
trans);
if (!present) {
clear_fault(index, FT_CORE_PS, FT_SYSTEM);
sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
}
break;
/* power supply 7 / pps 1 */
case 7:
if (IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)) {
cmn_err(CE_NOTE, "%s 1 %s", ft_str_table[FT_PPS],
trans);
if (!present) {
clear_fault(1, FT_PPS, FT_SYSTEM);
}
} else {
cmn_err(CE_NOTE, "%s %d %s", ft_str_table[FT_CORE_PS],
index, trans);
if (!present) {
clear_fault(7, FT_CORE_PS, FT_SYSTEM);
sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
}
}
break;
/* the peripheral power supply 0 */
case SYS_PPS0_INDEX:
cmn_err(CE_NOTE, "%s 0 %s", ft_str_table[FT_PPS], trans);
if (!present) {
clear_fault(0, FT_PPS, FT_SYSTEM);
sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
}
break;
/* the peripheral rack fan assy */
case SYS_P_FAN_INDEX:
cmn_err(CE_NOTE, "%s %s", ft_str_table[FT_PPS_FAN], trans);
if (!present) {
clear_fault(0, FT_PPS_FAN, FT_SYSTEM);
}
break;
/* we don't mention a change of presence state for any other power */
}
}
/*
* log the change of power supply status
*/
static void
ps_log_state_change(struct sysctrl_soft_state *softsp, int index, int ps_ok)
{
int level = ps_ok ? CE_NOTE : CE_WARN;
char *s = ps_ok ? "OK" : "Failing";
switch (index) {
/* the core power supplies (except 7) */
case 0: case 1: case 2: case 3:
case 4: case 5: case 6:
cmn_err(level, "%s %d %s", ft_str_table[FT_CORE_PS], index, s);
if (ps_ok) {
clear_fault(index, FT_CORE_PS, FT_SYSTEM);
} else {
reg_fault(index, FT_CORE_PS, FT_SYSTEM);
}
break;
/* power supply 7 / pps 1 */
case 7:
if (IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)) {
cmn_err(level, "%s 1 %s", ft_str_table[FT_PPS], s);
if (ps_ok) {
clear_fault(1, FT_PPS, FT_SYSTEM);
} else {
reg_fault(1, FT_PPS, FT_SYSTEM);
}
} else {
cmn_err(level, "%s %d %s", ft_str_table[FT_CORE_PS],
index, s);
if (ps_ok) {
clear_fault(index, FT_CORE_PS, FT_SYSTEM);
} else {
reg_fault(index, FT_CORE_PS, FT_SYSTEM);
}
}
break;
/* the peripheral power supply */
case SYS_PPS0_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_PPS], s);
if (ps_ok) {
clear_fault(0, FT_PPS, FT_SYSTEM);
} else {
reg_fault(0, FT_PPS, FT_SYSTEM);
}
break;
/* shared 3.3v clock power */
case SYS_CLK_33_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_CLK_33], s);
if (ps_ok) {
clear_fault(0, FT_CLK_33, FT_SYSTEM);
} else {
reg_fault(0, FT_CLK_33, FT_SYSTEM);
}
break;
/* shared 5.0v clock power */
case SYS_CLK_50_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_CLK_50], s);
if (ps_ok) {
clear_fault(0, FT_CLK_50, FT_SYSTEM);
} else {
reg_fault(0, FT_CLK_50, FT_SYSTEM);
}
break;
/* peripheral 5v */
case SYS_V5_P_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V5_P], s);
if (ps_ok) {
clear_fault(0, FT_V5_P, FT_SYSTEM);
} else {
reg_fault(0, FT_V5_P, FT_SYSTEM);
}
break;
/* peripheral 12v */
case SYS_V12_P_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V12_P], s);
if (ps_ok) {
clear_fault(0, FT_V12_P, FT_SYSTEM);
} else {
reg_fault(0, FT_V12_P, FT_SYSTEM);
}
break;
/* aux 5v */
case SYS_V5_AUX_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V5_AUX], s);
if (ps_ok) {
clear_fault(0, FT_V5_AUX, FT_SYSTEM);
} else {
reg_fault(0, FT_V5_AUX, FT_SYSTEM);
}
break;
/* peripheral 5v precharge */
case SYS_V5_P_PCH_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V5_P_PCH], s);
if (ps_ok) {
clear_fault(0, FT_V5_P_PCH, FT_SYSTEM);
} else {
reg_fault(0, FT_V5_P_PCH, FT_SYSTEM);
}
break;
/* peripheral 12v precharge */
case SYS_V12_P_PCH_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V12_P_PCH], s);
if (ps_ok) {
clear_fault(0, FT_V12_P_PCH, FT_SYSTEM);
} else {
reg_fault(0, FT_V12_P_PCH, FT_SYSTEM);
}
break;
/* 3.3v precharge */
case SYS_V3_PCH_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V3_PCH], s);
if (ps_ok) {
clear_fault(0, FT_V3_PCH, FT_SYSTEM);
} else {
reg_fault(0, FT_V3_PCH, FT_SYSTEM);
}
break;
/* 5v precharge */
case SYS_V5_PCH_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_V5_PCH], s);
if (ps_ok) {
clear_fault(0, FT_V5_PCH, FT_SYSTEM);
} else {
reg_fault(0, FT_V5_PCH, FT_SYSTEM);
}
break;
/* peripheral power supply fans */
case SYS_P_FAN_INDEX:
cmn_err(level, "%s %s", ft_str_table[FT_PPS_FAN], s);
if (ps_ok) {
clear_fault(0, FT_PPS_FAN, FT_SYSTEM);
} else {
reg_fault(0, FT_PPS_FAN, FT_SYSTEM);
}
break;
}
}
/*
* The timeout from ps_fail_handler() that simply re-triggers a check
* of the ps condition.
*/
static void
ps_fail_retry(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->ps_fail_poll_id);
}
/*
* pps_fanfail_handler
*
* This routine is called from the high level handler.
*/
static uint_t
pps_fanfail_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
/* always check again in a bit by re-enabling the fan interrupt */
(void) timeout(pps_fanfail_retry, softsp, pps_fan_timeout_hz);
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)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->pps_fan_high_id);
}
/*
* The other half of the retry handler run from the interrupt context
*/
static uint_t
pps_fanfail_reenable(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t tmp_reg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
/*
* 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...
*/
softsp->pps_fan_saved = SYS_AC_FAN_OK | SYS_KEYSW_FAN_OK;
*(softsp->csr) |= SYS_PPS_FAN_FAIL_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
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)
{
struct sysctrl_soft_state *softsp = arg;
int i;
ASSERT(softsp);
for (i = 0; i < SYS_PPS_FAN_COUNT; i++) {
int fanfail = FALSE;
/* determine fan status */
switch (i) {
case RACK:
fanfail = softsp->pps_fan_saved & SYS_RACK_FANFAIL;
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.
*/
fanfail = !(IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
!(softsp->pps_fan_saved & SYS_AC_FAN_OK);
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.
*/
fanfail = (!(IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
(softsp->ps_stats[SYS_V5_AUX_INDEX].dcshadow !=
PS_OK)) ||
!(softsp->pps_fan_saved & SYS_KEYSW_FAN_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 */
pps_fan_state_change(softsp, i, FALSE);
}
/* always restart the fan OK counter */
softsp->pps_fan_state_count[i] = PPS_FROM_FAIL_TICKS;
} 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 */
pps_fan_state_change(softsp, i, TRUE);
}
}
}
}
/* always check again in a bit by re-enabling the fan interrupt */
(void) timeout(pps_fan_poll, softsp, pps_fan_timeout_hz);
}
/*
* pps_fan_state_change()
*
* Log the changed fan condition and update the external status.
*/
static void
pps_fan_state_change(struct sysctrl_soft_state *softsp, int index, int fan_ok)
{
char *fan_type;
char *state = fan_ok ? "fans OK" : "fan failure detected";
switch (index) {
case RACK:
/* 4 and 5 slot systems behave the same */
fan_type = (IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) ?
"Disk Drive" : "Rack Exhaust";
if (fan_ok) {
softsp->pps_fan_external_state &= ~SYS_RACK_FANFAIL;
clear_fault(0, (IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) ? FT_DSK_FAN :
FT_RACK_EXH, FT_SYSTEM);
} else {
softsp->pps_fan_external_state |= SYS_RACK_FANFAIL;
reg_fault(0, (IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) ? FT_DSK_FAN :
FT_RACK_EXH, FT_SYSTEM);
}
break;
case AC:
fan_type = "AC Box";
if (fan_ok) {
softsp->pps_fan_external_state |= SYS_AC_FAN_OK;
clear_fault(0, FT_AC_FAN, FT_SYSTEM);
} else {
softsp->pps_fan_external_state &= ~SYS_AC_FAN_OK;
reg_fault(0, FT_AC_FAN, FT_SYSTEM);
}
break;
case KEYSW:
fan_type = "Keyswitch";
if (fan_ok) {
softsp->pps_fan_external_state |= SYS_KEYSW_FAN_OK;
clear_fault(0, FT_KEYSW_FAN, FT_SYSTEM);
} else {
softsp->pps_fan_external_state &= ~SYS_KEYSW_FAN_OK;
reg_fault(0, FT_KEYSW_FAN, FT_SYSTEM);
}
break;
default:
fan_type = "[invalid fan id]";
break;
}
/* now log the state change */
cmn_err(fan_ok ? CE_NOTE : CE_WARN, "%s %s", fan_type, state);
}
static uint_t
bd_insert_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
DPRINTF(SYSCTRL_ATTACH_DEBUG, ("bd_insert_handler()"));
(void) timeout(bd_insert_timeout, softsp, bd_insert_delay_hz);
return (DDI_INTR_CLAIMED);
}
void
bd_remove_poll(struct sysctrl_soft_state *softsp)
{
ASSERT(fhc_bdlist_locked());
if (!bd_remove_to_id) {
bd_remove_to_id = timeout(bd_remove_timeout, softsp,
bd_remove_timeout_hz);
} else {
DPRINTF(SYSCTRL_ATTACH_DEBUG,
("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)
{
struct sysctrl_soft_state *softsp = arg;
int found;
ASSERT(softsp);
if (sysctrl_hotplug_disabled) {
sysc_policy_update(softsp, NULL, SYSC_EVT_BD_HP_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) {
DPRINTF(SYSCTRL_ATTACH_DEBUG,
("bd_insert_timeout starting bd_remove_poll()"));
bd_remove_poll(softsp);
}
fhc_bdlist_unlock();
}
/*
* Enable interrupts.
*/
ddi_trigger_softintr(softsp->sbrd_gone_id);
}
static void
bd_remove_timeout(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
int keep_polling;
ASSERT(softsp);
/*
* 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 */
keep_polling = fhc_bd_remove_scan();
if (keep_polling) {
bd_remove_poll(softsp);
} else {
DPRINTF(SYSCTRL_ATTACH_DEBUG, ("exiting bd_remove_poll."));
}
fhc_bdlist_unlock();
}
static uint_t
bd_insert_normal(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t tmp_reg;
ASSERT(softsp);
/* has the condition been removed? */
/* XXX add deglitch state machine here */
if (!(*(softsp->status1) & SYS_NOT_BRD_PRES)) {
/* check again in a few */
(void) timeout(bd_insert_timeout, softsp, bd_insert_retry_hz);
} else {
/* Turn on the enable bit for this interrupt */
mutex_enter(&softsp->csr_mutex);
*(softsp->csr) |= SYS_SBRD_PRES_EN;
/* flush the hardware store buffer */
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
}
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
blink_led_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
uchar_t tmp_reg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
/*
* 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. */
tmp_reg = *(softsp->csr);
/* Only turn on the OS System LED bit if the softsp state is on. */
if (softsp->sys_led) {
tmp_reg |= SYS_LED_RIGHT;
} else {
tmp_reg &= ~SYS_LED_RIGHT;
}
/* Turn on the yellow LED if system fault status is set. */
if (softsp->sys_fault) {
tmp_reg |= SYS_LED_MID;
} else {
tmp_reg &= ~SYS_LED_MID;
}
/* write to the hardware register */
*(softsp->csr) = tmp_reg;
/* flush the hardware store buffer */
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
(void) timeout(blink_led_timeout, softsp, blink_led_timeout_hz);
return (DDI_INTR_CLAIMED);
}
/*
* simply re-trigger the interrupt handler on led timeout
*/
static void
blink_led_timeout(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
int led_state;
ASSERT(softsp);
/*
* 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.
*/
softsp->sys_fault = process_fault_list();
/* blink the system board OS LED */
mutex_enter(&softsp->sys_led_lock);
softsp->sys_led = !softsp->sys_led;
led_state = softsp->sys_led;
mutex_exit(&softsp->sys_led_lock);
toggle_board_green_leds(led_state);
ddi_trigger_softintr(softsp->blink_led_id);
}
void
toggle_board_green_leds(int led_state)
{
fhc_bd_t *list;
(void) fhc_bdlist_lock(-1);
for (list = fhc_bd_first(); list; list = fhc_bd_next(list)) {
uint_t value = 0;
if (list->sc.in_transition ||
(list->sc.rstate != SYSC_CFGA_RSTATE_CONNECTED))
continue;
ASSERT(list->sc.type != CLOCK_BOARD);
ASSERT(list->sc.type != DISK_BOARD);
ASSERT(list->softsp);
if ((list->sc.ostate == SYSC_CFGA_OSTATE_CONFIGURED) &&
led_state)
value |= FHC_LED_RIGHT;
if (list->fault)
value |= FHC_LED_MID;
else
value &= ~FHC_LED_MID;
update_board_leds(list, FHC_LED_RIGHT|FHC_LED_MID, value);
}
fhc_bdlist_unlock();
}
/*
* timestamp an AC power failure in nvram
*/
static void
nvram_update_powerfail(struct sysctrl_soft_state *softsp)
{
char buf[80];
int len = 0;
numtos(gethrestime_sec(), buf);
if (softsp->options_nodeid) {
len = prom_setprop(softsp->options_nodeid, "powerfail-time",
buf, strlen(buf)+1);
}
if (len <= 0) {
cmn_err(CE_WARN, "sysctrl%d: failed to set powerfail-time "
"to %s\n", ddi_get_instance(softsp->dip), buf);
}
}
void
sysctrl_add_kstats(struct sysctrl_soft_state *softsp)
{
struct kstat *ksp; /* Generic sysctrl kstats */
struct kstat *pksp; /* Power Supply kstat */
struct kstat *tksp; /* Sysctrl temperatrure kstat */
struct kstat *ttsp; /* Sysctrl temperature test kstat */
if ((ksp = kstat_create("unix", ddi_get_instance(softsp->dip),
SYSCTRL_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
sizeof (struct sysctrl_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
ddi_get_instance(softsp->dip));
} else {
struct sysctrl_kstat *sysksp;
sysksp = (struct sysctrl_kstat *)(ksp->ks_data);
/* now init the named kstats */
kstat_named_init(&sysksp->csr, CSR_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->status1, STAT1_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->status2, STAT2_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->clk_freq2, CLK_FREQ2_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->fan_status, FAN_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->key_status, KEY_KSTAT_NAMED,
KSTAT_DATA_CHAR);
kstat_named_init(&sysksp->power_state, POWER_KSTAT_NAMED,
KSTAT_DATA_INT32);
kstat_named_init(&sysksp->clk_ver, CLK_VER_KSTAT_NAME,
KSTAT_DATA_CHAR);
ksp->ks_update = sysctrl_kstat_update;
ksp->ks_private = (void *)softsp;
kstat_install(ksp);
}
if ((tksp = kstat_create("unix", CLOCK_BOARD_INDEX,
OVERTEMP_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
sizeof (struct temp_stats), KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
ddi_get_instance(softsp->dip));
} else {
tksp->ks_update = overtemp_kstat_update;
tksp->ks_private = (void *)&softsp->tempstat;
kstat_install(tksp);
}
if ((ttsp = kstat_create("unix", CLOCK_BOARD_INDEX,
TEMP_OVERRIDE_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, sizeof (short),
KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
ddi_get_instance(softsp->dip));
} else {
ttsp->ks_update = temp_override_kstat_update;
ttsp->ks_private = (void *)&softsp->tempstat.override;
kstat_install(ttsp);
}
if ((pksp = kstat_create("unix", ddi_get_instance(softsp->dip),
PSSHAD_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
SYS_PS_COUNT, KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
ddi_get_instance(softsp->dip));
} else {
pksp->ks_update = psstat_kstat_update;
pksp->ks_private = (void *)softsp;
kstat_install(pksp);
}
}
static int
sysctrl_kstat_update(kstat_t *ksp, int rw)
{
struct sysctrl_kstat *sysksp;
struct sysctrl_soft_state *softsp;
sysksp = (struct sysctrl_kstat *)(ksp->ks_data);
softsp = (struct sysctrl_soft_state *)(ksp->ks_private);
/* 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.
*/
sysksp->csr.value.c[0] = *(softsp->csr);
sysksp->status1.value.c[0] = *(softsp->status1);
sysksp->status2.value.c[0] = *(softsp->status2);
sysksp->clk_freq2.value.c[0] = *(softsp->clk_freq2);
sysksp->fan_status.value.c[0] = softsp->pps_fan_external_state;
sysksp->key_status.value.c[0] = softsp->key_shadow;
sysksp->power_state.value.i32 = softsp->power_state;
/*
* non-existence of the clock version register returns the
* value 0xff when the hardware register location is read
*/
if (softsp->clk_ver != NULL)
sysksp->clk_ver.value.c[0] = *(softsp->clk_ver);
else
sysksp->clk_ver.value.c[0] = (char)0xff;
}
return (0);
}
static int
psstat_kstat_update(kstat_t *ksp, int rw)
{
struct sysctrl_soft_state *softsp;
uchar_t *ptr = (uchar_t *)(ksp->ks_data);
int ps;
softsp = (struct sysctrl_soft_state *)(ksp->ks_private);
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
for (ps = 0; ps < SYS_PS_COUNT; ps++) {
*ptr++ = softsp->ps_stats[ps].dcshadow;
}
}
return (0);
}
static void
sysctrl_thread_wakeup(void *arg)
{
int type = (int)(uintptr_t)arg;
/*
* grab mutex to guarantee that our wakeup call
* arrives after we go to sleep -- so we can't sleep forever.
*/
mutex_enter(&sslist_mutex);
switch (type) {
case OVERTEMP_POLL:
cv_signal(&overtemp_cv);
break;
case KEYSWITCH_POLL:
cv_signal(&keyswitch_cv);
break;
default:
cmn_err(CE_WARN, "sysctrl: invalid type %d to wakeup\n", type);
break;
}
mutex_exit(&sslist_mutex);
}
static void
sysctrl_overtemp_poll(void)
{
struct sysctrl_soft_state *list;
callb_cpr_t cprinfo;
CALLB_CPR_INIT(&cprinfo, &sslist_mutex, callb_generic_cpr, "overtemp");
/* The overtemp data structures are protected by a mutex. */
mutex_enter(&sslist_mutex);
while (sysctrl_do_overtemp_thread) {
for (list = sys_list; list != NULL; list = list->next) {
if (list->temp_reg != NULL) {
update_temp(list->pdip, &list->tempstat,
*(list->temp_reg));
}
}
CALLB_CPR_SAFE_BEGIN(&cprinfo);
/* now have this thread sleep for a while */
(void) timeout(sysctrl_thread_wakeup, (void *)OVERTEMP_POLL,
overtemp_timeout_hz);
cv_wait(&overtemp_cv, &sslist_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &sslist_mutex);
}
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
/* NOTREACHED */
}
static void
sysctrl_keyswitch_poll(void)
{
struct sysctrl_soft_state *list;
callb_cpr_t cprinfo;
CALLB_CPR_INIT(&cprinfo, &sslist_mutex, callb_generic_cpr, "keyswitch");
/* The keyswitch data strcutures are protected by a mutex. */
mutex_enter(&sslist_mutex);
while (sysctrl_do_keyswitch_thread) {
for (list = sys_list; list != NULL; list = list->next) {
if (list->status1 != NULL)
update_key_state(list);
}
CALLB_CPR_SAFE_BEGIN(&cprinfo);
/* now have this thread sleep for a while */
(void) timeout(sysctrl_thread_wakeup, (void *)KEYSWITCH_POLL,
keyswitch_timeout_hz);
cv_wait(&keyswitch_cv, &sslist_mutex);
CALLB_CPR_SAFE_END(&cprinfo, &sslist_mutex);
}
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
/* NOTREACHED */
}
/*
* check the key switch position for state changes
*/
static void
update_key_state(struct sysctrl_soft_state *list)
{
enum keyswitch_state key;
/*
* snapshot current hardware key position
*/
if (*(list->status1) & SYS_NOT_SECURE)
key = KEY_NOT_SECURE;
else
key = KEY_SECURE;
/*
* check for state transition
*/
if (key != list->key_shadow) {
/*
* handle state transition
*/
switch (list->key_shadow) {
case KEY_BOOT:
cmn_err(CE_CONT, "?sysctrl%d: Key switch is%sin the "
"secure position\n", ddi_get_instance(list->dip),
(key == KEY_SECURE) ? " " : " not ");
list->key_shadow = key;
break;
case KEY_SECURE:
case KEY_NOT_SECURE:
cmn_err(CE_NOTE, "sysctrl%d: Key switch has changed"
" to the %s position",
ddi_get_instance(list->dip),
(key == KEY_SECURE) ? "secure" : "not-secure");
list->key_shadow = key;
break;
default:
cmn_err(CE_CONT,
"?sysctrl%d: Key switch is in an unknown position,"
"treated as being in the %s position\n",
ddi_get_instance(list->dip),
(list->key_shadow == KEY_SECURE) ?
"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;
uint_t secure = 0;
char buf[64], inst[4];
/*
* if any of the key switch positions are secure,
* then disallow entry to the prom/debugger
*/
mutex_enter(&sslist_mutex);
buf[0] = (char)0;
for (list = sys_list; list != NULL; list = list->next) {
if (!(*(list->status1) & SYS_NOT_SECURE)) {
if (secure++)
(void) strcat(buf, ",");
/*
* XXX: later, replace instance number with nodeid
*/
(void) sprintf(inst, "%d", ddi_get_instance(list->dip));
(void) strcat(buf, inst);
}
}
mutex_exit(&sslist_mutex);
if (secure) {
cmn_err(CE_CONT,
"!sysctrl(%s): ignoring debug enter sequence\n", buf);
} else {
cmn_err(CE_CONT, "!sysctrl: allowing debug enter\n");
debug_enter(msg);
}
}
#define TABLE_END 0xFF
struct uart_cmd {
uchar_t reg;
uchar_t data;
};
/*
* Time constant defined by this formula:
* ((4915200/32)/(baud) -2)
*/
struct uart_cmd uart_table[] = {
{ 0x09, 0xc0 }, /* Force hardware reset */
{ 0x04, 0x46 }, /* X16 clock mode, 1 stop bit/char, no parity */
{ 0x03, 0xc0 }, /* Rx is 8 bits/char */
{ 0x05, 0xe2 }, /* DTR, Tx is 8 bits/char, RTS */
{ 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 */
{ 0x03, 0xc1 }, /* Rx is 8 bits/char, Rx is enabled */
{ 0x05, 0xea }, /* DTR, Tx is 8 bits/char, Tx is enabled, RTS */
{ 0x0e, 0x03 }, /* BR comes from PCLK, BR generator is enabled */
{ 0x00, 0x30 }, /* Error reset */
{ 0x00, 0x30 }, /* Error reset */
{ 0x00, 0x10 }, /* external status reset */
{ 0x03, 0xc1 }, /* Rx is 8 bits/char, Rx is enabled */
{ TABLE_END, 0x0 }
};
static void
init_remote_console_uart(struct sysctrl_soft_state *softsp)
{
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.
* So walk thru table writing the register/data pairs to
* the serial port chip.
*/
while (uart_table[i].reg != TABLE_END) {
*(softsp->rcons_ctl) = uart_table[i].reg;
*(softsp->rcons_ctl) = uart_table[i].data;
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
sysc_slot_info(int nslots, int *start, int *limit, int *incr)
{
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
rcons_reinit(struct sysctrl_soft_state *softsp)
{
uchar_t tmp_reg;
if (!(softsp->rcons_ctl))
/*
* 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.
*/
if (ddi_map_regs(softsp->dip, 1, (caddr_t *)&softsp->rcons_ctl,
RMT_CONS_OFFSET, RMT_CONS_LEN)) {
cmn_err(CE_WARN, "Unable to reinitialize "
"remote console.");
return;
}
/* Disable the remote console reset control bits. */
*(softsp->clk_freq2) &= ~RCONS_UART_EN;
/* flush the hardware buffers */
tmp_reg = *(softsp->csr);
/*
* Program the UART to watch ttya console.
*/
init_remote_console_uart(softsp);
/* Now enable the remote console reset control bits. */
*(softsp->clk_freq2) |= RCONS_UART_EN;
/* flush the hardware buffers */
tmp_reg = *(softsp->csr);
/* print some info for user to watch */
cmn_err(CE_NOTE, "Remote console reinitialized");
#ifdef lint
tmp_reg = tmp_reg;
#endif
}