xref: /titanic_51/usr/src/uts/sun4u/sunfire/io/fhc.c (revision 8682d1ef2a0960ed5a9f05b9448eaa3e68ac931f)
129949e86Sstevel /*
229949e86Sstevel  * CDDL HEADER START
329949e86Sstevel  *
429949e86Sstevel  * The contents of this file are subject to the terms of the
529949e86Sstevel  * Common Development and Distribution License (the "License").
629949e86Sstevel  * You may not use this file except in compliance with the License.
729949e86Sstevel  *
829949e86Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
929949e86Sstevel  * or http://www.opensolaris.org/os/licensing.
1029949e86Sstevel  * See the License for the specific language governing permissions
1129949e86Sstevel  * and limitations under the License.
1229949e86Sstevel  *
1329949e86Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1429949e86Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1529949e86Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1629949e86Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1729949e86Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1829949e86Sstevel  *
1929949e86Sstevel  * CDDL HEADER END
2029949e86Sstevel  */
2129949e86Sstevel 
2229949e86Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2429949e86Sstevel  * Use is subject to license terms.
2529949e86Sstevel  */
2629949e86Sstevel 
2729949e86Sstevel 
2829949e86Sstevel #include <sys/types.h>
2929949e86Sstevel #include <sys/conf.h>
3029949e86Sstevel #include <sys/ddi.h>
3129949e86Sstevel #include <sys/sunddi.h>
3229949e86Sstevel #include <sys/ddi_impldefs.h>
3329949e86Sstevel #include <sys/obpdefs.h>
3429949e86Sstevel #include <sys/promif.h>
3529949e86Sstevel #include <sys/cmn_err.h>
3629949e86Sstevel #include <sys/errno.h>
3729949e86Sstevel #include <sys/kmem.h>
3829949e86Sstevel #include <sys/vmem.h>
3929949e86Sstevel #include <sys/debug.h>
4029949e86Sstevel #include <sys/sysmacros.h>
4129949e86Sstevel #include <sys/intreg.h>
4229949e86Sstevel #include <sys/autoconf.h>
4329949e86Sstevel #include <sys/modctl.h>
4429949e86Sstevel #include <sys/spl.h>
4529949e86Sstevel #include <sys/time.h>
4629949e86Sstevel #include <sys/systm.h>
4729949e86Sstevel #include <sys/machsystm.h>
4829949e86Sstevel #include <sys/cpu.h>
4929949e86Sstevel #include <sys/cpuvar.h>
5029949e86Sstevel #include <sys/x_call.h>		/* xt_one() */
5129949e86Sstevel #include <sys/membar.h>
5229949e86Sstevel #include <sys/vm.h>
5329949e86Sstevel #include <vm/seg_kmem.h>
5429949e86Sstevel #include <vm/hat_sfmmu.h>
5529949e86Sstevel #include <sys/promimpl.h>
5629949e86Sstevel #include <sys/prom_plat.h>
5729949e86Sstevel #include <sys/cpu_module.h>	/* flush_instr_mem() */
5829949e86Sstevel #include <sys/procset.h>
5929949e86Sstevel #include <sys/fhc.h>
6029949e86Sstevel #include <sys/ac.h>
6129949e86Sstevel #include <sys/environ.h>
6229949e86Sstevel #include <sys/jtag.h>
6329949e86Sstevel #include <sys/nexusdebug.h>
6429949e86Sstevel #include <sys/ac.h>
6529949e86Sstevel #include <sys/ddi_subrdefs.h>
6629949e86Sstevel #include <sys/eeprom.h>
6729949e86Sstevel #include <sys/sdt.h>
6829949e86Sstevel #include <sys/ddi_implfuncs.h>
6929949e86Sstevel #include <sys/ontrap.h>
7029949e86Sstevel 
7129949e86Sstevel #ifndef TRUE
7229949e86Sstevel #define	TRUE (1)
7329949e86Sstevel #endif
7429949e86Sstevel #ifndef FALSE
7529949e86Sstevel #define	FALSE (0)
7629949e86Sstevel #endif
7729949e86Sstevel 
7829949e86Sstevel /*
7929949e86Sstevel  * Function to register and deregister callbacks, for sunfire only.
8029949e86Sstevel  */
8129949e86Sstevel extern void plat_register_tod_fault(void (*func)(enum tod_fault_type));
8229949e86Sstevel 
8329949e86Sstevel /*
8429949e86Sstevel  * This table represents the FHC interrupt priorities.  They range from
8529949e86Sstevel  * 1-15, and have been modeled after the sun4d interrupts. The mondo
8629949e86Sstevel  * number anded with 0x7 is used to index into this table. This was
8729949e86Sstevel  * done to save table space.
8829949e86Sstevel  */
8929949e86Sstevel static int fhc_int_priorities[] = {
9029949e86Sstevel 	PIL_15,			/* System interrupt priority */
9129949e86Sstevel 	PIL_12,			/* zs interrupt priority */
9229949e86Sstevel 	PIL_15,			/* TOD interrupt priority */
9329949e86Sstevel 	PIL_15			/* Fan Fail priority */
9429949e86Sstevel };
9529949e86Sstevel 
9629949e86Sstevel static void fhc_tod_fault(enum tod_fault_type tod_bad);
97*8682d1efSRichard Lowe static void fhc_cpu_shutdown_self(void);
98*8682d1efSRichard Lowe static void os_completes_shutdown(void);
9929949e86Sstevel 
10029949e86Sstevel /*
10129949e86Sstevel  * The dont_calibrate variable is meant to be set to one in /etc/system
10229949e86Sstevel  * or by boot -h so that the calibration tables are not used. This
10329949e86Sstevel  * is useful for checking thermistors whose output seems to be incorrect.
10429949e86Sstevel  */
10529949e86Sstevel static int dont_calibrate = 0;
10629949e86Sstevel 
10729949e86Sstevel /* Only one processor should powerdown the system. */
10829949e86Sstevel static int powerdown_started = 0;
10929949e86Sstevel 
11029949e86Sstevel /* Let user disable overtemp powerdown. */
11129949e86Sstevel int enable_overtemp_powerdown = 1;
11229949e86Sstevel 
11329949e86Sstevel /*
11429949e86Sstevel  * The following tables correspond to the degress Celcius for each count
11529949e86Sstevel  * value possible from the 8-bit A/C convertors on each type of system
11629949e86Sstevel  * board for the UltraSPARC Server systems. To access a temperature,
11729949e86Sstevel  * just index into the correct table using the count from the A/D convertor
11829949e86Sstevel  * register, and that is the correct temperature in degress Celsius. These
11929949e86Sstevel  * values can be negative.
12029949e86Sstevel  */
12129949e86Sstevel static short cpu_table[] = {
12229949e86Sstevel -16,	-14,	-12,	-10,	-8,	-6,	-4,	-2,	/* 0-7 */
12329949e86Sstevel 1,	4,	6,	8,	10,	12,	13,	15,	/* 8-15 */
12429949e86Sstevel 16,	18,	19,	20,	22,	23,	24,	25,	/* 16-23 */
12529949e86Sstevel 26,	27,	28,	29,	30,	31,	32,	33,	/* 24-31 */
12629949e86Sstevel 34,	35,	35,	36,	37,	38,	39,	39,	/* 32-39 */
12729949e86Sstevel 40,	41,	41,	42,	43,	44,	44,	45,	/* 40-47 */
12829949e86Sstevel 46,	46,	47,	47,	48,	49,	49,	50,	/* 48-55 */
12929949e86Sstevel 51,	51,	52,	53,	53,	54,	54,	55,	/* 56-63 */
13029949e86Sstevel 55,	56,	56,	57,	57,	58,	58,	59,	/* 64-71 */
13129949e86Sstevel 60,	60,	61,	61,	62,	62,	63,	63,	/* 72-79 */
13229949e86Sstevel 64,	64,	65,	65,	66,	66,	67,	67,	/* 80-87 */
13329949e86Sstevel 68,	68,	69,	69,	70,	70,	71,	71,	/* 88-95 */
13429949e86Sstevel 72,	72,	73,	73,	74,	74,	75,	75,	/* 96-103 */
13529949e86Sstevel 76,	76,	77,	77,	78,	78,	79,	79,	/* 104-111 */
13629949e86Sstevel 80,	80,	81,	81,	82,	82,	83,	83,	/* 112-119 */
13729949e86Sstevel 84,	84,	85,	85,	86,	86,	87,	87,	/* 120-127 */
13829949e86Sstevel 88,	88,	89,	89,	90,	90,	91,	91,	/* 128-135 */
13929949e86Sstevel 92,	92,	93,	93,	94,	94,	95,	95,	/* 136-143 */
14029949e86Sstevel 96,	96,	97,	98,	98,	99,	99,	100,	/* 144-151 */
14129949e86Sstevel 100,	101,	101,	102,	103,	103,	104,	104,	/* 152-159 */
14229949e86Sstevel 105,	106,	106,	107,	107,	108,	109,	109,	/* 160-167 */
14329949e86Sstevel 110,								/* 168 */
14429949e86Sstevel };
14529949e86Sstevel 
14629949e86Sstevel #define	CPU_MX_CNT	(sizeof (cpu_table)/sizeof (short))
14729949e86Sstevel 
14829949e86Sstevel static short cpu2_table[] = {
14929949e86Sstevel -17,	-16,	-15,	-14,	-13,	-12,	-11,	-10,	/* 0-7 */
15029949e86Sstevel -9,	-8,	-7,	-6,	-5,	-4,	-3,	-2,	/* 8-15 */
15129949e86Sstevel -1,	0,	1,	2,	3,	4,	5,	6,	/* 16-23 */
15229949e86Sstevel 7,	8,	9,	10,	11,	12,	13,	13,	/* 24-31 */
15329949e86Sstevel 14,	15,	16,	16,	17,	18,	18,	19,	/* 32-39 */
15429949e86Sstevel 20,	20,	21,	22,	22,	23,	24,	24,	/* 40-47 */
15529949e86Sstevel 25,	25,	26,	26,	27,	27,	28,	28,	/* 48-55 */
15629949e86Sstevel 29,	30,	30,	31,	31,	32,	32,	33,	/* 56-63 */
15729949e86Sstevel 33,	34,	34,	35,	35,	36,	36,	37,	/* 64-71 */
15829949e86Sstevel 37,	37,	38,	38,	39,	39,	40,	40,	/* 72-79 */
15929949e86Sstevel 41,	41,	42,	42,	43,	43,	43,	44,	/* 80-87 */
16029949e86Sstevel 44,	45,	45,	46,	46,	46,	47,	47,	/* 88-95 */
16129949e86Sstevel 48,	48,	49,	49,	50,	50,	50,	51,	/* 96-103 */
16229949e86Sstevel 51,	52,	52,	53,	53,	53,	54,	54,	/* 104-111 */
16329949e86Sstevel 55,	55,	56,	56,	56,	57,	57,	58,	/* 112-119 */
16429949e86Sstevel 58,	59,	59,	59,	60,	60,	61,	61,	/* 120-127 */
16529949e86Sstevel 62,	62,	63,	63,	63,	64,	64,	65,	/* 128-135 */
16629949e86Sstevel 65,	66,	66,	67,	67,	68,	68,	68,	/* 136-143 */
16729949e86Sstevel 69,	69,	70,	70,	71,	71,	72,	72,	/* 144-151 */
16829949e86Sstevel 73,	73,	74,	74,	75,	75,	76,	76,	/* 152-159 */
16929949e86Sstevel 77,	77,	78,	78,	79,	79,	80,	80,	/* 160-167 */
17029949e86Sstevel 81,	81,	82,	83,	83,	84,	84,	85,	/* 168-175 */
17129949e86Sstevel 85,	86,	87,	87,	88,	88,	89,	90,	/* 176-183 */
17229949e86Sstevel 90,	91,	92,	92,	93,	94,	94,	95,	/* 184-191 */
17329949e86Sstevel 96,	96,	97,	98,	99,	99,	100,	101,	/* 192-199 */
17429949e86Sstevel 102,	103,	103,	104,	105,	106,	107,	108,	/* 200-207 */
17529949e86Sstevel 109,	110,							/* 208-209 */
17629949e86Sstevel };
17729949e86Sstevel 
17829949e86Sstevel #define	CPU2_MX_CNT	(sizeof (cpu2_table)/sizeof (short))
17929949e86Sstevel 
18029949e86Sstevel static short io_table[] = {
18129949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 0-7 */
18229949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 8-15 */
18329949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 16-23 */
18429949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 24-31 */
18529949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 32-39 */
18629949e86Sstevel 0,	3,	7,	10,	13,	15,	17,	19,	/* 40-47 */
18729949e86Sstevel 21,	23,	25,	27,	28,	30,	31,	32,	/* 48-55 */
18829949e86Sstevel 34,	35,	36,	37,	38,	39,	41,	42,	/* 56-63 */
18929949e86Sstevel 43,	44,	45,	46,	46,	47,	48,	49,	/* 64-71 */
19029949e86Sstevel 50,	51,	52,	53,	53,	54,	55,	56,	/* 72-79 */
19129949e86Sstevel 57,	57,	58,	59,	60,	60,	61,	62,	/* 80-87 */
19229949e86Sstevel 62,	63,	64,	64,	65,	66,	66,	67,	/* 88-95 */
19329949e86Sstevel 68,	68,	69,	70,	70,	71,	72,	72,	/* 96-103 */
19429949e86Sstevel 73,	73,	74,	75,	75,	76,	77,	77,	/* 104-111 */
19529949e86Sstevel 78,	78,	79,	80,	80,	81,	81,	82,	/* 112-119 */
19629949e86Sstevel };
19729949e86Sstevel 
19829949e86Sstevel #define	IO_MN_CNT	40
19929949e86Sstevel #define	IO_MX_CNT	(sizeof (io_table)/sizeof (short))
20029949e86Sstevel 
20129949e86Sstevel static short clock_table[] = {
20229949e86Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 0-7 */
20329949e86Sstevel 0,	0,	0,	0,	1,	2,	4,	5,	/* 8-15 */
20429949e86Sstevel 7,	8,	10,	11,	12,	13,	14,	15,	/* 16-23 */
20529949e86Sstevel 17,	18,	19,	20,	21,	22,	23,	24,	/* 24-31 */
20629949e86Sstevel 24,	25,	26,	27,	28,	29,	29,	30,	/* 32-39 */
20729949e86Sstevel 31,	32,	32,	33,	34,	35,	35,	36,	/* 40-47 */
20829949e86Sstevel 37,	38,	38,	39,	40,	40,	41,	42,	/* 48-55 */
20929949e86Sstevel 42,	43,	44,	44,	45,	46,	46,	47,	/* 56-63 */
21029949e86Sstevel 48,	48,	49,	50,	50,	51,	52,	52,	/* 64-71 */
21129949e86Sstevel 53,	54,	54,	55,	56,	57,	57,	58,	/* 72-79 */
21229949e86Sstevel 59,	59,	60,	60,	61,	62,	63,	63,	/* 80-87 */
21329949e86Sstevel 64,	65,	65,	66,	67,	68,	68,	69,	/* 88-95 */
21429949e86Sstevel 70,	70,	71,	72,	73,	74,	74,	75,	/* 96-103 */
21529949e86Sstevel 76,	77,	78,	78,	79,	80,	81,	82,	/* 104-111 */
21629949e86Sstevel };
21729949e86Sstevel 
21829949e86Sstevel #define	CLK_MN_CNT	11
21929949e86Sstevel #define	CLK_MX_CNT	(sizeof (clock_table)/sizeof (short))
22029949e86Sstevel 
22129949e86Sstevel /*
22229949e86Sstevel  * System temperature limits.
22329949e86Sstevel  *
22429949e86Sstevel  * The following variables are the warning and danger limits for the
22529949e86Sstevel  * different types of system boards. The limits are different because
22629949e86Sstevel  * the various boards reach different nominal temperatures because
22729949e86Sstevel  * of the different components that they contain.
22829949e86Sstevel  *
22929949e86Sstevel  * The warning limit is the temperature at which the user is warned.
23029949e86Sstevel  * The danger limit is the temperature at which the system is shutdown.
23129949e86Sstevel  * In the case of CPU/Memory system boards, the system will attempt
23229949e86Sstevel  * to offline and power down processors on a board in an attempt to
23329949e86Sstevel  * bring the board back into the nominal temperature range before
23429949e86Sstevel  * shutting down the system.
23529949e86Sstevel  *
23629949e86Sstevel  * These values can be tuned via /etc/system or boot -h.
23729949e86Sstevel  */
23829949e86Sstevel short cpu_warn_temp = 73;	/* CPU/Memory Warning Temperature */
23929949e86Sstevel short cpu_danger_temp = 83;	/* CPU/Memory Danger Temperature */
24029949e86Sstevel short io_warn_temp = 60;	/* IO Board Warning Temperature */
24129949e86Sstevel short io_danger_temp = 68;	/* IO Board Danger Temperature */
24229949e86Sstevel short clk_warn_temp = 60;	/* Clock Board Warning Temperature */
24329949e86Sstevel short clk_danger_temp = 68;	/* Clock Board Danger Temperature */
24429949e86Sstevel 
24529949e86Sstevel short dft_warn_temp = 60;	/* default warning temp value */
24629949e86Sstevel short dft_danger_temp = 68;	/* default danger temp value */
24729949e86Sstevel 
24829949e86Sstevel short cpu_warn_temp_4x = 60; 	/* CPU/Memory warning temp for 400 MHZ */
24929949e86Sstevel short cpu_danger_temp_4x = 68;	/* CPU/Memory danger temp for 400 MHZ */
25029949e86Sstevel 
25129949e86Sstevel /*
25229949e86Sstevel  * This variable tells us if we are in a heat chamber. It is set
25329949e86Sstevel  * early on in boot, after we check the OBP 'mfg-mode' property in
25429949e86Sstevel  * the options node.
25529949e86Sstevel  */
25629949e86Sstevel static int temperature_chamber = -1;
25729949e86Sstevel 
25829949e86Sstevel /*
25929949e86Sstevel  * The fhc memloc structure is protected under the bdlist lock
26029949e86Sstevel  */
26129949e86Sstevel static struct fhc_memloc *fhc_base_memloc = NULL;
26229949e86Sstevel 
26329949e86Sstevel /*
26429949e86Sstevel  * Driver global fault list mutex and list head pointer. The list is
26529949e86Sstevel  * protected by the mutex and contains a record of all known faults.
26629949e86Sstevel  * Faults can be inherited from the PROM or detected by the kernel.
26729949e86Sstevel  */
26829949e86Sstevel static kmutex_t ftlist_mutex;
26929949e86Sstevel static struct ft_link_list *ft_list = NULL;
27029949e86Sstevel static int ft_nfaults = 0;
27129949e86Sstevel 
27229949e86Sstevel /*
27329949e86Sstevel  * Table of all known fault strings. This table is indexed by the fault
27429949e86Sstevel  * type. Do not change the ordering of the table without redefining the
27529949e86Sstevel  * fault type enum list on fhc.h.
27629949e86Sstevel  */
27729949e86Sstevel char *ft_str_table[] = {
27829949e86Sstevel 	"Core Power Supply",		/* FT_CORE_PS */
27929949e86Sstevel 	"Overtemp",			/* FT_OVERTEMP */
28029949e86Sstevel 	"AC Power",			/* FT_AC_PWR */
28129949e86Sstevel 	"Peripheral Power Supply",	/* FT_PPS */
28229949e86Sstevel 	"System 3.3 Volt Power",	/* FT_CLK_33 */
28329949e86Sstevel 	"System 5.0 Volt Power",	/* FT_CLK_50 */
28429949e86Sstevel 	"Peripheral 5.0 Volt Power",	/* FT_V5_P */
28529949e86Sstevel 	"Peripheral 12 Volt Power",	/* FT_V12_P */
28629949e86Sstevel 	"Auxiliary 5.0 Volt Power",	/* FT_V5_AUX */
28729949e86Sstevel 	"Peripheral 5.0 Volt Precharge", /* FT_V5_P_PCH */
28829949e86Sstevel 	"Peripheral 12 Volt Precharge",	/* FT_V12_P_PCH */
28929949e86Sstevel 	"System 3.3 Volt Precharge",	/* FT_V3_PCH */
29029949e86Sstevel 	"System 5.0 Volt Precharge",	/* FT_V5_PCH */
29129949e86Sstevel 	"Peripheral Power Supply Fans",	/* FT_PPS_FAN */
29229949e86Sstevel 	"Rack Exhaust Fan",		/* FT_RACK_EXH */
29329949e86Sstevel 	"Disk Drive Fan",		/* FT_DSK_FAN */
29429949e86Sstevel 	"AC Box Fan",			/* FT_AC_FAN */
29529949e86Sstevel 	"Key Switch Fan",		/* FT_KEYSW_FAN */
29629949e86Sstevel 	"Minimum Power",		/* FT_INSUFFICIENT_POWER */
29729949e86Sstevel 	"PROM detected",		/* FT_PROM */
29829949e86Sstevel 	"Hot Plug Support System",	/* FT_HOT_PLUG */
29929949e86Sstevel 	"TOD"				/* FT_TODFAULT */
30029949e86Sstevel };
30129949e86Sstevel 
30229949e86Sstevel static int ft_max_index = (sizeof (ft_str_table) / sizeof (char *));
30329949e86Sstevel 
30429949e86Sstevel /*
30529949e86Sstevel  * Function prototypes
30629949e86Sstevel  */
30729949e86Sstevel static int fhc_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
30829949e86Sstevel 	void *, void *);
30929949e86Sstevel static int fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
31029949e86Sstevel 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
31129949e86Sstevel 
31229949e86Sstevel static int fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
31329949e86Sstevel 	ddi_intr_handle_impl_t *hdlp);
31429949e86Sstevel static void fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
31529949e86Sstevel 	ddi_intr_handle_impl_t *hdlp);
31629949e86Sstevel 
31729949e86Sstevel static int fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
31829949e86Sstevel static int fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
31929949e86Sstevel static int fhc_init(struct fhc_soft_state *softsp);
32029949e86Sstevel static void fhc_unmap_regs(struct fhc_soft_state *softsp);
32129949e86Sstevel static enum board_type fhc_board_type(struct fhc_soft_state *, int);
32229949e86Sstevel 
32329949e86Sstevel static void
32429949e86Sstevel fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign);
32529949e86Sstevel 
32629949e86Sstevel static int
32729949e86Sstevel fhc_ctlops_peekpoke(ddi_ctl_enum_t, peekpoke_ctlops_t *, void *result);
32829949e86Sstevel 
32929949e86Sstevel static void fhc_add_kstats(struct fhc_soft_state *);
33029949e86Sstevel static int fhc_kstat_update(kstat_t *, int);
33129949e86Sstevel static int check_for_chamber(void);
33229949e86Sstevel static int ft_ks_snapshot(struct kstat *, void *, int);
33329949e86Sstevel static int ft_ks_update(struct kstat *, int);
33429949e86Sstevel static int check_central(int board);
33529949e86Sstevel 
33629949e86Sstevel /*
33729949e86Sstevel  * board type and A/D convertor output passed in and real temperature
33829949e86Sstevel  * is returned.
33929949e86Sstevel  */
34029949e86Sstevel static short calibrate_temp(enum board_type, uchar_t, uint_t);
34129949e86Sstevel static enum temp_state get_temp_state(enum board_type, short, int);
34229949e86Sstevel 
34329949e86Sstevel /* Routine to determine if there are CPUs on this board. */
34429949e86Sstevel static int cpu_on_board(int);
34529949e86Sstevel 
34629949e86Sstevel static void build_bd_display_str(char *, enum board_type, int);
34729949e86Sstevel 
34829949e86Sstevel /* Interrupt distribution callback function. */
34929949e86Sstevel static void fhc_intrdist(void *);
35029949e86Sstevel 
35129949e86Sstevel /* CPU power control */
35229949e86Sstevel int fhc_cpu_poweroff(struct cpu *);	/* cpu_poweroff()->platform */
35329949e86Sstevel int fhc_cpu_poweron(struct cpu *);	/* cpu_poweron()->platform */
35429949e86Sstevel 
35529949e86Sstevel extern struct cpu_node cpunodes[];
35629949e86Sstevel extern void halt(char *);
35729949e86Sstevel 
35829949e86Sstevel /*
35929949e86Sstevel  * Configuration data structures
36029949e86Sstevel  */
36129949e86Sstevel static struct bus_ops fhc_bus_ops = {
36229949e86Sstevel 	BUSO_REV,
36329949e86Sstevel 	ddi_bus_map,		/* map */
36429949e86Sstevel 	0,			/* get_intrspec */
36529949e86Sstevel 	0,			/* add_intrspec */
36629949e86Sstevel 	0,			/* remove_intrspec */
36729949e86Sstevel 	i_ddi_map_fault,	/* map_fault */
36829949e86Sstevel 	ddi_no_dma_map,		/* dma_map */
36929949e86Sstevel 	ddi_no_dma_allochdl,
37029949e86Sstevel 	ddi_no_dma_freehdl,
37129949e86Sstevel 	ddi_no_dma_bindhdl,
37229949e86Sstevel 	ddi_no_dma_unbindhdl,
37329949e86Sstevel 	ddi_no_dma_flush,
37429949e86Sstevel 	ddi_no_dma_win,
37529949e86Sstevel 	ddi_dma_mctl,		/* dma_ctl */
37629949e86Sstevel 	fhc_ctlops,		/* ctl */
37729949e86Sstevel 	ddi_bus_prop_op,	/* prop_op */
37829949e86Sstevel 	0,			/* (*bus_get_eventcookie)();	*/
37929949e86Sstevel 	0,			/* (*bus_add_eventcall)();	*/
38029949e86Sstevel 	0,			/* (*bus_remove_eventcall)();	*/
38129949e86Sstevel 	0,			/* (*bus_post_event)();		*/
38229949e86Sstevel 	0,			/* (*bus_intr_control)();	*/
38329949e86Sstevel 	0,			/* (*bus_config)();		*/
38429949e86Sstevel 	0,			/* (*bus_unconfig)();		*/
38529949e86Sstevel 	0,			/* (*bus_fm_init)();		*/
38629949e86Sstevel 	0,			/* (*bus_fm_fini)();		*/
38729949e86Sstevel 	0,			/* (*bus_fm_access_enter)();	*/
38829949e86Sstevel 	0,			/* (*bus_fm_access_exit)();	*/
38929949e86Sstevel 	0,			/* (*bus_power)();		*/
39029949e86Sstevel 	fhc_intr_ops		/* (*bus_intr_op)();		*/
39129949e86Sstevel };
39229949e86Sstevel 
39329949e86Sstevel static struct cb_ops fhc_cb_ops = {
39429949e86Sstevel 	nulldev,		/* open */
39529949e86Sstevel 	nulldev,		/* close */
39629949e86Sstevel 	nulldev,		/* strategy */
39729949e86Sstevel 	nulldev,		/* print */
39829949e86Sstevel 	nulldev,		/* dump */
39929949e86Sstevel 	nulldev,		/* read */
40029949e86Sstevel 	nulldev,		/* write */
40129949e86Sstevel 	nulldev, 		/* ioctl */
40229949e86Sstevel 	nodev,			/* devmap */
40329949e86Sstevel 	nodev,			/* mmap */
40429949e86Sstevel 	nodev,			/* segmap */
40529949e86Sstevel 	nochpoll,		/* poll */
40629949e86Sstevel 	ddi_prop_op,		/* cb_prop_op */
40729949e86Sstevel 	0,			/* streamtab */
40829949e86Sstevel 	D_MP|D_NEW|D_HOTPLUG,	/* Driver compatibility flag */
40929949e86Sstevel 	CB_REV,			/* rev */
41029949e86Sstevel 	nodev,			/* cb_aread */
41129949e86Sstevel 	nodev			/* cb_awrite */
41229949e86Sstevel };
41329949e86Sstevel 
41429949e86Sstevel static struct dev_ops fhc_ops = {
41529949e86Sstevel 	DEVO_REV,		/* rev */
41629949e86Sstevel 	0,			/* refcnt  */
41729949e86Sstevel 	ddi_no_info,		/* getinfo */
41829949e86Sstevel 	nulldev,		/* identify */
41929949e86Sstevel 	nulldev,		/* probe */
42029949e86Sstevel 	fhc_attach,		/* attach */
42129949e86Sstevel 	fhc_detach,		/* detach */
42229949e86Sstevel 	nulldev,		/* reset */
42329949e86Sstevel 	&fhc_cb_ops,		/* cb_ops */
42429949e86Sstevel 	&fhc_bus_ops,		/* bus_ops */
42519397407SSherry Moore 	nulldev,		/* power */
42619397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce */
42729949e86Sstevel };
42829949e86Sstevel 
42929949e86Sstevel /*
43029949e86Sstevel  * Driver globals
43129949e86Sstevel  * TODO - We need to investigate what locking needs to be done here.
43229949e86Sstevel  */
43329949e86Sstevel void *fhcp;				/* fhc soft state hook */
43429949e86Sstevel 
43529949e86Sstevel extern struct mod_ops mod_driverops;
43629949e86Sstevel 
43729949e86Sstevel static struct modldrv modldrv = {
43829949e86Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
43919397407SSherry Moore 	"FHC Nexus",		/* Name of module. */
44029949e86Sstevel 	&fhc_ops,		/* driver ops */
44129949e86Sstevel };
44229949e86Sstevel 
44329949e86Sstevel static struct modlinkage modlinkage = {
44429949e86Sstevel 	MODREV_1,		/* rev */
44529949e86Sstevel 	(void *)&modldrv,
44629949e86Sstevel 	NULL
44729949e86Sstevel };
44829949e86Sstevel 
44929949e86Sstevel 
45029949e86Sstevel /*
45129949e86Sstevel  * These are the module initialization routines.
45229949e86Sstevel  */
45329949e86Sstevel 
45429949e86Sstevel static caddr_t shutdown_va;
45529949e86Sstevel 
45629949e86Sstevel int
45729949e86Sstevel _init(void)
45829949e86Sstevel {
45929949e86Sstevel 	int error;
46029949e86Sstevel 
46129949e86Sstevel 	if ((error = ddi_soft_state_init(&fhcp,
46229949e86Sstevel 	    sizeof (struct fhc_soft_state), 1)) != 0)
46329949e86Sstevel 		return (error);
46429949e86Sstevel 
46529949e86Sstevel 	fhc_bdlist_init();
46629949e86Sstevel 	mutex_init(&ftlist_mutex, NULL, MUTEX_DEFAULT, NULL);
46729949e86Sstevel 
46829949e86Sstevel 	shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
46929949e86Sstevel 	ASSERT(shutdown_va != NULL);
47029949e86Sstevel 
47129949e86Sstevel 	plat_register_tod_fault(fhc_tod_fault);
47229949e86Sstevel 
47329949e86Sstevel 	return (mod_install(&modlinkage));
47429949e86Sstevel }
47529949e86Sstevel 
47629949e86Sstevel int
47729949e86Sstevel _fini(void)
47829949e86Sstevel {
47929949e86Sstevel 	int error;
48029949e86Sstevel 
48129949e86Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
48229949e86Sstevel 		return (error);
48329949e86Sstevel 
48429949e86Sstevel 	plat_register_tod_fault(NULL);
48529949e86Sstevel 
48629949e86Sstevel 	mutex_destroy(&ftlist_mutex);
48729949e86Sstevel 
48829949e86Sstevel 	fhc_bdlist_fini();
48929949e86Sstevel 
49029949e86Sstevel 	ddi_soft_state_fini(&fhcp);
49129949e86Sstevel 
49229949e86Sstevel 	return (0);
49329949e86Sstevel }
49429949e86Sstevel 
49529949e86Sstevel int
49629949e86Sstevel _info(struct modinfo *modinfop)
49729949e86Sstevel {
49829949e86Sstevel 	return (mod_info(&modlinkage, modinfop));
49929949e86Sstevel }
50029949e86Sstevel 
50129949e86Sstevel /*
50229949e86Sstevel  * Reset the interrupt mapping registers.
50329949e86Sstevel  * This function resets the values during DDI_RESUME.
50429949e86Sstevel  *
50529949e86Sstevel  * NOTE: This function will not work for a full CPR cycle
50629949e86Sstevel  * and is currently designed to handle the RESUME after a connect.
50729949e86Sstevel  *
50829949e86Sstevel  * Note about the PROM handling of moving CENTRAL to another board:
50929949e86Sstevel  * The PROM moves the IGN identity (igr register) from the
51029949e86Sstevel  * original CENTRAL to the new one. This means that we do not
51129949e86Sstevel  * duplicate the fhc_attach code that sets it to (board number * 2).
51229949e86Sstevel  * We rely on only using FHC interrupts from one board only
51329949e86Sstevel  * (the UART and SYS interrupts) so that the values of the other IGNs
51429949e86Sstevel  * are irrelevant. The benefit of this approach is that we don't
51529949e86Sstevel  * have to have to tear down and rebuild the interrupt records
51629949e86Sstevel  * for UART and SYS. It is also why we don't try to change the
51729949e86Sstevel  * board number in the fhc instance for the clock board.
51829949e86Sstevel  */
51929949e86Sstevel static void
52029949e86Sstevel fhc_handle_imr(struct fhc_soft_state *softsp)
52129949e86Sstevel {
52229949e86Sstevel 	int i;
52329949e86Sstevel 	int cent;
52429949e86Sstevel 	uint_t tmp_reg;
52529949e86Sstevel 
52629949e86Sstevel 
52729949e86Sstevel 	if (softsp->is_central) {
52829949e86Sstevel 		uint_t want_igr, act_igr;
52929949e86Sstevel 
53029949e86Sstevel 		want_igr = softsp->list->sc.board << 1;
53129949e86Sstevel 		act_igr = *softsp->igr & 0x1f;
53229949e86Sstevel 		if (want_igr != act_igr) {
53329949e86Sstevel 			*softsp->igr = want_igr;
53429949e86Sstevel 			tmp_reg = *softsp->igr;
53529949e86Sstevel #ifdef lint
53629949e86Sstevel 			tmp_reg = tmp_reg;
53729949e86Sstevel #endif
53829949e86Sstevel 			/* We must now re-issue any pending interrupts. */
53929949e86Sstevel 			for (i = 0; i < FHC_MAX_INO; i++) {
54029949e86Sstevel 				if (*(softsp->intr_regs[i].clear_reg) == 3) {
54129949e86Sstevel 					*(softsp->intr_regs[i].clear_reg) =
54229949e86Sstevel 					    ISM_IDLE;
54329949e86Sstevel 
54429949e86Sstevel 					tmp_reg =
54529949e86Sstevel 					    *(softsp->intr_regs[i].clear_reg);
54629949e86Sstevel #ifdef lint
54729949e86Sstevel 					tmp_reg = tmp_reg;
54829949e86Sstevel #endif
54929949e86Sstevel 				}
55029949e86Sstevel 			}
55129949e86Sstevel 			cmn_err(CE_NOTE, "central IGN corruption fixed: "
55229949e86Sstevel 			    "got %x wanted %x", act_igr, want_igr);
55329949e86Sstevel 		}
55429949e86Sstevel 		return;
55529949e86Sstevel 	}
55629949e86Sstevel 
55729949e86Sstevel 	ASSERT(softsp->list->sc.board == FHC_BSR_TO_BD(*(softsp->bsr)));
55829949e86Sstevel 	cent = check_central(softsp->list->sc.board);
55929949e86Sstevel 
56029949e86Sstevel 	/* Loop through all 4 FHC interrupt mapping registers */
56129949e86Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
56229949e86Sstevel 
56329949e86Sstevel 		if (i == FHC_SYS_INO &&
56429949e86Sstevel 		    *(softsp->intr_regs[i].clear_reg) == 3) {
56529949e86Sstevel 			cmn_err(CE_NOTE,
56629949e86Sstevel 			    "found lost system interrupt, resetting..");
56729949e86Sstevel 
56829949e86Sstevel 			*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
56929949e86Sstevel 
57029949e86Sstevel 			/*
57129949e86Sstevel 			 * ensure atomic write with this read.
57229949e86Sstevel 			 */
57329949e86Sstevel 			tmp_reg = *(softsp->intr_regs[i].clear_reg);
57429949e86Sstevel #ifdef lint
57529949e86Sstevel 			tmp_reg = tmp_reg;
57629949e86Sstevel #endif
57729949e86Sstevel 		}
57829949e86Sstevel 
57929949e86Sstevel 		/*
58029949e86Sstevel 		 * The mapping registers on the board with the "central" bit
58129949e86Sstevel 		 * set should not be touched as it has been taken care by POST.
58229949e86Sstevel 		 */
58329949e86Sstevel 
58429949e86Sstevel 		if (cent)
58529949e86Sstevel 			continue;
58629949e86Sstevel 
58729949e86Sstevel 		*(softsp->intr_regs[i].mapping_reg) = 0;
58829949e86Sstevel 
58929949e86Sstevel 		/*
59029949e86Sstevel 		 * ensure atomic write with this read.
59129949e86Sstevel 		 */
59229949e86Sstevel 		tmp_reg = *(softsp->intr_regs[i].mapping_reg);
59329949e86Sstevel #ifdef lint
59429949e86Sstevel 		tmp_reg = tmp_reg;
59529949e86Sstevel #endif
59629949e86Sstevel 
59729949e86Sstevel 	}
59829949e86Sstevel }
59929949e86Sstevel 
60029949e86Sstevel static int
60129949e86Sstevel check_central(int board)
60229949e86Sstevel {
60329949e86Sstevel 	uint_t cs_value;
60429949e86Sstevel 
60529949e86Sstevel 	/*
60629949e86Sstevel 	 * This is the value of AC configuration and status reg
60729949e86Sstevel 	 * in the Local Devices space. We access it as a physical
60829949e86Sstevel 	 * address.
60929949e86Sstevel 	 */
61029949e86Sstevel 	cs_value = ldphysio(AC_BCSR(board));
61129949e86Sstevel 	if (cs_value & AC_CENTRAL)
61229949e86Sstevel 		return (TRUE);
61329949e86Sstevel 	else
61429949e86Sstevel 		return (FALSE);
61529949e86Sstevel }
61629949e86Sstevel 
61729949e86Sstevel static int
61829949e86Sstevel fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
61929949e86Sstevel {
62029949e86Sstevel 	struct fhc_soft_state *softsp;
62129949e86Sstevel 	int instance;
62229949e86Sstevel 
62329949e86Sstevel 	instance = ddi_get_instance(devi);
62429949e86Sstevel 
62529949e86Sstevel 	switch (cmd) {
62629949e86Sstevel 	case DDI_ATTACH:
62729949e86Sstevel 		break;
62829949e86Sstevel 
62929949e86Sstevel 	case DDI_RESUME:
63029949e86Sstevel 		softsp = ddi_get_soft_state(fhcp, instance);
63129949e86Sstevel 		/* IGR, NOT_BRD_PRES handled by prom */
63229949e86Sstevel 		/* reset interrupt mapping registers */
63329949e86Sstevel 		fhc_handle_imr(softsp);
63429949e86Sstevel 
63529949e86Sstevel 		return (DDI_SUCCESS);
63629949e86Sstevel 
63729949e86Sstevel 	default:
63829949e86Sstevel 		return (DDI_FAILURE);
63929949e86Sstevel 	}
64029949e86Sstevel 
64129949e86Sstevel 
64229949e86Sstevel 	if (ddi_soft_state_zalloc(fhcp, instance) != DDI_SUCCESS)
64329949e86Sstevel 		return (DDI_FAILURE);
64429949e86Sstevel 
64529949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, instance);
64629949e86Sstevel 
64729949e86Sstevel 	/* Set the dip in the soft state */
64829949e86Sstevel 	softsp->dip = devi;
64929949e86Sstevel 
65029949e86Sstevel 	if (fhc_init(softsp) != DDI_SUCCESS)
65129949e86Sstevel 		goto bad;
65229949e86Sstevel 
65329949e86Sstevel 	ddi_report_dev(devi);
65429949e86Sstevel 
65529949e86Sstevel 	return (DDI_SUCCESS);
65629949e86Sstevel 
65729949e86Sstevel bad:
65829949e86Sstevel 	ddi_soft_state_free(fhcp, instance);
65929949e86Sstevel 	return (DDI_FAILURE);
66029949e86Sstevel }
66129949e86Sstevel 
66229949e86Sstevel static int
66329949e86Sstevel fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
66429949e86Sstevel {
66529949e86Sstevel 	int board;
66629949e86Sstevel 	int instance;
66729949e86Sstevel 	struct fhc_soft_state *softsp;
66829949e86Sstevel 	fhc_bd_t *list = NULL;
66929949e86Sstevel 
67029949e86Sstevel 	/* get the instance of this devi */
67129949e86Sstevel 	instance = ddi_get_instance(devi);
67229949e86Sstevel 
67329949e86Sstevel 	/* get the soft state pointer for this device node */
67429949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, instance);
67529949e86Sstevel 
67629949e86Sstevel 	board = softsp->list->sc.board;
67729949e86Sstevel 
67829949e86Sstevel 	switch (cmd) {
67929949e86Sstevel 	case DDI_SUSPEND:
68029949e86Sstevel 
68129949e86Sstevel 		return (DDI_SUCCESS);
68229949e86Sstevel 
68329949e86Sstevel 	case DDI_DETACH:
68429949e86Sstevel 		/* grab the lock on the board list */
68529949e86Sstevel 		list = fhc_bdlist_lock(board);
68629949e86Sstevel 
68729949e86Sstevel 		if (fhc_bd_detachable(board) &&
68829949e86Sstevel 		    !fhc_bd_is_jtag_master(board))
68929949e86Sstevel 			break;
69029949e86Sstevel 		else
69129949e86Sstevel 			fhc_bdlist_unlock();
69229949e86Sstevel 		/* FALLTHROUGH */
69329949e86Sstevel 
69429949e86Sstevel 	default:
69529949e86Sstevel 		return (DDI_FAILURE);
69629949e86Sstevel 	}
69729949e86Sstevel 
69829949e86Sstevel 	/* Remove the interrupt redistribution callback. */
69929949e86Sstevel 	intr_dist_rem(fhc_intrdist, (void *)devi);
70029949e86Sstevel 
70129949e86Sstevel 	/* remove the soft state pointer from the board list */
70229949e86Sstevel 	list->softsp = NULL;
70329949e86Sstevel 
70429949e86Sstevel 	/* clear inherited faults from the PROM. */
70529949e86Sstevel 	clear_fault(list->sc.board, FT_PROM, FT_BOARD);
70629949e86Sstevel 
70729949e86Sstevel 	/* remove the kstat for this board */
70829949e86Sstevel 	kstat_delete(softsp->fhc_ksp);
70929949e86Sstevel 
71029949e86Sstevel 	/* destroy the mutexes in this soft state structure */
71129949e86Sstevel 	mutex_destroy(&softsp->poll_list_lock);
71229949e86Sstevel 	mutex_destroy(&softsp->ctrl_lock);
71329949e86Sstevel 
71429949e86Sstevel 	/* unmap all the register sets */
71529949e86Sstevel 	fhc_unmap_regs(softsp);
71629949e86Sstevel 
71729949e86Sstevel 	/* release the board list lock now */
71829949e86Sstevel 	fhc_bdlist_unlock();
71929949e86Sstevel 
72029949e86Sstevel 	/* free the soft state structure */
72129949e86Sstevel 	ddi_soft_state_free(fhcp, instance);
72229949e86Sstevel 
72329949e86Sstevel 	return (DDI_SUCCESS);
72429949e86Sstevel }
72529949e86Sstevel 
72629949e86Sstevel static enum board_type
72729949e86Sstevel fhc_board_type(struct fhc_soft_state *softsp, int board)
72829949e86Sstevel {
72929949e86Sstevel 	int proplen;
73029949e86Sstevel 	char *board_type;
73129949e86Sstevel 	enum board_type type;
73229949e86Sstevel 
73329949e86Sstevel 	if (softsp->is_central)
73429949e86Sstevel 		type = CLOCK_BOARD;
73529949e86Sstevel 	else if (ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip,
73629949e86Sstevel 	    DDI_PROP_DONTPASS, "board-type", (caddr_t)&board_type,
73729949e86Sstevel 	    &proplen) == DDI_PROP_SUCCESS) {
73829949e86Sstevel 		/* match the board-type string */
73929949e86Sstevel 		if (strcmp(CPU_BD_NAME, board_type) == 0) {
74029949e86Sstevel 			type = CPU_BOARD;
74129949e86Sstevel 		} else if (strcmp(MEM_BD_NAME, board_type) == 0) {
74229949e86Sstevel 			type = MEM_BOARD;
74329949e86Sstevel 		} else if (strcmp(IO_2SBUS_BD_NAME, board_type) == 0) {
74429949e86Sstevel 			type = IO_2SBUS_BOARD;
74529949e86Sstevel 		} else if (strcmp(IO_SBUS_FFB_BD_NAME, board_type) == 0) {
74629949e86Sstevel 			type = IO_SBUS_FFB_BOARD;
74729949e86Sstevel 		} else if (strcmp(IO_2SBUS_SOCPLUS_BD_NAME, board_type) == 0) {
74829949e86Sstevel 			type = IO_2SBUS_SOCPLUS_BOARD;
74929949e86Sstevel 		} else if (strcmp(IO_SBUS_FFB_SOCPLUS_BD_NAME, board_type)
75029949e86Sstevel 		    == 0) {
75129949e86Sstevel 			type = IO_SBUS_FFB_SOCPLUS_BOARD;
75229949e86Sstevel 		} else if (strcmp(IO_PCI_BD_NAME, board_type) == 0) {
75329949e86Sstevel 			type = IO_PCI_BOARD;
75429949e86Sstevel 		} else {
75529949e86Sstevel 			type = UNKNOWN_BOARD;
75629949e86Sstevel 		}
75729949e86Sstevel 		kmem_free(board_type, proplen);
75829949e86Sstevel 	} else
75929949e86Sstevel 		type = UNKNOWN_BOARD;
76029949e86Sstevel 
76129949e86Sstevel 	/*
76229949e86Sstevel 	 * if the board type is indeterminate, it must be determined.
76329949e86Sstevel 	 */
76429949e86Sstevel 	if (type == UNKNOWN_BOARD) {
76529949e86Sstevel 		/*
76629949e86Sstevel 		 * Use the UPA64 bits from the FHC.
76729949e86Sstevel 		 * This is not the best solution since we
76829949e86Sstevel 		 * cannot fully type the IO boards.
76929949e86Sstevel 		 */
77029949e86Sstevel 		if (cpu_on_board(board))
77129949e86Sstevel 			type = CPU_BOARD;
77229949e86Sstevel 		else if ((*(softsp->bsr) & FHC_UPADATA64A) ||
77329949e86Sstevel 		    (*(softsp->bsr) & FHC_UPADATA64B))
77429949e86Sstevel 			type = IO_2SBUS_BOARD;
77529949e86Sstevel 		else
77629949e86Sstevel 			type = MEM_BOARD;
77729949e86Sstevel 	}
77829949e86Sstevel 
77929949e86Sstevel 	return (type);
78029949e86Sstevel }
78129949e86Sstevel 
78229949e86Sstevel static void
78329949e86Sstevel fhc_unmap_regs(struct fhc_soft_state *softsp)
78429949e86Sstevel {
78529949e86Sstevel 	dev_info_t *dip = softsp->dip;
78629949e86Sstevel 
78729949e86Sstevel 	if (softsp->id) {
78829949e86Sstevel 		ddi_unmap_regs(dip, 0, (caddr_t *)&softsp->id, 0, 0);
78929949e86Sstevel 		softsp->id = NULL;
79029949e86Sstevel 	}
79129949e86Sstevel 	if (softsp->igr) {
79229949e86Sstevel 		ddi_unmap_regs(dip, 1, (caddr_t *)&softsp->igr, 0, 0);
79329949e86Sstevel 		softsp->igr = NULL;
79429949e86Sstevel 	}
79529949e86Sstevel 	if (softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg) {
79629949e86Sstevel 		ddi_unmap_regs(dip, 2,
79729949e86Sstevel 		    (caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
79829949e86Sstevel 		    0, 0);
79929949e86Sstevel 		softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg = NULL;
80029949e86Sstevel 	}
80129949e86Sstevel 	if (softsp->intr_regs[FHC_SYS_INO].mapping_reg) {
80229949e86Sstevel 		ddi_unmap_regs(dip, 3,
80329949e86Sstevel 		    (caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
80429949e86Sstevel 		    0, 0);
80529949e86Sstevel 		softsp->intr_regs[FHC_SYS_INO].mapping_reg = NULL;
80629949e86Sstevel 	}
80729949e86Sstevel 	if (softsp->intr_regs[FHC_UART_INO].mapping_reg) {
80829949e86Sstevel 		ddi_unmap_regs(dip, 4,
80929949e86Sstevel 		    (caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
81029949e86Sstevel 		    0, 0);
81129949e86Sstevel 		softsp->intr_regs[FHC_UART_INO].mapping_reg = NULL;
81229949e86Sstevel 	}
81329949e86Sstevel 	if (softsp->intr_regs[FHC_TOD_INO].mapping_reg) {
81429949e86Sstevel 		ddi_unmap_regs(dip, 5,
81529949e86Sstevel 		    (caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
81629949e86Sstevel 		    0, 0);
81729949e86Sstevel 		softsp->intr_regs[FHC_TOD_INO].mapping_reg = NULL;
81829949e86Sstevel 	}
81929949e86Sstevel }
82029949e86Sstevel 
82129949e86Sstevel static int
82229949e86Sstevel fhc_init(struct fhc_soft_state *softsp)
82329949e86Sstevel {
82429949e86Sstevel 	int i;
82529949e86Sstevel 	uint_t tmp_reg;
82629949e86Sstevel 	int board;
82729949e86Sstevel 
82829949e86Sstevel 	/*
82929949e86Sstevel 	 * Map in the FHC registers. Specifying length and offset of
83029949e86Sstevel 	 * zero maps in the entire OBP register set.
83129949e86Sstevel 	 */
83229949e86Sstevel 
83329949e86Sstevel 	/* map in register set 0 */
83429949e86Sstevel 	if (ddi_map_regs(softsp->dip, 0,
83529949e86Sstevel 	    (caddr_t *)&softsp->id, 0, 0)) {
83629949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map internal "
83729949e86Sstevel 		    "registers", ddi_get_instance(softsp->dip));
83829949e86Sstevel 		goto bad;
83929949e86Sstevel 	}
84029949e86Sstevel 
84129949e86Sstevel 	/*
84229949e86Sstevel 	 * Fill in the virtual addresses of the registers in the
84329949e86Sstevel 	 * fhc_soft_state structure.
84429949e86Sstevel 	 */
84529949e86Sstevel 	softsp->rctrl = (uint_t *)((char *)(softsp->id) +
84629949e86Sstevel 	    FHC_OFF_RCTRL);
84729949e86Sstevel 	softsp->ctrl = (uint_t *)((char *)(softsp->id) +
84829949e86Sstevel 	    FHC_OFF_CTRL);
84929949e86Sstevel 	softsp->bsr = (uint_t *)((char *)(softsp->id) +
85029949e86Sstevel 	    FHC_OFF_BSR);
85129949e86Sstevel 	softsp->jtag_ctrl = (uint_t *)((char *)(softsp->id) +
85229949e86Sstevel 	    FHC_OFF_JTAG_CTRL);
85329949e86Sstevel 	softsp->jt_master.jtag_cmd = (uint_t *)((char *)(softsp->id) +
85429949e86Sstevel 	    FHC_OFF_JTAG_CMD);
85529949e86Sstevel 
85629949e86Sstevel 	/* map in register set 1 */
85729949e86Sstevel 	if (ddi_map_regs(softsp->dip, 1,
85829949e86Sstevel 	    (caddr_t *)&softsp->igr, 0, 0)) {
85929949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map IGR "
86029949e86Sstevel 		    "register", ddi_get_instance(softsp->dip));
86129949e86Sstevel 		goto bad;
86229949e86Sstevel 	}
86329949e86Sstevel 
86429949e86Sstevel 	/*
86529949e86Sstevel 	 * map in register set 2
86629949e86Sstevel 	 * XXX this can never be used as an interrupt generator
86729949e86Sstevel 	 * (hardware queue overflow in fhc)
86829949e86Sstevel 	 */
86929949e86Sstevel 	if (ddi_map_regs(softsp->dip, 2,
87029949e86Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
87129949e86Sstevel 	    0, 0)) {
87229949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map Fan Fail "
87329949e86Sstevel 		    "IMR register", ddi_get_instance(softsp->dip));
87429949e86Sstevel 		goto bad;
87529949e86Sstevel 	}
87629949e86Sstevel 
87729949e86Sstevel 	/* map in register set 3 */
87829949e86Sstevel 	if (ddi_map_regs(softsp->dip, 3,
87929949e86Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
88029949e86Sstevel 	    0, 0)) {
88129949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map System "
88229949e86Sstevel 		    "IMR register\n", ddi_get_instance(softsp->dip));
88329949e86Sstevel 		goto bad;
88429949e86Sstevel 	}
88529949e86Sstevel 
88629949e86Sstevel 	/* map in register set 4 */
88729949e86Sstevel 	if (ddi_map_regs(softsp->dip, 4,
88829949e86Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
88929949e86Sstevel 	    0, 0)) {
89029949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map UART "
89129949e86Sstevel 		    "IMR register\n", ddi_get_instance(softsp->dip));
89229949e86Sstevel 		goto bad;
89329949e86Sstevel 	}
89429949e86Sstevel 
89529949e86Sstevel 	/* map in register set 5 */
89629949e86Sstevel 	if (ddi_map_regs(softsp->dip, 5,
89729949e86Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
89829949e86Sstevel 	    0, 0)) {
89929949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map FHC TOD "
90029949e86Sstevel 		    "IMR register", ddi_get_instance(softsp->dip));
90129949e86Sstevel 		goto bad;
90229949e86Sstevel 	}
90329949e86Sstevel 
90429949e86Sstevel 	/* Loop over all intr sets and setup the VAs for the ISMR */
90529949e86Sstevel 	/* TODO - Make sure we are calculating the ISMR correctly. */
90629949e86Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
90729949e86Sstevel 		softsp->intr_regs[i].clear_reg =
90829949e86Sstevel 		    (uint_t *)((char *)(softsp->intr_regs[i].mapping_reg) +
90929949e86Sstevel 		    FHC_OFF_ISMR);
91029949e86Sstevel 
91129949e86Sstevel 		/* Now clear the state machines to idle */
91229949e86Sstevel 		*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
91329949e86Sstevel 	}
91429949e86Sstevel 
91529949e86Sstevel 	/*
91629949e86Sstevel 	 * It is OK to not have a OBP_BOARDNUM property. This happens for
91729949e86Sstevel 	 * the board which is a child of central. However this FHC
91829949e86Sstevel 	 * still needs a proper Interrupt Group Number programmed
91929949e86Sstevel 	 * into the Interrupt Group register, because the other
92029949e86Sstevel 	 * instance of FHC, which is not under central, will properly
92129949e86Sstevel 	 * program the IGR. The numbers from the two settings of the
92229949e86Sstevel 	 * IGR need to be the same. One driver cannot wait for the
92329949e86Sstevel 	 * other to program the IGR, because there is no guarantee
92429949e86Sstevel 	 * which instance of FHC will get attached first.
92529949e86Sstevel 	 */
92629949e86Sstevel 	if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
92729949e86Sstevel 	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
92829949e86Sstevel 		/*
92929949e86Sstevel 		 * Now determine the board number by reading the
93029949e86Sstevel 		 * hardware register.
93129949e86Sstevel 		 */
93229949e86Sstevel 		board = FHC_BSR_TO_BD(*(softsp->bsr));
93329949e86Sstevel 		softsp->is_central = 1;
93429949e86Sstevel 	}
93529949e86Sstevel 
93629949e86Sstevel 	/*
93729949e86Sstevel 	 * If this fhc holds JTAG master line, and is not the central fhc,
93829949e86Sstevel 	 * (this avoids two JTAG master nodes) then initialize the
93929949e86Sstevel 	 * mutex and set the flag in the structure.
94029949e86Sstevel 	 */
94129949e86Sstevel 	if ((*(softsp->jtag_ctrl) & JTAG_MASTER_EN) && !softsp->is_central) {
94229949e86Sstevel 		mutex_init(&(softsp->jt_master.lock), NULL, MUTEX_DEFAULT,
94329949e86Sstevel 		    NULL);
94429949e86Sstevel 		softsp->jt_master.is_master = 1;
94529949e86Sstevel 	} else {
94629949e86Sstevel 		softsp->jt_master.is_master = 0;
94729949e86Sstevel 	}
94829949e86Sstevel 
94929949e86Sstevel 	fhc_bd_init(softsp, board, fhc_board_type(softsp, board));
95029949e86Sstevel 
95129949e86Sstevel 	/* Initialize the mutex guarding the poll_list. */
95229949e86Sstevel 	mutex_init(&softsp->poll_list_lock, NULL, MUTEX_DRIVER, NULL);
95329949e86Sstevel 
95429949e86Sstevel 	/* Initialize the mutex guarding the FHC CSR */
95529949e86Sstevel 	mutex_init(&softsp->ctrl_lock, NULL, MUTEX_DRIVER, NULL);
95629949e86Sstevel 
95729949e86Sstevel 	/* Initialize the poll_list to be empty */
95829949e86Sstevel 	for (i = 0; i < MAX_ZS_CNT; i++) {
95929949e86Sstevel 		softsp->poll_list[i].funcp = NULL;
96029949e86Sstevel 	}
96129949e86Sstevel 
96229949e86Sstevel 	/* Modify the various registers in the FHC now */
96329949e86Sstevel 
96429949e86Sstevel 	/*
96529949e86Sstevel 	 * We know this board to be present now, record that state and
96629949e86Sstevel 	 * remove the NOT_BRD_PRES condition
96729949e86Sstevel 	 */
96829949e86Sstevel 	if (!(softsp->is_central)) {
96929949e86Sstevel 		mutex_enter(&softsp->ctrl_lock);
97029949e86Sstevel 		*(softsp->ctrl) |= FHC_NOT_BRD_PRES;
97129949e86Sstevel 		/* Now flush the hardware store buffers. */
97229949e86Sstevel 		tmp_reg = *(softsp->ctrl);
97329949e86Sstevel #ifdef lint
97429949e86Sstevel 		tmp_reg = tmp_reg;
97529949e86Sstevel #endif
97629949e86Sstevel 		/* XXX record the board state in global space */
97729949e86Sstevel 		mutex_exit(&softsp->ctrl_lock);
97829949e86Sstevel 
97929949e86Sstevel 		/* Add kstats for all non-central instances of the FHC. */
98029949e86Sstevel 		fhc_add_kstats(softsp);
98129949e86Sstevel 	}
98229949e86Sstevel 
98329949e86Sstevel 	/*
98429949e86Sstevel 	 * Read the device tree to see if this system is in an environmental
98529949e86Sstevel 	 * chamber.
98629949e86Sstevel 	 */
98729949e86Sstevel 	if (temperature_chamber == -1) {
98829949e86Sstevel 		temperature_chamber = check_for_chamber();
98929949e86Sstevel 	}
99029949e86Sstevel 
99129949e86Sstevel 	/* Check for inherited faults from the PROM. */
99229949e86Sstevel 	if (*softsp->ctrl & FHC_LED_MID) {
99329949e86Sstevel 		reg_fault(softsp->list->sc.board, FT_PROM, FT_BOARD);
99429949e86Sstevel 	}
99529949e86Sstevel 
99629949e86Sstevel 	/*
99729949e86Sstevel 	 * setup the IGR. Shift the board number over by one to get
99829949e86Sstevel 	 * the UPA MID.
99929949e86Sstevel 	 */
100029949e86Sstevel 	*(softsp->igr) = (softsp->list->sc.board) << 1;
100129949e86Sstevel 
100229949e86Sstevel 	/* Now flush the hardware store buffers. */
100329949e86Sstevel 	tmp_reg = *(softsp->id);
100429949e86Sstevel #ifdef lint
100529949e86Sstevel 	tmp_reg = tmp_reg;
100629949e86Sstevel #endif
100729949e86Sstevel 
100829949e86Sstevel 	/* Add the interrupt redistribution callback. */
100929949e86Sstevel 	intr_dist_add(fhc_intrdist, (void *)softsp->dip);
101029949e86Sstevel 
101129949e86Sstevel 	return (DDI_SUCCESS);
101229949e86Sstevel bad:
101329949e86Sstevel 	fhc_unmap_regs(softsp);
101429949e86Sstevel 	return (DDI_FAILURE);
101529949e86Sstevel }
101629949e86Sstevel 
101729949e86Sstevel static uint_t
101829949e86Sstevel fhc_intr_wrapper(caddr_t arg)
101929949e86Sstevel {
102029949e86Sstevel 	uint_t intr_return;
102129949e86Sstevel 	uint_t tmpreg;
102229949e86Sstevel 	struct fhc_wrapper_arg *intr_info = (struct fhc_wrapper_arg *)arg;
102329949e86Sstevel 	uint_t (*funcp)(caddr_t, caddr_t) = intr_info->funcp;
102429949e86Sstevel 	caddr_t iarg1 = intr_info->arg1;
102529949e86Sstevel 	caddr_t iarg2 = intr_info->arg2;
102629949e86Sstevel 	dev_info_t *dip = intr_info->child;
102729949e86Sstevel 
102829949e86Sstevel 	tmpreg = ISM_IDLE;
102929949e86Sstevel 
103029949e86Sstevel 	DTRACE_PROBE4(interrupt__start, dev_info_t, dip,
103129949e86Sstevel 	    void *, funcp, caddr_t, iarg1, caddr_t, iarg2);
103229949e86Sstevel 
103329949e86Sstevel 	intr_return = (*funcp)(iarg1, iarg2);
103429949e86Sstevel 
103529949e86Sstevel 	DTRACE_PROBE4(interrupt__complete, dev_info_t, dip,
103629949e86Sstevel 	    void *, funcp, caddr_t, iarg1, int, intr_return);
103729949e86Sstevel 
103829949e86Sstevel 	/* Idle the state machine. */
103929949e86Sstevel 	*(intr_info->clear_reg) = tmpreg;
104029949e86Sstevel 
104129949e86Sstevel 	/* Flush the hardware store buffers. */
104229949e86Sstevel 	tmpreg = *(intr_info->clear_reg);
104329949e86Sstevel #ifdef lint
104429949e86Sstevel 	tmpreg = tmpreg;
104529949e86Sstevel #endif	/* lint */
104629949e86Sstevel 
104729949e86Sstevel 	return (intr_return);
104829949e86Sstevel }
104929949e86Sstevel 
105029949e86Sstevel /*
105129949e86Sstevel  * fhc_zs_intr_wrapper
105229949e86Sstevel  *
105329949e86Sstevel  * This function handles intrerrupts where more than one device may interupt
105429949e86Sstevel  * the fhc with the same mondo.
105529949e86Sstevel  */
105629949e86Sstevel 
105729949e86Sstevel #define	MAX_INTR_CNT 10
105829949e86Sstevel 
105929949e86Sstevel static uint_t
106029949e86Sstevel fhc_zs_intr_wrapper(caddr_t arg)
106129949e86Sstevel {
106229949e86Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)arg;
106329949e86Sstevel 	uint_t (*funcp0)(caddr_t, caddr_t);
106429949e86Sstevel 	uint_t (*funcp1)(caddr_t, caddr_t);
106529949e86Sstevel 	caddr_t funcp0_arg1, funcp0_arg2, funcp1_arg1, funcp1_arg2;
106629949e86Sstevel 	uint_t tmp_reg;
106729949e86Sstevel 	uint_t result = DDI_INTR_UNCLAIMED;
106829949e86Sstevel 	volatile uint_t *clear_reg;
106929949e86Sstevel 	uchar_t *spurious_cntr = &softsp->spurious_zs_cntr;
107029949e86Sstevel 
107129949e86Sstevel 	funcp0 = softsp->poll_list[0].funcp;
107229949e86Sstevel 	funcp1 = softsp->poll_list[1].funcp;
107329949e86Sstevel 	funcp0_arg1 = softsp->poll_list[0].arg1;
107429949e86Sstevel 	funcp0_arg2 = softsp->poll_list[0].arg2;
107529949e86Sstevel 	funcp1_arg1 = softsp->poll_list[1].arg1;
107629949e86Sstevel 	funcp1_arg2 = softsp->poll_list[1].arg2;
107729949e86Sstevel 	clear_reg = softsp->intr_regs[FHC_UART_INO].clear_reg;
107829949e86Sstevel 
107929949e86Sstevel 	if (funcp0 != NULL) {
108029949e86Sstevel 		if ((funcp0)(funcp0_arg1, funcp0_arg2) == DDI_INTR_CLAIMED) {
108129949e86Sstevel 			result = DDI_INTR_CLAIMED;
108229949e86Sstevel 		}
108329949e86Sstevel 	}
108429949e86Sstevel 
108529949e86Sstevel 	if (funcp1 != NULL) {
108629949e86Sstevel 		if ((funcp1)(funcp1_arg1, funcp1_arg2) == DDI_INTR_CLAIMED) {
108729949e86Sstevel 			result = DDI_INTR_CLAIMED;
108829949e86Sstevel 		}
108929949e86Sstevel 	}
109029949e86Sstevel 
109129949e86Sstevel 	if (result == DDI_INTR_UNCLAIMED) {
109229949e86Sstevel 		(*spurious_cntr)++;
109329949e86Sstevel 
109429949e86Sstevel 		if (*spurious_cntr < MAX_INTR_CNT) {
109529949e86Sstevel 			result = DDI_INTR_CLAIMED;
109629949e86Sstevel 		} else {
109729949e86Sstevel 			*spurious_cntr = (uchar_t)0;
109829949e86Sstevel 		}
109929949e86Sstevel 	} else {
110029949e86Sstevel 		*spurious_cntr = (uchar_t)0;
110129949e86Sstevel 	}
110229949e86Sstevel 
110329949e86Sstevel 	/* Idle the state machine. */
110429949e86Sstevel 	*(clear_reg) = ISM_IDLE;
110529949e86Sstevel 
110629949e86Sstevel 	/* flush the store buffers. */
110729949e86Sstevel 	tmp_reg = *(clear_reg);
110829949e86Sstevel #ifdef lint
110929949e86Sstevel 	tmp_reg = tmp_reg;
111029949e86Sstevel #endif
111129949e86Sstevel 
111229949e86Sstevel 	return (result);
111329949e86Sstevel }
111429949e86Sstevel 
111529949e86Sstevel 
111629949e86Sstevel /*
111729949e86Sstevel  * add_intrspec - Add an interrupt specification.
111829949e86Sstevel  */
111929949e86Sstevel static int
112029949e86Sstevel fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
112129949e86Sstevel     ddi_intr_handle_impl_t *hdlp)
112229949e86Sstevel {
112329949e86Sstevel 	int ino;
112429949e86Sstevel 	struct fhc_wrapper_arg *fhc_arg;
112529949e86Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)
112629949e86Sstevel 	    ddi_get_soft_state(fhcp, ddi_get_instance(dip));
112729949e86Sstevel 	volatile uint_t *mondo_vec_reg;
112829949e86Sstevel 	uint_t tmp_mondo_vec;
112929949e86Sstevel 	uint_t tmpreg; /* HW flush reg */
113029949e86Sstevel 	uint_t cpu_id;
113129949e86Sstevel 	int ret = DDI_SUCCESS;
113229949e86Sstevel 
113329949e86Sstevel 	/* Xlate the interrupt */
113429949e86Sstevel 	fhc_xlate_intrs(hdlp,
113529949e86Sstevel 	    (softsp->list->sc.board << BD_IVINTR_SHFT));
113629949e86Sstevel 
113729949e86Sstevel 	/* get the mondo number */
113829949e86Sstevel 	ino = FHC_INO(hdlp->ih_vector);
113929949e86Sstevel 	mondo_vec_reg = softsp->intr_regs[ino].mapping_reg;
114029949e86Sstevel 
114129949e86Sstevel 	ASSERT(ino < FHC_MAX_INO);
114229949e86Sstevel 
114329949e86Sstevel 	/* We don't use the two spare interrupts. */
114429949e86Sstevel 	if (ino >= FHC_MAX_INO) {
114529949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: Spare interrupt %d not usable",
114629949e86Sstevel 		    ddi_get_instance(dip), ino);
114729949e86Sstevel 		return (DDI_FAILURE);
114829949e86Sstevel 	}
114929949e86Sstevel 
115029949e86Sstevel 	/* TOD and Fan Fail interrupts are not usable */
115129949e86Sstevel 	if (ino == FHC_TOD_INO) {
115229949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: TOD interrupt not usable",
115329949e86Sstevel 		    ddi_get_instance(dip));
115429949e86Sstevel 		return (DDI_FAILURE);
115529949e86Sstevel 	}
115629949e86Sstevel 	if (ino == FHC_FANFAIL_INO) {
115729949e86Sstevel 		cmn_err(CE_WARN, "fhc%d: Fan fail interrupt not usable",
115829949e86Sstevel 		    ddi_get_instance(dip));
115929949e86Sstevel 		return (DDI_FAILURE);
116029949e86Sstevel 	}
116129949e86Sstevel 
116229949e86Sstevel 	/*
116329949e86Sstevel 	 * If the interrupt is for the zs chips, use the vector
116429949e86Sstevel 	 * polling lists. Otherwise use a straight handler.
116529949e86Sstevel 	 */
116629949e86Sstevel 	if (ino == FHC_UART_INO) {
116729949e86Sstevel 		int32_t zs_inst;
116829949e86Sstevel 		/* First lock the mutex for this poll_list */
116929949e86Sstevel 		mutex_enter(&softsp->poll_list_lock);
117029949e86Sstevel 
117129949e86Sstevel 		/*
117229949e86Sstevel 		 * Add this interrupt to the polling list.
117329949e86Sstevel 		 */
117429949e86Sstevel 
117529949e86Sstevel 		/* figure out where to add this item in the list */
117629949e86Sstevel 		for (zs_inst = 0; zs_inst < MAX_ZS_CNT; zs_inst++) {
117729949e86Sstevel 			if (softsp->poll_list[zs_inst].funcp == NULL) {
117829949e86Sstevel 				softsp->poll_list[zs_inst].arg1 =
117929949e86Sstevel 				    hdlp->ih_cb_arg1;
118029949e86Sstevel 				softsp->poll_list[zs_inst].arg2 =
118129949e86Sstevel 				    hdlp->ih_cb_arg2;
118229949e86Sstevel 				softsp->poll_list[zs_inst].funcp =
118329949e86Sstevel 				    (ddi_intr_handler_t *)
118429949e86Sstevel 				    hdlp->ih_cb_func;
118529949e86Sstevel 				softsp->poll_list[zs_inst].inum =
118629949e86Sstevel 				    hdlp->ih_inum;
118729949e86Sstevel 				softsp->poll_list[zs_inst].child = rdip;
118829949e86Sstevel 
118929949e86Sstevel 				break;
119029949e86Sstevel 			}
119129949e86Sstevel 		}
119229949e86Sstevel 
119329949e86Sstevel 		if (zs_inst >= MAX_ZS_CNT) {
119429949e86Sstevel 			cmn_err(CE_WARN,
119529949e86Sstevel 			    "fhc%d: poll list overflow",
119629949e86Sstevel 			    ddi_get_instance(dip));
119729949e86Sstevel 			mutex_exit(&softsp->poll_list_lock);
119829949e86Sstevel 			ret = DDI_FAILURE;
119929949e86Sstevel 			goto done;
120029949e86Sstevel 		}
120129949e86Sstevel 
120229949e86Sstevel 		/*
120329949e86Sstevel 		 * If polling list is empty, then install handler
120429949e86Sstevel 		 * and enable interrupts for this ino.
120529949e86Sstevel 		 */
120629949e86Sstevel 		if (zs_inst == 0) {
120729949e86Sstevel 			DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
120829949e86Sstevel 			    (ddi_intr_handler_t *)fhc_zs_intr_wrapper,
120929949e86Sstevel 			    (caddr_t)softsp, NULL);
121029949e86Sstevel 
121129949e86Sstevel 			ret = i_ddi_add_ivintr(hdlp);
121229949e86Sstevel 
121329949e86Sstevel 			DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
121429949e86Sstevel 			    softsp->poll_list[zs_inst].funcp,
121529949e86Sstevel 			    softsp->poll_list[zs_inst].arg1,
121629949e86Sstevel 			    softsp->poll_list[zs_inst].arg2);
121729949e86Sstevel 
121829949e86Sstevel 			if (ret != DDI_SUCCESS)
121929949e86Sstevel 				goto done;
122029949e86Sstevel 		}
122129949e86Sstevel 
122229949e86Sstevel 		/*
122329949e86Sstevel 		 * If both zs handlers are active, then this is the
122429949e86Sstevel 		 * second add_intrspec called, so do not enable
122529949e86Sstevel 		 * the IMR_VALID bit, it is already on.
122629949e86Sstevel 		 */
122729949e86Sstevel 		if (zs_inst > 0) {
122829949e86Sstevel 				/* now release the mutex and return */
122929949e86Sstevel 			mutex_exit(&softsp->poll_list_lock);
123029949e86Sstevel 
123129949e86Sstevel 			goto done;
123229949e86Sstevel 		} else {
123329949e86Sstevel 			/* just release the mutex */
123429949e86Sstevel 			mutex_exit(&softsp->poll_list_lock);
123529949e86Sstevel 		}
123629949e86Sstevel 	} else {	/* normal interrupt installation */
123729949e86Sstevel 		int32_t i;
123829949e86Sstevel 
123929949e86Sstevel 		/* Allocate a nexus interrupt data structure */
124029949e86Sstevel 		fhc_arg = kmem_alloc(sizeof (struct fhc_wrapper_arg), KM_SLEEP);
124129949e86Sstevel 		fhc_arg->child = rdip;
124229949e86Sstevel 		fhc_arg->mapping_reg = mondo_vec_reg;
124329949e86Sstevel 		fhc_arg->clear_reg = (softsp->intr_regs[ino].clear_reg);
124429949e86Sstevel 		fhc_arg->softsp = softsp;
124529949e86Sstevel 		fhc_arg->funcp =
124629949e86Sstevel 		    (ddi_intr_handler_t *)hdlp->ih_cb_func;
124729949e86Sstevel 		fhc_arg->arg1 = hdlp->ih_cb_arg1;
124829949e86Sstevel 		fhc_arg->arg2 = hdlp->ih_cb_arg2;
124929949e86Sstevel 		fhc_arg->inum = hdlp->ih_inum;
125029949e86Sstevel 
125129949e86Sstevel 		for (i = 0; i < FHC_MAX_INO; i++) {
125229949e86Sstevel 			if (softsp->intr_list[i] == 0) {
125329949e86Sstevel 				softsp->intr_list[i] = fhc_arg;
125429949e86Sstevel 				break;
125529949e86Sstevel 			}
125629949e86Sstevel 		}
125729949e86Sstevel 
125829949e86Sstevel 		/*
125929949e86Sstevel 		 * Save the fhc_arg in the ispec so we can use this info
126029949e86Sstevel 		 * later to uninstall this interrupt spec.
126129949e86Sstevel 		 */
126229949e86Sstevel 		DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
126329949e86Sstevel 		    (ddi_intr_handler_t *)fhc_intr_wrapper,
126429949e86Sstevel 		    (caddr_t)fhc_arg, NULL);
126529949e86Sstevel 
126629949e86Sstevel 		ret = i_ddi_add_ivintr(hdlp);
126729949e86Sstevel 
126829949e86Sstevel 		DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, fhc_arg->funcp,
126929949e86Sstevel 		    fhc_arg->arg1, fhc_arg->arg2);
127029949e86Sstevel 
127129949e86Sstevel 		if (ret != DDI_SUCCESS)
127229949e86Sstevel 			goto done;
127329949e86Sstevel 	}
127429949e86Sstevel 
127529949e86Sstevel 	/*
127629949e86Sstevel 	 * Clear out a stale 'pending' or 'transmit' state in
127729949e86Sstevel 	 * this device's ISM that might have been left from a
127829949e86Sstevel 	 * previous session.
127929949e86Sstevel 	 *
128029949e86Sstevel 	 * Since all FHC interrupts are level interrupts, any
128129949e86Sstevel 	 * real interrupting condition will immediately transition
128229949e86Sstevel 	 * the ISM back to pending.
128329949e86Sstevel 	 */
128429949e86Sstevel 	*(softsp->intr_regs[ino].clear_reg) = ISM_IDLE;
128529949e86Sstevel 
128629949e86Sstevel 	/*
128729949e86Sstevel 	 * Program the mondo vector accordingly.  This MUST be the
128829949e86Sstevel 	 * last thing we do.  Once we program the ino, the device
128929949e86Sstevel 	 * may begin to interrupt.
129029949e86Sstevel 	 */
129129949e86Sstevel 	cpu_id = intr_dist_cpuid();
129229949e86Sstevel 
129329949e86Sstevel 	tmp_mondo_vec = cpu_id << INR_PID_SHIFT;
129429949e86Sstevel 
129529949e86Sstevel 	/* don't do this for fan because fan has a special control */
129629949e86Sstevel 	if (ino == FHC_FANFAIL_INO)
129729949e86Sstevel 		panic("fhc%d: enabling fanfail interrupt",
129829949e86Sstevel 		    ddi_get_instance(dip));
129929949e86Sstevel 	else
130029949e86Sstevel 		tmp_mondo_vec |= IMR_VALID;
130129949e86Sstevel 
130229949e86Sstevel 	DPRINTF(FHC_INTERRUPT_DEBUG,
130307d06da5SSurya Prakki 	    ("Mondo 0x%x mapping reg: 0x%p", hdlp->ih_vector,
130407d06da5SSurya Prakki 	    (void *)mondo_vec_reg));
130529949e86Sstevel 
130629949e86Sstevel 	/* Store it in the hardware reg. */
130729949e86Sstevel 	*mondo_vec_reg = tmp_mondo_vec;
130829949e86Sstevel 
130929949e86Sstevel 	/* Read a FHC register to flush store buffers */
131029949e86Sstevel 	tmpreg = *(softsp->id);
131129949e86Sstevel #ifdef lint
131229949e86Sstevel 	tmpreg = tmpreg;
131329949e86Sstevel #endif
131429949e86Sstevel 
131529949e86Sstevel done:
131629949e86Sstevel 	return (ret);
131729949e86Sstevel }
131829949e86Sstevel 
131929949e86Sstevel /*
132029949e86Sstevel  * remove_intrspec - Remove an interrupt specification.
132129949e86Sstevel  */
132229949e86Sstevel static void
132329949e86Sstevel fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
132429949e86Sstevel 	ddi_intr_handle_impl_t *hdlp)
132529949e86Sstevel {
132629949e86Sstevel 	volatile uint_t *mondo_vec_reg;
132729949e86Sstevel 	volatile uint_t tmpreg;
132829949e86Sstevel 	int i;
132929949e86Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)
133029949e86Sstevel 	    ddi_get_soft_state(fhcp, ddi_get_instance(dip));
133129949e86Sstevel 	int ino;
133229949e86Sstevel 
133329949e86Sstevel 	/* Xlate the interrupt */
133429949e86Sstevel 	fhc_xlate_intrs(hdlp,
133529949e86Sstevel 	    (softsp->list->sc.board << BD_IVINTR_SHFT));
133629949e86Sstevel 
133729949e86Sstevel 	/* get the mondo number */
133829949e86Sstevel 	ino = FHC_INO(hdlp->ih_vector);
133929949e86Sstevel 
134029949e86Sstevel 	if (ino == FHC_UART_INO) {
134129949e86Sstevel 		int intr_found = 0;
134229949e86Sstevel 
134329949e86Sstevel 		/* Lock the poll_list first */
134429949e86Sstevel 		mutex_enter(&softsp->poll_list_lock);
134529949e86Sstevel 
134629949e86Sstevel 		/*
134729949e86Sstevel 		 * Find which entry in the poll list belongs to this
134829949e86Sstevel 		 * intrspec.
134929949e86Sstevel 		 */
135029949e86Sstevel 		for (i = 0; i < MAX_ZS_CNT; i++) {
135129949e86Sstevel 			if (softsp->poll_list[i].child == rdip &&
135229949e86Sstevel 			    softsp->poll_list[i].inum == hdlp->ih_inum) {
135329949e86Sstevel 				softsp->poll_list[i].funcp = NULL;
135429949e86Sstevel 				intr_found++;
135529949e86Sstevel 			}
135629949e86Sstevel 		}
135729949e86Sstevel 
135829949e86Sstevel 		/* If we did not find an entry, then we have a problem */
135929949e86Sstevel 		if (!intr_found) {
136029949e86Sstevel 			cmn_err(CE_WARN, "fhc%d: Intrspec not found in"
136129949e86Sstevel 			    " poll list", ddi_get_instance(dip));
136229949e86Sstevel 			mutex_exit(&softsp->poll_list_lock);
136329949e86Sstevel 			goto done;
136429949e86Sstevel 		}
136529949e86Sstevel 
136629949e86Sstevel 		/*
136729949e86Sstevel 		 * If we have removed all active entries for the poll
136829949e86Sstevel 		 * list, then we have to disable interupts at this point.
136929949e86Sstevel 		 */
137029949e86Sstevel 		if ((softsp->poll_list[0].funcp == NULL) &&
137129949e86Sstevel 		    (softsp->poll_list[1].funcp == NULL)) {
137229949e86Sstevel 			mondo_vec_reg =
137329949e86Sstevel 			    softsp->intr_regs[FHC_UART_INO].mapping_reg;
137429949e86Sstevel 			*mondo_vec_reg &= ~IMR_VALID;
137529949e86Sstevel 
137629949e86Sstevel 			/* flush the hardware buffers */
137729949e86Sstevel 			tmpreg = *(softsp->ctrl);
137829949e86Sstevel 
137929949e86Sstevel 			/* Eliminate the particular handler from the system. */
138029949e86Sstevel 			i_ddi_rem_ivintr(hdlp);
138129949e86Sstevel 		}
138229949e86Sstevel 
138329949e86Sstevel 		mutex_exit(&softsp->poll_list_lock);
138429949e86Sstevel 	} else {
138529949e86Sstevel 		int32_t i;
138629949e86Sstevel 
138729949e86Sstevel 
138829949e86Sstevel 		for (i = 0; i < FHC_MAX_INO; i++)
138929949e86Sstevel 			if (softsp->intr_list[i]->child == rdip &&
139029949e86Sstevel 			    softsp->intr_list[i]->inum == hdlp->ih_inum)
139129949e86Sstevel 				break;
139229949e86Sstevel 
139329949e86Sstevel 		if (i >= FHC_MAX_INO)
139429949e86Sstevel 			goto done;
139529949e86Sstevel 
139629949e86Sstevel 		mondo_vec_reg = softsp->intr_list[i]->mapping_reg;
139729949e86Sstevel 
139829949e86Sstevel 		/* Turn off the valid bit in the mapping register. */
139929949e86Sstevel 		/* XXX what about FHC_FANFAIL owned imr? */
140029949e86Sstevel 		*mondo_vec_reg &= ~IMR_VALID;
140129949e86Sstevel 
140229949e86Sstevel 		/* flush the hardware store buffers */
140329949e86Sstevel 		tmpreg = *(softsp->id);
140429949e86Sstevel #ifdef lint
140529949e86Sstevel 		tmpreg = tmpreg;
140629949e86Sstevel #endif
140729949e86Sstevel 
140829949e86Sstevel 		/* Eliminate the particular handler from the system. */
140929949e86Sstevel 		i_ddi_rem_ivintr(hdlp);
141029949e86Sstevel 
141129949e86Sstevel 		kmem_free(softsp->intr_list[i],
141229949e86Sstevel 		    sizeof (struct fhc_wrapper_arg));
141329949e86Sstevel 		softsp->intr_list[i] = 0;
141429949e86Sstevel 	}
141529949e86Sstevel 
141629949e86Sstevel done:
141729949e86Sstevel 	;
141829949e86Sstevel }
141929949e86Sstevel 
142029949e86Sstevel /* new intr_ops structure */
142129949e86Sstevel static int
142229949e86Sstevel fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
142329949e86Sstevel     ddi_intr_handle_impl_t *hdlp, void *result)
142429949e86Sstevel {
142529949e86Sstevel 	int	ret = DDI_SUCCESS;
142629949e86Sstevel 
142729949e86Sstevel 	switch (intr_op) {
142829949e86Sstevel 	case DDI_INTROP_GETCAP:
142929949e86Sstevel 		*(int *)result = DDI_INTR_FLAG_LEVEL;
143029949e86Sstevel 		break;
143129949e86Sstevel 	case DDI_INTROP_ALLOC:
143229949e86Sstevel 		*(int *)result = hdlp->ih_scratch1;
143329949e86Sstevel 		break;
143429949e86Sstevel 	case DDI_INTROP_FREE:
143529949e86Sstevel 		break;
143629949e86Sstevel 	case DDI_INTROP_GETPRI:
143729949e86Sstevel 		if (hdlp->ih_pri == 0) {
143829949e86Sstevel 			struct fhc_soft_state *softsp =
143929949e86Sstevel 			    (struct fhc_soft_state *)ddi_get_soft_state(fhcp,
144029949e86Sstevel 			    ddi_get_instance(dip));
144129949e86Sstevel 
144229949e86Sstevel 			/* Xlate the interrupt */
144329949e86Sstevel 			fhc_xlate_intrs(hdlp,
144429949e86Sstevel 			    (softsp->list->sc.board << BD_IVINTR_SHFT));
144529949e86Sstevel 		}
144629949e86Sstevel 
144729949e86Sstevel 		*(int *)result = hdlp->ih_pri;
144829949e86Sstevel 		break;
144929949e86Sstevel 	case DDI_INTROP_SETPRI:
145029949e86Sstevel 		break;
145129949e86Sstevel 	case DDI_INTROP_ADDISR:
145229949e86Sstevel 		ret = fhc_add_intr_impl(dip, rdip, hdlp);
145329949e86Sstevel 		break;
145429949e86Sstevel 	case DDI_INTROP_REMISR:
145529949e86Sstevel 		fhc_remove_intr_impl(dip, rdip, hdlp);
145629949e86Sstevel 		break;
145729949e86Sstevel 	case DDI_INTROP_ENABLE:
145829949e86Sstevel 	case DDI_INTROP_DISABLE:
145929949e86Sstevel 		break;
146029949e86Sstevel 	case DDI_INTROP_NINTRS:
146129949e86Sstevel 	case DDI_INTROP_NAVAIL:
1462a54f81fbSanish 		*(int *)result = i_ddi_get_intx_nintrs(rdip);
146329949e86Sstevel 		break;
146429949e86Sstevel 	case DDI_INTROP_SETCAP:
146529949e86Sstevel 	case DDI_INTROP_SETMASK:
146629949e86Sstevel 	case DDI_INTROP_CLRMASK:
146729949e86Sstevel 	case DDI_INTROP_GETPENDING:
146829949e86Sstevel 		ret = DDI_ENOTSUP;
146929949e86Sstevel 		break;
147029949e86Sstevel 	case DDI_INTROP_SUPPORTED_TYPES:
147129949e86Sstevel 		/* only support fixed interrupts */
1472a54f81fbSanish 		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
147329949e86Sstevel 		    DDI_INTR_TYPE_FIXED : 0;
147429949e86Sstevel 		break;
147529949e86Sstevel 	default:
147629949e86Sstevel 		ret = i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result);
147729949e86Sstevel 		break;
147829949e86Sstevel 	}
147929949e86Sstevel 
148029949e86Sstevel 	return (ret);
148129949e86Sstevel }
148229949e86Sstevel 
148329949e86Sstevel /*
148429949e86Sstevel  * FHC Control Ops routine
148529949e86Sstevel  *
148629949e86Sstevel  * Requests handled here:
148729949e86Sstevel  *	DDI_CTLOPS_INITCHILD	see impl_ddi_sunbus_initchild() for details
148829949e86Sstevel  *	DDI_CTLOPS_UNINITCHILD	see fhc_uninit_child() for details
148929949e86Sstevel  *	DDI_CTLOPS_REPORTDEV	TODO - need to implement this.
149029949e86Sstevel  */
149129949e86Sstevel static int
149229949e86Sstevel fhc_ctlops(dev_info_t *dip, dev_info_t *rdip,
149329949e86Sstevel 	ddi_ctl_enum_t op, void *arg, void *result)
149429949e86Sstevel {
149529949e86Sstevel 
149629949e86Sstevel 	switch (op) {
149729949e86Sstevel 	case DDI_CTLOPS_INITCHILD:
149829949e86Sstevel 		DPRINTF(FHC_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
149929949e86Sstevel 		return (impl_ddi_sunbus_initchild((dev_info_t *)arg));
150029949e86Sstevel 
150129949e86Sstevel 	case DDI_CTLOPS_UNINITCHILD:
150229949e86Sstevel 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
150329949e86Sstevel 		return (DDI_SUCCESS);
150429949e86Sstevel 
150529949e86Sstevel 	case DDI_CTLOPS_REPORTDEV:
150629949e86Sstevel 		/*
150729949e86Sstevel 		 * TODO - Figure out what makes sense to report here.
150829949e86Sstevel 		 */
150929949e86Sstevel 		return (DDI_SUCCESS);
151029949e86Sstevel 
151129949e86Sstevel 	case DDI_CTLOPS_POKE:
151229949e86Sstevel 	case DDI_CTLOPS_PEEK:
151329949e86Sstevel 		return (fhc_ctlops_peekpoke(op, (peekpoke_ctlops_t *)arg,
151429949e86Sstevel 		    result));
151529949e86Sstevel 
151629949e86Sstevel 	default:
151729949e86Sstevel 		return (ddi_ctlops(dip, rdip, op, arg, result));
151829949e86Sstevel 	}
151929949e86Sstevel }
152029949e86Sstevel 
152129949e86Sstevel 
152229949e86Sstevel /*
152329949e86Sstevel  * We're prepared to claim that the interrupt string is in
152429949e86Sstevel  * the form of a list of <FHCintr> specifications, or we're dealing
152529949e86Sstevel  * with on-board devices and we have an interrupt_number property which
152629949e86Sstevel  * gives us our mondo number.
152729949e86Sstevel  * Translate the mondos into fhcintrspecs.
152829949e86Sstevel  */
152929949e86Sstevel /* ARGSUSED */
153029949e86Sstevel static void
153129949e86Sstevel fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign)
153229949e86Sstevel 
153329949e86Sstevel {
153429949e86Sstevel 	uint32_t mondo;
153529949e86Sstevel 
153629949e86Sstevel 	mondo = hdlp->ih_vector;
153729949e86Sstevel 
153829949e86Sstevel 	hdlp->ih_vector = (mondo | ign);
153929949e86Sstevel 	if (hdlp->ih_pri == 0)
154029949e86Sstevel 		hdlp->ih_pri = fhc_int_priorities[FHC_INO(mondo)];
154129949e86Sstevel }
154229949e86Sstevel 
154329949e86Sstevel static int
154429949e86Sstevel fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args,
154529949e86Sstevel     void *result)
154629949e86Sstevel {
154729949e86Sstevel 	int err = DDI_SUCCESS;
154829949e86Sstevel 	on_trap_data_t otd;
154929949e86Sstevel 
155029949e86Sstevel 	/* No safe access except for peek/poke is supported. */
155129949e86Sstevel 	if (in_args->handle != NULL)
155229949e86Sstevel 		return (DDI_FAILURE);
155329949e86Sstevel 
155429949e86Sstevel 	/* Set up protected environment. */
155529949e86Sstevel 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
155629949e86Sstevel 		uintptr_t tramp = otd.ot_trampoline;
155729949e86Sstevel 
155829949e86Sstevel 		if (cmd == DDI_CTLOPS_POKE) {
155929949e86Sstevel 			otd.ot_trampoline = (uintptr_t)&poke_fault;
156029949e86Sstevel 			err = do_poke(in_args->size, (void *)in_args->dev_addr,
156129949e86Sstevel 			    (void *)in_args->host_addr);
156229949e86Sstevel 		} else {
156329949e86Sstevel 			otd.ot_trampoline = (uintptr_t)&peek_fault;
156429949e86Sstevel 			err = do_peek(in_args->size, (void *)in_args->dev_addr,
156529949e86Sstevel 			    (void *)in_args->host_addr);
156629949e86Sstevel 			result = (void *)in_args->host_addr;
156729949e86Sstevel 		}
156829949e86Sstevel 		otd.ot_trampoline = tramp;
156929949e86Sstevel 	} else
157029949e86Sstevel 		err = DDI_FAILURE;
157129949e86Sstevel 
157229949e86Sstevel 	/* Take down protected environment. */
157329949e86Sstevel 	no_trap();
157429949e86Sstevel 
157529949e86Sstevel 	return (err);
157629949e86Sstevel }
157729949e86Sstevel 
157829949e86Sstevel /*
157929949e86Sstevel  * This function initializes the temperature arrays for use. All
158029949e86Sstevel  * temperatures are set in to invalid value to start.
158129949e86Sstevel  */
158229949e86Sstevel void
158329949e86Sstevel init_temp_arrays(struct temp_stats *envstat)
158429949e86Sstevel {
158529949e86Sstevel 	int i;
158629949e86Sstevel 
158729949e86Sstevel 	envstat->index = 0;
158829949e86Sstevel 
158929949e86Sstevel 	for (i = 0; i < L1_SZ; i++) {
159029949e86Sstevel 		envstat->l1[i] = NA_TEMP;
159129949e86Sstevel 	}
159229949e86Sstevel 
159329949e86Sstevel 	for (i = 0; i < L2_SZ; i++) {
159429949e86Sstevel 		envstat->l2[i] = NA_TEMP;
159529949e86Sstevel 	}
159629949e86Sstevel 
159729949e86Sstevel 	for (i = 0; i < L3_SZ; i++) {
159829949e86Sstevel 		envstat->l3[i] = NA_TEMP;
159929949e86Sstevel 	}
160029949e86Sstevel 
160129949e86Sstevel 	for (i = 0; i < L4_SZ; i++) {
160229949e86Sstevel 		envstat->l4[i] = NA_TEMP;
160329949e86Sstevel 	}
160429949e86Sstevel 
160529949e86Sstevel 	for (i = 0; i < L5_SZ; i++) {
160629949e86Sstevel 		envstat->l5[i] = NA_TEMP;
160729949e86Sstevel 	}
160829949e86Sstevel 
160929949e86Sstevel 	envstat->max = NA_TEMP;
161029949e86Sstevel 	envstat->min = NA_TEMP;
161129949e86Sstevel 	envstat->trend = TREND_UNKNOWN;
161229949e86Sstevel 	envstat->version = TEMP_KSTAT_VERSION;
161329949e86Sstevel 	envstat->override = NA_TEMP;
161429949e86Sstevel }
161529949e86Sstevel 
161629949e86Sstevel /* Inhibit warning messages below this temperature, eg for CPU poweron. */
161729949e86Sstevel static uint_t fhc_cpu_warning_temp_threshold = FHC_CPU_WARNING_TEMP_THRESHOLD;
161829949e86Sstevel 
161929949e86Sstevel /*
162029949e86Sstevel  * This function manages the temperature history in the temperature
162129949e86Sstevel  * statistics buffer passed in. It calls the temperature calibration
162229949e86Sstevel  * routines and maintains the time averaged temperature data.
162329949e86Sstevel  */
162429949e86Sstevel void
162529949e86Sstevel update_temp(dev_info_t *pdip, struct temp_stats *envstat, uchar_t value)
162629949e86Sstevel {
162729949e86Sstevel 	uint_t index;		    /* The absolute temperature counter */
162829949e86Sstevel 	uint_t tmp_index;	    /* temp index into upper level array */
162929949e86Sstevel 	int count;		    /* Count of non-zero values in array */
163029949e86Sstevel 	int total;		    /* sum total of non-zero values in array */
163129949e86Sstevel 	short real_temp;	    /* calibrated temperature */
163229949e86Sstevel 	int i;
163329949e86Sstevel 	struct fhc_soft_state *softsp;
163429949e86Sstevel 	char buffer[256];	    /* buffer for warning of overtemp */
163529949e86Sstevel 	enum temp_state temp_state; /* Temperature state */
163629949e86Sstevel 
163729949e86Sstevel 	/*
163829949e86Sstevel 	 * NOTE: This global counter is not protected since we're called
163929949e86Sstevel 	 * serially for each board.
164029949e86Sstevel 	 */
164129949e86Sstevel 	static int shutdown_msg = 0; /* Flag if shutdown warning issued */
164229949e86Sstevel 
164329949e86Sstevel 	/* determine soft state pointer of parent */
164429949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(pdip));
164529949e86Sstevel 
164629949e86Sstevel 	envstat->index++;
164729949e86Sstevel 	index = envstat->index;
164829949e86Sstevel 
164929949e86Sstevel 	/*
165029949e86Sstevel 	 * You need to update the level 5 intervals first, since
165129949e86Sstevel 	 * they are based on the data from the level 4 intervals,
165229949e86Sstevel 	 * and so on, down to the level 1 intervals.
165329949e86Sstevel 	 */
165429949e86Sstevel 
165529949e86Sstevel 	/* update the level 5 intervals if it is time */
165629949e86Sstevel 	if (((tmp_index = L5_INDEX(index)) > 0) && (L5_REM(index) == 0)) {
165729949e86Sstevel 		/* Generate the index within the level 5 array */
165829949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
165929949e86Sstevel 		tmp_index = tmp_index % L5_SZ;
166029949e86Sstevel 
166129949e86Sstevel 		/* take an average of the level 4 array */
166229949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L4_SZ; i++) {
166329949e86Sstevel 			/* Do not include zero values in average */
166429949e86Sstevel 			if (envstat->l4[i] != NA_TEMP) {
166529949e86Sstevel 				total += (int)envstat->l4[i];
166629949e86Sstevel 				count++;
166729949e86Sstevel 			}
166829949e86Sstevel 		}
166929949e86Sstevel 
167029949e86Sstevel 		/*
167129949e86Sstevel 		 * If there were any level 4 data points to average,
167229949e86Sstevel 		 * do so.
167329949e86Sstevel 		 */
167429949e86Sstevel 		if (count != 0) {
167529949e86Sstevel 			envstat->l5[tmp_index] = total/count;
167629949e86Sstevel 		} else {
167729949e86Sstevel 			envstat->l5[tmp_index] = NA_TEMP;
167829949e86Sstevel 		}
167929949e86Sstevel 	}
168029949e86Sstevel 
168129949e86Sstevel 	/* update the level 4 intervals if it is time */
168229949e86Sstevel 	if (((tmp_index = L4_INDEX(index)) > 0) && (L4_REM(index) == 0)) {
168329949e86Sstevel 		/* Generate the index within the level 4 array */
168429949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
168529949e86Sstevel 		tmp_index = tmp_index % L4_SZ;
168629949e86Sstevel 
168729949e86Sstevel 		/* take an average of the level 3 array */
168829949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L3_SZ; i++) {
168929949e86Sstevel 			/* Do not include zero values in average */
169029949e86Sstevel 			if (envstat->l3[i] != NA_TEMP) {
169129949e86Sstevel 				total += (int)envstat->l3[i];
169229949e86Sstevel 				count++;
169329949e86Sstevel 			}
169429949e86Sstevel 		}
169529949e86Sstevel 
169629949e86Sstevel 		/*
169729949e86Sstevel 		 * If there were any level 3 data points to average,
169829949e86Sstevel 		 * do so.
169929949e86Sstevel 		 */
170029949e86Sstevel 		if (count != 0) {
170129949e86Sstevel 			envstat->l4[tmp_index] = total/count;
170229949e86Sstevel 		} else {
170329949e86Sstevel 			envstat->l4[tmp_index] = NA_TEMP;
170429949e86Sstevel 		}
170529949e86Sstevel 	}
170629949e86Sstevel 
170729949e86Sstevel 	/* update the level 3 intervals if it is time */
170829949e86Sstevel 	if (((tmp_index = L3_INDEX(index)) > 0) && (L3_REM(index) == 0)) {
170929949e86Sstevel 		/* Generate the index within the level 3 array */
171029949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
171129949e86Sstevel 		tmp_index = tmp_index % L3_SZ;
171229949e86Sstevel 
171329949e86Sstevel 		/* take an average of the level 2 array */
171429949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L2_SZ; i++) {
171529949e86Sstevel 			/* Do not include zero values in average */
171629949e86Sstevel 			if (envstat->l2[i] != NA_TEMP) {
171729949e86Sstevel 				total += (int)envstat->l2[i];
171829949e86Sstevel 				count++;
171929949e86Sstevel 			}
172029949e86Sstevel 		}
172129949e86Sstevel 
172229949e86Sstevel 		/*
172329949e86Sstevel 		 * If there were any level 2 data points to average,
172429949e86Sstevel 		 * do so.
172529949e86Sstevel 		 */
172629949e86Sstevel 		if (count != 0) {
172729949e86Sstevel 			envstat->l3[tmp_index] = total/count;
172829949e86Sstevel 		} else {
172929949e86Sstevel 			envstat->l3[tmp_index] = NA_TEMP;
173029949e86Sstevel 		}
173129949e86Sstevel 	}
173229949e86Sstevel 
173329949e86Sstevel 	/* update the level 2 intervals if it is time */
173429949e86Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0)) {
173529949e86Sstevel 		/* Generate the index within the level 2 array */
173629949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
173729949e86Sstevel 		tmp_index = tmp_index % L2_SZ;
173829949e86Sstevel 
173929949e86Sstevel 		/* take an average of the level 1 array */
174029949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L1_SZ; i++) {
174129949e86Sstevel 			/* Do not include zero values in average */
174229949e86Sstevel 			if (envstat->l1[i] != NA_TEMP) {
174329949e86Sstevel 				total += (int)envstat->l1[i];
174429949e86Sstevel 				count++;
174529949e86Sstevel 			}
174629949e86Sstevel 		}
174729949e86Sstevel 
174829949e86Sstevel 		/*
174929949e86Sstevel 		 * If there were any level 1 data points to average,
175029949e86Sstevel 		 * do so.
175129949e86Sstevel 		 */
175229949e86Sstevel 		if (count != 0) {
175329949e86Sstevel 			envstat->l2[tmp_index] = total/count;
175429949e86Sstevel 		} else {
175529949e86Sstevel 			envstat->l2[tmp_index] = NA_TEMP;
175629949e86Sstevel 		}
175729949e86Sstevel 	}
175829949e86Sstevel 
175929949e86Sstevel 	/* determine the current temperature in degrees Celcius */
176029949e86Sstevel 	if (envstat->override != NA_TEMP) {
176129949e86Sstevel 		/* use override temperature for this board */
176229949e86Sstevel 		real_temp = envstat->override;
176329949e86Sstevel 	} else {
176429949e86Sstevel 		/* Run the calibration function using this board type */
176529949e86Sstevel 		real_temp = calibrate_temp(softsp->list->sc.type, value,
176629949e86Sstevel 		    softsp->list->sc.ac_compid);
176729949e86Sstevel 	}
176829949e86Sstevel 
176929949e86Sstevel 	envstat->l1[index % L1_SZ] = real_temp;
177029949e86Sstevel 
177129949e86Sstevel 	/* check if the temperature state for this device needs to change */
177229949e86Sstevel 	temp_state = get_temp_state(softsp->list->sc.type, real_temp,
177329949e86Sstevel 	    softsp->list->sc.board);
177429949e86Sstevel 
177529949e86Sstevel 	/* has the state changed? Then get the board string ready */
177629949e86Sstevel 	if (temp_state != envstat->state) {
177729949e86Sstevel 		int board = softsp->list->sc.board;
177829949e86Sstevel 		enum board_type type = softsp->list->sc.type;
177929949e86Sstevel 
178029949e86Sstevel 		build_bd_display_str(buffer, type, board);
178129949e86Sstevel 
178229949e86Sstevel 		if (temp_state > envstat->state) {
178329949e86Sstevel 			if (envstat->state == TEMP_OK) {
178429949e86Sstevel 				if (type == CLOCK_BOARD) {
178529949e86Sstevel 					reg_fault(0, FT_OVERTEMP, FT_SYSTEM);
178629949e86Sstevel 				} else {
178729949e86Sstevel 					reg_fault(board, FT_OVERTEMP,
178829949e86Sstevel 					    FT_BOARD);
178929949e86Sstevel 				}
179029949e86Sstevel 			}
179129949e86Sstevel 
179229949e86Sstevel 			/* heating up, change state now */
179329949e86Sstevel 			envstat->temp_cnt = 0;
179429949e86Sstevel 			envstat->state = temp_state;
179529949e86Sstevel 
179629949e86Sstevel 			if (temp_state == TEMP_WARN) {
179729949e86Sstevel 				/* now warn the user of the problem */
179829949e86Sstevel 				cmn_err(CE_WARN,
179929949e86Sstevel 				    "%s is warm (temperature: %dC). "
180029949e86Sstevel 				    "Please check system cooling", buffer,
180129949e86Sstevel 				    real_temp);
180229949e86Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_OVERTEMP);
180329949e86Sstevel 				if (temperature_chamber == -1)
180429949e86Sstevel 					temperature_chamber =
180529949e86Sstevel 					    check_for_chamber();
180629949e86Sstevel 			} else if (temp_state == TEMP_DANGER) {
180729949e86Sstevel 				cmn_err(CE_WARN,
180829949e86Sstevel 				    "%s is very hot (temperature: %dC)",
180929949e86Sstevel 				    buffer, real_temp);
181029949e86Sstevel 
181129949e86Sstevel 				envstat->shutdown_cnt = 1;
181229949e86Sstevel 				if (temperature_chamber == -1)
181329949e86Sstevel 					temperature_chamber =
181429949e86Sstevel 					    check_for_chamber();
181529949e86Sstevel 				if ((temperature_chamber == 0) &&
181629949e86Sstevel 				    enable_overtemp_powerdown) {
181729949e86Sstevel 					/*
181829949e86Sstevel 					 * NOTE: The "%d seconds" is not
181929949e86Sstevel 					 * necessarily accurate in the case
182029949e86Sstevel 					 * where we have multiple boards
182129949e86Sstevel 					 * overheating and subsequently cooling
182229949e86Sstevel 					 * down.
182329949e86Sstevel 					 */
182429949e86Sstevel 					if (shutdown_msg == 0) {
182529949e86Sstevel 						cmn_err(CE_WARN, "System "
182629949e86Sstevel 						    "shutdown scheduled "
182729949e86Sstevel 						    "in %d seconds due to "
182829949e86Sstevel 						    "over-temperature "
182929949e86Sstevel 						    "condition on %s",
183029949e86Sstevel 						    SHUTDOWN_TIMEOUT_SEC,
183129949e86Sstevel 						    buffer);
183229949e86Sstevel 					}
183329949e86Sstevel 					shutdown_msg++;
183429949e86Sstevel 				}
183529949e86Sstevel 			}
183629949e86Sstevel 
183729949e86Sstevel 			/*
183829949e86Sstevel 			 * If this is a cpu board, power them off.
183929949e86Sstevel 			 */
184029949e86Sstevel 			if (temperature_chamber == 0) {
184129949e86Sstevel 				mutex_enter(&cpu_lock);
184229949e86Sstevel 				(void) fhc_board_poweroffcpus(board, NULL,
184329949e86Sstevel 				    CPU_FORCED);
184429949e86Sstevel 				mutex_exit(&cpu_lock);
184529949e86Sstevel 			}
184629949e86Sstevel 		} else if (temp_state < envstat->state) {
184729949e86Sstevel 			/*
184829949e86Sstevel 			 * Avert the sigpower that would
184929949e86Sstevel 			 * otherwise be sent to init.
185029949e86Sstevel 			 */
185129949e86Sstevel 			envstat->shutdown_cnt = 0;
185229949e86Sstevel 
185329949e86Sstevel 			/* cooling down, use state counter */
185429949e86Sstevel 			if (envstat->temp_cnt == 0) {
185529949e86Sstevel 				envstat->temp_cnt = TEMP_STATE_COUNT;
185629949e86Sstevel 			} else if (--envstat->temp_cnt == 0) {
185729949e86Sstevel 				if (temp_state == TEMP_WARN) {
185829949e86Sstevel 					cmn_err(CE_NOTE,
185929949e86Sstevel 					    "%s is cooling "
186029949e86Sstevel 					    "(temperature: %dC)", buffer,
186129949e86Sstevel 					    real_temp);
186229949e86Sstevel 
186329949e86Sstevel 				} else if (temp_state == TEMP_OK) {
186429949e86Sstevel 					cmn_err(CE_NOTE,
186529949e86Sstevel 					    "%s has cooled down "
186629949e86Sstevel 					    "(temperature: %dC), system OK",
186729949e86Sstevel 					    buffer, real_temp);
186829949e86Sstevel 
186929949e86Sstevel 					if (type == CLOCK_BOARD) {
187029949e86Sstevel 						clear_fault(0, FT_OVERTEMP,
187129949e86Sstevel 						    FT_SYSTEM);
187229949e86Sstevel 					} else {
187329949e86Sstevel 						clear_fault(board, FT_OVERTEMP,
187429949e86Sstevel 						    FT_BOARD);
187529949e86Sstevel 					}
187629949e86Sstevel 				}
187729949e86Sstevel 
187829949e86Sstevel 				/*
187929949e86Sstevel 				 * If we just came out of TEMP_DANGER, and
188029949e86Sstevel 				 * a warning was issued about shutting down,
188129949e86Sstevel 				 * let the user know it's been cancelled
188229949e86Sstevel 				 */
188329949e86Sstevel 				if (envstat->state == TEMP_DANGER &&
188429949e86Sstevel 				    (temperature_chamber == 0) &&
188529949e86Sstevel 				    enable_overtemp_powerdown &&
188629949e86Sstevel 				    (powerdown_started == 0) &&
188729949e86Sstevel 				    (--shutdown_msg == 0)) {
188829949e86Sstevel 					cmn_err(CE_NOTE, "System "
188929949e86Sstevel 					    "shutdown due to over-"
189029949e86Sstevel 					    "temperature "
189129949e86Sstevel 					    "condition cancelled");
189229949e86Sstevel 				}
189329949e86Sstevel 				envstat->state = temp_state;
189429949e86Sstevel 
189529949e86Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_TEMP_OK);
189629949e86Sstevel 			}
189729949e86Sstevel 		}
189829949e86Sstevel 	} else {
189929949e86Sstevel 		envstat->temp_cnt = 0;
190029949e86Sstevel 
190129949e86Sstevel 		if (temp_state == TEMP_DANGER) {
190229949e86Sstevel 			if (temperature_chamber == -1) {
190329949e86Sstevel 				temperature_chamber = check_for_chamber();
190429949e86Sstevel 			}
190529949e86Sstevel 
190629949e86Sstevel 			if ((envstat->shutdown_cnt++ >= SHUTDOWN_COUNT) &&
190729949e86Sstevel 			    (temperature_chamber == 0) &&
190829949e86Sstevel 			    enable_overtemp_powerdown &&
190929949e86Sstevel 			    (powerdown_started == 0)) {
191029949e86Sstevel 				powerdown_started = 1;
191129949e86Sstevel 
191229949e86Sstevel 				/* the system is still too hot */
191329949e86Sstevel 				build_bd_display_str(buffer,
191429949e86Sstevel 				    softsp->list->sc.type,
191529949e86Sstevel 				    softsp->list->sc.board);
191629949e86Sstevel 
191729949e86Sstevel 				cmn_err(CE_WARN, "%s still too hot "
191829949e86Sstevel 				    "(temperature: %dC)."
191929949e86Sstevel 				    " Overtemp shutdown started", buffer,
192029949e86Sstevel 				    real_temp);
192129949e86Sstevel 
192229949e86Sstevel 				fhc_reboot();
192329949e86Sstevel 			}
192429949e86Sstevel 		}
192529949e86Sstevel 	}
192629949e86Sstevel 
192729949e86Sstevel 	/* update the maximum and minimum temperatures if necessary */
192829949e86Sstevel 	if ((envstat->max == NA_TEMP) || (real_temp > envstat->max)) {
192929949e86Sstevel 		envstat->max = real_temp;
193029949e86Sstevel 	}
193129949e86Sstevel 
193229949e86Sstevel 	if ((envstat->min == NA_TEMP) || (real_temp < envstat->min)) {
193329949e86Sstevel 		envstat->min = real_temp;
193429949e86Sstevel 	}
193529949e86Sstevel 
193629949e86Sstevel 	/*
193729949e86Sstevel 	 * Update the temperature trend.  Currently, the temperature
193829949e86Sstevel 	 * trend algorithm is based on the level 2 stats.  So, we
193929949e86Sstevel 	 * only need to run every time the level 2 stats get updated.
194029949e86Sstevel 	 */
194129949e86Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0))  {
194229949e86Sstevel 		enum board_type type = softsp->list->sc.type;
194329949e86Sstevel 
194429949e86Sstevel 		envstat->trend = temp_trend(envstat);
194529949e86Sstevel 
194629949e86Sstevel 		/* Issue a warning if the temperature is rising rapidly. */
194729949e86Sstevel 		/* For CPU boards, don't warn if CPUs just powered on. */
194829949e86Sstevel 		if (envstat->trend == TREND_RAPID_RISE &&
194929949e86Sstevel 		    (type != CPU_BOARD || real_temp >
195029949e86Sstevel 		    fhc_cpu_warning_temp_threshold))  {
195129949e86Sstevel 			int board = softsp->list->sc.board;
195229949e86Sstevel 
195329949e86Sstevel 			build_bd_display_str(buffer, type, board);
195429949e86Sstevel 			cmn_err(CE_WARN, "%s temperature is rising rapidly!  "
195529949e86Sstevel 			    "Current temperature is %dC", buffer,
195629949e86Sstevel 			    real_temp);
195729949e86Sstevel 		}
195829949e86Sstevel 	}
195929949e86Sstevel }
196029949e86Sstevel 
196129949e86Sstevel #define	PREV_L2_INDEX(x)    ((x) ? ((x) - 1) : (L2_SZ - 1))
196229949e86Sstevel 
196329949e86Sstevel /*
196429949e86Sstevel  * This routine determines if the temp of the device passed in is heating
196529949e86Sstevel  * up, cooling down, or staying stable.
196629949e86Sstevel  */
196729949e86Sstevel enum temp_trend
196829949e86Sstevel temp_trend(struct temp_stats *tempstat)
196929949e86Sstevel {
197029949e86Sstevel 	int		ii;
197129949e86Sstevel 	uint_t		curr_index;
197229949e86Sstevel 	int		curr_temp;
197329949e86Sstevel 	uint_t		prev_index;
197429949e86Sstevel 	int		prev_temp;
197529949e86Sstevel 	int		trail_temp;
197629949e86Sstevel 	int		delta;
197729949e86Sstevel 	int		read_cnt;
197829949e86Sstevel 	enum temp_trend	result = TREND_STABLE;
197929949e86Sstevel 
198029949e86Sstevel 	if (tempstat == NULL)
198129949e86Sstevel 		return (TREND_UNKNOWN);
198229949e86Sstevel 
198329949e86Sstevel 	curr_index = (L2_INDEX(tempstat->index) - 1) % L2_SZ;
198429949e86Sstevel 	curr_temp = tempstat->l2[curr_index];
198529949e86Sstevel 
198629949e86Sstevel 	/* Count how many temperature readings are available */
198729949e86Sstevel 	prev_index = curr_index;
198829949e86Sstevel 	for (read_cnt = 0; read_cnt < L2_SZ - 1; read_cnt++) {
198929949e86Sstevel 		if (tempstat->l2[prev_index] == NA_TEMP)
199029949e86Sstevel 			break;
199129949e86Sstevel 		prev_index = PREV_L2_INDEX(prev_index);
199229949e86Sstevel 	}
199329949e86Sstevel 
199429949e86Sstevel 	switch (read_cnt) {
199529949e86Sstevel 	case 0:
199629949e86Sstevel 	case 1:
199729949e86Sstevel 		result = TREND_UNKNOWN;
199829949e86Sstevel 		break;
199929949e86Sstevel 
200029949e86Sstevel 	default:
200129949e86Sstevel 		delta = curr_temp - tempstat->l2[PREV_L2_INDEX(curr_index)];
200229949e86Sstevel 		prev_index = curr_index;
200329949e86Sstevel 		trail_temp = prev_temp = curr_temp;
200429949e86Sstevel 		if (delta >= RAPID_RISE_THRESH) {	    /* rapid rise? */
200529949e86Sstevel 			result = TREND_RAPID_RISE;
200629949e86Sstevel 		} else if (delta > 0) {			    /* rise? */
200729949e86Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
200829949e86Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
200929949e86Sstevel 				prev_temp = tempstat->l2[prev_index];
201029949e86Sstevel 				if (prev_temp > trail_temp) {
201129949e86Sstevel 					break;
201229949e86Sstevel 				}
201329949e86Sstevel 				trail_temp = prev_temp;
201429949e86Sstevel 				if (prev_temp <= curr_temp - NOISE_THRESH) {
201529949e86Sstevel 					result = TREND_RISE;
201629949e86Sstevel 					break;
201729949e86Sstevel 				}
201829949e86Sstevel 			}
201929949e86Sstevel 		} else if (delta <= -RAPID_FALL_THRESH) {   /* rapid fall? */
202029949e86Sstevel 			result = TREND_RAPID_FALL;
202129949e86Sstevel 		} else if (delta < 0) {			    /* fall? */
202229949e86Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
202329949e86Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
202429949e86Sstevel 				prev_temp = tempstat->l2[prev_index];
202529949e86Sstevel 				if (prev_temp < trail_temp) {
202629949e86Sstevel 					break;
202729949e86Sstevel 				}
202829949e86Sstevel 				trail_temp = prev_temp;
202929949e86Sstevel 				if (prev_temp >= curr_temp + NOISE_THRESH) {
203029949e86Sstevel 					result = TREND_FALL;
203129949e86Sstevel 					break;
203229949e86Sstevel 				}
203329949e86Sstevel 			}
203429949e86Sstevel 		}
203529949e86Sstevel 	}
203629949e86Sstevel 	return (result);
203729949e86Sstevel }
203829949e86Sstevel 
203929949e86Sstevel /*
204029949e86Sstevel  * Reboot the system if we can, otherwise attempt a power down
204129949e86Sstevel  */
204229949e86Sstevel void
204329949e86Sstevel fhc_reboot(void)
204429949e86Sstevel {
204529949e86Sstevel 	proc_t *initpp;
204629949e86Sstevel 
204729949e86Sstevel 	/* send a SIGPWR to init process */
204829949e86Sstevel 	mutex_enter(&pidlock);
204929949e86Sstevel 	initpp = prfind(P_INITPID);
205029949e86Sstevel 	mutex_exit(&pidlock);
205129949e86Sstevel 
205229949e86Sstevel 	/*
205329949e86Sstevel 	 * If we're still booting and init(1) isn't
205429949e86Sstevel 	 * set up yet, simply halt.
205529949e86Sstevel 	 */
205629949e86Sstevel 	if (initpp != NULL) {
205729949e86Sstevel 		psignal(initpp, SIGFPE);	/* init 6 */
205829949e86Sstevel 	} else {
205929949e86Sstevel 		power_down("Environmental Shutdown");
206029949e86Sstevel 		halt("Power off the System");
206129949e86Sstevel 	}
206229949e86Sstevel }
206329949e86Sstevel 
206429949e86Sstevel int
206529949e86Sstevel overtemp_kstat_update(kstat_t *ksp, int rw)
206629949e86Sstevel {
206729949e86Sstevel 	struct temp_stats *tempstat;
206829949e86Sstevel 	char *kstatp;
206929949e86Sstevel 	int i;
207029949e86Sstevel 
207129949e86Sstevel 	kstatp = (char *)ksp->ks_data;
207229949e86Sstevel 	tempstat = (struct temp_stats *)ksp->ks_private;
207329949e86Sstevel 
207429949e86Sstevel 	/*
207529949e86Sstevel 	 * Kstat reads are used to retrieve the current system temperature
207629949e86Sstevel 	 * history. Kstat writes are used to reset the max and min
207729949e86Sstevel 	 * temperatures.
207829949e86Sstevel 	 */
207929949e86Sstevel 	if (rw == KSTAT_WRITE) {
208029949e86Sstevel 		short max;	/* temporary copy of max temperature */
208129949e86Sstevel 		short min;	/* temporary copy of min temperature */
208229949e86Sstevel 
208329949e86Sstevel 		/*
208429949e86Sstevel 		 * search for and reset the max and min to the current
208529949e86Sstevel 		 * array contents. Old max and min values will get
208629949e86Sstevel 		 * averaged out as they move into the higher level arrays.
208729949e86Sstevel 		 */
208829949e86Sstevel 		max = tempstat->l1[0];
208929949e86Sstevel 		min = tempstat->l1[0];
209029949e86Sstevel 
209129949e86Sstevel 		/* Pull the max and min from Level 1 array */
209229949e86Sstevel 		for (i = 0; i < L1_SZ; i++) {
209329949e86Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
209429949e86Sstevel 			    (tempstat->l1[i] > max)) {
209529949e86Sstevel 				max = tempstat->l1[i];
209629949e86Sstevel 			}
209729949e86Sstevel 
209829949e86Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
209929949e86Sstevel 			    (tempstat->l1[i] < min)) {
210029949e86Sstevel 				min = tempstat->l1[i];
210129949e86Sstevel 			}
210229949e86Sstevel 		}
210329949e86Sstevel 
210429949e86Sstevel 		/* Pull the max and min from Level 2 array */
210529949e86Sstevel 		for (i = 0; i < L2_SZ; i++) {
210629949e86Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
210729949e86Sstevel 			    (tempstat->l2[i] > max)) {
210829949e86Sstevel 				max = tempstat->l2[i];
210929949e86Sstevel 			}
211029949e86Sstevel 
211129949e86Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
211229949e86Sstevel 			    (tempstat->l2[i] < min)) {
211329949e86Sstevel 				min = tempstat->l2[i];
211429949e86Sstevel 			}
211529949e86Sstevel 		}
211629949e86Sstevel 
211729949e86Sstevel 		/* Pull the max and min from Level 3 array */
211829949e86Sstevel 		for (i = 0; i < L3_SZ; i++) {
211929949e86Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
212029949e86Sstevel 			    (tempstat->l3[i] > max)) {
212129949e86Sstevel 				max = tempstat->l3[i];
212229949e86Sstevel 			}
212329949e86Sstevel 
212429949e86Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
212529949e86Sstevel 			    (tempstat->l3[i] < min)) {
212629949e86Sstevel 				min = tempstat->l3[i];
212729949e86Sstevel 			}
212829949e86Sstevel 		}
212929949e86Sstevel 
213029949e86Sstevel 		/* Pull the max and min from Level 4 array */
213129949e86Sstevel 		for (i = 0; i < L4_SZ; i++) {
213229949e86Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
213329949e86Sstevel 			    (tempstat->l4[i] > max)) {
213429949e86Sstevel 				max = tempstat->l4[i];
213529949e86Sstevel 			}
213629949e86Sstevel 
213729949e86Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
213829949e86Sstevel 			    (tempstat->l4[i] < min)) {
213929949e86Sstevel 				min = tempstat->l4[i];
214029949e86Sstevel 			}
214129949e86Sstevel 		}
214229949e86Sstevel 
214329949e86Sstevel 		/* Pull the max and min from Level 5 array */
214429949e86Sstevel 		for (i = 0; i < L5_SZ; i++) {
214529949e86Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
214629949e86Sstevel 			    (tempstat->l5[i] > max)) {
214729949e86Sstevel 				max = tempstat->l5[i];
214829949e86Sstevel 			}
214929949e86Sstevel 
215029949e86Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
215129949e86Sstevel 			    (tempstat->l5[i] < min)) {
215229949e86Sstevel 				min = tempstat->l5[i];
215329949e86Sstevel 			}
215429949e86Sstevel 		}
215529949e86Sstevel 	} else {
215629949e86Sstevel 		/*
215729949e86Sstevel 		 * copy the temperature history buffer into the
215829949e86Sstevel 		 * kstat structure.
215929949e86Sstevel 		 */
216029949e86Sstevel 		bcopy(tempstat, kstatp, sizeof (struct temp_stats));
216129949e86Sstevel 	}
216229949e86Sstevel 	return (0);
216329949e86Sstevel }
216429949e86Sstevel 
216529949e86Sstevel int
216629949e86Sstevel temp_override_kstat_update(kstat_t *ksp, int rw)
216729949e86Sstevel {
216829949e86Sstevel 	short *over;
216929949e86Sstevel 	short *kstatp;
217029949e86Sstevel 
217129949e86Sstevel 	kstatp = (short *)ksp->ks_data;
217229949e86Sstevel 	over = (short *)ksp->ks_private;
217329949e86Sstevel 
217429949e86Sstevel 	/*
217529949e86Sstevel 	 * Kstat reads are used to get the temperature override setting.
217629949e86Sstevel 	 * Kstat writes are used to set the temperature override setting.
217729949e86Sstevel 	 */
217829949e86Sstevel 	if (rw == KSTAT_WRITE) {
217929949e86Sstevel 		*over = *kstatp;
218029949e86Sstevel 	} else {
218129949e86Sstevel 		*kstatp = *over;
218229949e86Sstevel 	}
218329949e86Sstevel 	return (0);
218429949e86Sstevel }
218529949e86Sstevel 
218629949e86Sstevel /*
218729949e86Sstevel  * This function uses the calibration tables at the beginning of this file
218829949e86Sstevel  * to lookup the actual temperature of the thermistor in degrees Celcius.
218929949e86Sstevel  * If the measurement is out of the bounds of the acceptable values, the
219029949e86Sstevel  * closest boundary value is used instead.
219129949e86Sstevel  */
219229949e86Sstevel static short
219329949e86Sstevel calibrate_temp(enum board_type type, uchar_t temp, uint_t ac_comp)
219429949e86Sstevel {
219529949e86Sstevel 	short result = NA_TEMP;
219629949e86Sstevel 
219729949e86Sstevel 	if (dont_calibrate == 1) {
219829949e86Sstevel 		return ((short)temp);
219929949e86Sstevel 	}
220029949e86Sstevel 
220129949e86Sstevel 	switch (type) {
220229949e86Sstevel 	case CPU_BOARD:
220329949e86Sstevel 		/*
220429949e86Sstevel 		 * If AC chip revision is >= 4 or if it is unitialized,
220529949e86Sstevel 		 * then use the new calibration tables.
220629949e86Sstevel 		 */
220729949e86Sstevel 		if ((CHIP_REV(ac_comp) >= 4) || (CHIP_REV(ac_comp) == 0)) {
220829949e86Sstevel 			if (temp >= CPU2_MX_CNT) {
220929949e86Sstevel 				result = cpu2_table[CPU2_MX_CNT-1];
221029949e86Sstevel 			} else {
221129949e86Sstevel 				result = cpu2_table[temp];
221229949e86Sstevel 			}
221329949e86Sstevel 		} else {
221429949e86Sstevel 			if (temp >= CPU_MX_CNT) {
221529949e86Sstevel 				result = cpu_table[CPU_MX_CNT-1];
221629949e86Sstevel 			} else {
221729949e86Sstevel 				result = cpu_table[temp];
221829949e86Sstevel 			}
221929949e86Sstevel 		}
222029949e86Sstevel 		break;
222129949e86Sstevel 
222229949e86Sstevel 	case IO_2SBUS_BOARD:
222329949e86Sstevel 	case IO_SBUS_FFB_BOARD:
222429949e86Sstevel 	case IO_PCI_BOARD:
222529949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
222629949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
222729949e86Sstevel 		if (temp < IO_MN_CNT) {
222829949e86Sstevel 			result = io_table[IO_MN_CNT];
222929949e86Sstevel 		} else if (temp >= IO_MX_CNT) {
223029949e86Sstevel 			result = io_table[IO_MX_CNT-1];
223129949e86Sstevel 		} else {
223229949e86Sstevel 			result = io_table[temp];
223329949e86Sstevel 		}
223429949e86Sstevel 		break;
223529949e86Sstevel 
223629949e86Sstevel 	case CLOCK_BOARD:
223729949e86Sstevel 		if (temp < CLK_MN_CNT) {
223829949e86Sstevel 			result = clock_table[CLK_MN_CNT];
223929949e86Sstevel 		} else if (temp >= CLK_MX_CNT) {
224029949e86Sstevel 			result = clock_table[CLK_MX_CNT-1];
224129949e86Sstevel 		} else {
224229949e86Sstevel 			result = clock_table[temp];
224329949e86Sstevel 		}
224429949e86Sstevel 		break;
224529949e86Sstevel 
224629949e86Sstevel 	default:
224729949e86Sstevel 		break;
224829949e86Sstevel 	}
224929949e86Sstevel 
225029949e86Sstevel 	return (result);
225129949e86Sstevel }
225229949e86Sstevel 
225329949e86Sstevel /*
225429949e86Sstevel  * Determine the temperature state of this board based on its type and
225529949e86Sstevel  * the actual temperature in degrees Celcius.
225629949e86Sstevel  */
225729949e86Sstevel static enum temp_state
225829949e86Sstevel get_temp_state(enum board_type type, short temp, int board)
225929949e86Sstevel {
226029949e86Sstevel 	enum temp_state state = TEMP_OK;
226129949e86Sstevel 	short warn_limit;
226229949e86Sstevel 	short danger_limit;
226329949e86Sstevel 	struct cpu *cpa, *cpb;
226429949e86Sstevel 
226529949e86Sstevel 	switch (type) {
226629949e86Sstevel 	case CPU_BOARD:
226729949e86Sstevel 		warn_limit = cpu_warn_temp;
226829949e86Sstevel 		danger_limit = cpu_danger_temp;
226929949e86Sstevel 
227029949e86Sstevel 		/*
227129949e86Sstevel 		 * For CPU boards with frequency >= 400 MHZ,
227229949e86Sstevel 		 * temperature zones are different.
227329949e86Sstevel 		 */
227429949e86Sstevel 
227529949e86Sstevel 		mutex_enter(&cpu_lock);
227629949e86Sstevel 
227729949e86Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL) {
227829949e86Sstevel 			if ((cpa->cpu_type_info.pi_clock) >= 400) {
227929949e86Sstevel 				warn_limit = cpu_warn_temp_4x;
228029949e86Sstevel 				danger_limit = cpu_danger_temp_4x;
228129949e86Sstevel 			}
228229949e86Sstevel 		}
228329949e86Sstevel 		if ((cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL) {
228429949e86Sstevel 			if ((cpb->cpu_type_info.pi_clock) >= 400) {
228529949e86Sstevel 				warn_limit = cpu_warn_temp_4x;
228629949e86Sstevel 				danger_limit = cpu_danger_temp_4x;
228729949e86Sstevel 			}
228829949e86Sstevel 		}
228929949e86Sstevel 
229029949e86Sstevel 		mutex_exit(&cpu_lock);
229129949e86Sstevel 
229229949e86Sstevel 		break;
229329949e86Sstevel 
229429949e86Sstevel 	case IO_2SBUS_BOARD:
229529949e86Sstevel 	case IO_SBUS_FFB_BOARD:
229629949e86Sstevel 	case IO_PCI_BOARD:
229729949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
229829949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
229929949e86Sstevel 		warn_limit = io_warn_temp;
230029949e86Sstevel 		danger_limit = io_danger_temp;
230129949e86Sstevel 		break;
230229949e86Sstevel 
230329949e86Sstevel 	case CLOCK_BOARD:
230429949e86Sstevel 		warn_limit = clk_warn_temp;
230529949e86Sstevel 		danger_limit = clk_danger_temp;
230629949e86Sstevel 		break;
230729949e86Sstevel 
230829949e86Sstevel 	case UNINIT_BOARD:
230929949e86Sstevel 	case UNKNOWN_BOARD:
231029949e86Sstevel 	case MEM_BOARD:
231129949e86Sstevel 	default:
231229949e86Sstevel 		warn_limit = dft_warn_temp;
231329949e86Sstevel 		danger_limit = dft_danger_temp;
231429949e86Sstevel 		break;
231529949e86Sstevel 	}
231629949e86Sstevel 
231729949e86Sstevel 	if (temp >= danger_limit) {
231829949e86Sstevel 		state = TEMP_DANGER;
231929949e86Sstevel 	} else if (temp >= warn_limit) {
232029949e86Sstevel 		state = TEMP_WARN;
232129949e86Sstevel 	}
232229949e86Sstevel 
232329949e86Sstevel 	return (state);
232429949e86Sstevel }
232529949e86Sstevel 
232629949e86Sstevel static void
232729949e86Sstevel fhc_add_kstats(struct fhc_soft_state *softsp)
232829949e86Sstevel {
232929949e86Sstevel 	struct kstat *fhc_ksp;
233029949e86Sstevel 	struct fhc_kstat *fhc_named_ksp;
233129949e86Sstevel 
233229949e86Sstevel 	if ((fhc_ksp = kstat_create("unix", softsp->list->sc.board,
233329949e86Sstevel 	    FHC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
233429949e86Sstevel 	    sizeof (struct fhc_kstat) / sizeof (kstat_named_t),
233529949e86Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
233629949e86Sstevel 		cmn_err(CE_WARN, "fhc%d kstat_create failed",
233729949e86Sstevel 		    ddi_get_instance(softsp->dip));
233829949e86Sstevel 		return;
233929949e86Sstevel 	}
234029949e86Sstevel 
234129949e86Sstevel 	fhc_named_ksp = (struct fhc_kstat *)(fhc_ksp->ks_data);
234229949e86Sstevel 
234329949e86Sstevel 	/* initialize the named kstats */
234429949e86Sstevel 	kstat_named_init(&fhc_named_ksp->csr,
234529949e86Sstevel 	    CSR_KSTAT_NAMED,
234629949e86Sstevel 	    KSTAT_DATA_UINT32);
234729949e86Sstevel 
234829949e86Sstevel 	kstat_named_init(&fhc_named_ksp->bsr,
234929949e86Sstevel 	    BSR_KSTAT_NAMED,
235029949e86Sstevel 	    KSTAT_DATA_UINT32);
235129949e86Sstevel 
235229949e86Sstevel 	fhc_ksp->ks_update = fhc_kstat_update;
235329949e86Sstevel 	fhc_ksp->ks_private = (void *)softsp;
235429949e86Sstevel 	softsp->fhc_ksp = fhc_ksp;
235529949e86Sstevel 	kstat_install(fhc_ksp);
235629949e86Sstevel }
235729949e86Sstevel 
235829949e86Sstevel static int
235929949e86Sstevel fhc_kstat_update(kstat_t *ksp, int rw)
236029949e86Sstevel {
236129949e86Sstevel 	struct fhc_kstat *fhcksp;
236229949e86Sstevel 	struct fhc_soft_state *softsp;
236329949e86Sstevel 
236429949e86Sstevel 	fhcksp = (struct fhc_kstat *)ksp->ks_data;
236529949e86Sstevel 	softsp = (struct fhc_soft_state *)ksp->ks_private;
236629949e86Sstevel 
236729949e86Sstevel 	/* this is a read-only kstat. Bail out on a write */
236829949e86Sstevel 	if (rw == KSTAT_WRITE) {
236929949e86Sstevel 		return (EACCES);
237029949e86Sstevel 	} else {
237129949e86Sstevel 		/*
237229949e86Sstevel 		 * copy the current state of the hardware into the
237329949e86Sstevel 		 * kstat structure.
237429949e86Sstevel 		 */
237529949e86Sstevel 		fhcksp->csr.value.ui32 = *softsp->ctrl;
237629949e86Sstevel 		fhcksp->bsr.value.ui32 = *softsp->bsr;
237729949e86Sstevel 	}
237829949e86Sstevel 	return (0);
237929949e86Sstevel }
238029949e86Sstevel 
238129949e86Sstevel static int
238229949e86Sstevel cpu_on_board(int board)
238329949e86Sstevel {
238429949e86Sstevel 	int upa_a = board << 1;
238529949e86Sstevel 	int upa_b = (board << 1) + 1;
238629949e86Sstevel 
238729949e86Sstevel 	if ((cpunodes[upa_a].nodeid != NULL) ||
238829949e86Sstevel 	    (cpunodes[upa_b].nodeid != NULL)) {
238929949e86Sstevel 		return (1);
239029949e86Sstevel 	} else {
239129949e86Sstevel 		return (0);
239229949e86Sstevel 	}
239329949e86Sstevel }
239429949e86Sstevel 
239529949e86Sstevel /*
239629949e86Sstevel  * This function uses the board list and toggles the OS green board
239729949e86Sstevel  * LED. The mask input tells which bit fields are being modified,
239829949e86Sstevel  * and the value input tells the states of the bits.
239929949e86Sstevel  */
240029949e86Sstevel void
240129949e86Sstevel update_board_leds(fhc_bd_t *board, uint_t mask, uint_t value)
240229949e86Sstevel {
240329949e86Sstevel 	volatile uint_t temp;
240429949e86Sstevel 
240529949e86Sstevel 	ASSERT(fhc_bdlist_locked());
240629949e86Sstevel 
240729949e86Sstevel 	/* mask off mask and value for only the LED bits */
240829949e86Sstevel 	mask &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
240929949e86Sstevel 	value &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
241029949e86Sstevel 
241129949e86Sstevel 	if (board != NULL) {
241229949e86Sstevel 		mutex_enter(&board->softsp->ctrl_lock);
241329949e86Sstevel 
241429949e86Sstevel 		/* read the current register state */
241529949e86Sstevel 		temp = *board->softsp->ctrl;
241629949e86Sstevel 
241729949e86Sstevel 		/*
241829949e86Sstevel 		 * The EPDA bits are special since the register is
241929949e86Sstevel 		 * special.  We don't want to set them, since setting
242029949e86Sstevel 		 * the bits on a shutdown cpu keeps the cpu permanently
242129949e86Sstevel 		 * powered off.  Also, the CSR_SYNC bit must always be
242229949e86Sstevel 		 * set to 0 as it is an OBP semaphore that is expected to
242329949e86Sstevel 		 * be clear for cpu restart.
242429949e86Sstevel 		 */
242529949e86Sstevel 		temp &= ~(FHC_CSR_SYNC | FHC_EPDA_OFF | FHC_EPDB_OFF);
242629949e86Sstevel 
242729949e86Sstevel 		/* mask off the bits to change */
242829949e86Sstevel 		temp &= ~mask;
242929949e86Sstevel 
243029949e86Sstevel 		/* or in the new values of the bits. */
243129949e86Sstevel 		temp |= value;
243229949e86Sstevel 
243329949e86Sstevel 		/* update the register */
243429949e86Sstevel 		*board->softsp->ctrl = temp;
243529949e86Sstevel 
243629949e86Sstevel 		/* flush the hardware registers */
243729949e86Sstevel 		temp = *board->softsp->ctrl;
243829949e86Sstevel #ifdef lint
243929949e86Sstevel 		temp = temp;
244029949e86Sstevel #endif
244129949e86Sstevel 
244229949e86Sstevel 		mutex_exit(&board->softsp->ctrl_lock);
244329949e86Sstevel 	}
244429949e86Sstevel }
244529949e86Sstevel 
244629949e86Sstevel static int
244729949e86Sstevel check_for_chamber(void)
244829949e86Sstevel {
244929949e86Sstevel 	int chamber = 0;
245029949e86Sstevel 	dev_info_t *options_dip;
245129949e86Sstevel 	pnode_t options_node_id;
245229949e86Sstevel 	int mfgmode_len;
245329949e86Sstevel 	int retval;
245429949e86Sstevel 	char *mfgmode;
245529949e86Sstevel 
245629949e86Sstevel 	/*
245729949e86Sstevel 	 * The operator can disable overtemp powerdown from /etc/system or
245829949e86Sstevel 	 * boot -h.
245929949e86Sstevel 	 */
246029949e86Sstevel 	if (!enable_overtemp_powerdown) {
246129949e86Sstevel 		cmn_err(CE_WARN, "Operator has disabled overtemp powerdown");
246229949e86Sstevel 		return (1);
246329949e86Sstevel 	}
246429949e86Sstevel 
246529949e86Sstevel 	/*
246629949e86Sstevel 	 * An OBP option, 'mfg-mode' is being used to inform us as to
246729949e86Sstevel 	 * whether we are in an enviromental chamber. It exists in
246829949e86Sstevel 	 * the 'options' node. This is where all OBP 'setenv' (eeprom)
246929949e86Sstevel 	 * parameters live.
247029949e86Sstevel 	 */
247129949e86Sstevel 	if ((options_dip = ddi_find_devinfo("options", -1, 0)) != NULL) {
247229949e86Sstevel 		options_node_id = (pnode_t)ddi_get_nodeid(options_dip);
247329949e86Sstevel 		mfgmode_len = prom_getproplen(options_node_id, "mfg-mode");
247429949e86Sstevel 		if (mfgmode_len == -1) {
247529949e86Sstevel 			return (chamber);
247629949e86Sstevel 		}
247729949e86Sstevel 		mfgmode = kmem_alloc(mfgmode_len+1, KM_SLEEP);
247829949e86Sstevel 
247929949e86Sstevel 		retval = prom_getprop(options_node_id, "mfg-mode", mfgmode);
248029949e86Sstevel 		if (retval != -1) {
248129949e86Sstevel 			mfgmode[retval] = 0;
248229949e86Sstevel 			if (strcmp(mfgmode, CHAMBER_VALUE) == 0) {
248329949e86Sstevel 				chamber = 1;
248429949e86Sstevel 				cmn_err(CE_WARN, "System in Temperature"
248529949e86Sstevel 				    " Chamber Mode. Overtemperature"
248629949e86Sstevel 				    " Shutdown disabled");
248729949e86Sstevel 			}
248829949e86Sstevel 		}
248929949e86Sstevel 		kmem_free(mfgmode, mfgmode_len+1);
249029949e86Sstevel 	}
249129949e86Sstevel 	return (chamber);
249229949e86Sstevel }
249329949e86Sstevel 
249429949e86Sstevel static void
249529949e86Sstevel build_bd_display_str(char *buffer, enum board_type type, int board)
249629949e86Sstevel {
249729949e86Sstevel 	if (buffer == NULL) {
249829949e86Sstevel 		return;
249929949e86Sstevel 	}
250029949e86Sstevel 
250129949e86Sstevel 	/* fill in board type to display */
250229949e86Sstevel 	switch (type) {
250329949e86Sstevel 	case UNINIT_BOARD:
250429949e86Sstevel 		(void) sprintf(buffer, "Uninitialized Board type board %d",
250529949e86Sstevel 		    board);
250629949e86Sstevel 		break;
250729949e86Sstevel 
250829949e86Sstevel 	case UNKNOWN_BOARD:
250929949e86Sstevel 		(void) sprintf(buffer, "Unknown Board type board %d", board);
251029949e86Sstevel 		break;
251129949e86Sstevel 
251229949e86Sstevel 	case CPU_BOARD:
251329949e86Sstevel 	case MEM_BOARD:
251429949e86Sstevel 		(void) sprintf(buffer, "CPU/Memory board %d", board);
251529949e86Sstevel 		break;
251629949e86Sstevel 
251729949e86Sstevel 	case IO_2SBUS_BOARD:
251829949e86Sstevel 		(void) sprintf(buffer, "2 SBus IO board %d", board);
251929949e86Sstevel 		break;
252029949e86Sstevel 
252129949e86Sstevel 	case IO_SBUS_FFB_BOARD:
252229949e86Sstevel 		(void) sprintf(buffer, "SBus FFB IO board %d", board);
252329949e86Sstevel 		break;
252429949e86Sstevel 
252529949e86Sstevel 	case IO_PCI_BOARD:
252629949e86Sstevel 		(void) sprintf(buffer, "PCI IO board %d", board);
252729949e86Sstevel 		break;
252829949e86Sstevel 
252929949e86Sstevel 	case CLOCK_BOARD:
253029949e86Sstevel 		(void) sprintf(buffer, "Clock board");
253129949e86Sstevel 		break;
253229949e86Sstevel 
253329949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
253429949e86Sstevel 		(void) sprintf(buffer, "2 SBus SOC+ IO board %d", board);
253529949e86Sstevel 		break;
253629949e86Sstevel 
253729949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
253829949e86Sstevel 		(void) sprintf(buffer, "SBus FFB SOC+ IO board %d", board);
253929949e86Sstevel 		break;
254029949e86Sstevel 
254129949e86Sstevel 	default:
254229949e86Sstevel 		(void) sprintf(buffer, "Unrecognized board type board %d",
254329949e86Sstevel 		    board);
254429949e86Sstevel 		break;
254529949e86Sstevel 	}
254629949e86Sstevel }
254729949e86Sstevel 
254829949e86Sstevel void
254929949e86Sstevel fhc_intrdist(void *arg)
255029949e86Sstevel {
255129949e86Sstevel 	struct fhc_soft_state *softsp;
255229949e86Sstevel 	dev_info_t *dip = (dev_info_t *)arg;
255329949e86Sstevel 	volatile uint_t *mondo_vec_reg;
255429949e86Sstevel 	volatile uint_t *intr_state_reg;
255529949e86Sstevel 	uint_t mondo_vec;
255629949e86Sstevel 	uint_t tmp_reg;
255729949e86Sstevel 	uint_t cpu_id;
255829949e86Sstevel 	uint_t i;
255929949e86Sstevel 
256029949e86Sstevel 	/* extract the soft state pointer */
256129949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(dip));
256229949e86Sstevel 
256329949e86Sstevel 	/*
256429949e86Sstevel 	 * Loop through all the interrupt mapping registers and reprogram
256529949e86Sstevel 	 * the target CPU for all valid registers.
256629949e86Sstevel 	 */
256729949e86Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
256829949e86Sstevel 		mondo_vec_reg = softsp->intr_regs[i].mapping_reg;
256929949e86Sstevel 		intr_state_reg = softsp->intr_regs[i].clear_reg;
257029949e86Sstevel 
257129949e86Sstevel 		if ((*mondo_vec_reg & IMR_VALID) == 0)
257229949e86Sstevel 			continue;
257329949e86Sstevel 
257429949e86Sstevel 		cpu_id = intr_dist_cpuid();
257529949e86Sstevel 
257629949e86Sstevel 		/* Check the current target of the mondo */
257729949e86Sstevel 		if (((*mondo_vec_reg & INR_PID_MASK) >> INR_PID_SHIFT) ==
257829949e86Sstevel 		    cpu_id) {
257929949e86Sstevel 			/* It is the same, don't reprogram */
258029949e86Sstevel 			return;
258129949e86Sstevel 		}
258229949e86Sstevel 
258329949e86Sstevel 		/* So it's OK to reprogram the CPU target */
258429949e86Sstevel 
258529949e86Sstevel 		/* turn off the valid bit */
258629949e86Sstevel 		*mondo_vec_reg &= ~IMR_VALID;
258729949e86Sstevel 
258829949e86Sstevel 		/* flush the hardware registers */
258929949e86Sstevel 		tmp_reg = *softsp->id;
259029949e86Sstevel 
259129949e86Sstevel 		/*
259229949e86Sstevel 		 * wait for the state machine to idle. Do not loop on panic, so
259329949e86Sstevel 		 * that system does not hang.
259429949e86Sstevel 		 */
259529949e86Sstevel 		while (((*intr_state_reg & INT_PENDING) == INT_PENDING) &&
259629949e86Sstevel 		    !panicstr)
259729949e86Sstevel 			;
259829949e86Sstevel 
259929949e86Sstevel 		/* re-target the mondo and turn it on */
260029949e86Sstevel 		mondo_vec = (cpu_id << INR_PID_SHIFT) | IMR_VALID;
260129949e86Sstevel 
260229949e86Sstevel 		/* write it back to the hardware. */
260329949e86Sstevel 		*mondo_vec_reg = mondo_vec;
260429949e86Sstevel 
260529949e86Sstevel 		/* flush the hardware buffers. */
260629949e86Sstevel 		tmp_reg = *(softsp->id);
260729949e86Sstevel 
260829949e86Sstevel #ifdef	lint
260929949e86Sstevel 		tmp_reg = tmp_reg;
261029949e86Sstevel #endif	/* lint */
261129949e86Sstevel 	}
261229949e86Sstevel }
261329949e86Sstevel 
261429949e86Sstevel /*
261529949e86Sstevel  * reg_fault
261629949e86Sstevel  *
261729949e86Sstevel  * This routine registers a fault in the fault list. If the fault
261829949e86Sstevel  * is unique (does not exist in fault list) then a new fault is
261929949e86Sstevel  * added to the fault list, with the appropriate structure elements
262029949e86Sstevel  * filled in.
262129949e86Sstevel  */
262229949e86Sstevel void
262329949e86Sstevel reg_fault(int unit, enum ft_type type, enum ft_class fclass)
262429949e86Sstevel {
262529949e86Sstevel 	struct ft_link_list *list;	/* temporary list pointer */
262629949e86Sstevel 
262729949e86Sstevel 	if (type >= ft_max_index) {
262829949e86Sstevel 		cmn_err(CE_WARN, "Illegal Fault type %x", type);
262929949e86Sstevel 		return;
263029949e86Sstevel 	}
263129949e86Sstevel 
263229949e86Sstevel 	mutex_enter(&ftlist_mutex);
263329949e86Sstevel 
263429949e86Sstevel 	/* Search for the requested fault. If it already exists, return. */
263529949e86Sstevel 	for (list = ft_list; list != NULL; list = list->next) {
263629949e86Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
263729949e86Sstevel 		    (list->f.fclass == fclass)) {
263829949e86Sstevel 			mutex_exit(&ftlist_mutex);
263929949e86Sstevel 			return;
264029949e86Sstevel 		}
264129949e86Sstevel 	}
264229949e86Sstevel 
264329949e86Sstevel 	/* Allocate a new fault structure. */
264429949e86Sstevel 	list = kmem_zalloc(sizeof (struct ft_link_list), KM_SLEEP);
264529949e86Sstevel 
264629949e86Sstevel 	/* fill in the fault list elements */
264729949e86Sstevel 	list->f.unit = unit;
264829949e86Sstevel 	list->f.type = type;
264929949e86Sstevel 	list->f.fclass = fclass;
265029949e86Sstevel 	list->f.create_time = (time32_t)gethrestime_sec(); /* XX64 */
265129949e86Sstevel 	(void) strncpy(list->f.msg, ft_str_table[type], MAX_FT_DESC);
265229949e86Sstevel 
265329949e86Sstevel 	/* link it into the list. */
265429949e86Sstevel 	list->next = ft_list;
265529949e86Sstevel 	ft_list = list;
265629949e86Sstevel 
265729949e86Sstevel 	/* Update the total fault count */
265829949e86Sstevel 	ft_nfaults++;
265929949e86Sstevel 
266029949e86Sstevel 	mutex_exit(&ftlist_mutex);
266129949e86Sstevel }
266229949e86Sstevel 
266329949e86Sstevel /*
266429949e86Sstevel  * clear_fault
266529949e86Sstevel  *
266629949e86Sstevel  * This routine finds the fault list entry specified by the caller,
266729949e86Sstevel  * deletes it from the fault list, and frees up the memory used for
266829949e86Sstevel  * the entry. If the requested fault is not found, it exits silently.
266929949e86Sstevel  */
267029949e86Sstevel void
267129949e86Sstevel clear_fault(int unit, enum ft_type type, enum ft_class fclass)
267229949e86Sstevel {
267329949e86Sstevel 	struct ft_link_list *list;		/* temporary list pointer */
267429949e86Sstevel 	struct ft_link_list **vect;
267529949e86Sstevel 
267629949e86Sstevel 	mutex_enter(&ftlist_mutex);
267729949e86Sstevel 
267829949e86Sstevel 	list = ft_list;
267929949e86Sstevel 	vect = &ft_list;
268029949e86Sstevel 
268129949e86Sstevel 	/*
268229949e86Sstevel 	 * Search for the requested fault. If it exists, delete it
268329949e86Sstevel 	 * and relink the fault list.
268429949e86Sstevel 	 */
268529949e86Sstevel 	for (; list != NULL; vect = &list->next, list = list->next) {
268629949e86Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
268729949e86Sstevel 		    (list->f.fclass == fclass)) {
268829949e86Sstevel 			/* remove the item from the list */
268929949e86Sstevel 			*vect = list->next;
269029949e86Sstevel 
269129949e86Sstevel 			/* free the memory allocated */
269229949e86Sstevel 			kmem_free(list, sizeof (struct ft_link_list));
269329949e86Sstevel 
269429949e86Sstevel 			/* Update the total fault count */
269529949e86Sstevel 			ft_nfaults--;
269629949e86Sstevel 			break;
269729949e86Sstevel 		}
269829949e86Sstevel 	}
269929949e86Sstevel 	mutex_exit(&ftlist_mutex);
270029949e86Sstevel }
270129949e86Sstevel 
270229949e86Sstevel /*
270329949e86Sstevel  * process_fault_list
270429949e86Sstevel  *
270529949e86Sstevel  * This routine walks the global fault list and updates the board list
270629949e86Sstevel  * with the current status of each Yellow LED. If any faults are found
270729949e86Sstevel  * in the system, then a non-zero value is returned. Else zero is returned.
270829949e86Sstevel  */
270929949e86Sstevel int
271029949e86Sstevel process_fault_list(void)
271129949e86Sstevel {
271229949e86Sstevel 	int fault = 0;
271329949e86Sstevel 	struct ft_link_list *ftlist;	/* fault list pointer */
271429949e86Sstevel 	fhc_bd_t *bdlist;		/* board list pointer */
271529949e86Sstevel 
271629949e86Sstevel 	/*
271729949e86Sstevel 	 * Note on locking. The bdlist mutex is always acquired and
271829949e86Sstevel 	 * held around the ftlist mutex when both are needed for an
271929949e86Sstevel 	 * operation. This is to avoid deadlock.
272029949e86Sstevel 	 */
272129949e86Sstevel 
272229949e86Sstevel 	/* First lock the board list */
272329949e86Sstevel 	(void) fhc_bdlist_lock(-1);
272429949e86Sstevel 
272529949e86Sstevel 	/* Grab the fault list lock first */
272629949e86Sstevel 	mutex_enter(&ftlist_mutex);
272729949e86Sstevel 
272829949e86Sstevel 	/* clear the board list of all faults first */
272929949e86Sstevel 	for (bdlist = fhc_bd_first(); bdlist; bdlist = fhc_bd_next(bdlist))
273029949e86Sstevel 		bdlist->fault = 0;
273129949e86Sstevel 
273229949e86Sstevel 	/* walk the fault list here */
273329949e86Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
273429949e86Sstevel 		fault++;
273529949e86Sstevel 
273629949e86Sstevel 		/*
273729949e86Sstevel 		 * If this is a board level fault, find the board, The
273829949e86Sstevel 		 * unit number for all board class faults must be the
273929949e86Sstevel 		 * actual board number. The caller of reg_fault must
274029949e86Sstevel 		 * ensure this for FT_BOARD class faults.
274129949e86Sstevel 		 */
274229949e86Sstevel 		if (ftlist->f.fclass == FT_BOARD) {
274329949e86Sstevel 			/* Sanity check the board first */
274429949e86Sstevel 			if (fhc_bd_valid(ftlist->f.unit)) {
274529949e86Sstevel 				bdlist = fhc_bd(ftlist->f.unit);
274629949e86Sstevel 				bdlist->fault = 1;
274729949e86Sstevel 			} else {
274829949e86Sstevel 				cmn_err(CE_WARN, "No board %d list entry found",
274929949e86Sstevel 				    ftlist->f.unit);
275029949e86Sstevel 			}
275129949e86Sstevel 		}
275229949e86Sstevel 	}
275329949e86Sstevel 
275429949e86Sstevel 	/* now unlock the fault list */
275529949e86Sstevel 	mutex_exit(&ftlist_mutex);
275629949e86Sstevel 
275729949e86Sstevel 	/* unlock the board list before leaving */
275829949e86Sstevel 	fhc_bdlist_unlock();
275929949e86Sstevel 
276029949e86Sstevel 	return (fault);
276129949e86Sstevel }
276229949e86Sstevel 
276329949e86Sstevel /*
276429949e86Sstevel  * Add a new memloc to the database (and keep 'em sorted by PA)
276529949e86Sstevel  */
276629949e86Sstevel void
276729949e86Sstevel fhc_add_memloc(int board, uint64_t pa, uint_t size)
276829949e86Sstevel {
276929949e86Sstevel 	struct fhc_memloc *p, **pp;
277029949e86Sstevel 	uint_t ipa = pa >> FHC_MEMLOC_SHIFT;
277129949e86Sstevel 
277229949e86Sstevel 	ASSERT(fhc_bdlist_locked());
277329949e86Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
277429949e86Sstevel 
277529949e86Sstevel 	/* look for a comparable memloc (as long as new PA smaller) */
277629949e86Sstevel 	for (p = fhc_base_memloc, pp = &fhc_base_memloc;
277729949e86Sstevel 	    p != NULL; pp = &p->next, p = p->next) {
277829949e86Sstevel 		/* have we passed our place in the sort? */
277929949e86Sstevel 		if (ipa < p->pa) {
278029949e86Sstevel 			break;
278129949e86Sstevel 		}
278229949e86Sstevel 	}
278329949e86Sstevel 	p = kmem_alloc(sizeof (struct fhc_memloc), KM_SLEEP);
278429949e86Sstevel 	p->next = *pp;
278529949e86Sstevel 	p->board = board;
278629949e86Sstevel 	p->pa = ipa;
278729949e86Sstevel 	p->size = size;
278829949e86Sstevel #ifdef DEBUG_MEMDEC
278929949e86Sstevel 	cmn_err(CE_NOTE, "fhc_add_memloc: adding %d 0x%x 0x%x",
279029949e86Sstevel 	    p->board, p->pa, p->size);
279129949e86Sstevel #endif /* DEBUG_MEMDEC */
279229949e86Sstevel 	*pp = p;
279329949e86Sstevel }
279429949e86Sstevel 
279529949e86Sstevel /*
279629949e86Sstevel  * Delete all memloc records for a board from the database
279729949e86Sstevel  */
279829949e86Sstevel void
279929949e86Sstevel fhc_del_memloc(int board)
280029949e86Sstevel {
280129949e86Sstevel 	struct fhc_memloc *p, **pp;
280229949e86Sstevel 
280329949e86Sstevel 	ASSERT(fhc_bdlist_locked());
280429949e86Sstevel 
280529949e86Sstevel 	/* delete all entries that match board */
280629949e86Sstevel 	pp = &fhc_base_memloc;
280729949e86Sstevel 	while ((p = *pp) != NULL) {
280829949e86Sstevel 		if (p->board == board) {
280929949e86Sstevel #ifdef DEBUG_MEMDEC
281029949e86Sstevel 			cmn_err(CE_NOTE, "fhc_del_memloc: removing %d "
281129949e86Sstevel 			    "0x%x 0x%x", board, p->pa, p->size);
281229949e86Sstevel #endif /* DEBUG_MEMDEC */
281329949e86Sstevel 			*pp = p->next;
281429949e86Sstevel 			kmem_free(p, sizeof (struct fhc_memloc));
281529949e86Sstevel 		} else {
281629949e86Sstevel 			pp = &(p->next);
281729949e86Sstevel 		}
281829949e86Sstevel 	}
281929949e86Sstevel }
282029949e86Sstevel 
282129949e86Sstevel /*
282229949e86Sstevel  * Find a physical address range of sufficient size and return a starting PA
282329949e86Sstevel  */
282429949e86Sstevel uint64_t
282529949e86Sstevel fhc_find_memloc_gap(uint_t size)
282629949e86Sstevel {
282729949e86Sstevel 	struct fhc_memloc *p;
282829949e86Sstevel 	uint_t base_pa = 0;
282929949e86Sstevel 	uint_t mask = ~(size-1);
283029949e86Sstevel 
283129949e86Sstevel 	ASSERT(fhc_bdlist_locked());
283229949e86Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
283329949e86Sstevel 
283429949e86Sstevel 	/*
283529949e86Sstevel 	 * walk the list of known memlocs and measure the 'gaps'.
283629949e86Sstevel 	 * we will need a hole that can align the 'size' requested.
283729949e86Sstevel 	 * (e.g. a 256mb bank needs to be on a 256mb boundary).
283829949e86Sstevel 	 */
283929949e86Sstevel 	for (p = fhc_base_memloc; p != NULL; p = p->next) {
284029949e86Sstevel 		if (base_pa != (base_pa & mask))
284129949e86Sstevel 			base_pa = (base_pa + size) & mask;
284229949e86Sstevel 		if (base_pa + size <= p->pa)
284329949e86Sstevel 			break;
284429949e86Sstevel 		base_pa = p->pa + p->size;
284529949e86Sstevel 	}
284629949e86Sstevel 
284729949e86Sstevel 	/*
284829949e86Sstevel 	 * At this point, we assume that base_pa is good enough.
284929949e86Sstevel 	 */
285029949e86Sstevel 	ASSERT((base_pa + size) <= FHC_MEMLOC_MAX);
285129949e86Sstevel 	if (base_pa != (base_pa & mask))
285229949e86Sstevel 		base_pa = (base_pa + size) & mask;	/* align */
285329949e86Sstevel 	return ((uint64_t)base_pa << FHC_MEMLOC_SHIFT);
285429949e86Sstevel }
285529949e86Sstevel 
285629949e86Sstevel /*
285729949e86Sstevel  * This simple function to write the MCRs can only be used when
285829949e86Sstevel  * the contents of memory are not valid as there is a bug in the AC
285929949e86Sstevel  * ASIC concerning refresh.
286029949e86Sstevel  */
286129949e86Sstevel static void
286229949e86Sstevel fhc_write_mcrs(
286329949e86Sstevel 	uint64_t cpa,
286429949e86Sstevel 	uint64_t dpa0,
286529949e86Sstevel 	uint64_t dpa1,
286629949e86Sstevel 	uint64_t c,
286729949e86Sstevel 	uint64_t d0,
286829949e86Sstevel 	uint64_t d1)
286929949e86Sstevel {
287029949e86Sstevel 	stdphysio(cpa, c & ~AC_CSR_REFEN);
287129949e86Sstevel 	(void) lddphysio(cpa);
287229949e86Sstevel 	if (GRP_SIZE_IS_SET(d0)) {
287329949e86Sstevel 		stdphysio(dpa0, d0);
287429949e86Sstevel 		(void) lddphysio(dpa0);
287529949e86Sstevel 	}
287629949e86Sstevel 	if (GRP_SIZE_IS_SET(d1)) {
287729949e86Sstevel 		stdphysio(dpa1, d1);
287829949e86Sstevel 		(void) lddphysio(dpa1);
287929949e86Sstevel 	}
288029949e86Sstevel 	stdphysio(cpa, c);
288129949e86Sstevel 	(void) lddphysio(cpa);
288229949e86Sstevel }
288329949e86Sstevel 
288429949e86Sstevel /* compute the appropriate RASIZE for bank size */
288529949e86Sstevel static uint_t
288629949e86Sstevel fhc_cvt_size(uint64_t bsz)
288729949e86Sstevel {
288829949e86Sstevel 	uint_t csz;
288929949e86Sstevel 
289029949e86Sstevel 	csz = 0;
289129949e86Sstevel 	bsz /= 64;
289229949e86Sstevel 	while (bsz) {
289329949e86Sstevel 		csz++;
289429949e86Sstevel 		bsz /= 2;
289529949e86Sstevel 	}
289629949e86Sstevel 	csz /= 2;
289729949e86Sstevel 
289829949e86Sstevel 	return (csz);
289929949e86Sstevel }
290029949e86Sstevel 
290129949e86Sstevel void
290229949e86Sstevel fhc_program_memory(int board, uint64_t pa)
290329949e86Sstevel {
290429949e86Sstevel 	uint64_t cpa, dpa0, dpa1;
290529949e86Sstevel 	uint64_t c, d0, d1;
290629949e86Sstevel 	uint64_t b0_pa, b1_pa;
290729949e86Sstevel 	uint64_t memdec0, memdec1;
290829949e86Sstevel 	uint_t b0_size, b1_size;
290929949e86Sstevel 
291029949e86Sstevel 	/* XXX gross hack to get to board via board number */
291129949e86Sstevel 	cpa = 0x1c0f9000060ull + (board * 0x400000000ull);
291229949e86Sstevel #ifdef DEBUG_MEMDEC
291329949e86Sstevel 	prom_printf("cpa = 0x%llx\n", cpa);
291429949e86Sstevel #endif /* DEBUG_MEMDEC */
291529949e86Sstevel 	dpa0 = cpa + 0x10;
291629949e86Sstevel 	dpa1 = cpa + 0x20;
291729949e86Sstevel 
291829949e86Sstevel /* assume size is set by connect */
291929949e86Sstevel 	memdec0 = lddphysio(dpa0);
292029949e86Sstevel #ifdef DEBUG_MEMDEC
292129949e86Sstevel 	prom_printf("memdec0 = 0x%llx\n", memdec0);
292229949e86Sstevel #endif /* DEBUG_MEMDEC */
292329949e86Sstevel 	memdec1 = lddphysio(dpa1);
292429949e86Sstevel #ifdef DEBUG_MEMDEC
292529949e86Sstevel 	prom_printf("memdec1 = 0x%llx\n", memdec1);
292629949e86Sstevel #endif /* DEBUG_MEMDEC */
292729949e86Sstevel 	if (GRP_SIZE_IS_SET(memdec0)) {
292829949e86Sstevel 		b0_size = GRP_SPANMB(memdec0);
292929949e86Sstevel 	} else {
293029949e86Sstevel 		b0_size = 0;
293129949e86Sstevel 	}
293229949e86Sstevel 	if (GRP_SIZE_IS_SET(memdec1)) {
293329949e86Sstevel 		b1_size = GRP_SPANMB(memdec1);
293429949e86Sstevel 	} else {
293529949e86Sstevel 		b1_size = 0;
293629949e86Sstevel 	}
293729949e86Sstevel 
293829949e86Sstevel 	c = lddphysio(cpa);
293929949e86Sstevel #ifdef DEBUG_MEMDEC
294029949e86Sstevel 	prom_printf("c = 0x%llx\n", c);
294129949e86Sstevel #endif /* DEBUG_MEMDEC */
294229949e86Sstevel 	if (b0_size) {
294329949e86Sstevel 		b0_pa = pa;
294429949e86Sstevel 		d0 = SETUP_DECODE(b0_pa, b0_size, 0, 0);
294529949e86Sstevel 		d0 |= AC_MEM_VALID;
294629949e86Sstevel 
294729949e86Sstevel 		c &= ~0x7;
294829949e86Sstevel 		c |= 0;
294929949e86Sstevel 		c &= ~(0x7 << 8);
295029949e86Sstevel 		c |= (fhc_cvt_size(b0_size) << 8);  /* match row size */
295129949e86Sstevel 	} else {
295229949e86Sstevel 		d0 = memdec0;
295329949e86Sstevel 	}
295429949e86Sstevel 	if (b1_size) {
295529949e86Sstevel 		b1_pa = pa + 0x80000000ull; /* XXX 2gb */
295629949e86Sstevel 		d1 = SETUP_DECODE(b1_pa, b1_size, 0, 0);
295729949e86Sstevel 		d1 |= AC_MEM_VALID;
295829949e86Sstevel 
295929949e86Sstevel 		c &= ~(0x7 << 3);
296029949e86Sstevel 		c |= (0 << 3);
296129949e86Sstevel 		c &= ~(0x7 << 11);
296229949e86Sstevel 		c |= (fhc_cvt_size(b1_size) << 11); /* match row size */
296329949e86Sstevel 	} else {
296429949e86Sstevel 		d1 = memdec1;
296529949e86Sstevel 	}
296629949e86Sstevel #ifdef DEBUG_MEMDEC
296729949e86Sstevel 	prom_printf("c 0x%llx, d0 0x%llx, d1 0x%llx\n", c, d0, d1);
296829949e86Sstevel #endif /* DEBUG_MEMDEC */
296929949e86Sstevel 	fhc_write_mcrs(cpa, dpa0, dpa1, c, d0, d1);
297029949e86Sstevel }
297129949e86Sstevel 
297229949e86Sstevel /*
297329949e86Sstevel  * Creates a variable sized virtual kstat with a snapshot routine in order
297429949e86Sstevel  * to pass the linked list fault list up to userland. Also creates a
297529949e86Sstevel  * virtual kstat to pass up the string table for faults.
297629949e86Sstevel  */
297729949e86Sstevel void
297829949e86Sstevel create_ft_kstats(int instance)
297929949e86Sstevel {
298029949e86Sstevel 	struct kstat *ksp;
298129949e86Sstevel 
298229949e86Sstevel 	ksp = kstat_create("unix", instance, FT_LIST_KSTAT_NAME, "misc",
298329949e86Sstevel 	    KSTAT_TYPE_RAW, 1, KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_VAR_SIZE);
298429949e86Sstevel 
298529949e86Sstevel 	if (ksp != NULL) {
298629949e86Sstevel 		ksp->ks_data = NULL;
298729949e86Sstevel 		ksp->ks_update = ft_ks_update;
298829949e86Sstevel 		ksp->ks_snapshot = ft_ks_snapshot;
298929949e86Sstevel 		ksp->ks_data_size = 1;
299029949e86Sstevel 		ksp->ks_lock = &ftlist_mutex;
299129949e86Sstevel 		kstat_install(ksp);
299229949e86Sstevel 	}
299329949e86Sstevel }
299429949e86Sstevel 
299529949e86Sstevel /*
299629949e86Sstevel  * This routine creates a snapshot of all the fault list data. It is
299729949e86Sstevel  * called by the kstat framework when a kstat read is done.
299829949e86Sstevel  */
299929949e86Sstevel static int
300029949e86Sstevel ft_ks_snapshot(struct kstat *ksp, void *buf, int rw)
300129949e86Sstevel {
300229949e86Sstevel 	struct ft_link_list *ftlist;
300329949e86Sstevel 
300429949e86Sstevel 	if (rw == KSTAT_WRITE) {
300529949e86Sstevel 		return (EACCES);
300629949e86Sstevel 	}
300729949e86Sstevel 
300829949e86Sstevel 	ksp->ks_snaptime = gethrtime();
300929949e86Sstevel 
301029949e86Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
301129949e86Sstevel 		bcopy(&ftlist->f, buf, sizeof (struct ft_list));
301229949e86Sstevel 		buf = ((struct ft_list *)buf) + 1;
301329949e86Sstevel 	}
301429949e86Sstevel 	return (0);
301529949e86Sstevel }
301629949e86Sstevel 
301729949e86Sstevel /*
301829949e86Sstevel  * Setup the kstat data size for the kstat framework. This is used in
301929949e86Sstevel  * conjunction with the ks_snapshot routine. This routine sets the size,
302029949e86Sstevel  * the kstat framework allocates the memory, and ks_shapshot does the
302129949e86Sstevel  * data transfer.
302229949e86Sstevel  */
302329949e86Sstevel static int
302429949e86Sstevel ft_ks_update(struct kstat *ksp, int rw)
302529949e86Sstevel {
302629949e86Sstevel 	if (rw == KSTAT_WRITE) {
302729949e86Sstevel 		return (EACCES);
302829949e86Sstevel 	} else {
302929949e86Sstevel 		if (ft_nfaults) {
303029949e86Sstevel 			ksp->ks_data_size = ft_nfaults *
303129949e86Sstevel 			    sizeof (struct ft_list);
303229949e86Sstevel 		} else {
303329949e86Sstevel 			ksp->ks_data_size = 1;
303429949e86Sstevel 		}
303529949e86Sstevel 	}
303629949e86Sstevel 
303729949e86Sstevel 	return (0);
303829949e86Sstevel }
303929949e86Sstevel 
304029949e86Sstevel /*
304129949e86Sstevel  * Power off any cpus on the board.
304229949e86Sstevel  */
304329949e86Sstevel int
304429949e86Sstevel fhc_board_poweroffcpus(int board, char *errbuf, int cpu_flags)
304529949e86Sstevel {
304629949e86Sstevel 	cpu_t *cpa, *cpb;
304729949e86Sstevel 	enum board_type type;
304829949e86Sstevel 	int error = 0;
304929949e86Sstevel 
305029949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
305129949e86Sstevel 
305229949e86Sstevel 	/*
305329949e86Sstevel 	 * what type of board are we dealing with?
305429949e86Sstevel 	 */
305529949e86Sstevel 	type = fhc_bd_type(board);
305629949e86Sstevel 
305729949e86Sstevel 	switch (type) {
305829949e86Sstevel 	case CPU_BOARD:
305929949e86Sstevel 
306029949e86Sstevel 		/*
306129949e86Sstevel 		 * the shutdown sequence will be:
306229949e86Sstevel 		 *
306329949e86Sstevel 		 * idle both cpus then shut them off.
306429949e86Sstevel 		 * it looks like the hardware gets corrupted if one
306529949e86Sstevel 		 * cpu is busy while the other is shutting down...
306629949e86Sstevel 		 */
306729949e86Sstevel 
306829949e86Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL &&
306929949e86Sstevel 		    cpu_is_active(cpa)) {
307029949e86Sstevel 			if (!cpu_intr_on(cpa)) {
307129949e86Sstevel 				cpu_intr_enable(cpa);
307229949e86Sstevel 			}
307329949e86Sstevel 			if ((error = cpu_offline(cpa, cpu_flags)) != 0) {
307429949e86Sstevel 				cmn_err(CE_WARN,
307529949e86Sstevel 				    "Processor %d failed to offline.",
307629949e86Sstevel 				    cpa->cpu_id);
307729949e86Sstevel 				if (errbuf != NULL) {
307829949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
307929949e86Sstevel 					    "processor %d failed to offline",
308029949e86Sstevel 					    cpa->cpu_id);
308129949e86Sstevel 				}
308229949e86Sstevel 			}
308329949e86Sstevel 		}
308429949e86Sstevel 
308529949e86Sstevel 		if (error == 0 &&
308629949e86Sstevel 		    (cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL &&
308729949e86Sstevel 		    cpu_is_active(cpb)) {
308829949e86Sstevel 			if (!cpu_intr_on(cpb)) {
308929949e86Sstevel 				cpu_intr_enable(cpb);
309029949e86Sstevel 			}
309129949e86Sstevel 			if ((error = cpu_offline(cpb, cpu_flags)) != 0) {
309229949e86Sstevel 				cmn_err(CE_WARN,
309329949e86Sstevel 				    "Processor %d failed to offline.",
309429949e86Sstevel 				    cpb->cpu_id);
309529949e86Sstevel 
309629949e86Sstevel 				if (errbuf != NULL) {
309729949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
309829949e86Sstevel 					    "processor %d failed to offline",
309929949e86Sstevel 					    cpb->cpu_id);
310029949e86Sstevel 				}
310129949e86Sstevel 			}
310229949e86Sstevel 		}
310329949e86Sstevel 
310429949e86Sstevel 		if (error == 0 && cpa != NULL && cpu_is_offline(cpa)) {
310529949e86Sstevel 			if ((error = cpu_poweroff(cpa)) != 0) {
310629949e86Sstevel 				cmn_err(CE_WARN,
310729949e86Sstevel 				    "Processor %d failed to power off.",
310829949e86Sstevel 				    cpa->cpu_id);
310929949e86Sstevel 				if (errbuf != NULL) {
311029949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
311129949e86Sstevel 					    "processor %d failed to power off",
311229949e86Sstevel 					    cpa->cpu_id);
311329949e86Sstevel 				}
311429949e86Sstevel 			} else {
311529949e86Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
311629949e86Sstevel 				    cpa->cpu_id);
311729949e86Sstevel 			}
311829949e86Sstevel 		}
311929949e86Sstevel 
312029949e86Sstevel 		if (error == 0 && cpb != NULL && cpu_is_offline(cpb)) {
312129949e86Sstevel 			if ((error = cpu_poweroff(cpb)) != 0) {
312229949e86Sstevel 				cmn_err(CE_WARN,
312329949e86Sstevel 				    "Processor %d failed to power off.",
312429949e86Sstevel 				    cpb->cpu_id);
312529949e86Sstevel 
312629949e86Sstevel 				if (errbuf != NULL) {
312729949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
312829949e86Sstevel 					    "processor %d failed to power off",
312929949e86Sstevel 					    cpb->cpu_id);
313029949e86Sstevel 				}
313129949e86Sstevel 			} else {
313229949e86Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
313329949e86Sstevel 				    cpb->cpu_id);
313429949e86Sstevel 			}
313529949e86Sstevel 		}
313629949e86Sstevel 
313729949e86Sstevel 		/*
313829949e86Sstevel 		 * If all the shutdowns completed, ONLY THEN, clear the
313929949e86Sstevel 		 * incorrectly valid dtags...
314029949e86Sstevel 		 *
314129949e86Sstevel 		 * IMPORTANT: it is an error to read or write dtags while
314229949e86Sstevel 		 * they are 'active'
314329949e86Sstevel 		 */
314429949e86Sstevel 		if (error == 0 && (cpa != NULL || cpb != NULL)) {
314529949e86Sstevel 			u_longlong_t base = 0;
314629949e86Sstevel 			int i;
314729949e86Sstevel #ifdef DEBUG
314829949e86Sstevel 			int nonz0 = 0;
314929949e86Sstevel 			int nonz1 = 0;
315029949e86Sstevel #endif
315129949e86Sstevel 			if (cpa != NULL)
315229949e86Sstevel 				base = FHC_DTAG_BASE(cpa->cpu_id);
315329949e86Sstevel 			if (cpb != NULL)
315429949e86Sstevel 				base = FHC_DTAG_BASE(cpb->cpu_id);
315529949e86Sstevel 			ASSERT(base != 0);
315629949e86Sstevel 
315729949e86Sstevel 			for (i = 0; i < FHC_DTAG_SIZE; i += FHC_DTAG_SKIP) {
315829949e86Sstevel 				u_longlong_t value = lddphysio(base+i);
315929949e86Sstevel #ifdef lint
316029949e86Sstevel 				value = value;
316129949e86Sstevel #endif
316229949e86Sstevel #ifdef DEBUG
316329949e86Sstevel 				if (cpa != NULL && (value & FHC_DTAG_LOW))
316429949e86Sstevel 					nonz0++;
316529949e86Sstevel 				if (cpb != NULL && (value & FHC_DTAG_HIGH))
316629949e86Sstevel 					nonz1++;
316729949e86Sstevel #endif
316829949e86Sstevel 				/* always clear the dtags */
316929949e86Sstevel 				stdphysio(base + i, 0ull);
317029949e86Sstevel 			}
317129949e86Sstevel #ifdef DEBUG
317229949e86Sstevel 			if (nonz0 || nonz1) {
317329949e86Sstevel 				cmn_err(CE_NOTE, "!dtag results: "
317429949e86Sstevel 				    "cpua valid %d, cpub valid %d",
317529949e86Sstevel 				    nonz0, nonz1);
317629949e86Sstevel 			}
317729949e86Sstevel #endif
317829949e86Sstevel 		}
317929949e86Sstevel 
318029949e86Sstevel 		break;
318129949e86Sstevel 
318229949e86Sstevel 	default:
318329949e86Sstevel 		break;
318429949e86Sstevel 	}
318529949e86Sstevel 
318629949e86Sstevel 	return (error);
318729949e86Sstevel }
318829949e86Sstevel 
318929949e86Sstevel /*
319029949e86Sstevel  * platform code for shutting down cpus.
319129949e86Sstevel  */
319229949e86Sstevel int
319329949e86Sstevel fhc_cpu_poweroff(struct cpu *cp)
319429949e86Sstevel {
319529949e86Sstevel 	int board;
319629949e86Sstevel 	fhc_bd_t *bd_list;
319729949e86Sstevel 	int delays;
319829949e86Sstevel 	extern void idle_stop_xcall(void);
319929949e86Sstevel 
320029949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
320129949e86Sstevel 	ASSERT((cp->cpu_flags & (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED)) ==
320229949e86Sstevel 	    (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED));
320329949e86Sstevel 
320429949e86Sstevel 	/*
320529949e86Sstevel 	 * Lock the board so that we can safely access the
320629949e86Sstevel 	 * registers. This cannot be done inside the pause_cpus().
320729949e86Sstevel 	 */
320829949e86Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
320929949e86Sstevel 	bd_list = fhc_bdlist_lock(board);
321029949e86Sstevel 	ASSERT(fhc_bd_valid(board) && (bd_list->sc.type == CPU_BOARD));
321129949e86Sstevel 
321229949e86Sstevel 	/*
321329949e86Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
321429949e86Sstevel 	 * crosscalls to the detaching proc until it has cleared its
321529949e86Sstevel 	 * bit in cpu_ready_set.
321629949e86Sstevel 	 *
321729949e86Sstevel 	 * The CPU's remain paused and the prom_mutex is known to be free.
321829949e86Sstevel 	 * This prevents the x-trap victim from blocking when doing prom
321929949e86Sstevel 	 * IEEE-1275 calls at a high PIL level.
322029949e86Sstevel 	 */
322129949e86Sstevel 	promsafe_pause_cpus();
322229949e86Sstevel 
322329949e86Sstevel 	/*
322429949e86Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
322529949e86Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
322629949e86Sstevel 	 * prevent it from receiving cross calls and cross traps.
322729949e86Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
322829949e86Sstevel 	 */
322929949e86Sstevel 	mp_cpu_quiesce(cp);
323029949e86Sstevel 
323129949e86Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
323229949e86Sstevel 	    (uint64_t)fhc_cpu_shutdown_self, (uint64_t)NULL);
323329949e86Sstevel 
323429949e86Sstevel 	/*
323529949e86Sstevel 	 * Wait for slave cpu to shutdown.
323629949e86Sstevel 	 * Sense this by watching the hardware EPDx bit.
323729949e86Sstevel 	 */
323829949e86Sstevel 	for (delays = FHC_SHUTDOWN_WAIT_MSEC; delays != 0; delays--) {
323929949e86Sstevel 		uint_t temp;
324029949e86Sstevel 
324129949e86Sstevel 		DELAY(1000);
324229949e86Sstevel 
324329949e86Sstevel 		/* get the current cpu power status */
324429949e86Sstevel 		temp = *bd_list->softsp->ctrl;
324529949e86Sstevel 
324629949e86Sstevel 		/* has the cpu actually signalled shutdown? */
324729949e86Sstevel 		if (FHC_CPU_IS_A(cp->cpu_id)) {
324829949e86Sstevel 			if (temp & FHC_EPDA_OFF)
324929949e86Sstevel 				break;
325029949e86Sstevel 		} else {
325129949e86Sstevel 			if (temp & FHC_EPDB_OFF)
325229949e86Sstevel 				break;
325329949e86Sstevel 		}
325429949e86Sstevel 	}
325529949e86Sstevel 
325629949e86Sstevel 	start_cpus();
325729949e86Sstevel 
325829949e86Sstevel 	fhc_bdlist_unlock();
325929949e86Sstevel 
326029949e86Sstevel 	/* A timeout means we've lost control of the cpu. */
326129949e86Sstevel 	if (delays == 0)
326229949e86Sstevel 		panic("Processor %d failed during shutdown", cp->cpu_id);
326329949e86Sstevel 
326429949e86Sstevel 	return (0);
326529949e86Sstevel }
326629949e86Sstevel 
326729949e86Sstevel /*
326829949e86Sstevel  * shutdown_self
326929949e86Sstevel  * slave side shutdown.  clean up and execute the shutdown sequence.
327029949e86Sstevel  */
327129949e86Sstevel static void
327229949e86Sstevel fhc_cpu_shutdown_self(void)
327329949e86Sstevel {
327429949e86Sstevel 	extern void flush_windows(void);
327529949e86Sstevel 
327629949e86Sstevel 	flush_windows();
327729949e86Sstevel 
327829949e86Sstevel 	ASSERT(CPU->cpu_intr_actv == 0);
327929949e86Sstevel 	ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread ||
328029949e86Sstevel 	    CPU->cpu_thread == CPU->cpu_startup_thread);
328129949e86Sstevel 
328229949e86Sstevel 	CPU->cpu_flags = CPU_POWEROFF | CPU_OFFLINE | CPU_QUIESCED;
328329949e86Sstevel 
328429949e86Sstevel 	(void) prom_sunfire_cpu_off();	/* inform Ultra Enterprise prom */
328529949e86Sstevel 
328629949e86Sstevel 	os_completes_shutdown();
328729949e86Sstevel 
328829949e86Sstevel 	panic("fhc_cpu_shutdown_self: cannot return");
328929949e86Sstevel 	/*NOTREACHED*/
329029949e86Sstevel }
329129949e86Sstevel 
329229949e86Sstevel /*
329329949e86Sstevel  * Warm start CPU.
329429949e86Sstevel  */
329529949e86Sstevel static int
329629949e86Sstevel fhc_cpu_start(struct cpu *cp)
329729949e86Sstevel {
329829949e86Sstevel 	int rv;
329929949e86Sstevel 	int cpuid = cp->cpu_id;
330029949e86Sstevel 	pnode_t nodeid;
330129949e86Sstevel 	extern void restart_other_cpu(int);
330229949e86Sstevel 
330329949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
330429949e86Sstevel 
330529949e86Sstevel 	/* power on cpu */
330629949e86Sstevel 	nodeid = cpunodes[cpuid].nodeid;
330729949e86Sstevel 	ASSERT(nodeid != (pnode_t)0);
330829949e86Sstevel 	rv = prom_wakeupcpu(nodeid);
330929949e86Sstevel 	if (rv != 0) {
331029949e86Sstevel 		cmn_err(CE_WARN, "Processor %d failed to power on.", cpuid);
331129949e86Sstevel 		return (EBUSY);
331229949e86Sstevel 	}
331329949e86Sstevel 
331429949e86Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
331529949e86Sstevel 
331629949e86Sstevel 	/*
331729949e86Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the slave cpu start.
331829949e86Sstevel 	 * This helps to quiesce the bus traffic a bit which makes
331929949e86Sstevel 	 * the tick sync routine in the prom more robust.
332029949e86Sstevel 	 */
332129949e86Sstevel 	restart_other_cpu(cpuid);
332229949e86Sstevel 
332329949e86Sstevel 	return (0);
332429949e86Sstevel }
332529949e86Sstevel 
332629949e86Sstevel /*
332729949e86Sstevel  * Power on CPU.
332829949e86Sstevel  */
332929949e86Sstevel int
333029949e86Sstevel fhc_cpu_poweron(struct cpu *cp)
333129949e86Sstevel {
333229949e86Sstevel 	fhc_bd_t *bd_list;
333329949e86Sstevel 	enum temp_state state;
333429949e86Sstevel 	int board;
333529949e86Sstevel 	int status;
333629949e86Sstevel 	int status_other;
333729949e86Sstevel 	struct cpu *cp_other;
333829949e86Sstevel 
333929949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
334029949e86Sstevel 	ASSERT(cpu_is_poweredoff(cp));
334129949e86Sstevel 
334229949e86Sstevel 	/* do not power on overtemperature cpu */
334329949e86Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
334429949e86Sstevel 	bd_list = fhc_bdlist_lock(board);
334529949e86Sstevel 
334629949e86Sstevel 	ASSERT(bd_list != NULL);
334729949e86Sstevel 	ASSERT(bd_list->sc.type == CPU_BOARD);
334829949e86Sstevel 	ASSERT(bd_list->dev_softsp != NULL);
334929949e86Sstevel 
335029949e86Sstevel 	state = ((struct environ_soft_state *)
335129949e86Sstevel 	    bd_list->dev_softsp)->tempstat.state;
335229949e86Sstevel 
335329949e86Sstevel 	fhc_bdlist_unlock();
335429949e86Sstevel 	if ((state == TEMP_WARN) || (state == TEMP_DANGER))
335529949e86Sstevel 		return (EBUSY);
335629949e86Sstevel 
335729949e86Sstevel 	status = fhc_cpu_start(cp);
335829949e86Sstevel 
335929949e86Sstevel 	/* policy for dual cpu boards */
336029949e86Sstevel 
336129949e86Sstevel 	if ((status == 0) &&
336229949e86Sstevel 	    ((cp_other = cpu_get(FHC_OTHER_CPU_ID(cp->cpu_id))) != NULL)) {
336329949e86Sstevel 		/*
336429949e86Sstevel 		 * Do not leave board's other cpu idling in the prom.
336529949e86Sstevel 		 * Start the other cpu and set its state to P_OFFLINE.
336629949e86Sstevel 		 */
336729949e86Sstevel 		status_other = fhc_cpu_start(cp_other);
336829949e86Sstevel 		if (status_other != 0) {
336929949e86Sstevel 			panic("fhc: failed to start second CPU"
337029949e86Sstevel 			    " in pair %d & %d, error %d",
337129949e86Sstevel 			    cp->cpu_id, cp_other->cpu_id, status_other);
337229949e86Sstevel 		}
337329949e86Sstevel 	}
337429949e86Sstevel 
337529949e86Sstevel 	return (status);
337629949e86Sstevel }
337729949e86Sstevel 
337829949e86Sstevel /*
337929949e86Sstevel  * complete the shutdown sequence in case the firmware doesn't.
338029949e86Sstevel  *
338129949e86Sstevel  * If the firmware returns, then complete the shutdown code.
338229949e86Sstevel  * (sunfire firmware presently only updates its status.  the
338329949e86Sstevel  * OS must flush the D-tags and execute the shutdown instruction.)
338429949e86Sstevel  */
338529949e86Sstevel static void
338629949e86Sstevel os_completes_shutdown(void)
338729949e86Sstevel {
338829949e86Sstevel 	pfn_t 			pfn;
338929949e86Sstevel 	tte_t			tte;
339029949e86Sstevel 	volatile uint_t		*src;
339129949e86Sstevel 	volatile uint_t		*dst;
339229949e86Sstevel 	caddr_t			copy_addr;
339329949e86Sstevel 	extern void fhc_shutdown_asm(u_longlong_t, int);
339429949e86Sstevel 	extern void fhc_shutdown_asm_end(void);
339529949e86Sstevel 
339629949e86Sstevel 	copy_addr = shutdown_va + FHC_SRAM_OS_OFFSET;
339729949e86Sstevel 
339829949e86Sstevel 	/* compute sram global address for this operation */
339929949e86Sstevel 	pfn = FHC_LOCAL_OS_PAGEBASE >> MMU_PAGESHIFT;
340029949e86Sstevel 
340129949e86Sstevel 	/* force load i and d translations */
340229949e86Sstevel 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
340329949e86Sstevel 	    TTE_PFN_INTHI(pfn);
340429949e86Sstevel 	tte.tte_intlo = TTE_PFN_INTLO(pfn) |
340529949e86Sstevel 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT; /* un$ */
34061e2e7a75Shuah 	sfmmu_dtlb_ld_kva(shutdown_va, &tte);	/* load dtlb */
34071e2e7a75Shuah 	sfmmu_itlb_ld_kva(shutdown_va, &tte);	/* load itlb */
340829949e86Sstevel 
340929949e86Sstevel 	/*
341029949e86Sstevel 	 * copy the special shutdown function to sram
341129949e86Sstevel 	 * (this is a special integer copy that synchronizes with localspace
341229949e86Sstevel 	 * accesses.  we need special throttling to ensure copy integrity)
341329949e86Sstevel 	 */
341429949e86Sstevel 	for (src = (uint_t *)fhc_shutdown_asm, dst = (uint_t *)copy_addr;
341529949e86Sstevel 	    src < (uint_t *)fhc_shutdown_asm_end;
341629949e86Sstevel 	    src++, dst++) {
341729949e86Sstevel 		volatile uint_t dummy;
341829949e86Sstevel 
341929949e86Sstevel 		*dst = *src;
342029949e86Sstevel 		/*
342129949e86Sstevel 		 * ensure non corrupting single write operations to
342229949e86Sstevel 		 * localspace sram by interleaving reads with writes.
342329949e86Sstevel 		 */
342429949e86Sstevel 		dummy = *dst;
342529949e86Sstevel #ifdef lint
342629949e86Sstevel 		dummy = dummy;
342729949e86Sstevel #endif
342829949e86Sstevel 	}
342929949e86Sstevel 
343029949e86Sstevel 	/*
343129949e86Sstevel 	 * Call the shutdown sequencer.
343229949e86Sstevel 	 * NOTE: the base flush address must be unique for each MID.
343329949e86Sstevel 	 */
343429949e86Sstevel 	((void (*)(u_longlong_t, int))copy_addr)(
343529949e86Sstevel 	    FHC_BASE_NOMEM + CPU->cpu_id * FHC_MAX_ECACHE_SIZE,
343629949e86Sstevel 	    cpunodes[CPU->cpu_id].ecache_size);
343729949e86Sstevel }
343829949e86Sstevel 
343929949e86Sstevel enum temp_state
344029949e86Sstevel fhc_env_temp_state(int board)
344129949e86Sstevel {
344229949e86Sstevel 	fhc_bd_t *bdp;
344329949e86Sstevel 	struct environ_soft_state *envp;
344429949e86Sstevel 
344529949e86Sstevel 	ASSERT(fhc_bd_valid(board));
344629949e86Sstevel 
344729949e86Sstevel 	bdp = fhc_bd(board);
344829949e86Sstevel 
344929949e86Sstevel 	/*
345029949e86Sstevel 	 * Due to asynchronous attach of environ, environ may
345129949e86Sstevel 	 * not be attached by the time we start calling this routine
345229949e86Sstevel 	 * to check the temperature state.  Environ not attaching is
345329949e86Sstevel 	 * pathological so this will only cover the time between
345429949e86Sstevel 	 * board connect and environ attach.
345529949e86Sstevel 	 */
345629949e86Sstevel 	if (!bdp->dev_softsp) {
345729949e86Sstevel 		return (TEMP_OK);
345829949e86Sstevel 	}
345929949e86Sstevel 	envp = (struct environ_soft_state *)bdp->dev_softsp;
346029949e86Sstevel 
346129949e86Sstevel 	return (envp->tempstat.state);
346229949e86Sstevel }
346329949e86Sstevel 
346429949e86Sstevel static void
346529949e86Sstevel fhc_tod_fault(enum tod_fault_type tod_bad)
346629949e86Sstevel {
346729949e86Sstevel 	int board_num = 0;
346829949e86Sstevel 	enum ft_class class = FT_SYSTEM;
346929949e86Sstevel 	uint64_t addr;
347029949e86Sstevel 
347129949e86Sstevel 	addr = (va_to_pa((void *)v_eeprom_addr)) >> BOARD_PHYADDR_SHIFT;
347229949e86Sstevel 
347329949e86Sstevel 	if ((addr & CLOCKBOARD_PHYADDR_BITS) != CLOCKBOARD_PHYADDR_BITS) {
347429949e86Sstevel 		/* if tod is not on clock board, */
347529949e86Sstevel 		/* it'd be on one of io boards */
347629949e86Sstevel 		board_num = (addr >> IO_BOARD_NUMBER_SHIFT)
347729949e86Sstevel 		    & IO_BOARD_NUMBER_MASK;
347829949e86Sstevel 		class = FT_BOARD;
347929949e86Sstevel 	}
348029949e86Sstevel 
348129949e86Sstevel 	switch (tod_bad) {
348229949e86Sstevel 	case TOD_NOFAULT:
348329949e86Sstevel 		clear_fault(board_num, FT_TODFAULT, class);
348429949e86Sstevel 		break;
348529949e86Sstevel 	case TOD_REVERSED:
348629949e86Sstevel 	case TOD_STALLED:
348729949e86Sstevel 	case TOD_JUMPED:
348829949e86Sstevel 	case TOD_RATECHANGED:
348929949e86Sstevel 		reg_fault(board_num, FT_TODFAULT, class);
349029949e86Sstevel 		break;
349129949e86Sstevel 	default:
349229949e86Sstevel 		break;
349329949e86Sstevel 	}
349429949e86Sstevel }
3495