#include <sys/types.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ddi_impldefs.h>
#include <sys/obpdefs.h>
#include <sys/promif.h>
#include <sys/cmn_err.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/vmem.h>
#include <sys/debug.h>
#include <sys/sysmacros.h>
#include <sys/intreg.h>
#include <sys/autoconf.h>
#include <sys/modctl.h>
#include <sys/spl.h>
#include <sys/time.h>
#include <sys/systm.h>
#include <sys/machsystm.h>
#include <sys/cpu.h>
#include <sys/cpuvar.h>
#include <sys/x_call.h>
#include <sys/membar.h>
#include <sys/vm.h>
#include <vm/seg_kmem.h>
#include <vm/hat_sfmmu.h>
#include <sys/promimpl.h>
#include <sys/prom_plat.h>
#include <sys/cpu_module.h>
#include <sys/procset.h>
#include <sys/fhc.h>
#include <sys/ac.h>
#include <sys/environ.h>
#include <sys/jtag.h>
#include <sys/nexusdebug.h>
#include <sys/ac.h>
#include <sys/ddi_subrdefs.h>
#include <sys/eeprom.h>
#include <sys/sdt.h>
#include <sys/ddi_implfuncs.h>
#include <sys/ontrap.h>
#ifndef TRUE
#define TRUE (1)
#endif
#ifndef FALSE
#define FALSE (0)
#endif
extern void plat_register_tod_fault(void (*func)(enum tod_fault_type));
static int fhc_int_priorities[] = {
PIL_15,
PIL_12,
PIL_15,
PIL_15
};
static void fhc_tod_fault(enum tod_fault_type tod_bad);
static void fhc_cpu_shutdown_self(void);
static void os_completes_shutdown(void);
static int dont_calibrate = 0;
static int powerdown_started = 0;
int enable_overtemp_powerdown = 1;
static short cpu_table[] = {
-16, -14, -12, -10, -8, -6, -4, -2,
1, 4, 6, 8, 10, 12, 13, 15,
16, 18, 19, 20, 22, 23, 24, 25,
26, 27, 28, 29, 30, 31, 32, 33,
34, 35, 35, 36, 37, 38, 39, 39,
40, 41, 41, 42, 43, 44, 44, 45,
46, 46, 47, 47, 48, 49, 49, 50,
51, 51, 52, 53, 53, 54, 54, 55,
55, 56, 56, 57, 57, 58, 58, 59,
60, 60, 61, 61, 62, 62, 63, 63,
64, 64, 65, 65, 66, 66, 67, 67,
68, 68, 69, 69, 70, 70, 71, 71,
72, 72, 73, 73, 74, 74, 75, 75,
76, 76, 77, 77, 78, 78, 79, 79,
80, 80, 81, 81, 82, 82, 83, 83,
84, 84, 85, 85, 86, 86, 87, 87,
88, 88, 89, 89, 90, 90, 91, 91,
92, 92, 93, 93, 94, 94, 95, 95,
96, 96, 97, 98, 98, 99, 99, 100,
100, 101, 101, 102, 103, 103, 104, 104,
105, 106, 106, 107, 107, 108, 109, 109,
110,
};
#define CPU_MX_CNT (sizeof (cpu_table)/sizeof (short))
static short cpu2_table[] = {
-17, -16, -15, -14, -13, -12, -11, -10,
-9, -8, -7, -6, -5, -4, -3, -2,
-1, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 13,
14, 15, 16, 16, 17, 18, 18, 19,
20, 20, 21, 22, 22, 23, 24, 24,
25, 25, 26, 26, 27, 27, 28, 28,
29, 30, 30, 31, 31, 32, 32, 33,
33, 34, 34, 35, 35, 36, 36, 37,
37, 37, 38, 38, 39, 39, 40, 40,
41, 41, 42, 42, 43, 43, 43, 44,
44, 45, 45, 46, 46, 46, 47, 47,
48, 48, 49, 49, 50, 50, 50, 51,
51, 52, 52, 53, 53, 53, 54, 54,
55, 55, 56, 56, 56, 57, 57, 58,
58, 59, 59, 59, 60, 60, 61, 61,
62, 62, 63, 63, 63, 64, 64, 65,
65, 66, 66, 67, 67, 68, 68, 68,
69, 69, 70, 70, 71, 71, 72, 72,
73, 73, 74, 74, 75, 75, 76, 76,
77, 77, 78, 78, 79, 79, 80, 80,
81, 81, 82, 83, 83, 84, 84, 85,
85, 86, 87, 87, 88, 88, 89, 90,
90, 91, 92, 92, 93, 94, 94, 95,
96, 96, 97, 98, 99, 99, 100, 101,
102, 103, 103, 104, 105, 106, 107, 108,
109, 110,
};
#define CPU2_MX_CNT (sizeof (cpu2_table)/sizeof (short))
static short io_table[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 3, 7, 10, 13, 15, 17, 19,
21, 23, 25, 27, 28, 30, 31, 32,
34, 35, 36, 37, 38, 39, 41, 42,
43, 44, 45, 46, 46, 47, 48, 49,
50, 51, 52, 53, 53, 54, 55, 56,
57, 57, 58, 59, 60, 60, 61, 62,
62, 63, 64, 64, 65, 66, 66, 67,
68, 68, 69, 70, 70, 71, 72, 72,
73, 73, 74, 75, 75, 76, 77, 77,
78, 78, 79, 80, 80, 81, 81, 82,
};
#define IO_MN_CNT 40
#define IO_MX_CNT (sizeof (io_table)/sizeof (short))
static short clock_table[] = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 4, 5,
7, 8, 10, 11, 12, 13, 14, 15,
17, 18, 19, 20, 21, 22, 23, 24,
24, 25, 26, 27, 28, 29, 29, 30,
31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 38, 39, 40, 40, 41, 42,
42, 43, 44, 44, 45, 46, 46, 47,
48, 48, 49, 50, 50, 51, 52, 52,
53, 54, 54, 55, 56, 57, 57, 58,
59, 59, 60, 60, 61, 62, 63, 63,
64, 65, 65, 66, 67, 68, 68, 69,
70, 70, 71, 72, 73, 74, 74, 75,
76, 77, 78, 78, 79, 80, 81, 82,
};
#define CLK_MN_CNT 11
#define CLK_MX_CNT (sizeof (clock_table)/sizeof (short))
short cpu_warn_temp = 73;
short cpu_danger_temp = 83;
short io_warn_temp = 60;
short io_danger_temp = 68;
short clk_warn_temp = 60;
short clk_danger_temp = 68;
short dft_warn_temp = 60;
short dft_danger_temp = 68;
short cpu_warn_temp_4x = 60;
short cpu_danger_temp_4x = 68;
static int temperature_chamber = -1;
static struct fhc_memloc *fhc_base_memloc = NULL;
static kmutex_t ftlist_mutex;
static struct ft_link_list *ft_list = NULL;
static int ft_nfaults = 0;
char *ft_str_table[] = {
"Core Power Supply",
"Overtemp",
"AC Power",
"Peripheral Power Supply",
"System 3.3 Volt Power",
"System 5.0 Volt Power",
"Peripheral 5.0 Volt Power",
"Peripheral 12 Volt Power",
"Auxiliary 5.0 Volt Power",
"Peripheral 5.0 Volt Precharge",
"Peripheral 12 Volt Precharge",
"System 3.3 Volt Precharge",
"System 5.0 Volt Precharge",
"Peripheral Power Supply Fans",
"Rack Exhaust Fan",
"Disk Drive Fan",
"AC Box Fan",
"Key Switch Fan",
"Minimum Power",
"PROM detected",
"Hot Plug Support System",
"TOD"
};
static int ft_max_index = (sizeof (ft_str_table) / sizeof (char *));
static int fhc_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
void *, void *);
static int fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
static int fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static void fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp);
static int fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
static int fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
static int fhc_init(struct fhc_soft_state *softsp);
static void fhc_unmap_regs(struct fhc_soft_state *softsp);
static enum board_type fhc_board_type(struct fhc_soft_state *, int);
static void
fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign);
static int
fhc_ctlops_peekpoke(ddi_ctl_enum_t, peekpoke_ctlops_t *, void *result);
static void fhc_add_kstats(struct fhc_soft_state *);
static int fhc_kstat_update(kstat_t *, int);
static int check_for_chamber(void);
static int ft_ks_snapshot(struct kstat *, void *, int);
static int ft_ks_update(struct kstat *, int);
static int check_central(int board);
static short calibrate_temp(enum board_type, uchar_t, uint_t);
static enum temp_state get_temp_state(enum board_type, short, int);
static int cpu_on_board(int);
static void build_bd_display_str(char *, enum board_type, int);
static void fhc_intrdist(void *);
int fhc_cpu_poweroff(struct cpu *);
int fhc_cpu_poweron(struct cpu *);
extern struct cpu_node cpunodes[];
extern void halt(char *);
static struct bus_ops fhc_bus_ops = {
BUSO_REV,
ddi_bus_map,
0,
0,
0,
i_ddi_map_fault,
ddi_no_dma_map,
ddi_no_dma_allochdl,
ddi_no_dma_freehdl,
ddi_no_dma_bindhdl,
ddi_no_dma_unbindhdl,
ddi_no_dma_flush,
ddi_no_dma_win,
ddi_dma_mctl,
fhc_ctlops,
ddi_bus_prop_op,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
fhc_intr_ops
};
static struct cb_ops fhc_cb_ops = {
nulldev,
nulldev,
nulldev,
nulldev,
nulldev,
nulldev,
nulldev,
nulldev,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
0,
D_MP|D_NEW|D_HOTPLUG,
CB_REV,
nodev,
nodev
};
static struct dev_ops fhc_ops = {
DEVO_REV,
0,
ddi_no_info,
nulldev,
nulldev,
fhc_attach,
fhc_detach,
nulldev,
&fhc_cb_ops,
&fhc_bus_ops,
nulldev,
ddi_quiesce_not_needed,
};
void *fhcp;
extern struct mod_ops mod_driverops;
static struct modldrv modldrv = {
&mod_driverops,
"FHC Nexus",
&fhc_ops,
};
static struct modlinkage modlinkage = {
MODREV_1,
(void *)&modldrv,
NULL
};
static caddr_t shutdown_va;
int
_init(void)
{
int error;
if ((error = ddi_soft_state_init(&fhcp,
sizeof (struct fhc_soft_state), 1)) != 0)
return (error);
fhc_bdlist_init();
mutex_init(&ftlist_mutex, NULL, MUTEX_DEFAULT, NULL);
shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
ASSERT(shutdown_va != NULL);
plat_register_tod_fault(fhc_tod_fault);
return (mod_install(&modlinkage));
}
int
_fini(void)
{
int error;
if ((error = mod_remove(&modlinkage)) != 0)
return (error);
plat_register_tod_fault(NULL);
mutex_destroy(&ftlist_mutex);
fhc_bdlist_fini();
ddi_soft_state_fini(&fhcp);
return (0);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
static void
fhc_handle_imr(struct fhc_soft_state *softsp)
{
int i;
int cent;
uint_t tmp_reg;
if (softsp->is_central) {
uint_t want_igr, act_igr;
want_igr = softsp->list->sc.board << 1;
act_igr = *softsp->igr & 0x1f;
if (want_igr != act_igr) {
*softsp->igr = want_igr;
tmp_reg = *softsp->igr;
#ifdef lint
tmp_reg = tmp_reg;
#endif
for (i = 0; i < FHC_MAX_INO; i++) {
if (*(softsp->intr_regs[i].clear_reg) == 3) {
*(softsp->intr_regs[i].clear_reg) =
ISM_IDLE;
tmp_reg =
*(softsp->intr_regs[i].clear_reg);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
}
cmn_err(CE_NOTE, "central IGN corruption fixed: "
"got %x wanted %x", act_igr, want_igr);
}
return;
}
ASSERT(softsp->list->sc.board == FHC_BSR_TO_BD(*(softsp->bsr)));
cent = check_central(softsp->list->sc.board);
for (i = 0; i < FHC_MAX_INO; i++) {
if (i == FHC_SYS_INO &&
*(softsp->intr_regs[i].clear_reg) == 3) {
cmn_err(CE_NOTE,
"found lost system interrupt, resetting..");
*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
tmp_reg = *(softsp->intr_regs[i].clear_reg);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
if (cent)
continue;
*(softsp->intr_regs[i].mapping_reg) = 0;
tmp_reg = *(softsp->intr_regs[i].mapping_reg);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
}
static int
check_central(int board)
{
uint_t cs_value;
cs_value = ldphysio(AC_BCSR(board));
if (cs_value & AC_CENTRAL)
return (TRUE);
else
return (FALSE);
}
static int
fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
struct fhc_soft_state *softsp;
int instance;
instance = ddi_get_instance(devi);
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
softsp = ddi_get_soft_state(fhcp, instance);
fhc_handle_imr(softsp);
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
if (ddi_soft_state_zalloc(fhcp, instance) != DDI_SUCCESS)
return (DDI_FAILURE);
softsp = ddi_get_soft_state(fhcp, instance);
softsp->dip = devi;
if (fhc_init(softsp) != DDI_SUCCESS)
goto bad;
ddi_report_dev(devi);
return (DDI_SUCCESS);
bad:
ddi_soft_state_free(fhcp, instance);
return (DDI_FAILURE);
}
static int
fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
int board;
int instance;
struct fhc_soft_state *softsp;
fhc_bd_t *list = NULL;
instance = ddi_get_instance(devi);
softsp = ddi_get_soft_state(fhcp, instance);
board = softsp->list->sc.board;
switch (cmd) {
case DDI_SUSPEND:
return (DDI_SUCCESS);
case DDI_DETACH:
list = fhc_bdlist_lock(board);
if (fhc_bd_detachable(board) &&
!fhc_bd_is_jtag_master(board))
break;
else
fhc_bdlist_unlock();
default:
return (DDI_FAILURE);
}
intr_dist_rem(fhc_intrdist, (void *)devi);
list->softsp = NULL;
clear_fault(list->sc.board, FT_PROM, FT_BOARD);
kstat_delete(softsp->fhc_ksp);
mutex_destroy(&softsp->poll_list_lock);
mutex_destroy(&softsp->ctrl_lock);
fhc_unmap_regs(softsp);
fhc_bdlist_unlock();
ddi_soft_state_free(fhcp, instance);
return (DDI_SUCCESS);
}
static enum board_type
fhc_board_type(struct fhc_soft_state *softsp, int board)
{
int proplen;
char *board_type;
enum board_type type;
if (softsp->is_central)
type = CLOCK_BOARD;
else if (ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, "board-type", (caddr_t)&board_type,
&proplen) == DDI_PROP_SUCCESS) {
if (strcmp(CPU_BD_NAME, board_type) == 0) {
type = CPU_BOARD;
} else if (strcmp(MEM_BD_NAME, board_type) == 0) {
type = MEM_BOARD;
} else if (strcmp(IO_2SBUS_BD_NAME, board_type) == 0) {
type = IO_2SBUS_BOARD;
} else if (strcmp(IO_SBUS_FFB_BD_NAME, board_type) == 0) {
type = IO_SBUS_FFB_BOARD;
} else if (strcmp(IO_2SBUS_SOCPLUS_BD_NAME, board_type) == 0) {
type = IO_2SBUS_SOCPLUS_BOARD;
} else if (strcmp(IO_SBUS_FFB_SOCPLUS_BD_NAME, board_type)
== 0) {
type = IO_SBUS_FFB_SOCPLUS_BOARD;
} else if (strcmp(IO_PCI_BD_NAME, board_type) == 0) {
type = IO_PCI_BOARD;
} else {
type = UNKNOWN_BOARD;
}
kmem_free(board_type, proplen);
} else
type = UNKNOWN_BOARD;
if (type == UNKNOWN_BOARD) {
if (cpu_on_board(board))
type = CPU_BOARD;
else if ((*(softsp->bsr) & FHC_UPADATA64A) ||
(*(softsp->bsr) & FHC_UPADATA64B))
type = IO_2SBUS_BOARD;
else
type = MEM_BOARD;
}
return (type);
}
static void
fhc_unmap_regs(struct fhc_soft_state *softsp)
{
dev_info_t *dip = softsp->dip;
if (softsp->id) {
ddi_unmap_regs(dip, 0, (caddr_t *)&softsp->id, 0, 0);
softsp->id = NULL;
}
if (softsp->igr) {
ddi_unmap_regs(dip, 1, (caddr_t *)&softsp->igr, 0, 0);
softsp->igr = NULL;
}
if (softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg) {
ddi_unmap_regs(dip, 2,
(caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
0, 0);
softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg = NULL;
}
if (softsp->intr_regs[FHC_SYS_INO].mapping_reg) {
ddi_unmap_regs(dip, 3,
(caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
0, 0);
softsp->intr_regs[FHC_SYS_INO].mapping_reg = NULL;
}
if (softsp->intr_regs[FHC_UART_INO].mapping_reg) {
ddi_unmap_regs(dip, 4,
(caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
0, 0);
softsp->intr_regs[FHC_UART_INO].mapping_reg = NULL;
}
if (softsp->intr_regs[FHC_TOD_INO].mapping_reg) {
ddi_unmap_regs(dip, 5,
(caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
0, 0);
softsp->intr_regs[FHC_TOD_INO].mapping_reg = NULL;
}
}
static int
fhc_init(struct fhc_soft_state *softsp)
{
int i;
uint_t tmp_reg;
int board;
if (ddi_map_regs(softsp->dip, 0,
(caddr_t *)&softsp->id, 0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map internal "
"registers", ddi_get_instance(softsp->dip));
goto bad;
}
softsp->rctrl = (uint_t *)((char *)(softsp->id) +
FHC_OFF_RCTRL);
softsp->ctrl = (uint_t *)((char *)(softsp->id) +
FHC_OFF_CTRL);
softsp->bsr = (uint_t *)((char *)(softsp->id) +
FHC_OFF_BSR);
softsp->jtag_ctrl = (uint_t *)((char *)(softsp->id) +
FHC_OFF_JTAG_CTRL);
softsp->jt_master.jtag_cmd = (uint_t *)((char *)(softsp->id) +
FHC_OFF_JTAG_CMD);
if (ddi_map_regs(softsp->dip, 1,
(caddr_t *)&softsp->igr, 0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map IGR "
"register", ddi_get_instance(softsp->dip));
goto bad;
}
if (ddi_map_regs(softsp->dip, 2,
(caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map Fan Fail "
"IMR register", ddi_get_instance(softsp->dip));
goto bad;
}
if (ddi_map_regs(softsp->dip, 3,
(caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map System "
"IMR register\n", ddi_get_instance(softsp->dip));
goto bad;
}
if (ddi_map_regs(softsp->dip, 4,
(caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map UART "
"IMR register\n", ddi_get_instance(softsp->dip));
goto bad;
}
if (ddi_map_regs(softsp->dip, 5,
(caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
0, 0)) {
cmn_err(CE_WARN, "fhc%d: unable to map FHC TOD "
"IMR register", ddi_get_instance(softsp->dip));
goto bad;
}
for (i = 0; i < FHC_MAX_INO; i++) {
softsp->intr_regs[i].clear_reg =
(uint_t *)((char *)(softsp->intr_regs[i].mapping_reg) +
FHC_OFF_ISMR);
*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
}
if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
board = FHC_BSR_TO_BD(*(softsp->bsr));
softsp->is_central = 1;
}
if ((*(softsp->jtag_ctrl) & JTAG_MASTER_EN) && !softsp->is_central) {
mutex_init(&(softsp->jt_master.lock), NULL, MUTEX_DEFAULT,
NULL);
softsp->jt_master.is_master = 1;
} else {
softsp->jt_master.is_master = 0;
}
fhc_bd_init(softsp, board, fhc_board_type(softsp, board));
mutex_init(&softsp->poll_list_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&softsp->ctrl_lock, NULL, MUTEX_DRIVER, NULL);
for (i = 0; i < MAX_ZS_CNT; i++) {
softsp->poll_list[i].funcp = NULL;
}
if (!(softsp->is_central)) {
mutex_enter(&softsp->ctrl_lock);
*(softsp->ctrl) |= FHC_NOT_BRD_PRES;
tmp_reg = *(softsp->ctrl);
#ifdef lint
tmp_reg = tmp_reg;
#endif
mutex_exit(&softsp->ctrl_lock);
fhc_add_kstats(softsp);
}
if (temperature_chamber == -1) {
temperature_chamber = check_for_chamber();
}
if (*softsp->ctrl & FHC_LED_MID) {
reg_fault(softsp->list->sc.board, FT_PROM, FT_BOARD);
}
*(softsp->igr) = (softsp->list->sc.board) << 1;
tmp_reg = *(softsp->id);
#ifdef lint
tmp_reg = tmp_reg;
#endif
intr_dist_add(fhc_intrdist, (void *)softsp->dip);
return (DDI_SUCCESS);
bad:
fhc_unmap_regs(softsp);
return (DDI_FAILURE);
}
static uint_t
fhc_intr_wrapper(caddr_t arg)
{
uint_t intr_return;
uint_t tmpreg;
struct fhc_wrapper_arg *intr_info = (struct fhc_wrapper_arg *)arg;
uint_t (*funcp)(caddr_t, caddr_t) = intr_info->funcp;
caddr_t iarg1 = intr_info->arg1;
caddr_t iarg2 = intr_info->arg2;
dev_info_t *dip = intr_info->child;
tmpreg = ISM_IDLE;
DTRACE_PROBE4(interrupt__start, dev_info_t, dip,
void *, funcp, caddr_t, iarg1, caddr_t, iarg2);
intr_return = (*funcp)(iarg1, iarg2);
DTRACE_PROBE4(interrupt__complete, dev_info_t, dip,
void *, funcp, caddr_t, iarg1, int, intr_return);
*(intr_info->clear_reg) = tmpreg;
tmpreg = *(intr_info->clear_reg);
#ifdef lint
tmpreg = tmpreg;
#endif
return (intr_return);
}
#define MAX_INTR_CNT 10
static uint_t
fhc_zs_intr_wrapper(caddr_t arg)
{
struct fhc_soft_state *softsp = (struct fhc_soft_state *)arg;
uint_t (*funcp0)(caddr_t, caddr_t);
uint_t (*funcp1)(caddr_t, caddr_t);
caddr_t funcp0_arg1, funcp0_arg2, funcp1_arg1, funcp1_arg2;
uint_t tmp_reg;
uint_t result = DDI_INTR_UNCLAIMED;
volatile uint_t *clear_reg;
uchar_t *spurious_cntr = &softsp->spurious_zs_cntr;
funcp0 = softsp->poll_list[0].funcp;
funcp1 = softsp->poll_list[1].funcp;
funcp0_arg1 = softsp->poll_list[0].arg1;
funcp0_arg2 = softsp->poll_list[0].arg2;
funcp1_arg1 = softsp->poll_list[1].arg1;
funcp1_arg2 = softsp->poll_list[1].arg2;
clear_reg = softsp->intr_regs[FHC_UART_INO].clear_reg;
if (funcp0 != NULL) {
if ((funcp0)(funcp0_arg1, funcp0_arg2) == DDI_INTR_CLAIMED) {
result = DDI_INTR_CLAIMED;
}
}
if (funcp1 != NULL) {
if ((funcp1)(funcp1_arg1, funcp1_arg2) == DDI_INTR_CLAIMED) {
result = DDI_INTR_CLAIMED;
}
}
if (result == DDI_INTR_UNCLAIMED) {
(*spurious_cntr)++;
if (*spurious_cntr < MAX_INTR_CNT) {
result = DDI_INTR_CLAIMED;
} else {
*spurious_cntr = (uchar_t)0;
}
} else {
*spurious_cntr = (uchar_t)0;
}
*(clear_reg) = ISM_IDLE;
tmp_reg = *(clear_reg);
#ifdef lint
tmp_reg = tmp_reg;
#endif
return (result);
}
static int
fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
int ino;
struct fhc_wrapper_arg *fhc_arg;
struct fhc_soft_state *softsp = (struct fhc_soft_state *)
ddi_get_soft_state(fhcp, ddi_get_instance(dip));
volatile uint_t *mondo_vec_reg;
uint_t tmp_mondo_vec;
uint_t tmpreg;
uint_t cpu_id;
int ret = DDI_SUCCESS;
fhc_xlate_intrs(hdlp,
(softsp->list->sc.board << BD_IVINTR_SHFT));
ino = FHC_INO(hdlp->ih_vector);
mondo_vec_reg = softsp->intr_regs[ino].mapping_reg;
ASSERT(ino < FHC_MAX_INO);
if (ino >= FHC_MAX_INO) {
cmn_err(CE_WARN, "fhc%d: Spare interrupt %d not usable",
ddi_get_instance(dip), ino);
return (DDI_FAILURE);
}
if (ino == FHC_TOD_INO) {
cmn_err(CE_WARN, "fhc%d: TOD interrupt not usable",
ddi_get_instance(dip));
return (DDI_FAILURE);
}
if (ino == FHC_FANFAIL_INO) {
cmn_err(CE_WARN, "fhc%d: Fan fail interrupt not usable",
ddi_get_instance(dip));
return (DDI_FAILURE);
}
if (ino == FHC_UART_INO) {
int32_t zs_inst;
mutex_enter(&softsp->poll_list_lock);
for (zs_inst = 0; zs_inst < MAX_ZS_CNT; zs_inst++) {
if (softsp->poll_list[zs_inst].funcp == NULL) {
softsp->poll_list[zs_inst].arg1 =
hdlp->ih_cb_arg1;
softsp->poll_list[zs_inst].arg2 =
hdlp->ih_cb_arg2;
softsp->poll_list[zs_inst].funcp =
(ddi_intr_handler_t *)
hdlp->ih_cb_func;
softsp->poll_list[zs_inst].inum =
hdlp->ih_inum;
softsp->poll_list[zs_inst].child = rdip;
break;
}
}
if (zs_inst >= MAX_ZS_CNT) {
cmn_err(CE_WARN,
"fhc%d: poll list overflow",
ddi_get_instance(dip));
mutex_exit(&softsp->poll_list_lock);
ret = DDI_FAILURE;
goto done;
}
if (zs_inst == 0) {
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
(ddi_intr_handler_t *)fhc_zs_intr_wrapper,
(caddr_t)softsp, NULL);
ret = i_ddi_add_ivintr(hdlp);
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
softsp->poll_list[zs_inst].funcp,
softsp->poll_list[zs_inst].arg1,
softsp->poll_list[zs_inst].arg2);
if (ret != DDI_SUCCESS)
goto done;
}
if (zs_inst > 0) {
mutex_exit(&softsp->poll_list_lock);
goto done;
} else {
mutex_exit(&softsp->poll_list_lock);
}
} else {
int32_t i;
fhc_arg = kmem_alloc(sizeof (struct fhc_wrapper_arg), KM_SLEEP);
fhc_arg->child = rdip;
fhc_arg->mapping_reg = mondo_vec_reg;
fhc_arg->clear_reg = (softsp->intr_regs[ino].clear_reg);
fhc_arg->softsp = softsp;
fhc_arg->funcp =
(ddi_intr_handler_t *)hdlp->ih_cb_func;
fhc_arg->arg1 = hdlp->ih_cb_arg1;
fhc_arg->arg2 = hdlp->ih_cb_arg2;
fhc_arg->inum = hdlp->ih_inum;
for (i = 0; i < FHC_MAX_INO; i++) {
if (softsp->intr_list[i] == 0) {
softsp->intr_list[i] = fhc_arg;
break;
}
}
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
(ddi_intr_handler_t *)fhc_intr_wrapper,
(caddr_t)fhc_arg, NULL);
ret = i_ddi_add_ivintr(hdlp);
DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, fhc_arg->funcp,
fhc_arg->arg1, fhc_arg->arg2);
if (ret != DDI_SUCCESS)
goto done;
}
*(softsp->intr_regs[ino].clear_reg) = ISM_IDLE;
cpu_id = intr_dist_cpuid();
tmp_mondo_vec = cpu_id << INR_PID_SHIFT;
if (ino == FHC_FANFAIL_INO)
panic("fhc%d: enabling fanfail interrupt",
ddi_get_instance(dip));
else
tmp_mondo_vec |= IMR_VALID;
DPRINTF(FHC_INTERRUPT_DEBUG,
("Mondo 0x%x mapping reg: 0x%p", hdlp->ih_vector,
(void *)mondo_vec_reg));
*mondo_vec_reg = tmp_mondo_vec;
tmpreg = *(softsp->id);
#ifdef lint
tmpreg = tmpreg;
#endif
done:
return (ret);
}
static void
fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
ddi_intr_handle_impl_t *hdlp)
{
volatile uint_t *mondo_vec_reg;
volatile uint_t tmpreg;
int i;
struct fhc_soft_state *softsp = (struct fhc_soft_state *)
ddi_get_soft_state(fhcp, ddi_get_instance(dip));
int ino;
fhc_xlate_intrs(hdlp,
(softsp->list->sc.board << BD_IVINTR_SHFT));
ino = FHC_INO(hdlp->ih_vector);
if (ino == FHC_UART_INO) {
int intr_found = 0;
mutex_enter(&softsp->poll_list_lock);
for (i = 0; i < MAX_ZS_CNT; i++) {
if (softsp->poll_list[i].child == rdip &&
softsp->poll_list[i].inum == hdlp->ih_inum) {
softsp->poll_list[i].funcp = NULL;
intr_found++;
}
}
if (!intr_found) {
cmn_err(CE_WARN, "fhc%d: Intrspec not found in"
" poll list", ddi_get_instance(dip));
mutex_exit(&softsp->poll_list_lock);
goto done;
}
if ((softsp->poll_list[0].funcp == NULL) &&
(softsp->poll_list[1].funcp == NULL)) {
mondo_vec_reg =
softsp->intr_regs[FHC_UART_INO].mapping_reg;
*mondo_vec_reg &= ~IMR_VALID;
tmpreg = *(softsp->ctrl);
i_ddi_rem_ivintr(hdlp);
}
mutex_exit(&softsp->poll_list_lock);
} else {
int32_t i;
for (i = 0; i < FHC_MAX_INO; i++)
if (softsp->intr_list[i]->child == rdip &&
softsp->intr_list[i]->inum == hdlp->ih_inum)
break;
if (i >= FHC_MAX_INO)
goto done;
mondo_vec_reg = softsp->intr_list[i]->mapping_reg;
*mondo_vec_reg &= ~IMR_VALID;
tmpreg = *(softsp->id);
#ifdef lint
tmpreg = tmpreg;
#endif
i_ddi_rem_ivintr(hdlp);
kmem_free(softsp->intr_list[i],
sizeof (struct fhc_wrapper_arg));
softsp->intr_list[i] = 0;
}
done:
;
}
static int
fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
ddi_intr_handle_impl_t *hdlp, void *result)
{
int ret = DDI_SUCCESS;
switch (intr_op) {
case DDI_INTROP_GETCAP:
*(int *)result = DDI_INTR_FLAG_LEVEL;
break;
case DDI_INTROP_ALLOC:
*(int *)result = hdlp->ih_scratch1;
break;
case DDI_INTROP_FREE:
break;
case DDI_INTROP_GETPRI:
if (hdlp->ih_pri == 0) {
struct fhc_soft_state *softsp =
(struct fhc_soft_state *)ddi_get_soft_state(fhcp,
ddi_get_instance(dip));
fhc_xlate_intrs(hdlp,
(softsp->list->sc.board << BD_IVINTR_SHFT));
}
*(int *)result = hdlp->ih_pri;
break;
case DDI_INTROP_SETPRI:
break;
case DDI_INTROP_ADDISR:
ret = fhc_add_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_REMISR:
fhc_remove_intr_impl(dip, rdip, hdlp);
break;
case DDI_INTROP_ENABLE:
case DDI_INTROP_DISABLE:
break;
case DDI_INTROP_NINTRS:
case DDI_INTROP_NAVAIL:
*(int *)result = i_ddi_get_intx_nintrs(rdip);
break;
case DDI_INTROP_SETCAP:
case DDI_INTROP_SETMASK:
case DDI_INTROP_CLRMASK:
case DDI_INTROP_GETPENDING:
ret = DDI_ENOTSUP;
break;
case DDI_INTROP_SUPPORTED_TYPES:
*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
DDI_INTR_TYPE_FIXED : 0;
break;
default:
ret = i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result);
break;
}
return (ret);
}
static int
fhc_ctlops(dev_info_t *dip, dev_info_t *rdip,
ddi_ctl_enum_t op, void *arg, void *result)
{
switch (op) {
case DDI_CTLOPS_INITCHILD:
DPRINTF(FHC_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
return (impl_ddi_sunbus_initchild((dev_info_t *)arg));
case DDI_CTLOPS_UNINITCHILD:
impl_ddi_sunbus_removechild((dev_info_t *)arg);
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
return (DDI_SUCCESS);
case DDI_CTLOPS_POKE:
case DDI_CTLOPS_PEEK:
return (fhc_ctlops_peekpoke(op, (peekpoke_ctlops_t *)arg,
result));
default:
return (ddi_ctlops(dip, rdip, op, arg, result));
}
}
static void
fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign)
{
uint32_t mondo;
mondo = hdlp->ih_vector;
hdlp->ih_vector = (mondo | ign);
if (hdlp->ih_pri == 0)
hdlp->ih_pri = fhc_int_priorities[FHC_INO(mondo)];
}
static int
fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args,
void *result)
{
int err = DDI_SUCCESS;
on_trap_data_t otd;
if (in_args->handle != NULL)
return (DDI_FAILURE);
if (!on_trap(&otd, OT_DATA_ACCESS)) {
uintptr_t tramp = otd.ot_trampoline;
if (cmd == DDI_CTLOPS_POKE) {
otd.ot_trampoline = (uintptr_t)&poke_fault;
err = do_poke(in_args->size, (void *)in_args->dev_addr,
(void *)in_args->host_addr);
} else {
otd.ot_trampoline = (uintptr_t)&peek_fault;
err = do_peek(in_args->size, (void *)in_args->dev_addr,
(void *)in_args->host_addr);
result = (void *)in_args->host_addr;
}
otd.ot_trampoline = tramp;
} else
err = DDI_FAILURE;
no_trap();
return (err);
}
void
init_temp_arrays(struct temp_stats *envstat)
{
int i;
envstat->index = 0;
for (i = 0; i < L1_SZ; i++) {
envstat->l1[i] = NA_TEMP;
}
for (i = 0; i < L2_SZ; i++) {
envstat->l2[i] = NA_TEMP;
}
for (i = 0; i < L3_SZ; i++) {
envstat->l3[i] = NA_TEMP;
}
for (i = 0; i < L4_SZ; i++) {
envstat->l4[i] = NA_TEMP;
}
for (i = 0; i < L5_SZ; i++) {
envstat->l5[i] = NA_TEMP;
}
envstat->max = NA_TEMP;
envstat->min = NA_TEMP;
envstat->trend = TREND_UNKNOWN;
envstat->version = TEMP_KSTAT_VERSION;
envstat->override = NA_TEMP;
}
static uint_t fhc_cpu_warning_temp_threshold = FHC_CPU_WARNING_TEMP_THRESHOLD;
void
update_temp(dev_info_t *pdip, struct temp_stats *envstat, uchar_t value)
{
uint_t index;
uint_t tmp_index;
int count;
int total;
short real_temp;
int i;
struct fhc_soft_state *softsp;
char buffer[256];
enum temp_state temp_state;
static int shutdown_msg = 0;
softsp = ddi_get_soft_state(fhcp, ddi_get_instance(pdip));
envstat->index++;
index = envstat->index;
if (((tmp_index = L5_INDEX(index)) > 0) && (L5_REM(index) == 0)) {
tmp_index -= 1;
tmp_index = tmp_index % L5_SZ;
for (i = 0, count = 0, total = 0; i < L4_SZ; i++) {
if (envstat->l4[i] != NA_TEMP) {
total += (int)envstat->l4[i];
count++;
}
}
if (count != 0) {
envstat->l5[tmp_index] = total/count;
} else {
envstat->l5[tmp_index] = NA_TEMP;
}
}
if (((tmp_index = L4_INDEX(index)) > 0) && (L4_REM(index) == 0)) {
tmp_index -= 1;
tmp_index = tmp_index % L4_SZ;
for (i = 0, count = 0, total = 0; i < L3_SZ; i++) {
if (envstat->l3[i] != NA_TEMP) {
total += (int)envstat->l3[i];
count++;
}
}
if (count != 0) {
envstat->l4[tmp_index] = total/count;
} else {
envstat->l4[tmp_index] = NA_TEMP;
}
}
if (((tmp_index = L3_INDEX(index)) > 0) && (L3_REM(index) == 0)) {
tmp_index -= 1;
tmp_index = tmp_index % L3_SZ;
for (i = 0, count = 0, total = 0; i < L2_SZ; i++) {
if (envstat->l2[i] != NA_TEMP) {
total += (int)envstat->l2[i];
count++;
}
}
if (count != 0) {
envstat->l3[tmp_index] = total/count;
} else {
envstat->l3[tmp_index] = NA_TEMP;
}
}
if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0)) {
tmp_index -= 1;
tmp_index = tmp_index % L2_SZ;
for (i = 0, count = 0, total = 0; i < L1_SZ; i++) {
if (envstat->l1[i] != NA_TEMP) {
total += (int)envstat->l1[i];
count++;
}
}
if (count != 0) {
envstat->l2[tmp_index] = total/count;
} else {
envstat->l2[tmp_index] = NA_TEMP;
}
}
if (envstat->override != NA_TEMP) {
real_temp = envstat->override;
} else {
real_temp = calibrate_temp(softsp->list->sc.type, value,
softsp->list->sc.ac_compid);
}
envstat->l1[index % L1_SZ] = real_temp;
temp_state = get_temp_state(softsp->list->sc.type, real_temp,
softsp->list->sc.board);
if (temp_state != envstat->state) {
int board = softsp->list->sc.board;
enum board_type type = softsp->list->sc.type;
build_bd_display_str(buffer, type, board);
if (temp_state > envstat->state) {
if (envstat->state == TEMP_OK) {
if (type == CLOCK_BOARD) {
reg_fault(0, FT_OVERTEMP, FT_SYSTEM);
} else {
reg_fault(board, FT_OVERTEMP,
FT_BOARD);
}
}
envstat->temp_cnt = 0;
envstat->state = temp_state;
if (temp_state == TEMP_WARN) {
cmn_err(CE_WARN,
"%s is warm (temperature: %dC). "
"Please check system cooling", buffer,
real_temp);
fhc_bd_update(board, SYSC_EVT_BD_OVERTEMP);
if (temperature_chamber == -1)
temperature_chamber =
check_for_chamber();
} else if (temp_state == TEMP_DANGER) {
cmn_err(CE_WARN,
"%s is very hot (temperature: %dC)",
buffer, real_temp);
envstat->shutdown_cnt = 1;
if (temperature_chamber == -1)
temperature_chamber =
check_for_chamber();
if ((temperature_chamber == 0) &&
enable_overtemp_powerdown) {
if (shutdown_msg == 0) {
cmn_err(CE_WARN, "System "
"shutdown scheduled "
"in %d seconds due to "
"over-temperature "
"condition on %s",
SHUTDOWN_TIMEOUT_SEC,
buffer);
}
shutdown_msg++;
}
}
if (temperature_chamber == 0) {
mutex_enter(&cpu_lock);
(void) fhc_board_poweroffcpus(board, NULL,
CPU_FORCED);
mutex_exit(&cpu_lock);
}
} else if (temp_state < envstat->state) {
envstat->shutdown_cnt = 0;
if (envstat->temp_cnt == 0) {
envstat->temp_cnt = TEMP_STATE_COUNT;
} else if (--envstat->temp_cnt == 0) {
if (temp_state == TEMP_WARN) {
cmn_err(CE_NOTE,
"%s is cooling "
"(temperature: %dC)", buffer,
real_temp);
} else if (temp_state == TEMP_OK) {
cmn_err(CE_NOTE,
"%s has cooled down "
"(temperature: %dC), system OK",
buffer, real_temp);
if (type == CLOCK_BOARD) {
clear_fault(0, FT_OVERTEMP,
FT_SYSTEM);
} else {
clear_fault(board, FT_OVERTEMP,
FT_BOARD);
}
}
if (envstat->state == TEMP_DANGER &&
(temperature_chamber == 0) &&
enable_overtemp_powerdown &&
(powerdown_started == 0) &&
(--shutdown_msg == 0)) {
cmn_err(CE_NOTE, "System "
"shutdown due to over-"
"temperature "
"condition cancelled");
}
envstat->state = temp_state;
fhc_bd_update(board, SYSC_EVT_BD_TEMP_OK);
}
}
} else {
envstat->temp_cnt = 0;
if (temp_state == TEMP_DANGER) {
if (temperature_chamber == -1) {
temperature_chamber = check_for_chamber();
}
if ((envstat->shutdown_cnt++ >= SHUTDOWN_COUNT) &&
(temperature_chamber == 0) &&
enable_overtemp_powerdown &&
(powerdown_started == 0)) {
powerdown_started = 1;
build_bd_display_str(buffer,
softsp->list->sc.type,
softsp->list->sc.board);
cmn_err(CE_WARN, "%s still too hot "
"(temperature: %dC)."
" Overtemp shutdown started", buffer,
real_temp);
fhc_reboot();
}
}
}
if ((envstat->max == NA_TEMP) || (real_temp > envstat->max)) {
envstat->max = real_temp;
}
if ((envstat->min == NA_TEMP) || (real_temp < envstat->min)) {
envstat->min = real_temp;
}
if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0)) {
enum board_type type = softsp->list->sc.type;
envstat->trend = temp_trend(envstat);
if (envstat->trend == TREND_RAPID_RISE &&
(type != CPU_BOARD || real_temp >
fhc_cpu_warning_temp_threshold)) {
int board = softsp->list->sc.board;
build_bd_display_str(buffer, type, board);
cmn_err(CE_WARN, "%s temperature is rising rapidly! "
"Current temperature is %dC", buffer,
real_temp);
}
}
}
#define PREV_L2_INDEX(x) ((x) ? ((x) - 1) : (L2_SZ - 1))
enum temp_trend
temp_trend(struct temp_stats *tempstat)
{
int ii;
uint_t curr_index;
int curr_temp;
uint_t prev_index;
int prev_temp;
int trail_temp;
int delta;
int read_cnt;
enum temp_trend result = TREND_STABLE;
if (tempstat == NULL)
return (TREND_UNKNOWN);
curr_index = (L2_INDEX(tempstat->index) - 1) % L2_SZ;
curr_temp = tempstat->l2[curr_index];
prev_index = curr_index;
for (read_cnt = 0; read_cnt < L2_SZ - 1; read_cnt++) {
if (tempstat->l2[prev_index] == NA_TEMP)
break;
prev_index = PREV_L2_INDEX(prev_index);
}
switch (read_cnt) {
case 0:
case 1:
result = TREND_UNKNOWN;
break;
default:
delta = curr_temp - tempstat->l2[PREV_L2_INDEX(curr_index)];
prev_index = curr_index;
trail_temp = prev_temp = curr_temp;
if (delta >= RAPID_RISE_THRESH) {
result = TREND_RAPID_RISE;
} else if (delta > 0) {
for (ii = 1; ii < read_cnt; ii++) {
prev_index = PREV_L2_INDEX(prev_index);
prev_temp = tempstat->l2[prev_index];
if (prev_temp > trail_temp) {
break;
}
trail_temp = prev_temp;
if (prev_temp <= curr_temp - NOISE_THRESH) {
result = TREND_RISE;
break;
}
}
} else if (delta <= -RAPID_FALL_THRESH) {
result = TREND_RAPID_FALL;
} else if (delta < 0) {
for (ii = 1; ii < read_cnt; ii++) {
prev_index = PREV_L2_INDEX(prev_index);
prev_temp = tempstat->l2[prev_index];
if (prev_temp < trail_temp) {
break;
}
trail_temp = prev_temp;
if (prev_temp >= curr_temp + NOISE_THRESH) {
result = TREND_FALL;
break;
}
}
}
}
return (result);
}
void
fhc_reboot(void)
{
proc_t *initpp;
mutex_enter(&pidlock);
initpp = prfind(P_INITPID);
mutex_exit(&pidlock);
if (initpp != NULL) {
psignal(initpp, SIGFPE);
} else {
power_down("Environmental Shutdown");
halt("Power off the System");
}
}
int
overtemp_kstat_update(kstat_t *ksp, int rw)
{
struct temp_stats *tempstat;
char *kstatp;
int i;
kstatp = (char *)ksp->ks_data;
tempstat = (struct temp_stats *)ksp->ks_private;
if (rw == KSTAT_WRITE) {
short max;
short min;
max = tempstat->l1[0];
min = tempstat->l1[0];
for (i = 0; i < L1_SZ; i++) {
if ((tempstat->l1[i] != NA_TEMP) &&
(tempstat->l1[i] > max)) {
max = tempstat->l1[i];
}
if ((tempstat->l1[i] != NA_TEMP) &&
(tempstat->l1[i] < min)) {
min = tempstat->l1[i];
}
}
for (i = 0; i < L2_SZ; i++) {
if ((tempstat->l2[i] != NA_TEMP) &&
(tempstat->l2[i] > max)) {
max = tempstat->l2[i];
}
if ((tempstat->l2[i] != NA_TEMP) &&
(tempstat->l2[i] < min)) {
min = tempstat->l2[i];
}
}
for (i = 0; i < L3_SZ; i++) {
if ((tempstat->l3[i] != NA_TEMP) &&
(tempstat->l3[i] > max)) {
max = tempstat->l3[i];
}
if ((tempstat->l3[i] != NA_TEMP) &&
(tempstat->l3[i] < min)) {
min = tempstat->l3[i];
}
}
for (i = 0; i < L4_SZ; i++) {
if ((tempstat->l4[i] != NA_TEMP) &&
(tempstat->l4[i] > max)) {
max = tempstat->l4[i];
}
if ((tempstat->l4[i] != NA_TEMP) &&
(tempstat->l4[i] < min)) {
min = tempstat->l4[i];
}
}
for (i = 0; i < L5_SZ; i++) {
if ((tempstat->l5[i] != NA_TEMP) &&
(tempstat->l5[i] > max)) {
max = tempstat->l5[i];
}
if ((tempstat->l5[i] != NA_TEMP) &&
(tempstat->l5[i] < min)) {
min = tempstat->l5[i];
}
}
} else {
bcopy(tempstat, kstatp, sizeof (struct temp_stats));
}
return (0);
}
int
temp_override_kstat_update(kstat_t *ksp, int rw)
{
short *over;
short *kstatp;
kstatp = (short *)ksp->ks_data;
over = (short *)ksp->ks_private;
if (rw == KSTAT_WRITE) {
*over = *kstatp;
} else {
*kstatp = *over;
}
return (0);
}
static short
calibrate_temp(enum board_type type, uchar_t temp, uint_t ac_comp)
{
short result = NA_TEMP;
if (dont_calibrate == 1) {
return ((short)temp);
}
switch (type) {
case CPU_BOARD:
if ((CHIP_REV(ac_comp) >= 4) || (CHIP_REV(ac_comp) == 0)) {
if (temp >= CPU2_MX_CNT) {
result = cpu2_table[CPU2_MX_CNT-1];
} else {
result = cpu2_table[temp];
}
} else {
if (temp >= CPU_MX_CNT) {
result = cpu_table[CPU_MX_CNT-1];
} else {
result = cpu_table[temp];
}
}
break;
case IO_2SBUS_BOARD:
case IO_SBUS_FFB_BOARD:
case IO_PCI_BOARD:
case IO_2SBUS_SOCPLUS_BOARD:
case IO_SBUS_FFB_SOCPLUS_BOARD:
if (temp < IO_MN_CNT) {
result = io_table[IO_MN_CNT];
} else if (temp >= IO_MX_CNT) {
result = io_table[IO_MX_CNT-1];
} else {
result = io_table[temp];
}
break;
case CLOCK_BOARD:
if (temp < CLK_MN_CNT) {
result = clock_table[CLK_MN_CNT];
} else if (temp >= CLK_MX_CNT) {
result = clock_table[CLK_MX_CNT-1];
} else {
result = clock_table[temp];
}
break;
default:
break;
}
return (result);
}
static enum temp_state
get_temp_state(enum board_type type, short temp, int board)
{
enum temp_state state = TEMP_OK;
short warn_limit;
short danger_limit;
struct cpu *cpa, *cpb;
switch (type) {
case CPU_BOARD:
warn_limit = cpu_warn_temp;
danger_limit = cpu_danger_temp;
mutex_enter(&cpu_lock);
if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL) {
if ((cpa->cpu_type_info.pi_clock) >= 400) {
warn_limit = cpu_warn_temp_4x;
danger_limit = cpu_danger_temp_4x;
}
}
if ((cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL) {
if ((cpb->cpu_type_info.pi_clock) >= 400) {
warn_limit = cpu_warn_temp_4x;
danger_limit = cpu_danger_temp_4x;
}
}
mutex_exit(&cpu_lock);
break;
case IO_2SBUS_BOARD:
case IO_SBUS_FFB_BOARD:
case IO_PCI_BOARD:
case IO_2SBUS_SOCPLUS_BOARD:
case IO_SBUS_FFB_SOCPLUS_BOARD:
warn_limit = io_warn_temp;
danger_limit = io_danger_temp;
break;
case CLOCK_BOARD:
warn_limit = clk_warn_temp;
danger_limit = clk_danger_temp;
break;
case UNINIT_BOARD:
case UNKNOWN_BOARD:
case MEM_BOARD:
default:
warn_limit = dft_warn_temp;
danger_limit = dft_danger_temp;
break;
}
if (temp >= danger_limit) {
state = TEMP_DANGER;
} else if (temp >= warn_limit) {
state = TEMP_WARN;
}
return (state);
}
static void
fhc_add_kstats(struct fhc_soft_state *softsp)
{
struct kstat *fhc_ksp;
struct fhc_kstat *fhc_named_ksp;
if ((fhc_ksp = kstat_create("unix", softsp->list->sc.board,
FHC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
sizeof (struct fhc_kstat) / sizeof (kstat_named_t),
KSTAT_FLAG_PERSISTENT)) == NULL) {
cmn_err(CE_WARN, "fhc%d kstat_create failed",
ddi_get_instance(softsp->dip));
return;
}
fhc_named_ksp = (struct fhc_kstat *)(fhc_ksp->ks_data);
kstat_named_init(&fhc_named_ksp->csr,
CSR_KSTAT_NAMED,
KSTAT_DATA_UINT32);
kstat_named_init(&fhc_named_ksp->bsr,
BSR_KSTAT_NAMED,
KSTAT_DATA_UINT32);
fhc_ksp->ks_update = fhc_kstat_update;
fhc_ksp->ks_private = (void *)softsp;
softsp->fhc_ksp = fhc_ksp;
kstat_install(fhc_ksp);
}
static int
fhc_kstat_update(kstat_t *ksp, int rw)
{
struct fhc_kstat *fhcksp;
struct fhc_soft_state *softsp;
fhcksp = (struct fhc_kstat *)ksp->ks_data;
softsp = (struct fhc_soft_state *)ksp->ks_private;
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
fhcksp->csr.value.ui32 = *softsp->ctrl;
fhcksp->bsr.value.ui32 = *softsp->bsr;
}
return (0);
}
static int
cpu_on_board(int board)
{
int upa_a = board << 1;
int upa_b = (board << 1) + 1;
if ((cpunodes[upa_a].nodeid != 0) ||
(cpunodes[upa_b].nodeid != 0)) {
return (1);
} else {
return (0);
}
}
void
update_board_leds(fhc_bd_t *board, uint_t mask, uint_t value)
{
volatile uint_t temp;
ASSERT(fhc_bdlist_locked());
mask &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
value &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
if (board != NULL) {
mutex_enter(&board->softsp->ctrl_lock);
temp = *board->softsp->ctrl;
temp &= ~(FHC_CSR_SYNC | FHC_EPDA_OFF | FHC_EPDB_OFF);
temp &= ~mask;
temp |= value;
*board->softsp->ctrl = temp;
temp = *board->softsp->ctrl;
#ifdef lint
temp = temp;
#endif
mutex_exit(&board->softsp->ctrl_lock);
}
}
static int
check_for_chamber(void)
{
int chamber = 0;
dev_info_t *options_dip;
pnode_t options_node_id;
int mfgmode_len;
int retval;
char *mfgmode;
if (!enable_overtemp_powerdown) {
cmn_err(CE_WARN, "Operator has disabled overtemp powerdown");
return (1);
}
if ((options_dip = ddi_find_devinfo("options", -1, 0)) != NULL) {
options_node_id = (pnode_t)ddi_get_nodeid(options_dip);
mfgmode_len = prom_getproplen(options_node_id, "mfg-mode");
if (mfgmode_len == -1) {
return (chamber);
}
mfgmode = kmem_alloc(mfgmode_len+1, KM_SLEEP);
retval = prom_getprop(options_node_id, "mfg-mode", mfgmode);
if (retval != -1) {
mfgmode[retval] = 0;
if (strcmp(mfgmode, CHAMBER_VALUE) == 0) {
chamber = 1;
cmn_err(CE_WARN, "System in Temperature"
" Chamber Mode. Overtemperature"
" Shutdown disabled");
}
}
kmem_free(mfgmode, mfgmode_len+1);
}
return (chamber);
}
static void
build_bd_display_str(char *buffer, enum board_type type, int board)
{
if (buffer == NULL) {
return;
}
switch (type) {
case UNINIT_BOARD:
(void) sprintf(buffer, "Uninitialized Board type board %d",
board);
break;
case UNKNOWN_BOARD:
(void) sprintf(buffer, "Unknown Board type board %d", board);
break;
case CPU_BOARD:
case MEM_BOARD:
(void) sprintf(buffer, "CPU/Memory board %d", board);
break;
case IO_2SBUS_BOARD:
(void) sprintf(buffer, "2 SBus IO board %d", board);
break;
case IO_SBUS_FFB_BOARD:
(void) sprintf(buffer, "SBus FFB IO board %d", board);
break;
case IO_PCI_BOARD:
(void) sprintf(buffer, "PCI IO board %d", board);
break;
case CLOCK_BOARD:
(void) sprintf(buffer, "Clock board");
break;
case IO_2SBUS_SOCPLUS_BOARD:
(void) sprintf(buffer, "2 SBus SOC+ IO board %d", board);
break;
case IO_SBUS_FFB_SOCPLUS_BOARD:
(void) sprintf(buffer, "SBus FFB SOC+ IO board %d", board);
break;
default:
(void) sprintf(buffer, "Unrecognized board type board %d",
board);
break;
}
}
void
fhc_intrdist(void *arg)
{
struct fhc_soft_state *softsp;
dev_info_t *dip = (dev_info_t *)arg;
volatile uint_t *mondo_vec_reg;
volatile uint_t *intr_state_reg;
uint_t mondo_vec;
uint_t tmp_reg;
uint_t cpu_id;
uint_t i;
softsp = ddi_get_soft_state(fhcp, ddi_get_instance(dip));
for (i = 0; i < FHC_MAX_INO; i++) {
mondo_vec_reg = softsp->intr_regs[i].mapping_reg;
intr_state_reg = softsp->intr_regs[i].clear_reg;
if ((*mondo_vec_reg & IMR_VALID) == 0)
continue;
cpu_id = intr_dist_cpuid();
if (((*mondo_vec_reg & INR_PID_MASK) >> INR_PID_SHIFT) ==
cpu_id) {
return;
}
*mondo_vec_reg &= ~IMR_VALID;
tmp_reg = *softsp->id;
while (((*intr_state_reg & INT_PENDING) == INT_PENDING) &&
!panicstr)
;
mondo_vec = (cpu_id << INR_PID_SHIFT) | IMR_VALID;
*mondo_vec_reg = mondo_vec;
tmp_reg = *(softsp->id);
#ifdef lint
tmp_reg = tmp_reg;
#endif
}
}
void
reg_fault(int unit, enum ft_type type, enum ft_class fclass)
{
struct ft_link_list *list;
if (type >= ft_max_index) {
cmn_err(CE_WARN, "Illegal Fault type %x", type);
return;
}
mutex_enter(&ftlist_mutex);
for (list = ft_list; list != NULL; list = list->next) {
if ((list->f.unit == unit) && (list->f.type == type) &&
(list->f.fclass == fclass)) {
mutex_exit(&ftlist_mutex);
return;
}
}
list = kmem_zalloc(sizeof (struct ft_link_list), KM_SLEEP);
list->f.unit = unit;
list->f.type = type;
list->f.fclass = fclass;
list->f.create_time = (time32_t)gethrestime_sec();
(void) strncpy(list->f.msg, ft_str_table[type], MAX_FT_DESC);
list->next = ft_list;
ft_list = list;
ft_nfaults++;
mutex_exit(&ftlist_mutex);
}
void
clear_fault(int unit, enum ft_type type, enum ft_class fclass)
{
struct ft_link_list *list;
struct ft_link_list **vect;
mutex_enter(&ftlist_mutex);
list = ft_list;
vect = &ft_list;
for (; list != NULL; vect = &list->next, list = list->next) {
if ((list->f.unit == unit) && (list->f.type == type) &&
(list->f.fclass == fclass)) {
*vect = list->next;
kmem_free(list, sizeof (struct ft_link_list));
ft_nfaults--;
break;
}
}
mutex_exit(&ftlist_mutex);
}
int
process_fault_list(void)
{
int fault = 0;
struct ft_link_list *ftlist;
fhc_bd_t *bdlist;
(void) fhc_bdlist_lock(-1);
mutex_enter(&ftlist_mutex);
for (bdlist = fhc_bd_first(); bdlist; bdlist = fhc_bd_next(bdlist))
bdlist->fault = 0;
for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
fault++;
if (ftlist->f.fclass == FT_BOARD) {
if (fhc_bd_valid(ftlist->f.unit)) {
bdlist = fhc_bd(ftlist->f.unit);
bdlist->fault = 1;
} else {
cmn_err(CE_WARN, "No board %d list entry found",
ftlist->f.unit);
}
}
}
mutex_exit(&ftlist_mutex);
fhc_bdlist_unlock();
return (fault);
}
void
fhc_add_memloc(int board, uint64_t pa, uint_t size)
{
struct fhc_memloc *p, **pp;
uint_t ipa = pa >> FHC_MEMLOC_SHIFT;
ASSERT(fhc_bdlist_locked());
ASSERT((size & (size-1)) == 0);
for (p = fhc_base_memloc, pp = &fhc_base_memloc;
p != NULL; pp = &p->next, p = p->next) {
if (ipa < p->pa) {
break;
}
}
p = kmem_alloc(sizeof (struct fhc_memloc), KM_SLEEP);
p->next = *pp;
p->board = board;
p->pa = ipa;
p->size = size;
#ifdef DEBUG_MEMDEC
cmn_err(CE_NOTE, "fhc_add_memloc: adding %d 0x%x 0x%x",
p->board, p->pa, p->size);
#endif
*pp = p;
}
void
fhc_del_memloc(int board)
{
struct fhc_memloc *p, **pp;
ASSERT(fhc_bdlist_locked());
pp = &fhc_base_memloc;
while ((p = *pp) != NULL) {
if (p->board == board) {
#ifdef DEBUG_MEMDEC
cmn_err(CE_NOTE, "fhc_del_memloc: removing %d "
"0x%x 0x%x", board, p->pa, p->size);
#endif
*pp = p->next;
kmem_free(p, sizeof (struct fhc_memloc));
} else {
pp = &(p->next);
}
}
}
uint64_t
fhc_find_memloc_gap(uint_t size)
{
struct fhc_memloc *p;
uint_t base_pa = 0;
uint_t mask = ~(size-1);
ASSERT(fhc_bdlist_locked());
ASSERT((size & (size-1)) == 0);
for (p = fhc_base_memloc; p != NULL; p = p->next) {
if (base_pa != (base_pa & mask))
base_pa = (base_pa + size) & mask;
if (base_pa + size <= p->pa)
break;
base_pa = p->pa + p->size;
}
ASSERT((base_pa + size) <= FHC_MEMLOC_MAX);
if (base_pa != (base_pa & mask))
base_pa = (base_pa + size) & mask;
return ((uint64_t)base_pa << FHC_MEMLOC_SHIFT);
}
static void
fhc_write_mcrs(
uint64_t cpa,
uint64_t dpa0,
uint64_t dpa1,
uint64_t c,
uint64_t d0,
uint64_t d1)
{
stdphysio(cpa, c & ~AC_CSR_REFEN);
(void) lddphysio(cpa);
if (GRP_SIZE_IS_SET(d0)) {
stdphysio(dpa0, d0);
(void) lddphysio(dpa0);
}
if (GRP_SIZE_IS_SET(d1)) {
stdphysio(dpa1, d1);
(void) lddphysio(dpa1);
}
stdphysio(cpa, c);
(void) lddphysio(cpa);
}
static uint_t
fhc_cvt_size(uint64_t bsz)
{
uint_t csz;
csz = 0;
bsz /= 64;
while (bsz) {
csz++;
bsz /= 2;
}
csz /= 2;
return (csz);
}
void
fhc_program_memory(int board, uint64_t pa)
{
uint64_t cpa, dpa0, dpa1;
uint64_t c, d0, d1;
uint64_t b0_pa, b1_pa;
uint64_t memdec0, memdec1;
uint_t b0_size, b1_size;
cpa = 0x1c0f9000060ull + (board * 0x400000000ull);
#ifdef DEBUG_MEMDEC
prom_printf("cpa = 0x%llx\n", cpa);
#endif
dpa0 = cpa + 0x10;
dpa1 = cpa + 0x20;
memdec0 = lddphysio(dpa0);
#ifdef DEBUG_MEMDEC
prom_printf("memdec0 = 0x%llx\n", memdec0);
#endif
memdec1 = lddphysio(dpa1);
#ifdef DEBUG_MEMDEC
prom_printf("memdec1 = 0x%llx\n", memdec1);
#endif
if (GRP_SIZE_IS_SET(memdec0)) {
b0_size = GRP_SPANMB(memdec0);
} else {
b0_size = 0;
}
if (GRP_SIZE_IS_SET(memdec1)) {
b1_size = GRP_SPANMB(memdec1);
} else {
b1_size = 0;
}
c = lddphysio(cpa);
#ifdef DEBUG_MEMDEC
prom_printf("c = 0x%llx\n", c);
#endif
if (b0_size) {
b0_pa = pa;
d0 = SETUP_DECODE(b0_pa, b0_size, 0, 0);
d0 |= AC_MEM_VALID;
c &= ~0x7;
c |= 0;
c &= ~(0x7 << 8);
c |= (fhc_cvt_size(b0_size) << 8);
} else {
d0 = memdec0;
}
if (b1_size) {
b1_pa = pa + 0x80000000ull;
d1 = SETUP_DECODE(b1_pa, b1_size, 0, 0);
d1 |= AC_MEM_VALID;
c &= ~(0x7 << 3);
c |= (0 << 3);
c &= ~(0x7 << 11);
c |= (fhc_cvt_size(b1_size) << 11);
} else {
d1 = memdec1;
}
#ifdef DEBUG_MEMDEC
prom_printf("c 0x%llx, d0 0x%llx, d1 0x%llx\n", c, d0, d1);
#endif
fhc_write_mcrs(cpa, dpa0, dpa1, c, d0, d1);
}
void
create_ft_kstats(int instance)
{
struct kstat *ksp;
ksp = kstat_create("unix", instance, FT_LIST_KSTAT_NAME, "misc",
KSTAT_TYPE_RAW, 1, KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_VAR_SIZE);
if (ksp != NULL) {
ksp->ks_data = NULL;
ksp->ks_update = ft_ks_update;
ksp->ks_snapshot = ft_ks_snapshot;
ksp->ks_data_size = 1;
ksp->ks_lock = &ftlist_mutex;
kstat_install(ksp);
}
}
static int
ft_ks_snapshot(struct kstat *ksp, void *buf, int rw)
{
struct ft_link_list *ftlist;
if (rw == KSTAT_WRITE) {
return (EACCES);
}
ksp->ks_snaptime = gethrtime();
for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
bcopy(&ftlist->f, buf, sizeof (struct ft_list));
buf = ((struct ft_list *)buf) + 1;
}
return (0);
}
static int
ft_ks_update(struct kstat *ksp, int rw)
{
if (rw == KSTAT_WRITE) {
return (EACCES);
} else {
if (ft_nfaults) {
ksp->ks_data_size = ft_nfaults *
sizeof (struct ft_list);
} else {
ksp->ks_data_size = 1;
}
}
return (0);
}
int
fhc_board_poweroffcpus(int board, char *errbuf, int cpu_flags)
{
cpu_t *cpa, *cpb;
enum board_type type;
int error = 0;
ASSERT(MUTEX_HELD(&cpu_lock));
type = fhc_bd_type(board);
switch (type) {
case CPU_BOARD:
if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL &&
cpu_is_active(cpa)) {
if (!cpu_intr_on(cpa)) {
cpu_intr_enable(cpa);
}
if ((error = cpu_offline(cpa, cpu_flags)) != 0) {
cmn_err(CE_WARN,
"Processor %d failed to offline.",
cpa->cpu_id);
if (errbuf != NULL) {
(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
"processor %d failed to offline",
cpa->cpu_id);
}
}
}
if (error == 0 &&
(cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL &&
cpu_is_active(cpb)) {
if (!cpu_intr_on(cpb)) {
cpu_intr_enable(cpb);
}
if ((error = cpu_offline(cpb, cpu_flags)) != 0) {
cmn_err(CE_WARN,
"Processor %d failed to offline.",
cpb->cpu_id);
if (errbuf != NULL) {
(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
"processor %d failed to offline",
cpb->cpu_id);
}
}
}
if (error == 0 && cpa != NULL && cpu_is_offline(cpa)) {
if ((error = cpu_poweroff(cpa)) != 0) {
cmn_err(CE_WARN,
"Processor %d failed to power off.",
cpa->cpu_id);
if (errbuf != NULL) {
(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
"processor %d failed to power off",
cpa->cpu_id);
}
} else {
cmn_err(CE_NOTE, "Processor %d powered off.",
cpa->cpu_id);
}
}
if (error == 0 && cpb != NULL && cpu_is_offline(cpb)) {
if ((error = cpu_poweroff(cpb)) != 0) {
cmn_err(CE_WARN,
"Processor %d failed to power off.",
cpb->cpu_id);
if (errbuf != NULL) {
(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
"processor %d failed to power off",
cpb->cpu_id);
}
} else {
cmn_err(CE_NOTE, "Processor %d powered off.",
cpb->cpu_id);
}
}
if (error == 0 && (cpa != NULL || cpb != NULL)) {
u_longlong_t base = 0;
int i;
#ifdef DEBUG
int nonz0 = 0;
int nonz1 = 0;
#endif
if (cpa != NULL)
base = FHC_DTAG_BASE(cpa->cpu_id);
if (cpb != NULL)
base = FHC_DTAG_BASE(cpb->cpu_id);
ASSERT(base != 0);
for (i = 0; i < FHC_DTAG_SIZE; i += FHC_DTAG_SKIP) {
u_longlong_t value = lddphysio(base+i);
#ifdef lint
value = value;
#endif
#ifdef DEBUG
if (cpa != NULL && (value & FHC_DTAG_LOW))
nonz0++;
if (cpb != NULL && (value & FHC_DTAG_HIGH))
nonz1++;
#endif
stdphysio(base + i, 0ull);
}
#ifdef DEBUG
if (nonz0 || nonz1) {
cmn_err(CE_NOTE, "!dtag results: "
"cpua valid %d, cpub valid %d",
nonz0, nonz1);
}
#endif
}
break;
default:
break;
}
return (error);
}
int
fhc_cpu_poweroff(struct cpu *cp)
{
int board;
fhc_bd_t *bd_list;
int delays;
extern void idle_stop_xcall(void);
ASSERT(MUTEX_HELD(&cpu_lock));
ASSERT((cp->cpu_flags & (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED)) ==
(CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED));
board = FHC_CPU2BOARD(cp->cpu_id);
bd_list = fhc_bdlist_lock(board);
ASSERT(fhc_bd_valid(board) && (bd_list->sc.type == CPU_BOARD));
promsafe_pause_cpus();
mp_cpu_quiesce(cp);
xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
(uint64_t)fhc_cpu_shutdown_self, (uint64_t)NULL);
for (delays = FHC_SHUTDOWN_WAIT_MSEC; delays != 0; delays--) {
uint_t temp;
DELAY(1000);
temp = *bd_list->softsp->ctrl;
if (FHC_CPU_IS_A(cp->cpu_id)) {
if (temp & FHC_EPDA_OFF)
break;
} else {
if (temp & FHC_EPDB_OFF)
break;
}
}
start_cpus();
fhc_bdlist_unlock();
if (delays == 0)
panic("Processor %d failed during shutdown", cp->cpu_id);
return (0);
}
static void
fhc_cpu_shutdown_self(void)
{
extern void flush_windows(void);
flush_windows();
ASSERT(CPU->cpu_intr_actv == 0);
ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread ||
CPU->cpu_thread == CPU->cpu_startup_thread);
CPU->cpu_flags = CPU_POWEROFF | CPU_OFFLINE | CPU_QUIESCED;
(void) prom_sunfire_cpu_off();
os_completes_shutdown();
panic("fhc_cpu_shutdown_self: cannot return");
}
static int
fhc_cpu_start(struct cpu *cp)
{
int rv;
int cpuid = cp->cpu_id;
pnode_t nodeid;
extern void restart_other_cpu(int);
ASSERT(MUTEX_HELD(&cpu_lock));
nodeid = cpunodes[cpuid].nodeid;
ASSERT(nodeid != (pnode_t)0);
rv = prom_wakeupcpu(nodeid);
if (rv != 0) {
cmn_err(CE_WARN, "Processor %d failed to power on.", cpuid);
return (EBUSY);
}
cp->cpu_flags &= ~CPU_POWEROFF;
restart_other_cpu(cpuid);
return (0);
}
int
fhc_cpu_poweron(struct cpu *cp)
{
fhc_bd_t *bd_list;
enum temp_state state;
int board;
int status;
int status_other;
struct cpu *cp_other;
ASSERT(MUTEX_HELD(&cpu_lock));
ASSERT(cpu_is_poweredoff(cp));
board = FHC_CPU2BOARD(cp->cpu_id);
bd_list = fhc_bdlist_lock(board);
ASSERT(bd_list != NULL);
ASSERT(bd_list->sc.type == CPU_BOARD);
ASSERT(bd_list->dev_softsp != NULL);
state = ((struct environ_soft_state *)
bd_list->dev_softsp)->tempstat.state;
fhc_bdlist_unlock();
if ((state == TEMP_WARN) || (state == TEMP_DANGER))
return (EBUSY);
status = fhc_cpu_start(cp);
if ((status == 0) &&
((cp_other = cpu_get(FHC_OTHER_CPU_ID(cp->cpu_id))) != NULL)) {
status_other = fhc_cpu_start(cp_other);
if (status_other != 0) {
panic("fhc: failed to start second CPU"
" in pair %d & %d, error %d",
cp->cpu_id, cp_other->cpu_id, status_other);
}
}
return (status);
}
static void
os_completes_shutdown(void)
{
pfn_t pfn;
tte_t tte;
volatile uint_t *src;
volatile uint_t *dst;
caddr_t copy_addr;
extern void fhc_shutdown_asm(u_longlong_t, int);
extern void fhc_shutdown_asm_end(void);
copy_addr = shutdown_va + FHC_SRAM_OS_OFFSET;
pfn = FHC_LOCAL_OS_PAGEBASE >> MMU_PAGESHIFT;
tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
TTE_PFN_INTHI(pfn);
tte.tte_intlo = TTE_PFN_INTLO(pfn) |
TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT;
sfmmu_dtlb_ld_kva(shutdown_va, &tte);
sfmmu_itlb_ld_kva(shutdown_va, &tte);
for (src = (uint_t *)fhc_shutdown_asm, dst = (uint_t *)copy_addr;
src < (uint_t *)fhc_shutdown_asm_end;
src++, dst++) {
volatile uint_t dummy;
*dst = *src;
dummy = *dst;
#ifdef lint
dummy = dummy;
#endif
}
((void (*)(u_longlong_t, int))copy_addr)(
FHC_BASE_NOMEM + CPU->cpu_id * FHC_MAX_ECACHE_SIZE,
cpunodes[CPU->cpu_id].ecache_size);
}
enum temp_state
fhc_env_temp_state(int board)
{
fhc_bd_t *bdp;
struct environ_soft_state *envp;
ASSERT(fhc_bd_valid(board));
bdp = fhc_bd(board);
if (!bdp->dev_softsp) {
return (TEMP_OK);
}
envp = (struct environ_soft_state *)bdp->dev_softsp;
return (envp->tempstat.state);
}
static void
fhc_tod_fault(enum tod_fault_type tod_bad)
{
int board_num = 0;
enum ft_class class = FT_SYSTEM;
uint64_t addr;
addr = (va_to_pa((void *)v_eeprom_addr)) >> BOARD_PHYADDR_SHIFT;
if ((addr & CLOCKBOARD_PHYADDR_BITS) != CLOCKBOARD_PHYADDR_BITS) {
board_num = (addr >> IO_BOARD_NUMBER_SHIFT)
& IO_BOARD_NUMBER_MASK;
class = FT_BOARD;
}
switch (tod_bad) {
case TOD_NOFAULT:
clear_fault(board_num, FT_TODFAULT, class);
break;
case TOD_REVERSED:
case TOD_STALLED:
case TOD_JUMPED:
case TOD_RATECHANGED:
reg_fault(board_num, FT_TODFAULT, class);
break;
default:
break;
}
}