#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>
#ifdef DEBUG
int sysc_debug_info = 1;
int sysc_debug_print_level = 0;
#endif
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);
static struct cb_ops sysctrl_cb_ops = {
sysctrl_open,
sysctrl_close,
nulldev,
nulldev,
nulldev,
nulldev,
nulldev,
sysctrl_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_MP|D_NEW,
CB_REV,
nodev,
nodev
};
static struct dev_ops sysctrl_ops = {
DEVO_REV,
0,
sysctrl_info,
nulldev,
nulldev,
sysctrl_attach,
sysctrl_detach,
nulldev,
&sysctrl_cb_ops,
(struct bus_ops *)0,
nulldev,
ddi_quiesce_not_supported,
};
void *sysctrlp;
static clock_t spur_timeout_hz;
static clock_t spur_long_timeout_hz;
static clock_t ac_timeout_hz;
static clock_t ps_fail_timeout_hz;
static clock_t pps_fan_timeout_hz;
static clock_t bd_insert_delay_hz;
static clock_t bd_insert_retry_hz;
static clock_t bd_remove_timeout_hz;
static clock_t blink_led_timeout_hz;
static clock_t overtemp_timeout_hz;
static clock_t keyswitch_timeout_hz;
int enable_sys_interrupt = SYS_AC_PWR_FAIL_EN | SYS_PPS_FAN_FAIL_EN |
SYS_PS_FAIL_EN | SYS_SBRD_PRES_EN;
static int sysctrl_do_overtemp_thread = 1;
static int sysctrl_do_keyswitch_thread = 1;
static timeout_id_t bd_remove_to_id = 0;
int disable_insufficient_power_reboot = 0;
int sysctrl_enable_detach_suspend = 0;
int sysctrl_hotplug_disabled = FALSE;
static int sysctrl_overtemp_thread_started = 0;
static int sysctrl_keyswitch_thread_started = 0;
static kmutex_t sslist_mutex;
static kcondvar_t overtemp_cv;
static kcondvar_t keyswitch_cv;
static kmutex_t sysctrl_branch_mutex;
static int sysctrl_ddi_branch_init;
struct sysctrl_soft_state *sys_list = NULL;
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"Clock Board",
&sysctrl_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
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));
}
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;
int limit;
int incr;
void set_clockbrd_info(void);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
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);
softsp->dip = devi;
softsp->pdip = ddi_get_parent(softsp->dip);
DPRINTF(SYSCTRL_ATTACH_DEBUG, ("sysctrl: devi= 0x%p\n, softsp=0x%p\n",
(void *)devi, (void *)softsp));
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);
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;
}
(void) ddi_map_regs(softsp->dip, 2, (caddr_t *)&softsp->clk_ver, 0, 0);
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();
if (watchdog_available && watchdog_enable)
*(softsp->clk_freq2) |= TOD_RESET_EN;
else
*(softsp->clk_freq2) &= ~TOD_RESET_EN;
if (*softsp->csr & SYS_LED_MID) {
reg_fault(0, FT_PROM, FT_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:
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_ft_kstats(instance);
tmp_reg = *(softsp->temp_reg);
DELAY(30);
*(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
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;
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;
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;
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;
if ((*(softsp->pppsr) & SYS_NOT_CURRENT_S) != 0) {
cmn_err(CE_NOTE, "Hot Plug not supported in this system");
sysctrl_hotplug_disabled = TRUE;
}
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);
fhc_bdlist_prime(start, limit, incr);
(void) fhc_bdlist_lock(-1);
DPRINTF(SYSCTRL_ATTACH_DEBUG,
("attach: start bd_remove_poll()..."));
bd_remove_poll(softsp);
fhc_bdlist_unlock();
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;
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);
softsp->pps_fan_saved = softsp->pps_fan_external_state =
SYS_AC_FAN_OK | SYS_KEYSW_FAN_OK;
if (enable_sys_interrupt & SYS_PS_FAIL_EN)
ddi_trigger_softintr(softsp->ps_fail_poll_id);
softsp->sys_led = FALSE;
ddi_trigger_softintr(softsp->blink_led_id);
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);
init_temp_arrays(&softsp->tempstat);
softsp->key_shadow = KEY_BOOT;
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);
sysctrl_add_kstats(softsp);
pps_fan_poll(softsp);
if (sysctrl_overtemp_thread_started == 0) {
cv_init(&overtemp_cv, NULL, CV_DRIVER, NULL);
(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)();
abort_seq_handler = sysctrl_abort_seq_handler;
cv_init(&keyswitch_cv, NULL, CV_DRIVER, NULL);
(void) thread_create(NULL, 0,
(void (*)())sysctrl_keyswitch_poll, NULL, 0, &p0,
TS_RUN, minclsyspri);
sysctrl_keyswitch_thread_started++;
}
if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL)
softsp->options_nodeid = (pnode_t)0;
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));
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);
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;
ASSERT(ddi_get_parent(dip) == ddi_root_node());
if (!ndi_dev_is_prom_node(dip) ||
strcmp(ddi_node_name(dip), "central") == 0)
return (DDI_WALK_PRUNECHILD);
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);
}
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
if (sysctrl_enable_detach_suspend == FALSE)
return (DDI_FAILURE);
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
break;
default:
return (DDI_FAILURE);
}
#ifdef SYSCTRL_SUPPORTS_DETACH
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);
ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches, &arg);
ndi_devi_exit(rdip);
sysctrl_ddi_branch_init = 0;
return (DDI_SUCCESS);
#endif
return (DDI_FAILURE);
}
static int
sysctrl_open(dev_t *devp, int flag, int otyp, cred_t *credp)
{
int instance;
int slot;
dev_t dev;
dev_info_t *rdip;
struct sysc_hold arg = {0};
struct sysctrl_soft_state *softsp;
dev = *devp;
instance = GETINSTANCE(dev);
slot = GETSLOT(dev);
if ((softsp = GETSOFTC(instance)) == NULL) {
cmn_err(CE_WARN, "sysctrl%d device not attached", instance);
return (ENXIO);
}
if (otyp != OTYP_CHR) {
return (EINVAL);
}
if (!fhc_bd_valid(slot))
return (ENXIO);
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);
ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches,
&arg);
ndi_devi_exit(rdip);
}
mutex_exit(&sysctrl_branch_mutex);
return (DDI_SUCCESS);
}
static int
sysctrl_close(dev_t devp, int flag, int otyp, cred_t *credp)
{
return (DDI_SUCCESS);
}
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;
list = fhc_bdlist_lock(slot);
if ((slot != -1) && (list == NULL)) {
fhc_bdlist_unlock();
return (FALSE);
}
glist = fhc_bd_clock();
if (slot == -1)
list = glist;
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);
}
}
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
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);
}
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);
switch (cmd) {
case SYSC_CFGA_CMD_GETSTATUS:
if (sysc_enter_transition(-1) != TRUE) {
retval = EBUSY;
goto cleanup_exit;
}
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:
if (!(flag & FWRITE)) {
retval = EPERM;
goto cleanup_exit;
}
retval = sysc_pkt_init(&sysc_pkt, arg, flag);
if (retval != 0)
goto cleanup_exit;
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;
}
bdp = fhc_bd(slot);
sc = &bdp->sc;
break;
default:
retval = ENOTTY;
goto cleanup_exit;
}
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:
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;
}
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);
}
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);
csr = *(softsp->csr);
status2 = *(softsp->status2);
if (csr & SYS_AC_PWR_FAIL_EN) {
if (status2 & SYS_AC_FAIL) {
nvram_update_powerfail(softsp);
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)) {
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)) {
softsp->pps_fan_saved = status2;
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)) {
csr &= ~SYS_SBRD_PRES_EN;
ddi_trigger_softintr(softsp->sbrd_pres_id);
serviced++;
}
}
if (!serviced) {
softsp->saved_en_state |= csr &
(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
csr &= ~(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
softsp->spur_count++;
ddi_trigger_softintr(softsp->spur_id);
}
*(softsp->csr) = csr;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
return (DDI_INTR_CLAIMED);
}
static uint_t
spur_delay(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
mutex_enter(&softsp->csr_mutex);
if (softsp->spur_count == MAX_SPUR_COUNT) {
char buf[128];
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");
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);
if (softsp->spur_timeout_id == 0) {
softsp->spur_timeout_id = timeout(spur_retry, softsp,
spur_timeout_hz);
}
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);
}
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);
}
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);
*(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
softsp->saved_en_state = 0;
mutex_exit(&softsp->csr_mutex);
return (DDI_INTR_CLAIMED);
}
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);
}
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);
}
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);
}
static void
ac_fail_retry(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
if (*softsp->status2 & SYS_AC_FAIL) {
(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);
}
}
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);
}
static uint_t
ps_fail_int_handler(caddr_t arg)
{
return (ps_fail_handler((struct sysctrl_soft_state *)arg, 1));
}
static uint_t
ps_fail_poll_handler(caddr_t arg)
{
return (ps_fail_handler((struct sysctrl_soft_state *)arg, 0));
}
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);
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;
switch (i) {
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;
case SYS_PPS0_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = status2 & SYS_PPS0_OK;
break;
case SYS_CLK_33_INDEX:
temp_pres = TRUE;
temp_psok = status2 & SYS_CLK_33_OK;
break;
case SYS_CLK_50_INDEX:
temp_pres = TRUE;
temp_psok = status2 & SYS_CLK_50_OK;
break;
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;
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;
case SYS_V5_AUX_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V5_AUX_OK;
break;
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;
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;
case SYS_V3_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V3_PCH_OK;
is_precharge = TRUE;
break;
case SYS_V5_PCH_INDEX:
temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
temp_psok = pppsr & SYS_V5_PCH_OK;
is_precharge = TRUE;
break;
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;
}
if (pstatp->pshadow == PRES_UNKNOWN) {
pstatp->pshadow = temp_pres ? PRES_IN : PRES_OUT;
pstatp->dcshadow = temp_pres ? PS_BOOT : PS_OUT;
} else {
if (!temp_pres ^ (pstatp->pshadow == PRES_IN)) {
pstatp->pctr = 0;
} else {
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;
ps_log_pres_change(softsp,
i, temp_pres);
}
}
}
if ((pstatp->dcshadow == PS_OUT) ||
((pstatp->dcshadow == PS_OK) && temp_psok) ||
((pstatp->dcshadow == PS_FAIL) && !temp_psok)) {
pstatp->dcctr = 0;
} else {
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));
}
}
if (--pstatp->dcctr == 0) {
if (!((pstatp->dcshadow == PS_BOOT) &&
temp_psok)) {
ps_log_state_change(softsp,
i, temp_psok);
}
if ((i == SYS_V5_AUX_INDEX) &&
(pstatp->dcshadow != PS_BOOT) &&
(softsp->enable_rcons_atboot)) {
if (temp_psok)
rcons_reinit(softsp);
else
*(softsp->clk_freq2) &=
~RCONS_UART_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
pstatp->dcshadow = temp_psok ? PS_OK : PS_FAIL;
sysc_policy_update(softsp, NULL,
SYSC_EVT_BD_PS_CHANGE);
}
}
if (!temp_psok ||
(pstatp->dcshadow == PS_UNKNOWN) ||
(pstatp->dcshadow == PS_FAIL)) {
poll_needed++;
}
}
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 (softsp->power_state == REDUNDANT) {
cmn_err(CE_WARN, "Redundant power lost");
} 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 (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();
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 (!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);
}
if (!fromint) {
(void) timeout(ps_fail_retry, softsp, ps_fail_timeout_hz);
}
return (DDI_INTR_CLAIMED);
}
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));
for (i = 0; i < 8; i++) {
if (i == 7 &&
(IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)))
continue;
if (softsp->ps_stats[i].dcshadow == PS_OK)
ok_supply_count++;
}
pps_ok = (softsp->ps_stats[SYS_PPS0_INDEX].dcshadow == PS_OK);
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 (IS8SLOT(softsp->nslots) && load_count >= 7 && pps_ok)
ok_supply_count++;
if (IS5SLOT(softsp->nslots) && (load_count >= 5))
ok_supply_count++;
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);
}
static void
ps_log_pres_change(struct sysctrl_soft_state *softsp, int index, int present)
{
char *trans = present ? "Installed" : "Removed";
switch (index) {
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;
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;
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;
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;
}
}
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) {
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
}
}
static void
ps_fail_retry(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->ps_fail_poll_id);
}
static uint_t
pps_fanfail_handler(caddr_t arg)
{
struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
ASSERT(softsp);
(void) timeout(pps_fanfail_retry, softsp, pps_fan_timeout_hz);
return (DDI_INTR_CLAIMED);
}
static void
pps_fanfail_retry(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
ASSERT(softsp);
ddi_trigger_softintr(softsp->pps_fan_high_id);
}
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);
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);
}
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;
switch (i) {
case RACK:
fanfail = softsp->pps_fan_saved & SYS_RACK_FANFAIL;
break;
case AC:
fanfail = !(IS4SLOT(softsp->nslots) ||
IS5SLOT(softsp->nslots)) &&
!(softsp->pps_fan_saved & SYS_AC_FAN_OK);
break;
case KEYSW:
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;
}
if (fanfail) {
if (softsp->pps_fan_state_count[i] == 0) {
pps_fan_state_change(softsp, i, FALSE);
}
softsp->pps_fan_state_count[i] = PPS_FROM_FAIL_TICKS;
} else {
if (softsp->pps_fan_state_count[i]) {
if (--softsp->pps_fan_state_count[i] == 0) {
pps_fan_state_change(softsp, i, TRUE);
}
}
}
}
(void) timeout(pps_fan_poll, softsp, pps_fan_timeout_hz);
}
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:
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;
}
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"));
}
}
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 {
(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();
}
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);
(void) fhc_bdlist_lock(-1);
bd_remove_to_id = 0;
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);
if (!(*(softsp->status1) & SYS_NOT_BRD_PRES)) {
(void) timeout(bd_insert_timeout, softsp, bd_insert_retry_hz);
} else {
mutex_enter(&softsp->csr_mutex);
*(softsp->csr) |= SYS_SBRD_PRES_EN;
tmp_reg = *(softsp->csr);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->csr_mutex);
}
return (DDI_INTR_CLAIMED);
}
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);
tmp_reg = *(softsp->csr);
if (softsp->sys_led) {
tmp_reg |= SYS_LED_RIGHT;
} else {
tmp_reg &= ~SYS_LED_RIGHT;
}
if (softsp->sys_fault) {
tmp_reg |= SYS_LED_MID;
} else {
tmp_reg &= ~SYS_LED_MID;
}
*(softsp->csr) = tmp_reg;
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);
}
static void
blink_led_timeout(void *arg)
{
struct sysctrl_soft_state *softsp = arg;
int led_state;
ASSERT(softsp);
softsp->sys_fault = process_fault_list();
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();
}
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;
struct kstat *pksp;
struct kstat *tksp;
struct kstat *ttsp;
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);
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);
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
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;
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;
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");
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);
(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();
}
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");
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);
(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();
}
static void
update_key_state(struct sysctrl_soft_state *list)
{
enum keyswitch_state key;
if (*(list->status1) & SYS_NOT_SECURE)
key = KEY_NOT_SECURE;
else
key = KEY_SECURE;
if (key != list->key_shadow) {
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;
}
}
}
static void
sysctrl_abort_seq_handler(char *msg)
{
struct sysctrl_soft_state *list;
uint_t secure = 0;
char buf[64], inst[4];
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, ",");
(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;
};
struct uart_cmd uart_table[] = {
{ 0x09, 0xc0 },
{ 0x04, 0x46 },
{ 0x03, 0xc0 },
{ 0x05, 0xe2 },
{ 0x09, 0x02 },
{ 0x0b, 0x55 },
{ 0x0c, 0x0e },
{ 0x0d, 0x00 },
{ 0x0e, 0x02 },
{ 0x03, 0xc1 },
{ 0x05, 0xea },
{ 0x0e, 0x03 },
{ 0x00, 0x30 },
{ 0x00, 0x30 },
{ 0x00, 0x10 },
{ 0x03, 0xc1 },
{ TABLE_END, 0x0 }
};
static void
init_remote_console_uart(struct sysctrl_soft_state *softsp)
{
int i = 0;
while (uart_table[i].reg != TABLE_END) {
*(softsp->rcons_ctl) = uart_table[i].reg;
*(softsp->rcons_ctl) = uart_table[i].data;
i++;
}
}
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;
}
}
static void
rcons_reinit(struct sysctrl_soft_state *softsp)
{
uchar_t tmp_reg;
if (!(softsp->rcons_ctl))
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;
}
*(softsp->clk_freq2) &= ~RCONS_UART_EN;
tmp_reg = *(softsp->csr);
init_remote_console_uart(softsp);
*(softsp->clk_freq2) |= RCONS_UART_EN;
tmp_reg = *(softsp->csr);
cmn_err(CE_NOTE, "Remote console reinitialized");
#ifdef lint
tmp_reg = tmp_reg;
#endif
}