xref: /illumos-gate/usr/src/uts/sun4u/sunfire/io/fhc.c (revision 50ed1c1e)
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);
978682d1efSRichard Lowe static void fhc_cpu_shutdown_self(void);
988682d1efSRichard 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 
248*50ed1c1eSToomas Soome 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 */
401*50ed1c1eSToomas Soome 	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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)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
fhc_handle_imr(struct fhc_soft_state * softsp)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: "
55219397407SSherry Moore 			    "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
check_central(int board)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
fhc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)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
fhc_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)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
fhc_board_type(struct fhc_soft_state * softsp,int board)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) ||
77319397407SSherry Moore 		    (*(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
fhc_unmap_regs(struct fhc_soft_state * softsp)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,
79719397407SSherry Moore 		    (caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
79819397407SSherry Moore 		    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,
80319397407SSherry Moore 		    (caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
80419397407SSherry Moore 		    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,
80919397407SSherry Moore 		    (caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
81019397407SSherry Moore 		    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,
81519397407SSherry Moore 		    (caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
81619397407SSherry Moore 		    0, 0);
81729949e86Sstevel 		softsp->intr_regs[FHC_TOD_INO].mapping_reg = NULL;
81829949e86Sstevel 	}
81929949e86Sstevel }
82029949e86Sstevel 
82129949e86Sstevel static int
fhc_init(struct fhc_soft_state * softsp)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 "
83719397407SSherry Moore 		    "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) +
84619397407SSherry Moore 	    FHC_OFF_RCTRL);
84729949e86Sstevel 	softsp->ctrl = (uint_t *)((char *)(softsp->id) +
84819397407SSherry Moore 	    FHC_OFF_CTRL);
84929949e86Sstevel 	softsp->bsr = (uint_t *)((char *)(softsp->id) +
85019397407SSherry Moore 	    FHC_OFF_BSR);
85129949e86Sstevel 	softsp->jtag_ctrl = (uint_t *)((char *)(softsp->id) +
85219397407SSherry Moore 	    FHC_OFF_JTAG_CTRL);
85329949e86Sstevel 	softsp->jt_master.jtag_cmd = (uint_t *)((char *)(softsp->id) +
85419397407SSherry Moore 	    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 "
86019397407SSherry Moore 		    "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 "
87319397407SSherry Moore 		    "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 "
88219397407SSherry Moore 		    "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 "
89119397407SSherry Moore 		    "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 "
90019397407SSherry Moore 		    "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 =
90819397407SSherry Moore 		    (uint_t *)((char *)(softsp->intr_regs[i].mapping_reg) +
90919397407SSherry Moore 		    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
fhc_intr_wrapper(caddr_t arg)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
fhc_zs_intr_wrapper(caddr_t arg)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
fhc_add_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)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
fhc_remove_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)132329949e86Sstevel fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
1324*50ed1c1eSToomas Soome     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 *)
133019397407SSherry Moore 	    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"
136119397407SSherry Moore 			    " 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
fhc_intr_ops(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t intr_op,ddi_intr_handle_impl_t * hdlp,void * result)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
fhc_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)149229949e86Sstevel fhc_ctlops(dev_info_t *dip, dev_info_t *rdip,
1493*50ed1c1eSToomas Soome     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
fhc_xlate_intrs(ddi_intr_handle_impl_t * hdlp,uint32_t ign)153129949e86Sstevel fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign)
153229949e86Sstevel {
153329949e86Sstevel 	uint32_t mondo;
153429949e86Sstevel 
153529949e86Sstevel 	mondo = hdlp->ih_vector;
153629949e86Sstevel 
153729949e86Sstevel 	hdlp->ih_vector = (mondo | ign);
153829949e86Sstevel 	if (hdlp->ih_pri == 0)
153929949e86Sstevel 		hdlp->ih_pri = fhc_int_priorities[FHC_INO(mondo)];
154029949e86Sstevel }
154129949e86Sstevel 
154229949e86Sstevel static int
fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd,peekpoke_ctlops_t * in_args,void * result)154329949e86Sstevel fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args,
154429949e86Sstevel     void *result)
154529949e86Sstevel {
154629949e86Sstevel 	int err = DDI_SUCCESS;
154729949e86Sstevel 	on_trap_data_t otd;
154829949e86Sstevel 
154929949e86Sstevel 	/* No safe access except for peek/poke is supported. */
155029949e86Sstevel 	if (in_args->handle != NULL)
155129949e86Sstevel 		return (DDI_FAILURE);
155229949e86Sstevel 
155329949e86Sstevel 	/* Set up protected environment. */
155429949e86Sstevel 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
155529949e86Sstevel 		uintptr_t tramp = otd.ot_trampoline;
155629949e86Sstevel 
155729949e86Sstevel 		if (cmd == DDI_CTLOPS_POKE) {
155829949e86Sstevel 			otd.ot_trampoline = (uintptr_t)&poke_fault;
155929949e86Sstevel 			err = do_poke(in_args->size, (void *)in_args->dev_addr,
156029949e86Sstevel 			    (void *)in_args->host_addr);
156129949e86Sstevel 		} else {
156229949e86Sstevel 			otd.ot_trampoline = (uintptr_t)&peek_fault;
156329949e86Sstevel 			err = do_peek(in_args->size, (void *)in_args->dev_addr,
156429949e86Sstevel 			    (void *)in_args->host_addr);
156529949e86Sstevel 			result = (void *)in_args->host_addr;
156629949e86Sstevel 		}
156729949e86Sstevel 		otd.ot_trampoline = tramp;
156829949e86Sstevel 	} else
156929949e86Sstevel 		err = DDI_FAILURE;
157029949e86Sstevel 
157129949e86Sstevel 	/* Take down protected environment. */
157229949e86Sstevel 	no_trap();
157329949e86Sstevel 
157429949e86Sstevel 	return (err);
157529949e86Sstevel }
157629949e86Sstevel 
157729949e86Sstevel /*
157829949e86Sstevel  * This function initializes the temperature arrays for use. All
157929949e86Sstevel  * temperatures are set in to invalid value to start.
158029949e86Sstevel  */
158129949e86Sstevel void
init_temp_arrays(struct temp_stats * envstat)158229949e86Sstevel init_temp_arrays(struct temp_stats *envstat)
158329949e86Sstevel {
158429949e86Sstevel 	int i;
158529949e86Sstevel 
158629949e86Sstevel 	envstat->index = 0;
158729949e86Sstevel 
158829949e86Sstevel 	for (i = 0; i < L1_SZ; i++) {
158929949e86Sstevel 		envstat->l1[i] = NA_TEMP;
159029949e86Sstevel 	}
159129949e86Sstevel 
159229949e86Sstevel 	for (i = 0; i < L2_SZ; i++) {
159329949e86Sstevel 		envstat->l2[i] = NA_TEMP;
159429949e86Sstevel 	}
159529949e86Sstevel 
159629949e86Sstevel 	for (i = 0; i < L3_SZ; i++) {
159729949e86Sstevel 		envstat->l3[i] = NA_TEMP;
159829949e86Sstevel 	}
159929949e86Sstevel 
160029949e86Sstevel 	for (i = 0; i < L4_SZ; i++) {
160129949e86Sstevel 		envstat->l4[i] = NA_TEMP;
160229949e86Sstevel 	}
160329949e86Sstevel 
160429949e86Sstevel 	for (i = 0; i < L5_SZ; i++) {
160529949e86Sstevel 		envstat->l5[i] = NA_TEMP;
160629949e86Sstevel 	}
160729949e86Sstevel 
160829949e86Sstevel 	envstat->max = NA_TEMP;
160929949e86Sstevel 	envstat->min = NA_TEMP;
161029949e86Sstevel 	envstat->trend = TREND_UNKNOWN;
161129949e86Sstevel 	envstat->version = TEMP_KSTAT_VERSION;
161229949e86Sstevel 	envstat->override = NA_TEMP;
161329949e86Sstevel }
161429949e86Sstevel 
161529949e86Sstevel /* Inhibit warning messages below this temperature, eg for CPU poweron. */
161629949e86Sstevel static uint_t fhc_cpu_warning_temp_threshold = FHC_CPU_WARNING_TEMP_THRESHOLD;
161729949e86Sstevel 
161829949e86Sstevel /*
161929949e86Sstevel  * This function manages the temperature history in the temperature
162029949e86Sstevel  * statistics buffer passed in. It calls the temperature calibration
162129949e86Sstevel  * routines and maintains the time averaged temperature data.
162229949e86Sstevel  */
162329949e86Sstevel void
update_temp(dev_info_t * pdip,struct temp_stats * envstat,uchar_t value)162429949e86Sstevel update_temp(dev_info_t *pdip, struct temp_stats *envstat, uchar_t value)
162529949e86Sstevel {
162629949e86Sstevel 	uint_t index;		    /* The absolute temperature counter */
162729949e86Sstevel 	uint_t tmp_index;	    /* temp index into upper level array */
162829949e86Sstevel 	int count;		    /* Count of non-zero values in array */
162929949e86Sstevel 	int total;		    /* sum total of non-zero values in array */
163029949e86Sstevel 	short real_temp;	    /* calibrated temperature */
163129949e86Sstevel 	int i;
163229949e86Sstevel 	struct fhc_soft_state *softsp;
163329949e86Sstevel 	char buffer[256];	    /* buffer for warning of overtemp */
163429949e86Sstevel 	enum temp_state temp_state; /* Temperature state */
163529949e86Sstevel 
163629949e86Sstevel 	/*
163729949e86Sstevel 	 * NOTE: This global counter is not protected since we're called
163829949e86Sstevel 	 * serially for each board.
163929949e86Sstevel 	 */
164029949e86Sstevel 	static int shutdown_msg = 0; /* Flag if shutdown warning issued */
164129949e86Sstevel 
164229949e86Sstevel 	/* determine soft state pointer of parent */
164329949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(pdip));
164429949e86Sstevel 
164529949e86Sstevel 	envstat->index++;
164629949e86Sstevel 	index = envstat->index;
164729949e86Sstevel 
164829949e86Sstevel 	/*
164929949e86Sstevel 	 * You need to update the level 5 intervals first, since
165029949e86Sstevel 	 * they are based on the data from the level 4 intervals,
165129949e86Sstevel 	 * and so on, down to the level 1 intervals.
165229949e86Sstevel 	 */
165329949e86Sstevel 
165429949e86Sstevel 	/* update the level 5 intervals if it is time */
165529949e86Sstevel 	if (((tmp_index = L5_INDEX(index)) > 0) && (L5_REM(index) == 0)) {
165629949e86Sstevel 		/* Generate the index within the level 5 array */
165729949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
165829949e86Sstevel 		tmp_index = tmp_index % L5_SZ;
165929949e86Sstevel 
166029949e86Sstevel 		/* take an average of the level 4 array */
166129949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L4_SZ; i++) {
166229949e86Sstevel 			/* Do not include zero values in average */
166329949e86Sstevel 			if (envstat->l4[i] != NA_TEMP) {
166429949e86Sstevel 				total += (int)envstat->l4[i];
166529949e86Sstevel 				count++;
166629949e86Sstevel 			}
166729949e86Sstevel 		}
166829949e86Sstevel 
166929949e86Sstevel 		/*
167029949e86Sstevel 		 * If there were any level 4 data points to average,
167129949e86Sstevel 		 * do so.
167229949e86Sstevel 		 */
167329949e86Sstevel 		if (count != 0) {
167429949e86Sstevel 			envstat->l5[tmp_index] = total/count;
167529949e86Sstevel 		} else {
167629949e86Sstevel 			envstat->l5[tmp_index] = NA_TEMP;
167729949e86Sstevel 		}
167829949e86Sstevel 	}
167929949e86Sstevel 
168029949e86Sstevel 	/* update the level 4 intervals if it is time */
168129949e86Sstevel 	if (((tmp_index = L4_INDEX(index)) > 0) && (L4_REM(index) == 0)) {
168229949e86Sstevel 		/* Generate the index within the level 4 array */
168329949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
168429949e86Sstevel 		tmp_index = tmp_index % L4_SZ;
168529949e86Sstevel 
168629949e86Sstevel 		/* take an average of the level 3 array */
168729949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L3_SZ; i++) {
168829949e86Sstevel 			/* Do not include zero values in average */
168929949e86Sstevel 			if (envstat->l3[i] != NA_TEMP) {
169029949e86Sstevel 				total += (int)envstat->l3[i];
169129949e86Sstevel 				count++;
169229949e86Sstevel 			}
169329949e86Sstevel 		}
169429949e86Sstevel 
169529949e86Sstevel 		/*
169629949e86Sstevel 		 * If there were any level 3 data points to average,
169729949e86Sstevel 		 * do so.
169829949e86Sstevel 		 */
169929949e86Sstevel 		if (count != 0) {
170029949e86Sstevel 			envstat->l4[tmp_index] = total/count;
170129949e86Sstevel 		} else {
170229949e86Sstevel 			envstat->l4[tmp_index] = NA_TEMP;
170329949e86Sstevel 		}
170429949e86Sstevel 	}
170529949e86Sstevel 
170629949e86Sstevel 	/* update the level 3 intervals if it is time */
170729949e86Sstevel 	if (((tmp_index = L3_INDEX(index)) > 0) && (L3_REM(index) == 0)) {
170829949e86Sstevel 		/* Generate the index within the level 3 array */
170929949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
171029949e86Sstevel 		tmp_index = tmp_index % L3_SZ;
171129949e86Sstevel 
171229949e86Sstevel 		/* take an average of the level 2 array */
171329949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L2_SZ; i++) {
171429949e86Sstevel 			/* Do not include zero values in average */
171529949e86Sstevel 			if (envstat->l2[i] != NA_TEMP) {
171629949e86Sstevel 				total += (int)envstat->l2[i];
171729949e86Sstevel 				count++;
171829949e86Sstevel 			}
171929949e86Sstevel 		}
172029949e86Sstevel 
172129949e86Sstevel 		/*
172229949e86Sstevel 		 * If there were any level 2 data points to average,
172329949e86Sstevel 		 * do so.
172429949e86Sstevel 		 */
172529949e86Sstevel 		if (count != 0) {
172629949e86Sstevel 			envstat->l3[tmp_index] = total/count;
172729949e86Sstevel 		} else {
172829949e86Sstevel 			envstat->l3[tmp_index] = NA_TEMP;
172929949e86Sstevel 		}
173029949e86Sstevel 	}
173129949e86Sstevel 
173229949e86Sstevel 	/* update the level 2 intervals if it is time */
173329949e86Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0)) {
173429949e86Sstevel 		/* Generate the index within the level 2 array */
173529949e86Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
173629949e86Sstevel 		tmp_index = tmp_index % L2_SZ;
173729949e86Sstevel 
173829949e86Sstevel 		/* take an average of the level 1 array */
173929949e86Sstevel 		for (i = 0, count = 0, total = 0; i < L1_SZ; i++) {
174029949e86Sstevel 			/* Do not include zero values in average */
174129949e86Sstevel 			if (envstat->l1[i] != NA_TEMP) {
174229949e86Sstevel 				total += (int)envstat->l1[i];
174329949e86Sstevel 				count++;
174429949e86Sstevel 			}
174529949e86Sstevel 		}
174629949e86Sstevel 
174729949e86Sstevel 		/*
174829949e86Sstevel 		 * If there were any level 1 data points to average,
174929949e86Sstevel 		 * do so.
175029949e86Sstevel 		 */
175129949e86Sstevel 		if (count != 0) {
175229949e86Sstevel 			envstat->l2[tmp_index] = total/count;
175329949e86Sstevel 		} else {
175429949e86Sstevel 			envstat->l2[tmp_index] = NA_TEMP;
175529949e86Sstevel 		}
175629949e86Sstevel 	}
175729949e86Sstevel 
175829949e86Sstevel 	/* determine the current temperature in degrees Celcius */
175929949e86Sstevel 	if (envstat->override != NA_TEMP) {
176029949e86Sstevel 		/* use override temperature for this board */
176129949e86Sstevel 		real_temp = envstat->override;
176229949e86Sstevel 	} else {
176329949e86Sstevel 		/* Run the calibration function using this board type */
176429949e86Sstevel 		real_temp = calibrate_temp(softsp->list->sc.type, value,
176519397407SSherry Moore 		    softsp->list->sc.ac_compid);
176629949e86Sstevel 	}
176729949e86Sstevel 
176829949e86Sstevel 	envstat->l1[index % L1_SZ] = real_temp;
176929949e86Sstevel 
177029949e86Sstevel 	/* check if the temperature state for this device needs to change */
177129949e86Sstevel 	temp_state = get_temp_state(softsp->list->sc.type, real_temp,
177219397407SSherry Moore 	    softsp->list->sc.board);
177329949e86Sstevel 
177429949e86Sstevel 	/* has the state changed? Then get the board string ready */
177529949e86Sstevel 	if (temp_state != envstat->state) {
177629949e86Sstevel 		int board = softsp->list->sc.board;
177729949e86Sstevel 		enum board_type type = softsp->list->sc.type;
177829949e86Sstevel 
177929949e86Sstevel 		build_bd_display_str(buffer, type, board);
178029949e86Sstevel 
178129949e86Sstevel 		if (temp_state > envstat->state) {
178229949e86Sstevel 			if (envstat->state == TEMP_OK) {
178329949e86Sstevel 				if (type == CLOCK_BOARD) {
178429949e86Sstevel 					reg_fault(0, FT_OVERTEMP, FT_SYSTEM);
178529949e86Sstevel 				} else {
178629949e86Sstevel 					reg_fault(board, FT_OVERTEMP,
178719397407SSherry Moore 					    FT_BOARD);
178829949e86Sstevel 				}
178929949e86Sstevel 			}
179029949e86Sstevel 
179129949e86Sstevel 			/* heating up, change state now */
179229949e86Sstevel 			envstat->temp_cnt = 0;
179329949e86Sstevel 			envstat->state = temp_state;
179429949e86Sstevel 
179529949e86Sstevel 			if (temp_state == TEMP_WARN) {
179629949e86Sstevel 				/* now warn the user of the problem */
179729949e86Sstevel 				cmn_err(CE_WARN,
179819397407SSherry Moore 				    "%s is warm (temperature: %dC). "
179919397407SSherry Moore 				    "Please check system cooling", buffer,
180019397407SSherry Moore 				    real_temp);
180129949e86Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_OVERTEMP);
180229949e86Sstevel 				if (temperature_chamber == -1)
180329949e86Sstevel 					temperature_chamber =
180429949e86Sstevel 					    check_for_chamber();
180529949e86Sstevel 			} else if (temp_state == TEMP_DANGER) {
180629949e86Sstevel 				cmn_err(CE_WARN,
180719397407SSherry Moore 				    "%s is very hot (temperature: %dC)",
180819397407SSherry Moore 				    buffer, real_temp);
180929949e86Sstevel 
181029949e86Sstevel 				envstat->shutdown_cnt = 1;
181129949e86Sstevel 				if (temperature_chamber == -1)
181229949e86Sstevel 					temperature_chamber =
181319397407SSherry Moore 					    check_for_chamber();
181429949e86Sstevel 				if ((temperature_chamber == 0) &&
181529949e86Sstevel 				    enable_overtemp_powerdown) {
181629949e86Sstevel 					/*
181729949e86Sstevel 					 * NOTE: The "%d seconds" is not
181829949e86Sstevel 					 * necessarily accurate in the case
181929949e86Sstevel 					 * where we have multiple boards
182029949e86Sstevel 					 * overheating and subsequently cooling
182129949e86Sstevel 					 * down.
182229949e86Sstevel 					 */
182329949e86Sstevel 					if (shutdown_msg == 0) {
182429949e86Sstevel 						cmn_err(CE_WARN, "System "
182519397407SSherry Moore 						    "shutdown scheduled "
182619397407SSherry Moore 						    "in %d seconds due to "
182719397407SSherry Moore 						    "over-temperature "
182819397407SSherry Moore 						    "condition on %s",
182919397407SSherry Moore 						    SHUTDOWN_TIMEOUT_SEC,
183019397407SSherry Moore 						    buffer);
183129949e86Sstevel 					}
183229949e86Sstevel 					shutdown_msg++;
183329949e86Sstevel 				}
183429949e86Sstevel 			}
183529949e86Sstevel 
183629949e86Sstevel 			/*
183729949e86Sstevel 			 * If this is a cpu board, power them off.
183829949e86Sstevel 			 */
183929949e86Sstevel 			if (temperature_chamber == 0) {
184029949e86Sstevel 				mutex_enter(&cpu_lock);
184129949e86Sstevel 				(void) fhc_board_poweroffcpus(board, NULL,
184229949e86Sstevel 				    CPU_FORCED);
184329949e86Sstevel 				mutex_exit(&cpu_lock);
184429949e86Sstevel 			}
184529949e86Sstevel 		} else if (temp_state < envstat->state) {
184629949e86Sstevel 			/*
184729949e86Sstevel 			 * Avert the sigpower that would
184829949e86Sstevel 			 * otherwise be sent to init.
184929949e86Sstevel 			 */
185029949e86Sstevel 			envstat->shutdown_cnt = 0;
185129949e86Sstevel 
185229949e86Sstevel 			/* cooling down, use state counter */
185329949e86Sstevel 			if (envstat->temp_cnt == 0) {
185429949e86Sstevel 				envstat->temp_cnt = TEMP_STATE_COUNT;
185529949e86Sstevel 			} else if (--envstat->temp_cnt == 0) {
185629949e86Sstevel 				if (temp_state == TEMP_WARN) {
185729949e86Sstevel 					cmn_err(CE_NOTE,
185819397407SSherry Moore 					    "%s is cooling "
185919397407SSherry Moore 					    "(temperature: %dC)", buffer,
186019397407SSherry Moore 					    real_temp);
186129949e86Sstevel 
186229949e86Sstevel 				} else if (temp_state == TEMP_OK) {
186329949e86Sstevel 					cmn_err(CE_NOTE,
186419397407SSherry Moore 					    "%s has cooled down "
186519397407SSherry Moore 					    "(temperature: %dC), system OK",
186619397407SSherry Moore 					    buffer, real_temp);
186729949e86Sstevel 
186829949e86Sstevel 					if (type == CLOCK_BOARD) {
186929949e86Sstevel 						clear_fault(0, FT_OVERTEMP,
187019397407SSherry Moore 						    FT_SYSTEM);
187129949e86Sstevel 					} else {
187229949e86Sstevel 						clear_fault(board, FT_OVERTEMP,
187319397407SSherry Moore 						    FT_BOARD);
187429949e86Sstevel 					}
187529949e86Sstevel 				}
187629949e86Sstevel 
187729949e86Sstevel 				/*
187829949e86Sstevel 				 * If we just came out of TEMP_DANGER, and
187929949e86Sstevel 				 * a warning was issued about shutting down,
188029949e86Sstevel 				 * let the user know it's been cancelled
188129949e86Sstevel 				 */
188229949e86Sstevel 				if (envstat->state == TEMP_DANGER &&
188329949e86Sstevel 				    (temperature_chamber == 0) &&
188429949e86Sstevel 				    enable_overtemp_powerdown &&
188529949e86Sstevel 				    (powerdown_started == 0) &&
188629949e86Sstevel 				    (--shutdown_msg == 0)) {
188729949e86Sstevel 					cmn_err(CE_NOTE, "System "
188819397407SSherry Moore 					    "shutdown due to over-"
188919397407SSherry Moore 					    "temperature "
189019397407SSherry Moore 					    "condition cancelled");
189129949e86Sstevel 				}
189229949e86Sstevel 				envstat->state = temp_state;
189329949e86Sstevel 
189429949e86Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_TEMP_OK);
189529949e86Sstevel 			}
189629949e86Sstevel 		}
189729949e86Sstevel 	} else {
189829949e86Sstevel 		envstat->temp_cnt = 0;
189929949e86Sstevel 
190029949e86Sstevel 		if (temp_state == TEMP_DANGER) {
190129949e86Sstevel 			if (temperature_chamber == -1) {
190229949e86Sstevel 				temperature_chamber = check_for_chamber();
190329949e86Sstevel 			}
190429949e86Sstevel 
190529949e86Sstevel 			if ((envstat->shutdown_cnt++ >= SHUTDOWN_COUNT) &&
190629949e86Sstevel 			    (temperature_chamber == 0) &&
190729949e86Sstevel 			    enable_overtemp_powerdown &&
190829949e86Sstevel 			    (powerdown_started == 0)) {
190929949e86Sstevel 				powerdown_started = 1;
191029949e86Sstevel 
191129949e86Sstevel 				/* the system is still too hot */
191229949e86Sstevel 				build_bd_display_str(buffer,
191319397407SSherry Moore 				    softsp->list->sc.type,
191419397407SSherry Moore 				    softsp->list->sc.board);
191529949e86Sstevel 
191629949e86Sstevel 				cmn_err(CE_WARN, "%s still too hot "
191719397407SSherry Moore 				    "(temperature: %dC)."
191819397407SSherry Moore 				    " Overtemp shutdown started", buffer,
191919397407SSherry Moore 				    real_temp);
192029949e86Sstevel 
192129949e86Sstevel 				fhc_reboot();
192229949e86Sstevel 			}
192329949e86Sstevel 		}
192429949e86Sstevel 	}
192529949e86Sstevel 
192629949e86Sstevel 	/* update the maximum and minimum temperatures if necessary */
192729949e86Sstevel 	if ((envstat->max == NA_TEMP) || (real_temp > envstat->max)) {
192829949e86Sstevel 		envstat->max = real_temp;
192929949e86Sstevel 	}
193029949e86Sstevel 
193129949e86Sstevel 	if ((envstat->min == NA_TEMP) || (real_temp < envstat->min)) {
193229949e86Sstevel 		envstat->min = real_temp;
193329949e86Sstevel 	}
193429949e86Sstevel 
193529949e86Sstevel 	/*
193629949e86Sstevel 	 * Update the temperature trend.  Currently, the temperature
193729949e86Sstevel 	 * trend algorithm is based on the level 2 stats.  So, we
193829949e86Sstevel 	 * only need to run every time the level 2 stats get updated.
193929949e86Sstevel 	 */
194029949e86Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0))  {
194129949e86Sstevel 		enum board_type type = softsp->list->sc.type;
194229949e86Sstevel 
194329949e86Sstevel 		envstat->trend = temp_trend(envstat);
194429949e86Sstevel 
194529949e86Sstevel 		/* Issue a warning if the temperature is rising rapidly. */
194629949e86Sstevel 		/* For CPU boards, don't warn if CPUs just powered on. */
194729949e86Sstevel 		if (envstat->trend == TREND_RAPID_RISE &&
194829949e86Sstevel 		    (type != CPU_BOARD || real_temp >
194929949e86Sstevel 		    fhc_cpu_warning_temp_threshold))  {
195029949e86Sstevel 			int board = softsp->list->sc.board;
195129949e86Sstevel 
195229949e86Sstevel 			build_bd_display_str(buffer, type, board);
195329949e86Sstevel 			cmn_err(CE_WARN, "%s temperature is rising rapidly!  "
195419397407SSherry Moore 			    "Current temperature is %dC", buffer,
195519397407SSherry Moore 			    real_temp);
195629949e86Sstevel 		}
195729949e86Sstevel 	}
195829949e86Sstevel }
195929949e86Sstevel 
196029949e86Sstevel #define	PREV_L2_INDEX(x)    ((x) ? ((x) - 1) : (L2_SZ - 1))
196129949e86Sstevel 
196229949e86Sstevel /*
196329949e86Sstevel  * This routine determines if the temp of the device passed in is heating
196429949e86Sstevel  * up, cooling down, or staying stable.
196529949e86Sstevel  */
196629949e86Sstevel enum temp_trend
temp_trend(struct temp_stats * tempstat)196729949e86Sstevel temp_trend(struct temp_stats *tempstat)
196829949e86Sstevel {
196929949e86Sstevel 	int		ii;
197029949e86Sstevel 	uint_t		curr_index;
197129949e86Sstevel 	int		curr_temp;
197229949e86Sstevel 	uint_t		prev_index;
197329949e86Sstevel 	int		prev_temp;
197429949e86Sstevel 	int		trail_temp;
197529949e86Sstevel 	int		delta;
197629949e86Sstevel 	int		read_cnt;
197729949e86Sstevel 	enum temp_trend	result = TREND_STABLE;
197829949e86Sstevel 
197929949e86Sstevel 	if (tempstat == NULL)
198029949e86Sstevel 		return (TREND_UNKNOWN);
198129949e86Sstevel 
198229949e86Sstevel 	curr_index = (L2_INDEX(tempstat->index) - 1) % L2_SZ;
198329949e86Sstevel 	curr_temp = tempstat->l2[curr_index];
198429949e86Sstevel 
198529949e86Sstevel 	/* Count how many temperature readings are available */
198629949e86Sstevel 	prev_index = curr_index;
198729949e86Sstevel 	for (read_cnt = 0; read_cnt < L2_SZ - 1; read_cnt++) {
198829949e86Sstevel 		if (tempstat->l2[prev_index] == NA_TEMP)
198929949e86Sstevel 			break;
199029949e86Sstevel 		prev_index = PREV_L2_INDEX(prev_index);
199129949e86Sstevel 	}
199229949e86Sstevel 
199329949e86Sstevel 	switch (read_cnt) {
199429949e86Sstevel 	case 0:
199529949e86Sstevel 	case 1:
199629949e86Sstevel 		result = TREND_UNKNOWN;
199729949e86Sstevel 		break;
199829949e86Sstevel 
199929949e86Sstevel 	default:
200029949e86Sstevel 		delta = curr_temp - tempstat->l2[PREV_L2_INDEX(curr_index)];
200129949e86Sstevel 		prev_index = curr_index;
200229949e86Sstevel 		trail_temp = prev_temp = curr_temp;
200329949e86Sstevel 		if (delta >= RAPID_RISE_THRESH) {	    /* rapid rise? */
200429949e86Sstevel 			result = TREND_RAPID_RISE;
200529949e86Sstevel 		} else if (delta > 0) {			    /* rise? */
200629949e86Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
200729949e86Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
200829949e86Sstevel 				prev_temp = tempstat->l2[prev_index];
200929949e86Sstevel 				if (prev_temp > trail_temp) {
201029949e86Sstevel 					break;
201129949e86Sstevel 				}
201229949e86Sstevel 				trail_temp = prev_temp;
201329949e86Sstevel 				if (prev_temp <= curr_temp - NOISE_THRESH) {
201429949e86Sstevel 					result = TREND_RISE;
201529949e86Sstevel 					break;
201629949e86Sstevel 				}
201729949e86Sstevel 			}
201829949e86Sstevel 		} else if (delta <= -RAPID_FALL_THRESH) {   /* rapid fall? */
201929949e86Sstevel 			result = TREND_RAPID_FALL;
202029949e86Sstevel 		} else if (delta < 0) {			    /* fall? */
202129949e86Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
202229949e86Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
202329949e86Sstevel 				prev_temp = tempstat->l2[prev_index];
202429949e86Sstevel 				if (prev_temp < trail_temp) {
202529949e86Sstevel 					break;
202629949e86Sstevel 				}
202729949e86Sstevel 				trail_temp = prev_temp;
202829949e86Sstevel 				if (prev_temp >= curr_temp + NOISE_THRESH) {
202929949e86Sstevel 					result = TREND_FALL;
203029949e86Sstevel 					break;
203129949e86Sstevel 				}
203229949e86Sstevel 			}
203329949e86Sstevel 		}
203429949e86Sstevel 	}
203529949e86Sstevel 	return (result);
203629949e86Sstevel }
203729949e86Sstevel 
203829949e86Sstevel /*
203929949e86Sstevel  * Reboot the system if we can, otherwise attempt a power down
204029949e86Sstevel  */
204129949e86Sstevel void
fhc_reboot(void)204229949e86Sstevel fhc_reboot(void)
204329949e86Sstevel {
204429949e86Sstevel 	proc_t *initpp;
204529949e86Sstevel 
204629949e86Sstevel 	/* send a SIGPWR to init process */
204729949e86Sstevel 	mutex_enter(&pidlock);
204829949e86Sstevel 	initpp = prfind(P_INITPID);
204929949e86Sstevel 	mutex_exit(&pidlock);
205029949e86Sstevel 
205129949e86Sstevel 	/*
205229949e86Sstevel 	 * If we're still booting and init(1) isn't
205329949e86Sstevel 	 * set up yet, simply halt.
205429949e86Sstevel 	 */
205529949e86Sstevel 	if (initpp != NULL) {
205629949e86Sstevel 		psignal(initpp, SIGFPE);	/* init 6 */
205729949e86Sstevel 	} else {
205829949e86Sstevel 		power_down("Environmental Shutdown");
205929949e86Sstevel 		halt("Power off the System");
206029949e86Sstevel 	}
206129949e86Sstevel }
206229949e86Sstevel 
206329949e86Sstevel int
overtemp_kstat_update(kstat_t * ksp,int rw)206429949e86Sstevel overtemp_kstat_update(kstat_t *ksp, int rw)
206529949e86Sstevel {
206629949e86Sstevel 	struct temp_stats *tempstat;
206729949e86Sstevel 	char *kstatp;
206829949e86Sstevel 	int i;
206929949e86Sstevel 
207029949e86Sstevel 	kstatp = (char *)ksp->ks_data;
207129949e86Sstevel 	tempstat = (struct temp_stats *)ksp->ks_private;
207229949e86Sstevel 
207329949e86Sstevel 	/*
207429949e86Sstevel 	 * Kstat reads are used to retrieve the current system temperature
207529949e86Sstevel 	 * history. Kstat writes are used to reset the max and min
207629949e86Sstevel 	 * temperatures.
207729949e86Sstevel 	 */
207829949e86Sstevel 	if (rw == KSTAT_WRITE) {
207929949e86Sstevel 		short max;	/* temporary copy of max temperature */
208029949e86Sstevel 		short min;	/* temporary copy of min temperature */
208129949e86Sstevel 
208229949e86Sstevel 		/*
208329949e86Sstevel 		 * search for and reset the max and min to the current
208429949e86Sstevel 		 * array contents. Old max and min values will get
208529949e86Sstevel 		 * averaged out as they move into the higher level arrays.
208629949e86Sstevel 		 */
208729949e86Sstevel 		max = tempstat->l1[0];
208829949e86Sstevel 		min = tempstat->l1[0];
208929949e86Sstevel 
209029949e86Sstevel 		/* Pull the max and min from Level 1 array */
209129949e86Sstevel 		for (i = 0; i < L1_SZ; i++) {
209229949e86Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
209329949e86Sstevel 			    (tempstat->l1[i] > max)) {
209429949e86Sstevel 				max = tempstat->l1[i];
209529949e86Sstevel 			}
209629949e86Sstevel 
209729949e86Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
209829949e86Sstevel 			    (tempstat->l1[i] < min)) {
209929949e86Sstevel 				min = tempstat->l1[i];
210029949e86Sstevel 			}
210129949e86Sstevel 		}
210229949e86Sstevel 
210329949e86Sstevel 		/* Pull the max and min from Level 2 array */
210429949e86Sstevel 		for (i = 0; i < L2_SZ; i++) {
210529949e86Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
210629949e86Sstevel 			    (tempstat->l2[i] > max)) {
210729949e86Sstevel 				max = tempstat->l2[i];
210829949e86Sstevel 			}
210929949e86Sstevel 
211029949e86Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
211129949e86Sstevel 			    (tempstat->l2[i] < min)) {
211229949e86Sstevel 				min = tempstat->l2[i];
211329949e86Sstevel 			}
211429949e86Sstevel 		}
211529949e86Sstevel 
211629949e86Sstevel 		/* Pull the max and min from Level 3 array */
211729949e86Sstevel 		for (i = 0; i < L3_SZ; i++) {
211829949e86Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
211929949e86Sstevel 			    (tempstat->l3[i] > max)) {
212029949e86Sstevel 				max = tempstat->l3[i];
212129949e86Sstevel 			}
212229949e86Sstevel 
212329949e86Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
212429949e86Sstevel 			    (tempstat->l3[i] < min)) {
212529949e86Sstevel 				min = tempstat->l3[i];
212629949e86Sstevel 			}
212729949e86Sstevel 		}
212829949e86Sstevel 
212929949e86Sstevel 		/* Pull the max and min from Level 4 array */
213029949e86Sstevel 		for (i = 0; i < L4_SZ; i++) {
213129949e86Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
213229949e86Sstevel 			    (tempstat->l4[i] > max)) {
213329949e86Sstevel 				max = tempstat->l4[i];
213429949e86Sstevel 			}
213529949e86Sstevel 
213629949e86Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
213729949e86Sstevel 			    (tempstat->l4[i] < min)) {
213829949e86Sstevel 				min = tempstat->l4[i];
213929949e86Sstevel 			}
214029949e86Sstevel 		}
214129949e86Sstevel 
214229949e86Sstevel 		/* Pull the max and min from Level 5 array */
214329949e86Sstevel 		for (i = 0; i < L5_SZ; i++) {
214429949e86Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
214529949e86Sstevel 			    (tempstat->l5[i] > max)) {
214629949e86Sstevel 				max = tempstat->l5[i];
214729949e86Sstevel 			}
214829949e86Sstevel 
214929949e86Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
215029949e86Sstevel 			    (tempstat->l5[i] < min)) {
215129949e86Sstevel 				min = tempstat->l5[i];
215229949e86Sstevel 			}
215329949e86Sstevel 		}
215429949e86Sstevel 	} else {
215529949e86Sstevel 		/*
215629949e86Sstevel 		 * copy the temperature history buffer into the
215729949e86Sstevel 		 * kstat structure.
215829949e86Sstevel 		 */
215929949e86Sstevel 		bcopy(tempstat, kstatp, sizeof (struct temp_stats));
216029949e86Sstevel 	}
216129949e86Sstevel 	return (0);
216229949e86Sstevel }
216329949e86Sstevel 
216429949e86Sstevel int
temp_override_kstat_update(kstat_t * ksp,int rw)216529949e86Sstevel temp_override_kstat_update(kstat_t *ksp, int rw)
216629949e86Sstevel {
216729949e86Sstevel 	short *over;
216829949e86Sstevel 	short *kstatp;
216929949e86Sstevel 
217029949e86Sstevel 	kstatp = (short *)ksp->ks_data;
217129949e86Sstevel 	over = (short *)ksp->ks_private;
217229949e86Sstevel 
217329949e86Sstevel 	/*
217429949e86Sstevel 	 * Kstat reads are used to get the temperature override setting.
217529949e86Sstevel 	 * Kstat writes are used to set the temperature override setting.
217629949e86Sstevel 	 */
217729949e86Sstevel 	if (rw == KSTAT_WRITE) {
217829949e86Sstevel 		*over = *kstatp;
217929949e86Sstevel 	} else {
218029949e86Sstevel 		*kstatp = *over;
218129949e86Sstevel 	}
218229949e86Sstevel 	return (0);
218329949e86Sstevel }
218429949e86Sstevel 
218529949e86Sstevel /*
218629949e86Sstevel  * This function uses the calibration tables at the beginning of this file
218729949e86Sstevel  * to lookup the actual temperature of the thermistor in degrees Celcius.
218829949e86Sstevel  * If the measurement is out of the bounds of the acceptable values, the
218929949e86Sstevel  * closest boundary value is used instead.
219029949e86Sstevel  */
219129949e86Sstevel static short
calibrate_temp(enum board_type type,uchar_t temp,uint_t ac_comp)219229949e86Sstevel calibrate_temp(enum board_type type, uchar_t temp, uint_t ac_comp)
219329949e86Sstevel {
219429949e86Sstevel 	short result = NA_TEMP;
219529949e86Sstevel 
219629949e86Sstevel 	if (dont_calibrate == 1) {
219729949e86Sstevel 		return ((short)temp);
219829949e86Sstevel 	}
219929949e86Sstevel 
220029949e86Sstevel 	switch (type) {
220129949e86Sstevel 	case CPU_BOARD:
220229949e86Sstevel 		/*
220329949e86Sstevel 		 * If AC chip revision is >= 4 or if it is unitialized,
220429949e86Sstevel 		 * then use the new calibration tables.
220529949e86Sstevel 		 */
220629949e86Sstevel 		if ((CHIP_REV(ac_comp) >= 4) || (CHIP_REV(ac_comp) == 0)) {
220729949e86Sstevel 			if (temp >= CPU2_MX_CNT) {
220829949e86Sstevel 				result = cpu2_table[CPU2_MX_CNT-1];
220929949e86Sstevel 			} else {
221029949e86Sstevel 				result = cpu2_table[temp];
221129949e86Sstevel 			}
221229949e86Sstevel 		} else {
221329949e86Sstevel 			if (temp >= CPU_MX_CNT) {
221429949e86Sstevel 				result = cpu_table[CPU_MX_CNT-1];
221529949e86Sstevel 			} else {
221629949e86Sstevel 				result = cpu_table[temp];
221729949e86Sstevel 			}
221829949e86Sstevel 		}
221929949e86Sstevel 		break;
222029949e86Sstevel 
222129949e86Sstevel 	case IO_2SBUS_BOARD:
222229949e86Sstevel 	case IO_SBUS_FFB_BOARD:
222329949e86Sstevel 	case IO_PCI_BOARD:
222429949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
222529949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
222629949e86Sstevel 		if (temp < IO_MN_CNT) {
222729949e86Sstevel 			result = io_table[IO_MN_CNT];
222829949e86Sstevel 		} else if (temp >= IO_MX_CNT) {
222929949e86Sstevel 			result = io_table[IO_MX_CNT-1];
223029949e86Sstevel 		} else {
223129949e86Sstevel 			result = io_table[temp];
223229949e86Sstevel 		}
223329949e86Sstevel 		break;
223429949e86Sstevel 
223529949e86Sstevel 	case CLOCK_BOARD:
223629949e86Sstevel 		if (temp < CLK_MN_CNT) {
223729949e86Sstevel 			result = clock_table[CLK_MN_CNT];
223829949e86Sstevel 		} else if (temp >= CLK_MX_CNT) {
223929949e86Sstevel 			result = clock_table[CLK_MX_CNT-1];
224029949e86Sstevel 		} else {
224129949e86Sstevel 			result = clock_table[temp];
224229949e86Sstevel 		}
224329949e86Sstevel 		break;
224429949e86Sstevel 
224529949e86Sstevel 	default:
224629949e86Sstevel 		break;
224729949e86Sstevel 	}
224829949e86Sstevel 
224929949e86Sstevel 	return (result);
225029949e86Sstevel }
225129949e86Sstevel 
225229949e86Sstevel /*
225329949e86Sstevel  * Determine the temperature state of this board based on its type and
225429949e86Sstevel  * the actual temperature in degrees Celcius.
225529949e86Sstevel  */
225629949e86Sstevel static enum temp_state
get_temp_state(enum board_type type,short temp,int board)225729949e86Sstevel get_temp_state(enum board_type type, short temp, int board)
225829949e86Sstevel {
225929949e86Sstevel 	enum temp_state state = TEMP_OK;
226029949e86Sstevel 	short warn_limit;
226129949e86Sstevel 	short danger_limit;
226229949e86Sstevel 	struct cpu *cpa, *cpb;
226329949e86Sstevel 
226429949e86Sstevel 	switch (type) {
226529949e86Sstevel 	case CPU_BOARD:
226629949e86Sstevel 		warn_limit = cpu_warn_temp;
226729949e86Sstevel 		danger_limit = cpu_danger_temp;
226829949e86Sstevel 
226929949e86Sstevel 		/*
227029949e86Sstevel 		 * For CPU boards with frequency >= 400 MHZ,
227129949e86Sstevel 		 * temperature zones are different.
227229949e86Sstevel 		 */
227329949e86Sstevel 
227429949e86Sstevel 		mutex_enter(&cpu_lock);
227529949e86Sstevel 
227629949e86Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL) {
227729949e86Sstevel 			if ((cpa->cpu_type_info.pi_clock) >= 400) {
227829949e86Sstevel 				warn_limit = cpu_warn_temp_4x;
227929949e86Sstevel 				danger_limit = cpu_danger_temp_4x;
228029949e86Sstevel 			}
228129949e86Sstevel 		}
228229949e86Sstevel 		if ((cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL) {
228329949e86Sstevel 			if ((cpb->cpu_type_info.pi_clock) >= 400) {
228429949e86Sstevel 				warn_limit = cpu_warn_temp_4x;
228529949e86Sstevel 				danger_limit = cpu_danger_temp_4x;
228629949e86Sstevel 			}
228729949e86Sstevel 		}
228829949e86Sstevel 
228929949e86Sstevel 		mutex_exit(&cpu_lock);
229029949e86Sstevel 
229129949e86Sstevel 		break;
229229949e86Sstevel 
229329949e86Sstevel 	case IO_2SBUS_BOARD:
229429949e86Sstevel 	case IO_SBUS_FFB_BOARD:
229529949e86Sstevel 	case IO_PCI_BOARD:
229629949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
229729949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
229829949e86Sstevel 		warn_limit = io_warn_temp;
229929949e86Sstevel 		danger_limit = io_danger_temp;
230029949e86Sstevel 		break;
230129949e86Sstevel 
230229949e86Sstevel 	case CLOCK_BOARD:
230329949e86Sstevel 		warn_limit = clk_warn_temp;
230429949e86Sstevel 		danger_limit = clk_danger_temp;
230529949e86Sstevel 		break;
230629949e86Sstevel 
230729949e86Sstevel 	case UNINIT_BOARD:
230829949e86Sstevel 	case UNKNOWN_BOARD:
230929949e86Sstevel 	case MEM_BOARD:
231029949e86Sstevel 	default:
231129949e86Sstevel 		warn_limit = dft_warn_temp;
231229949e86Sstevel 		danger_limit = dft_danger_temp;
231329949e86Sstevel 		break;
231429949e86Sstevel 	}
231529949e86Sstevel 
231629949e86Sstevel 	if (temp >= danger_limit) {
231729949e86Sstevel 		state = TEMP_DANGER;
231829949e86Sstevel 	} else if (temp >= warn_limit) {
231929949e86Sstevel 		state = TEMP_WARN;
232029949e86Sstevel 	}
232129949e86Sstevel 
232229949e86Sstevel 	return (state);
232329949e86Sstevel }
232429949e86Sstevel 
232529949e86Sstevel static void
fhc_add_kstats(struct fhc_soft_state * softsp)232629949e86Sstevel fhc_add_kstats(struct fhc_soft_state *softsp)
232729949e86Sstevel {
232829949e86Sstevel 	struct kstat *fhc_ksp;
232929949e86Sstevel 	struct fhc_kstat *fhc_named_ksp;
233029949e86Sstevel 
233129949e86Sstevel 	if ((fhc_ksp = kstat_create("unix", softsp->list->sc.board,
233229949e86Sstevel 	    FHC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
233329949e86Sstevel 	    sizeof (struct fhc_kstat) / sizeof (kstat_named_t),
233429949e86Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
233529949e86Sstevel 		cmn_err(CE_WARN, "fhc%d kstat_create failed",
233619397407SSherry Moore 		    ddi_get_instance(softsp->dip));
233729949e86Sstevel 		return;
233829949e86Sstevel 	}
233929949e86Sstevel 
234029949e86Sstevel 	fhc_named_ksp = (struct fhc_kstat *)(fhc_ksp->ks_data);
234129949e86Sstevel 
234229949e86Sstevel 	/* initialize the named kstats */
234329949e86Sstevel 	kstat_named_init(&fhc_named_ksp->csr,
234419397407SSherry Moore 	    CSR_KSTAT_NAMED,
234519397407SSherry Moore 	    KSTAT_DATA_UINT32);
234629949e86Sstevel 
234729949e86Sstevel 	kstat_named_init(&fhc_named_ksp->bsr,
234819397407SSherry Moore 	    BSR_KSTAT_NAMED,
234919397407SSherry Moore 	    KSTAT_DATA_UINT32);
235029949e86Sstevel 
235129949e86Sstevel 	fhc_ksp->ks_update = fhc_kstat_update;
235229949e86Sstevel 	fhc_ksp->ks_private = (void *)softsp;
235329949e86Sstevel 	softsp->fhc_ksp = fhc_ksp;
235429949e86Sstevel 	kstat_install(fhc_ksp);
235529949e86Sstevel }
235629949e86Sstevel 
235729949e86Sstevel static int
fhc_kstat_update(kstat_t * ksp,int rw)235829949e86Sstevel fhc_kstat_update(kstat_t *ksp, int rw)
235929949e86Sstevel {
236029949e86Sstevel 	struct fhc_kstat *fhcksp;
236129949e86Sstevel 	struct fhc_soft_state *softsp;
236229949e86Sstevel 
236329949e86Sstevel 	fhcksp = (struct fhc_kstat *)ksp->ks_data;
236429949e86Sstevel 	softsp = (struct fhc_soft_state *)ksp->ks_private;
236529949e86Sstevel 
236629949e86Sstevel 	/* this is a read-only kstat. Bail out on a write */
236729949e86Sstevel 	if (rw == KSTAT_WRITE) {
236829949e86Sstevel 		return (EACCES);
236929949e86Sstevel 	} else {
237029949e86Sstevel 		/*
237129949e86Sstevel 		 * copy the current state of the hardware into the
237229949e86Sstevel 		 * kstat structure.
237329949e86Sstevel 		 */
237429949e86Sstevel 		fhcksp->csr.value.ui32 = *softsp->ctrl;
237529949e86Sstevel 		fhcksp->bsr.value.ui32 = *softsp->bsr;
237629949e86Sstevel 	}
237729949e86Sstevel 	return (0);
237829949e86Sstevel }
237929949e86Sstevel 
238029949e86Sstevel static int
cpu_on_board(int board)238129949e86Sstevel cpu_on_board(int board)
238229949e86Sstevel {
238329949e86Sstevel 	int upa_a = board << 1;
238429949e86Sstevel 	int upa_b = (board << 1) + 1;
238529949e86Sstevel 
2386*50ed1c1eSToomas Soome 	if ((cpunodes[upa_a].nodeid != 0) ||
2387*50ed1c1eSToomas Soome 	    (cpunodes[upa_b].nodeid != 0)) {
238829949e86Sstevel 		return (1);
238929949e86Sstevel 	} else {
239029949e86Sstevel 		return (0);
239129949e86Sstevel 	}
239229949e86Sstevel }
239329949e86Sstevel 
239429949e86Sstevel /*
239529949e86Sstevel  * This function uses the board list and toggles the OS green board
239629949e86Sstevel  * LED. The mask input tells which bit fields are being modified,
239729949e86Sstevel  * and the value input tells the states of the bits.
239829949e86Sstevel  */
239929949e86Sstevel void
update_board_leds(fhc_bd_t * board,uint_t mask,uint_t value)240029949e86Sstevel update_board_leds(fhc_bd_t *board, uint_t mask, uint_t value)
240129949e86Sstevel {
240229949e86Sstevel 	volatile uint_t temp;
240329949e86Sstevel 
240429949e86Sstevel 	ASSERT(fhc_bdlist_locked());
240529949e86Sstevel 
240629949e86Sstevel 	/* mask off mask and value for only the LED bits */
240729949e86Sstevel 	mask &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
240829949e86Sstevel 	value &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
240929949e86Sstevel 
241029949e86Sstevel 	if (board != NULL) {
241129949e86Sstevel 		mutex_enter(&board->softsp->ctrl_lock);
241229949e86Sstevel 
241329949e86Sstevel 		/* read the current register state */
241429949e86Sstevel 		temp = *board->softsp->ctrl;
241529949e86Sstevel 
241629949e86Sstevel 		/*
241729949e86Sstevel 		 * The EPDA bits are special since the register is
241829949e86Sstevel 		 * special.  We don't want to set them, since setting
241929949e86Sstevel 		 * the bits on a shutdown cpu keeps the cpu permanently
242029949e86Sstevel 		 * powered off.  Also, the CSR_SYNC bit must always be
242129949e86Sstevel 		 * set to 0 as it is an OBP semaphore that is expected to
242229949e86Sstevel 		 * be clear for cpu restart.
242329949e86Sstevel 		 */
242429949e86Sstevel 		temp &= ~(FHC_CSR_SYNC | FHC_EPDA_OFF | FHC_EPDB_OFF);
242529949e86Sstevel 
242629949e86Sstevel 		/* mask off the bits to change */
242729949e86Sstevel 		temp &= ~mask;
242829949e86Sstevel 
242929949e86Sstevel 		/* or in the new values of the bits. */
243029949e86Sstevel 		temp |= value;
243129949e86Sstevel 
243229949e86Sstevel 		/* update the register */
243329949e86Sstevel 		*board->softsp->ctrl = temp;
243429949e86Sstevel 
243529949e86Sstevel 		/* flush the hardware registers */
243629949e86Sstevel 		temp = *board->softsp->ctrl;
243729949e86Sstevel #ifdef lint
243829949e86Sstevel 		temp = temp;
243929949e86Sstevel #endif
244029949e86Sstevel 
244129949e86Sstevel 		mutex_exit(&board->softsp->ctrl_lock);
244229949e86Sstevel 	}
244329949e86Sstevel }
244429949e86Sstevel 
244529949e86Sstevel static int
check_for_chamber(void)244629949e86Sstevel check_for_chamber(void)
244729949e86Sstevel {
244829949e86Sstevel 	int chamber = 0;
244929949e86Sstevel 	dev_info_t *options_dip;
245029949e86Sstevel 	pnode_t options_node_id;
245129949e86Sstevel 	int mfgmode_len;
245229949e86Sstevel 	int retval;
245329949e86Sstevel 	char *mfgmode;
245429949e86Sstevel 
245529949e86Sstevel 	/*
245629949e86Sstevel 	 * The operator can disable overtemp powerdown from /etc/system or
245729949e86Sstevel 	 * boot -h.
245829949e86Sstevel 	 */
245929949e86Sstevel 	if (!enable_overtemp_powerdown) {
246029949e86Sstevel 		cmn_err(CE_WARN, "Operator has disabled overtemp powerdown");
246129949e86Sstevel 		return (1);
246229949e86Sstevel 	}
246329949e86Sstevel 
246429949e86Sstevel 	/*
246529949e86Sstevel 	 * An OBP option, 'mfg-mode' is being used to inform us as to
246629949e86Sstevel 	 * whether we are in an enviromental chamber. It exists in
246729949e86Sstevel 	 * the 'options' node. This is where all OBP 'setenv' (eeprom)
246829949e86Sstevel 	 * parameters live.
246929949e86Sstevel 	 */
247029949e86Sstevel 	if ((options_dip = ddi_find_devinfo("options", -1, 0)) != NULL) {
247129949e86Sstevel 		options_node_id = (pnode_t)ddi_get_nodeid(options_dip);
247229949e86Sstevel 		mfgmode_len = prom_getproplen(options_node_id, "mfg-mode");
247329949e86Sstevel 		if (mfgmode_len == -1) {
247429949e86Sstevel 			return (chamber);
247529949e86Sstevel 		}
247629949e86Sstevel 		mfgmode = kmem_alloc(mfgmode_len+1, KM_SLEEP);
247729949e86Sstevel 
247829949e86Sstevel 		retval = prom_getprop(options_node_id, "mfg-mode", mfgmode);
247929949e86Sstevel 		if (retval != -1) {
248029949e86Sstevel 			mfgmode[retval] = 0;
248129949e86Sstevel 			if (strcmp(mfgmode, CHAMBER_VALUE) == 0) {
248229949e86Sstevel 				chamber = 1;
248329949e86Sstevel 				cmn_err(CE_WARN, "System in Temperature"
248419397407SSherry Moore 				    " Chamber Mode. Overtemperature"
248519397407SSherry Moore 				    " Shutdown disabled");
248629949e86Sstevel 			}
248729949e86Sstevel 		}
248829949e86Sstevel 		kmem_free(mfgmode, mfgmode_len+1);
248929949e86Sstevel 	}
249029949e86Sstevel 	return (chamber);
249129949e86Sstevel }
249229949e86Sstevel 
249329949e86Sstevel static void
build_bd_display_str(char * buffer,enum board_type type,int board)249429949e86Sstevel build_bd_display_str(char *buffer, enum board_type type, int board)
249529949e86Sstevel {
249629949e86Sstevel 	if (buffer == NULL) {
249729949e86Sstevel 		return;
249829949e86Sstevel 	}
249929949e86Sstevel 
250029949e86Sstevel 	/* fill in board type to display */
250129949e86Sstevel 	switch (type) {
250229949e86Sstevel 	case UNINIT_BOARD:
250329949e86Sstevel 		(void) sprintf(buffer, "Uninitialized Board type board %d",
250419397407SSherry Moore 		    board);
250529949e86Sstevel 		break;
250629949e86Sstevel 
250729949e86Sstevel 	case UNKNOWN_BOARD:
250829949e86Sstevel 		(void) sprintf(buffer, "Unknown Board type board %d", board);
250929949e86Sstevel 		break;
251029949e86Sstevel 
251129949e86Sstevel 	case CPU_BOARD:
251229949e86Sstevel 	case MEM_BOARD:
251329949e86Sstevel 		(void) sprintf(buffer, "CPU/Memory board %d", board);
251429949e86Sstevel 		break;
251529949e86Sstevel 
251629949e86Sstevel 	case IO_2SBUS_BOARD:
251729949e86Sstevel 		(void) sprintf(buffer, "2 SBus IO board %d", board);
251829949e86Sstevel 		break;
251929949e86Sstevel 
252029949e86Sstevel 	case IO_SBUS_FFB_BOARD:
252129949e86Sstevel 		(void) sprintf(buffer, "SBus FFB IO board %d", board);
252229949e86Sstevel 		break;
252329949e86Sstevel 
252429949e86Sstevel 	case IO_PCI_BOARD:
252529949e86Sstevel 		(void) sprintf(buffer, "PCI IO board %d", board);
252629949e86Sstevel 		break;
252729949e86Sstevel 
252829949e86Sstevel 	case CLOCK_BOARD:
252929949e86Sstevel 		(void) sprintf(buffer, "Clock board");
253029949e86Sstevel 		break;
253129949e86Sstevel 
253229949e86Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
253329949e86Sstevel 		(void) sprintf(buffer, "2 SBus SOC+ IO board %d", board);
253429949e86Sstevel 		break;
253529949e86Sstevel 
253629949e86Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
253729949e86Sstevel 		(void) sprintf(buffer, "SBus FFB SOC+ IO board %d", board);
253829949e86Sstevel 		break;
253929949e86Sstevel 
254029949e86Sstevel 	default:
254129949e86Sstevel 		(void) sprintf(buffer, "Unrecognized board type board %d",
254219397407SSherry Moore 		    board);
254329949e86Sstevel 		break;
254429949e86Sstevel 	}
254529949e86Sstevel }
254629949e86Sstevel 
254729949e86Sstevel void
fhc_intrdist(void * arg)254829949e86Sstevel fhc_intrdist(void *arg)
254929949e86Sstevel {
255029949e86Sstevel 	struct fhc_soft_state *softsp;
255129949e86Sstevel 	dev_info_t *dip = (dev_info_t *)arg;
255229949e86Sstevel 	volatile uint_t *mondo_vec_reg;
255329949e86Sstevel 	volatile uint_t *intr_state_reg;
255429949e86Sstevel 	uint_t mondo_vec;
255529949e86Sstevel 	uint_t tmp_reg;
255629949e86Sstevel 	uint_t cpu_id;
255729949e86Sstevel 	uint_t i;
255829949e86Sstevel 
255929949e86Sstevel 	/* extract the soft state pointer */
256029949e86Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(dip));
256129949e86Sstevel 
256229949e86Sstevel 	/*
256329949e86Sstevel 	 * Loop through all the interrupt mapping registers and reprogram
256429949e86Sstevel 	 * the target CPU for all valid registers.
256529949e86Sstevel 	 */
256629949e86Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
256729949e86Sstevel 		mondo_vec_reg = softsp->intr_regs[i].mapping_reg;
256829949e86Sstevel 		intr_state_reg = softsp->intr_regs[i].clear_reg;
256929949e86Sstevel 
257029949e86Sstevel 		if ((*mondo_vec_reg & IMR_VALID) == 0)
257129949e86Sstevel 			continue;
257229949e86Sstevel 
257329949e86Sstevel 		cpu_id = intr_dist_cpuid();
257429949e86Sstevel 
257529949e86Sstevel 		/* Check the current target of the mondo */
257629949e86Sstevel 		if (((*mondo_vec_reg & INR_PID_MASK) >> INR_PID_SHIFT) ==
257729949e86Sstevel 		    cpu_id) {
257829949e86Sstevel 			/* It is the same, don't reprogram */
257929949e86Sstevel 			return;
258029949e86Sstevel 		}
258129949e86Sstevel 
258229949e86Sstevel 		/* So it's OK to reprogram the CPU target */
258329949e86Sstevel 
258429949e86Sstevel 		/* turn off the valid bit */
258529949e86Sstevel 		*mondo_vec_reg &= ~IMR_VALID;
258629949e86Sstevel 
258729949e86Sstevel 		/* flush the hardware registers */
258829949e86Sstevel 		tmp_reg = *softsp->id;
258929949e86Sstevel 
259029949e86Sstevel 		/*
259129949e86Sstevel 		 * wait for the state machine to idle. Do not loop on panic, so
259229949e86Sstevel 		 * that system does not hang.
259329949e86Sstevel 		 */
259429949e86Sstevel 		while (((*intr_state_reg & INT_PENDING) == INT_PENDING) &&
259529949e86Sstevel 		    !panicstr)
259629949e86Sstevel 			;
259729949e86Sstevel 
259829949e86Sstevel 		/* re-target the mondo and turn it on */
259929949e86Sstevel 		mondo_vec = (cpu_id << INR_PID_SHIFT) | IMR_VALID;
260029949e86Sstevel 
260129949e86Sstevel 		/* write it back to the hardware. */
260229949e86Sstevel 		*mondo_vec_reg = mondo_vec;
260329949e86Sstevel 
260429949e86Sstevel 		/* flush the hardware buffers. */
260529949e86Sstevel 		tmp_reg = *(softsp->id);
260629949e86Sstevel 
260729949e86Sstevel #ifdef	lint
260829949e86Sstevel 		tmp_reg = tmp_reg;
260929949e86Sstevel #endif	/* lint */
261029949e86Sstevel 	}
261129949e86Sstevel }
261229949e86Sstevel 
261329949e86Sstevel /*
261429949e86Sstevel  * reg_fault
261529949e86Sstevel  *
261629949e86Sstevel  * This routine registers a fault in the fault list. If the fault
261729949e86Sstevel  * is unique (does not exist in fault list) then a new fault is
261829949e86Sstevel  * added to the fault list, with the appropriate structure elements
261929949e86Sstevel  * filled in.
262029949e86Sstevel  */
262129949e86Sstevel void
reg_fault(int unit,enum ft_type type,enum ft_class fclass)262229949e86Sstevel reg_fault(int unit, enum ft_type type, enum ft_class fclass)
262329949e86Sstevel {
262429949e86Sstevel 	struct ft_link_list *list;	/* temporary list pointer */
262529949e86Sstevel 
262629949e86Sstevel 	if (type >= ft_max_index) {
262729949e86Sstevel 		cmn_err(CE_WARN, "Illegal Fault type %x", type);
262829949e86Sstevel 		return;
262929949e86Sstevel 	}
263029949e86Sstevel 
263129949e86Sstevel 	mutex_enter(&ftlist_mutex);
263229949e86Sstevel 
263329949e86Sstevel 	/* Search for the requested fault. If it already exists, return. */
263429949e86Sstevel 	for (list = ft_list; list != NULL; list = list->next) {
263529949e86Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
263629949e86Sstevel 		    (list->f.fclass == fclass)) {
263729949e86Sstevel 			mutex_exit(&ftlist_mutex);
263829949e86Sstevel 			return;
263929949e86Sstevel 		}
264029949e86Sstevel 	}
264129949e86Sstevel 
264229949e86Sstevel 	/* Allocate a new fault structure. */
264329949e86Sstevel 	list = kmem_zalloc(sizeof (struct ft_link_list), KM_SLEEP);
264429949e86Sstevel 
264529949e86Sstevel 	/* fill in the fault list elements */
264629949e86Sstevel 	list->f.unit = unit;
264729949e86Sstevel 	list->f.type = type;
264829949e86Sstevel 	list->f.fclass = fclass;
264929949e86Sstevel 	list->f.create_time = (time32_t)gethrestime_sec(); /* XX64 */
265029949e86Sstevel 	(void) strncpy(list->f.msg, ft_str_table[type], MAX_FT_DESC);
265129949e86Sstevel 
265229949e86Sstevel 	/* link it into the list. */
265329949e86Sstevel 	list->next = ft_list;
265429949e86Sstevel 	ft_list = list;
265529949e86Sstevel 
265629949e86Sstevel 	/* Update the total fault count */
265729949e86Sstevel 	ft_nfaults++;
265829949e86Sstevel 
265929949e86Sstevel 	mutex_exit(&ftlist_mutex);
266029949e86Sstevel }
266129949e86Sstevel 
266229949e86Sstevel /*
266329949e86Sstevel  * clear_fault
266429949e86Sstevel  *
266529949e86Sstevel  * This routine finds the fault list entry specified by the caller,
266629949e86Sstevel  * deletes it from the fault list, and frees up the memory used for
266729949e86Sstevel  * the entry. If the requested fault is not found, it exits silently.
266829949e86Sstevel  */
266929949e86Sstevel void
clear_fault(int unit,enum ft_type type,enum ft_class fclass)267029949e86Sstevel clear_fault(int unit, enum ft_type type, enum ft_class fclass)
267129949e86Sstevel {
267229949e86Sstevel 	struct ft_link_list *list;		/* temporary list pointer */
267329949e86Sstevel 	struct ft_link_list **vect;
267429949e86Sstevel 
267529949e86Sstevel 	mutex_enter(&ftlist_mutex);
267629949e86Sstevel 
267729949e86Sstevel 	list = ft_list;
267829949e86Sstevel 	vect = &ft_list;
267929949e86Sstevel 
268029949e86Sstevel 	/*
268129949e86Sstevel 	 * Search for the requested fault. If it exists, delete it
268229949e86Sstevel 	 * and relink the fault list.
268329949e86Sstevel 	 */
268429949e86Sstevel 	for (; list != NULL; vect = &list->next, list = list->next) {
268529949e86Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
268629949e86Sstevel 		    (list->f.fclass == fclass)) {
268729949e86Sstevel 			/* remove the item from the list */
268829949e86Sstevel 			*vect = list->next;
268929949e86Sstevel 
269029949e86Sstevel 			/* free the memory allocated */
269129949e86Sstevel 			kmem_free(list, sizeof (struct ft_link_list));
269229949e86Sstevel 
269329949e86Sstevel 			/* Update the total fault count */
269429949e86Sstevel 			ft_nfaults--;
269529949e86Sstevel 			break;
269629949e86Sstevel 		}
269729949e86Sstevel 	}
269829949e86Sstevel 	mutex_exit(&ftlist_mutex);
269929949e86Sstevel }
270029949e86Sstevel 
270129949e86Sstevel /*
270229949e86Sstevel  * process_fault_list
270329949e86Sstevel  *
270429949e86Sstevel  * This routine walks the global fault list and updates the board list
270529949e86Sstevel  * with the current status of each Yellow LED. If any faults are found
270629949e86Sstevel  * in the system, then a non-zero value is returned. Else zero is returned.
270729949e86Sstevel  */
270829949e86Sstevel int
process_fault_list(void)270929949e86Sstevel process_fault_list(void)
271029949e86Sstevel {
271129949e86Sstevel 	int fault = 0;
271229949e86Sstevel 	struct ft_link_list *ftlist;	/* fault list pointer */
271329949e86Sstevel 	fhc_bd_t *bdlist;		/* board list pointer */
271429949e86Sstevel 
271529949e86Sstevel 	/*
271629949e86Sstevel 	 * Note on locking. The bdlist mutex is always acquired and
271729949e86Sstevel 	 * held around the ftlist mutex when both are needed for an
271829949e86Sstevel 	 * operation. This is to avoid deadlock.
271929949e86Sstevel 	 */
272029949e86Sstevel 
272129949e86Sstevel 	/* First lock the board list */
272229949e86Sstevel 	(void) fhc_bdlist_lock(-1);
272329949e86Sstevel 
272429949e86Sstevel 	/* Grab the fault list lock first */
272529949e86Sstevel 	mutex_enter(&ftlist_mutex);
272629949e86Sstevel 
272729949e86Sstevel 	/* clear the board list of all faults first */
272829949e86Sstevel 	for (bdlist = fhc_bd_first(); bdlist; bdlist = fhc_bd_next(bdlist))
272929949e86Sstevel 		bdlist->fault = 0;
273029949e86Sstevel 
273129949e86Sstevel 	/* walk the fault list here */
273229949e86Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
273329949e86Sstevel 		fault++;
273429949e86Sstevel 
273529949e86Sstevel 		/*
273629949e86Sstevel 		 * If this is a board level fault, find the board, The
273729949e86Sstevel 		 * unit number for all board class faults must be the
273829949e86Sstevel 		 * actual board number. The caller of reg_fault must
273929949e86Sstevel 		 * ensure this for FT_BOARD class faults.
274029949e86Sstevel 		 */
274129949e86Sstevel 		if (ftlist->f.fclass == FT_BOARD) {
274229949e86Sstevel 			/* Sanity check the board first */
274329949e86Sstevel 			if (fhc_bd_valid(ftlist->f.unit)) {
274429949e86Sstevel 				bdlist = fhc_bd(ftlist->f.unit);
274529949e86Sstevel 				bdlist->fault = 1;
274629949e86Sstevel 			} else {
274729949e86Sstevel 				cmn_err(CE_WARN, "No board %d list entry found",
274819397407SSherry Moore 				    ftlist->f.unit);
274929949e86Sstevel 			}
275029949e86Sstevel 		}
275129949e86Sstevel 	}
275229949e86Sstevel 
275329949e86Sstevel 	/* now unlock the fault list */
275429949e86Sstevel 	mutex_exit(&ftlist_mutex);
275529949e86Sstevel 
275629949e86Sstevel 	/* unlock the board list before leaving */
275729949e86Sstevel 	fhc_bdlist_unlock();
275829949e86Sstevel 
275929949e86Sstevel 	return (fault);
276029949e86Sstevel }
276129949e86Sstevel 
276229949e86Sstevel /*
276329949e86Sstevel  * Add a new memloc to the database (and keep 'em sorted by PA)
276429949e86Sstevel  */
276529949e86Sstevel void
fhc_add_memloc(int board,uint64_t pa,uint_t size)276629949e86Sstevel fhc_add_memloc(int board, uint64_t pa, uint_t size)
276729949e86Sstevel {
276829949e86Sstevel 	struct fhc_memloc *p, **pp;
276929949e86Sstevel 	uint_t ipa = pa >> FHC_MEMLOC_SHIFT;
277029949e86Sstevel 
277129949e86Sstevel 	ASSERT(fhc_bdlist_locked());
277229949e86Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
277329949e86Sstevel 
277429949e86Sstevel 	/* look for a comparable memloc (as long as new PA smaller) */
277529949e86Sstevel 	for (p = fhc_base_memloc, pp = &fhc_base_memloc;
277629949e86Sstevel 	    p != NULL; pp = &p->next, p = p->next) {
277729949e86Sstevel 		/* have we passed our place in the sort? */
277829949e86Sstevel 		if (ipa < p->pa) {
277929949e86Sstevel 			break;
278029949e86Sstevel 		}
278129949e86Sstevel 	}
278229949e86Sstevel 	p = kmem_alloc(sizeof (struct fhc_memloc), KM_SLEEP);
278329949e86Sstevel 	p->next = *pp;
278429949e86Sstevel 	p->board = board;
278529949e86Sstevel 	p->pa = ipa;
278629949e86Sstevel 	p->size = size;
278729949e86Sstevel #ifdef DEBUG_MEMDEC
278829949e86Sstevel 	cmn_err(CE_NOTE, "fhc_add_memloc: adding %d 0x%x 0x%x",
278919397407SSherry Moore 	    p->board, p->pa, p->size);
279029949e86Sstevel #endif /* DEBUG_MEMDEC */
279129949e86Sstevel 	*pp = p;
279229949e86Sstevel }
279329949e86Sstevel 
279429949e86Sstevel /*
279529949e86Sstevel  * Delete all memloc records for a board from the database
279629949e86Sstevel  */
279729949e86Sstevel void
fhc_del_memloc(int board)279829949e86Sstevel fhc_del_memloc(int board)
279929949e86Sstevel {
280029949e86Sstevel 	struct fhc_memloc *p, **pp;
280129949e86Sstevel 
280229949e86Sstevel 	ASSERT(fhc_bdlist_locked());
280329949e86Sstevel 
280429949e86Sstevel 	/* delete all entries that match board */
280529949e86Sstevel 	pp = &fhc_base_memloc;
280629949e86Sstevel 	while ((p = *pp) != NULL) {
280729949e86Sstevel 		if (p->board == board) {
280829949e86Sstevel #ifdef DEBUG_MEMDEC
280929949e86Sstevel 			cmn_err(CE_NOTE, "fhc_del_memloc: removing %d "
281029949e86Sstevel 			    "0x%x 0x%x", board, p->pa, p->size);
281129949e86Sstevel #endif /* DEBUG_MEMDEC */
281229949e86Sstevel 			*pp = p->next;
281329949e86Sstevel 			kmem_free(p, sizeof (struct fhc_memloc));
281429949e86Sstevel 		} else {
281529949e86Sstevel 			pp = &(p->next);
281629949e86Sstevel 		}
281729949e86Sstevel 	}
281829949e86Sstevel }
281929949e86Sstevel 
282029949e86Sstevel /*
282129949e86Sstevel  * Find a physical address range of sufficient size and return a starting PA
282229949e86Sstevel  */
282329949e86Sstevel uint64_t
fhc_find_memloc_gap(uint_t size)282429949e86Sstevel fhc_find_memloc_gap(uint_t size)
282529949e86Sstevel {
282629949e86Sstevel 	struct fhc_memloc *p;
282729949e86Sstevel 	uint_t base_pa = 0;
282829949e86Sstevel 	uint_t mask = ~(size-1);
282929949e86Sstevel 
283029949e86Sstevel 	ASSERT(fhc_bdlist_locked());
283129949e86Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
283229949e86Sstevel 
283329949e86Sstevel 	/*
283429949e86Sstevel 	 * walk the list of known memlocs and measure the 'gaps'.
283529949e86Sstevel 	 * we will need a hole that can align the 'size' requested.
283629949e86Sstevel 	 * (e.g. a 256mb bank needs to be on a 256mb boundary).
283729949e86Sstevel 	 */
283829949e86Sstevel 	for (p = fhc_base_memloc; p != NULL; p = p->next) {
283929949e86Sstevel 		if (base_pa != (base_pa & mask))
284029949e86Sstevel 			base_pa = (base_pa + size) & mask;
284129949e86Sstevel 		if (base_pa + size <= p->pa)
284229949e86Sstevel 			break;
284329949e86Sstevel 		base_pa = p->pa + p->size;
284429949e86Sstevel 	}
284529949e86Sstevel 
284629949e86Sstevel 	/*
284729949e86Sstevel 	 * At this point, we assume that base_pa is good enough.
284829949e86Sstevel 	 */
284929949e86Sstevel 	ASSERT((base_pa + size) <= FHC_MEMLOC_MAX);
285029949e86Sstevel 	if (base_pa != (base_pa & mask))
285129949e86Sstevel 		base_pa = (base_pa + size) & mask;	/* align */
285229949e86Sstevel 	return ((uint64_t)base_pa << FHC_MEMLOC_SHIFT);
285329949e86Sstevel }
285429949e86Sstevel 
285529949e86Sstevel /*
285629949e86Sstevel  * This simple function to write the MCRs can only be used when
285729949e86Sstevel  * the contents of memory are not valid as there is a bug in the AC
285829949e86Sstevel  * ASIC concerning refresh.
285929949e86Sstevel  */
286029949e86Sstevel static void
fhc_write_mcrs(uint64_t cpa,uint64_t dpa0,uint64_t dpa1,uint64_t c,uint64_t d0,uint64_t d1)286129949e86Sstevel fhc_write_mcrs(
286229949e86Sstevel 	uint64_t cpa,
286329949e86Sstevel 	uint64_t dpa0,
286429949e86Sstevel 	uint64_t dpa1,
286529949e86Sstevel 	uint64_t c,
286629949e86Sstevel 	uint64_t d0,
286729949e86Sstevel 	uint64_t d1)
286829949e86Sstevel {
286929949e86Sstevel 	stdphysio(cpa, c & ~AC_CSR_REFEN);
287029949e86Sstevel 	(void) lddphysio(cpa);
287129949e86Sstevel 	if (GRP_SIZE_IS_SET(d0)) {
287229949e86Sstevel 		stdphysio(dpa0, d0);
287329949e86Sstevel 		(void) lddphysio(dpa0);
287429949e86Sstevel 	}
287529949e86Sstevel 	if (GRP_SIZE_IS_SET(d1)) {
287629949e86Sstevel 		stdphysio(dpa1, d1);
287729949e86Sstevel 		(void) lddphysio(dpa1);
287829949e86Sstevel 	}
287929949e86Sstevel 	stdphysio(cpa, c);
288029949e86Sstevel 	(void) lddphysio(cpa);
288129949e86Sstevel }
288229949e86Sstevel 
288329949e86Sstevel /* compute the appropriate RASIZE for bank size */
288429949e86Sstevel static uint_t
fhc_cvt_size(uint64_t bsz)288529949e86Sstevel fhc_cvt_size(uint64_t bsz)
288629949e86Sstevel {
288729949e86Sstevel 	uint_t csz;
288829949e86Sstevel 
288929949e86Sstevel 	csz = 0;
289029949e86Sstevel 	bsz /= 64;
289129949e86Sstevel 	while (bsz) {
289229949e86Sstevel 		csz++;
289329949e86Sstevel 		bsz /= 2;
289429949e86Sstevel 	}
289529949e86Sstevel 	csz /= 2;
289629949e86Sstevel 
289729949e86Sstevel 	return (csz);
289829949e86Sstevel }
289929949e86Sstevel 
290029949e86Sstevel void
fhc_program_memory(int board,uint64_t pa)290129949e86Sstevel fhc_program_memory(int board, uint64_t pa)
290229949e86Sstevel {
290329949e86Sstevel 	uint64_t cpa, dpa0, dpa1;
290429949e86Sstevel 	uint64_t c, d0, d1;
290529949e86Sstevel 	uint64_t b0_pa, b1_pa;
290629949e86Sstevel 	uint64_t memdec0, memdec1;
290729949e86Sstevel 	uint_t b0_size, b1_size;
290829949e86Sstevel 
290929949e86Sstevel 	/* XXX gross hack to get to board via board number */
291029949e86Sstevel 	cpa = 0x1c0f9000060ull + (board * 0x400000000ull);
291129949e86Sstevel #ifdef DEBUG_MEMDEC
291229949e86Sstevel 	prom_printf("cpa = 0x%llx\n", cpa);
291329949e86Sstevel #endif /* DEBUG_MEMDEC */
291429949e86Sstevel 	dpa0 = cpa + 0x10;
291529949e86Sstevel 	dpa1 = cpa + 0x20;
291629949e86Sstevel 
291729949e86Sstevel /* assume size is set by connect */
291829949e86Sstevel 	memdec0 = lddphysio(dpa0);
291929949e86Sstevel #ifdef DEBUG_MEMDEC
292029949e86Sstevel 	prom_printf("memdec0 = 0x%llx\n", memdec0);
292129949e86Sstevel #endif /* DEBUG_MEMDEC */
292229949e86Sstevel 	memdec1 = lddphysio(dpa1);
292329949e86Sstevel #ifdef DEBUG_MEMDEC
292429949e86Sstevel 	prom_printf("memdec1 = 0x%llx\n", memdec1);
292529949e86Sstevel #endif /* DEBUG_MEMDEC */
292629949e86Sstevel 	if (GRP_SIZE_IS_SET(memdec0)) {
292729949e86Sstevel 		b0_size = GRP_SPANMB(memdec0);
292829949e86Sstevel 	} else {
292929949e86Sstevel 		b0_size = 0;
293029949e86Sstevel 	}
293129949e86Sstevel 	if (GRP_SIZE_IS_SET(memdec1)) {
293229949e86Sstevel 		b1_size = GRP_SPANMB(memdec1);
293329949e86Sstevel 	} else {
293429949e86Sstevel 		b1_size = 0;
293529949e86Sstevel 	}
293629949e86Sstevel 
293729949e86Sstevel 	c = lddphysio(cpa);
293829949e86Sstevel #ifdef DEBUG_MEMDEC
293929949e86Sstevel 	prom_printf("c = 0x%llx\n", c);
294029949e86Sstevel #endif /* DEBUG_MEMDEC */
294129949e86Sstevel 	if (b0_size) {
294229949e86Sstevel 		b0_pa = pa;
294329949e86Sstevel 		d0 = SETUP_DECODE(b0_pa, b0_size, 0, 0);
294429949e86Sstevel 		d0 |= AC_MEM_VALID;
294529949e86Sstevel 
294629949e86Sstevel 		c &= ~0x7;
294729949e86Sstevel 		c |= 0;
294829949e86Sstevel 		c &= ~(0x7 << 8);
294929949e86Sstevel 		c |= (fhc_cvt_size(b0_size) << 8);  /* match row size */
295029949e86Sstevel 	} else {
295129949e86Sstevel 		d0 = memdec0;
295229949e86Sstevel 	}
295329949e86Sstevel 	if (b1_size) {
295429949e86Sstevel 		b1_pa = pa + 0x80000000ull; /* XXX 2gb */
295529949e86Sstevel 		d1 = SETUP_DECODE(b1_pa, b1_size, 0, 0);
295629949e86Sstevel 		d1 |= AC_MEM_VALID;
295729949e86Sstevel 
295829949e86Sstevel 		c &= ~(0x7 << 3);
295929949e86Sstevel 		c |= (0 << 3);
296029949e86Sstevel 		c &= ~(0x7 << 11);
296129949e86Sstevel 		c |= (fhc_cvt_size(b1_size) << 11); /* match row size */
296229949e86Sstevel 	} else {
296329949e86Sstevel 		d1 = memdec1;
296429949e86Sstevel 	}
296529949e86Sstevel #ifdef DEBUG_MEMDEC
296629949e86Sstevel 	prom_printf("c 0x%llx, d0 0x%llx, d1 0x%llx\n", c, d0, d1);
296729949e86Sstevel #endif /* DEBUG_MEMDEC */
296829949e86Sstevel 	fhc_write_mcrs(cpa, dpa0, dpa1, c, d0, d1);
296929949e86Sstevel }
297029949e86Sstevel 
297129949e86Sstevel /*
297229949e86Sstevel  * Creates a variable sized virtual kstat with a snapshot routine in order
297329949e86Sstevel  * to pass the linked list fault list up to userland. Also creates a
297429949e86Sstevel  * virtual kstat to pass up the string table for faults.
297529949e86Sstevel  */
297629949e86Sstevel void
create_ft_kstats(int instance)297729949e86Sstevel create_ft_kstats(int instance)
297829949e86Sstevel {
297929949e86Sstevel 	struct kstat *ksp;
298029949e86Sstevel 
298129949e86Sstevel 	ksp = kstat_create("unix", instance, FT_LIST_KSTAT_NAME, "misc",
298219397407SSherry Moore 	    KSTAT_TYPE_RAW, 1, KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_VAR_SIZE);
298329949e86Sstevel 
298429949e86Sstevel 	if (ksp != NULL) {
298529949e86Sstevel 		ksp->ks_data = NULL;
298629949e86Sstevel 		ksp->ks_update = ft_ks_update;
298729949e86Sstevel 		ksp->ks_snapshot = ft_ks_snapshot;
298829949e86Sstevel 		ksp->ks_data_size = 1;
298929949e86Sstevel 		ksp->ks_lock = &ftlist_mutex;
299029949e86Sstevel 		kstat_install(ksp);
299129949e86Sstevel 	}
299229949e86Sstevel }
299329949e86Sstevel 
299429949e86Sstevel /*
299529949e86Sstevel  * This routine creates a snapshot of all the fault list data. It is
299629949e86Sstevel  * called by the kstat framework when a kstat read is done.
299729949e86Sstevel  */
299829949e86Sstevel static int
ft_ks_snapshot(struct kstat * ksp,void * buf,int rw)299929949e86Sstevel ft_ks_snapshot(struct kstat *ksp, void *buf, int rw)
300029949e86Sstevel {
300129949e86Sstevel 	struct ft_link_list *ftlist;
300229949e86Sstevel 
300329949e86Sstevel 	if (rw == KSTAT_WRITE) {
300429949e86Sstevel 		return (EACCES);
300529949e86Sstevel 	}
300629949e86Sstevel 
300729949e86Sstevel 	ksp->ks_snaptime = gethrtime();
300829949e86Sstevel 
300929949e86Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
301029949e86Sstevel 		bcopy(&ftlist->f, buf, sizeof (struct ft_list));
301129949e86Sstevel 		buf = ((struct ft_list *)buf) + 1;
301229949e86Sstevel 	}
301329949e86Sstevel 	return (0);
301429949e86Sstevel }
301529949e86Sstevel 
301629949e86Sstevel /*
301729949e86Sstevel  * Setup the kstat data size for the kstat framework. This is used in
301829949e86Sstevel  * conjunction with the ks_snapshot routine. This routine sets the size,
301929949e86Sstevel  * the kstat framework allocates the memory, and ks_shapshot does the
302029949e86Sstevel  * data transfer.
302129949e86Sstevel  */
302229949e86Sstevel static int
ft_ks_update(struct kstat * ksp,int rw)302329949e86Sstevel ft_ks_update(struct kstat *ksp, int rw)
302429949e86Sstevel {
302529949e86Sstevel 	if (rw == KSTAT_WRITE) {
302629949e86Sstevel 		return (EACCES);
302729949e86Sstevel 	} else {
302829949e86Sstevel 		if (ft_nfaults) {
302929949e86Sstevel 			ksp->ks_data_size = ft_nfaults *
303019397407SSherry Moore 			    sizeof (struct ft_list);
303129949e86Sstevel 		} else {
303229949e86Sstevel 			ksp->ks_data_size = 1;
303329949e86Sstevel 		}
303429949e86Sstevel 	}
303529949e86Sstevel 
303629949e86Sstevel 	return (0);
303729949e86Sstevel }
303829949e86Sstevel 
303929949e86Sstevel /*
304029949e86Sstevel  * Power off any cpus on the board.
304129949e86Sstevel  */
304229949e86Sstevel int
fhc_board_poweroffcpus(int board,char * errbuf,int cpu_flags)304329949e86Sstevel fhc_board_poweroffcpus(int board, char *errbuf, int cpu_flags)
304429949e86Sstevel {
304529949e86Sstevel 	cpu_t *cpa, *cpb;
304629949e86Sstevel 	enum board_type type;
304729949e86Sstevel 	int error = 0;
304829949e86Sstevel 
304929949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
305029949e86Sstevel 
305129949e86Sstevel 	/*
305229949e86Sstevel 	 * what type of board are we dealing with?
305329949e86Sstevel 	 */
305429949e86Sstevel 	type = fhc_bd_type(board);
305529949e86Sstevel 
305629949e86Sstevel 	switch (type) {
305729949e86Sstevel 	case CPU_BOARD:
305829949e86Sstevel 
305929949e86Sstevel 		/*
306029949e86Sstevel 		 * the shutdown sequence will be:
306129949e86Sstevel 		 *
306229949e86Sstevel 		 * idle both cpus then shut them off.
306329949e86Sstevel 		 * it looks like the hardware gets corrupted if one
306429949e86Sstevel 		 * cpu is busy while the other is shutting down...
306529949e86Sstevel 		 */
306629949e86Sstevel 
306729949e86Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL &&
306829949e86Sstevel 		    cpu_is_active(cpa)) {
306929949e86Sstevel 			if (!cpu_intr_on(cpa)) {
307029949e86Sstevel 				cpu_intr_enable(cpa);
307129949e86Sstevel 			}
307229949e86Sstevel 			if ((error = cpu_offline(cpa, cpu_flags)) != 0) {
307329949e86Sstevel 				cmn_err(CE_WARN,
307429949e86Sstevel 				    "Processor %d failed to offline.",
307529949e86Sstevel 				    cpa->cpu_id);
307629949e86Sstevel 				if (errbuf != NULL) {
307729949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
307829949e86Sstevel 					    "processor %d failed to offline",
307929949e86Sstevel 					    cpa->cpu_id);
308029949e86Sstevel 				}
308129949e86Sstevel 			}
308229949e86Sstevel 		}
308329949e86Sstevel 
308429949e86Sstevel 		if (error == 0 &&
308529949e86Sstevel 		    (cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL &&
308629949e86Sstevel 		    cpu_is_active(cpb)) {
308729949e86Sstevel 			if (!cpu_intr_on(cpb)) {
308829949e86Sstevel 				cpu_intr_enable(cpb);
308929949e86Sstevel 			}
309029949e86Sstevel 			if ((error = cpu_offline(cpb, cpu_flags)) != 0) {
309129949e86Sstevel 				cmn_err(CE_WARN,
309229949e86Sstevel 				    "Processor %d failed to offline.",
309329949e86Sstevel 				    cpb->cpu_id);
309429949e86Sstevel 
309529949e86Sstevel 				if (errbuf != NULL) {
309629949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
309729949e86Sstevel 					    "processor %d failed to offline",
309829949e86Sstevel 					    cpb->cpu_id);
309929949e86Sstevel 				}
310029949e86Sstevel 			}
310129949e86Sstevel 		}
310229949e86Sstevel 
310329949e86Sstevel 		if (error == 0 && cpa != NULL && cpu_is_offline(cpa)) {
310429949e86Sstevel 			if ((error = cpu_poweroff(cpa)) != 0) {
310529949e86Sstevel 				cmn_err(CE_WARN,
310629949e86Sstevel 				    "Processor %d failed to power off.",
310729949e86Sstevel 				    cpa->cpu_id);
310829949e86Sstevel 				if (errbuf != NULL) {
310929949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
311029949e86Sstevel 					    "processor %d failed to power off",
311129949e86Sstevel 					    cpa->cpu_id);
311229949e86Sstevel 				}
311329949e86Sstevel 			} else {
311429949e86Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
311529949e86Sstevel 				    cpa->cpu_id);
311629949e86Sstevel 			}
311729949e86Sstevel 		}
311829949e86Sstevel 
311929949e86Sstevel 		if (error == 0 && cpb != NULL && cpu_is_offline(cpb)) {
312029949e86Sstevel 			if ((error = cpu_poweroff(cpb)) != 0) {
312129949e86Sstevel 				cmn_err(CE_WARN,
312229949e86Sstevel 				    "Processor %d failed to power off.",
312329949e86Sstevel 				    cpb->cpu_id);
312429949e86Sstevel 
312529949e86Sstevel 				if (errbuf != NULL) {
312629949e86Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
312729949e86Sstevel 					    "processor %d failed to power off",
312829949e86Sstevel 					    cpb->cpu_id);
312929949e86Sstevel 				}
313029949e86Sstevel 			} else {
313129949e86Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
313229949e86Sstevel 				    cpb->cpu_id);
313329949e86Sstevel 			}
313429949e86Sstevel 		}
313529949e86Sstevel 
313629949e86Sstevel 		/*
313729949e86Sstevel 		 * If all the shutdowns completed, ONLY THEN, clear the
313829949e86Sstevel 		 * incorrectly valid dtags...
313929949e86Sstevel 		 *
314029949e86Sstevel 		 * IMPORTANT: it is an error to read or write dtags while
314129949e86Sstevel 		 * they are 'active'
314229949e86Sstevel 		 */
314329949e86Sstevel 		if (error == 0 && (cpa != NULL || cpb != NULL)) {
314429949e86Sstevel 			u_longlong_t base = 0;
314529949e86Sstevel 			int i;
314629949e86Sstevel #ifdef DEBUG
314729949e86Sstevel 			int nonz0 = 0;
314829949e86Sstevel 			int nonz1 = 0;
314929949e86Sstevel #endif
315029949e86Sstevel 			if (cpa != NULL)
315129949e86Sstevel 				base = FHC_DTAG_BASE(cpa->cpu_id);
315229949e86Sstevel 			if (cpb != NULL)
315329949e86Sstevel 				base = FHC_DTAG_BASE(cpb->cpu_id);
315429949e86Sstevel 			ASSERT(base != 0);
315529949e86Sstevel 
315629949e86Sstevel 			for (i = 0; i < FHC_DTAG_SIZE; i += FHC_DTAG_SKIP) {
315729949e86Sstevel 				u_longlong_t value = lddphysio(base+i);
315829949e86Sstevel #ifdef lint
315929949e86Sstevel 				value = value;
316029949e86Sstevel #endif
316129949e86Sstevel #ifdef DEBUG
316229949e86Sstevel 				if (cpa != NULL && (value & FHC_DTAG_LOW))
316329949e86Sstevel 					nonz0++;
316429949e86Sstevel 				if (cpb != NULL && (value & FHC_DTAG_HIGH))
316529949e86Sstevel 					nonz1++;
316629949e86Sstevel #endif
316729949e86Sstevel 				/* always clear the dtags */
316829949e86Sstevel 				stdphysio(base + i, 0ull);
316929949e86Sstevel 			}
317029949e86Sstevel #ifdef DEBUG
317129949e86Sstevel 			if (nonz0 || nonz1) {
317229949e86Sstevel 				cmn_err(CE_NOTE, "!dtag results: "
317329949e86Sstevel 				    "cpua valid %d, cpub valid %d",
317429949e86Sstevel 				    nonz0, nonz1);
317529949e86Sstevel 			}
317629949e86Sstevel #endif
317729949e86Sstevel 		}
317829949e86Sstevel 
317929949e86Sstevel 		break;
318029949e86Sstevel 
318129949e86Sstevel 	default:
318229949e86Sstevel 		break;
318329949e86Sstevel 	}
318429949e86Sstevel 
318529949e86Sstevel 	return (error);
318629949e86Sstevel }
318729949e86Sstevel 
318829949e86Sstevel /*
318929949e86Sstevel  * platform code for shutting down cpus.
319029949e86Sstevel  */
319129949e86Sstevel int
fhc_cpu_poweroff(struct cpu * cp)319229949e86Sstevel fhc_cpu_poweroff(struct cpu *cp)
319329949e86Sstevel {
319429949e86Sstevel 	int board;
319529949e86Sstevel 	fhc_bd_t *bd_list;
319629949e86Sstevel 	int delays;
319729949e86Sstevel 	extern void idle_stop_xcall(void);
319829949e86Sstevel 
319929949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
320029949e86Sstevel 	ASSERT((cp->cpu_flags & (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED)) ==
320119397407SSherry Moore 	    (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED));
320229949e86Sstevel 
320329949e86Sstevel 	/*
320429949e86Sstevel 	 * Lock the board so that we can safely access the
320529949e86Sstevel 	 * registers. This cannot be done inside the pause_cpus().
320629949e86Sstevel 	 */
320729949e86Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
320829949e86Sstevel 	bd_list = fhc_bdlist_lock(board);
320929949e86Sstevel 	ASSERT(fhc_bd_valid(board) && (bd_list->sc.type == CPU_BOARD));
321029949e86Sstevel 
321129949e86Sstevel 	/*
321229949e86Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
321329949e86Sstevel 	 * crosscalls to the detaching proc until it has cleared its
321429949e86Sstevel 	 * bit in cpu_ready_set.
321529949e86Sstevel 	 *
321629949e86Sstevel 	 * The CPU's remain paused and the prom_mutex is known to be free.
321729949e86Sstevel 	 * This prevents the x-trap victim from blocking when doing prom
321829949e86Sstevel 	 * IEEE-1275 calls at a high PIL level.
321929949e86Sstevel 	 */
322029949e86Sstevel 	promsafe_pause_cpus();
322129949e86Sstevel 
322229949e86Sstevel 	/*
322329949e86Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
322429949e86Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
322529949e86Sstevel 	 * prevent it from receiving cross calls and cross traps.
322629949e86Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
322729949e86Sstevel 	 */
322829949e86Sstevel 	mp_cpu_quiesce(cp);
322929949e86Sstevel 
323029949e86Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
323119397407SSherry Moore 	    (uint64_t)fhc_cpu_shutdown_self, (uint64_t)NULL);
323229949e86Sstevel 
323329949e86Sstevel 	/*
323429949e86Sstevel 	 * Wait for slave cpu to shutdown.
323529949e86Sstevel 	 * Sense this by watching the hardware EPDx bit.
323629949e86Sstevel 	 */
323729949e86Sstevel 	for (delays = FHC_SHUTDOWN_WAIT_MSEC; delays != 0; delays--) {
323829949e86Sstevel 		uint_t temp;
323929949e86Sstevel 
324029949e86Sstevel 		DELAY(1000);
324129949e86Sstevel 
324229949e86Sstevel 		/* get the current cpu power status */
324329949e86Sstevel 		temp = *bd_list->softsp->ctrl;
324429949e86Sstevel 
324529949e86Sstevel 		/* has the cpu actually signalled shutdown? */
324629949e86Sstevel 		if (FHC_CPU_IS_A(cp->cpu_id)) {
324729949e86Sstevel 			if (temp & FHC_EPDA_OFF)
324829949e86Sstevel 				break;
324929949e86Sstevel 		} else {
325029949e86Sstevel 			if (temp & FHC_EPDB_OFF)
325129949e86Sstevel 				break;
325229949e86Sstevel 		}
325329949e86Sstevel 	}
325429949e86Sstevel 
325529949e86Sstevel 	start_cpus();
325629949e86Sstevel 
325729949e86Sstevel 	fhc_bdlist_unlock();
325829949e86Sstevel 
325929949e86Sstevel 	/* A timeout means we've lost control of the cpu. */
326029949e86Sstevel 	if (delays == 0)
326129949e86Sstevel 		panic("Processor %d failed during shutdown", cp->cpu_id);
326229949e86Sstevel 
326329949e86Sstevel 	return (0);
326429949e86Sstevel }
326529949e86Sstevel 
326629949e86Sstevel /*
326729949e86Sstevel  * shutdown_self
326829949e86Sstevel  * slave side shutdown.  clean up and execute the shutdown sequence.
326929949e86Sstevel  */
327029949e86Sstevel static void
fhc_cpu_shutdown_self(void)327129949e86Sstevel fhc_cpu_shutdown_self(void)
327229949e86Sstevel {
327329949e86Sstevel 	extern void flush_windows(void);
327429949e86Sstevel 
327529949e86Sstevel 	flush_windows();
327629949e86Sstevel 
327729949e86Sstevel 	ASSERT(CPU->cpu_intr_actv == 0);
327829949e86Sstevel 	ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread ||
327929949e86Sstevel 	    CPU->cpu_thread == CPU->cpu_startup_thread);
328029949e86Sstevel 
328129949e86Sstevel 	CPU->cpu_flags = CPU_POWEROFF | CPU_OFFLINE | CPU_QUIESCED;
328229949e86Sstevel 
328329949e86Sstevel 	(void) prom_sunfire_cpu_off();	/* inform Ultra Enterprise prom */
328429949e86Sstevel 
328529949e86Sstevel 	os_completes_shutdown();
328629949e86Sstevel 
328729949e86Sstevel 	panic("fhc_cpu_shutdown_self: cannot return");
328829949e86Sstevel 	/*NOTREACHED*/
328929949e86Sstevel }
329029949e86Sstevel 
329129949e86Sstevel /*
329229949e86Sstevel  * Warm start CPU.
329329949e86Sstevel  */
329429949e86Sstevel static int
fhc_cpu_start(struct cpu * cp)329529949e86Sstevel fhc_cpu_start(struct cpu *cp)
329629949e86Sstevel {
329729949e86Sstevel 	int rv;
329829949e86Sstevel 	int cpuid = cp->cpu_id;
329929949e86Sstevel 	pnode_t nodeid;
330029949e86Sstevel 	extern void restart_other_cpu(int);
330129949e86Sstevel 
330229949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
330329949e86Sstevel 
330429949e86Sstevel 	/* power on cpu */
330529949e86Sstevel 	nodeid = cpunodes[cpuid].nodeid;
330629949e86Sstevel 	ASSERT(nodeid != (pnode_t)0);
330729949e86Sstevel 	rv = prom_wakeupcpu(nodeid);
330829949e86Sstevel 	if (rv != 0) {
330929949e86Sstevel 		cmn_err(CE_WARN, "Processor %d failed to power on.", cpuid);
331029949e86Sstevel 		return (EBUSY);
331129949e86Sstevel 	}
331229949e86Sstevel 
331329949e86Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
331429949e86Sstevel 
331529949e86Sstevel 	/*
331629949e86Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the slave cpu start.
331729949e86Sstevel 	 * This helps to quiesce the bus traffic a bit which makes
331829949e86Sstevel 	 * the tick sync routine in the prom more robust.
331929949e86Sstevel 	 */
332029949e86Sstevel 	restart_other_cpu(cpuid);
332129949e86Sstevel 
332229949e86Sstevel 	return (0);
332329949e86Sstevel }
332429949e86Sstevel 
332529949e86Sstevel /*
332629949e86Sstevel  * Power on CPU.
332729949e86Sstevel  */
332829949e86Sstevel int
fhc_cpu_poweron(struct cpu * cp)332929949e86Sstevel fhc_cpu_poweron(struct cpu *cp)
333029949e86Sstevel {
333129949e86Sstevel 	fhc_bd_t *bd_list;
333229949e86Sstevel 	enum temp_state state;
333329949e86Sstevel 	int board;
333429949e86Sstevel 	int status;
333529949e86Sstevel 	int status_other;
333629949e86Sstevel 	struct cpu *cp_other;
333729949e86Sstevel 
333829949e86Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
333929949e86Sstevel 	ASSERT(cpu_is_poweredoff(cp));
334029949e86Sstevel 
334129949e86Sstevel 	/* do not power on overtemperature cpu */
334229949e86Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
334329949e86Sstevel 	bd_list = fhc_bdlist_lock(board);
334429949e86Sstevel 
334529949e86Sstevel 	ASSERT(bd_list != NULL);
334629949e86Sstevel 	ASSERT(bd_list->sc.type == CPU_BOARD);
334729949e86Sstevel 	ASSERT(bd_list->dev_softsp != NULL);
334829949e86Sstevel 
334929949e86Sstevel 	state = ((struct environ_soft_state *)
335019397407SSherry Moore 	    bd_list->dev_softsp)->tempstat.state;
335129949e86Sstevel 
335229949e86Sstevel 	fhc_bdlist_unlock();
335329949e86Sstevel 	if ((state == TEMP_WARN) || (state == TEMP_DANGER))
335429949e86Sstevel 		return (EBUSY);
335529949e86Sstevel 
335629949e86Sstevel 	status = fhc_cpu_start(cp);
335729949e86Sstevel 
335829949e86Sstevel 	/* policy for dual cpu boards */
335929949e86Sstevel 
336029949e86Sstevel 	if ((status == 0) &&
336129949e86Sstevel 	    ((cp_other = cpu_get(FHC_OTHER_CPU_ID(cp->cpu_id))) != NULL)) {
336229949e86Sstevel 		/*
336329949e86Sstevel 		 * Do not leave board's other cpu idling in the prom.
336429949e86Sstevel 		 * Start the other cpu and set its state to P_OFFLINE.
336529949e86Sstevel 		 */
336629949e86Sstevel 		status_other = fhc_cpu_start(cp_other);
336729949e86Sstevel 		if (status_other != 0) {
336829949e86Sstevel 			panic("fhc: failed to start second CPU"
336929949e86Sstevel 			    " in pair %d & %d, error %d",
337029949e86Sstevel 			    cp->cpu_id, cp_other->cpu_id, status_other);
337129949e86Sstevel 		}
337229949e86Sstevel 	}
337329949e86Sstevel 
337429949e86Sstevel 	return (status);
337529949e86Sstevel }
337629949e86Sstevel 
337729949e86Sstevel /*
337829949e86Sstevel  * complete the shutdown sequence in case the firmware doesn't.
337929949e86Sstevel  *
338029949e86Sstevel  * If the firmware returns, then complete the shutdown code.
338129949e86Sstevel  * (sunfire firmware presently only updates its status.  the
338229949e86Sstevel  * OS must flush the D-tags and execute the shutdown instruction.)
338329949e86Sstevel  */
338429949e86Sstevel static void
os_completes_shutdown(void)338529949e86Sstevel os_completes_shutdown(void)
338629949e86Sstevel {
3387*50ed1c1eSToomas Soome 	pfn_t			pfn;
338829949e86Sstevel 	tte_t			tte;
338929949e86Sstevel 	volatile uint_t		*src;
339029949e86Sstevel 	volatile uint_t		*dst;
339129949e86Sstevel 	caddr_t			copy_addr;
339229949e86Sstevel 	extern void fhc_shutdown_asm(u_longlong_t, int);
339329949e86Sstevel 	extern void fhc_shutdown_asm_end(void);
339429949e86Sstevel 
339529949e86Sstevel 	copy_addr = shutdown_va + FHC_SRAM_OS_OFFSET;
339629949e86Sstevel 
339729949e86Sstevel 	/* compute sram global address for this operation */
339829949e86Sstevel 	pfn = FHC_LOCAL_OS_PAGEBASE >> MMU_PAGESHIFT;
339929949e86Sstevel 
340029949e86Sstevel 	/* force load i and d translations */
340129949e86Sstevel 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
340219397407SSherry Moore 	    TTE_PFN_INTHI(pfn);
340329949e86Sstevel 	tte.tte_intlo = TTE_PFN_INTLO(pfn) |
340419397407SSherry Moore 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT; /* un$ */
34051e2e7a75Shuah 	sfmmu_dtlb_ld_kva(shutdown_va, &tte);	/* load dtlb */
34061e2e7a75Shuah 	sfmmu_itlb_ld_kva(shutdown_va, &tte);	/* load itlb */
340729949e86Sstevel 
340829949e86Sstevel 	/*
340929949e86Sstevel 	 * copy the special shutdown function to sram
341029949e86Sstevel 	 * (this is a special integer copy that synchronizes with localspace
341129949e86Sstevel 	 * accesses.  we need special throttling to ensure copy integrity)
341229949e86Sstevel 	 */
341329949e86Sstevel 	for (src = (uint_t *)fhc_shutdown_asm, dst = (uint_t *)copy_addr;
341429949e86Sstevel 	    src < (uint_t *)fhc_shutdown_asm_end;
341529949e86Sstevel 	    src++, dst++) {
341629949e86Sstevel 		volatile uint_t dummy;
341729949e86Sstevel 
341829949e86Sstevel 		*dst = *src;
341929949e86Sstevel 		/*
342029949e86Sstevel 		 * ensure non corrupting single write operations to
342129949e86Sstevel 		 * localspace sram by interleaving reads with writes.
342229949e86Sstevel 		 */
342329949e86Sstevel 		dummy = *dst;
342429949e86Sstevel #ifdef lint
342529949e86Sstevel 		dummy = dummy;
342629949e86Sstevel #endif
342729949e86Sstevel 	}
342829949e86Sstevel 
342929949e86Sstevel 	/*
343029949e86Sstevel 	 * Call the shutdown sequencer.
343129949e86Sstevel 	 * NOTE: the base flush address must be unique for each MID.
343229949e86Sstevel 	 */
343329949e86Sstevel 	((void (*)(u_longlong_t, int))copy_addr)(
343419397407SSherry Moore 	    FHC_BASE_NOMEM + CPU->cpu_id * FHC_MAX_ECACHE_SIZE,
343519397407SSherry Moore 	    cpunodes[CPU->cpu_id].ecache_size);
343629949e86Sstevel }
343729949e86Sstevel 
343829949e86Sstevel enum temp_state
fhc_env_temp_state(int board)343929949e86Sstevel fhc_env_temp_state(int board)
344029949e86Sstevel {
344129949e86Sstevel 	fhc_bd_t *bdp;
344229949e86Sstevel 	struct environ_soft_state *envp;
344329949e86Sstevel 
344429949e86Sstevel 	ASSERT(fhc_bd_valid(board));
344529949e86Sstevel 
344629949e86Sstevel 	bdp = fhc_bd(board);
344729949e86Sstevel 
344829949e86Sstevel 	/*
344929949e86Sstevel 	 * Due to asynchronous attach of environ, environ may
345029949e86Sstevel 	 * not be attached by the time we start calling this routine
345129949e86Sstevel 	 * to check the temperature state.  Environ not attaching is
345229949e86Sstevel 	 * pathological so this will only cover the time between
345329949e86Sstevel 	 * board connect and environ attach.
345429949e86Sstevel 	 */
345529949e86Sstevel 	if (!bdp->dev_softsp) {
345629949e86Sstevel 		return (TEMP_OK);
345729949e86Sstevel 	}
345829949e86Sstevel 	envp = (struct environ_soft_state *)bdp->dev_softsp;
345929949e86Sstevel 
346029949e86Sstevel 	return (envp->tempstat.state);
346129949e86Sstevel }
346229949e86Sstevel 
346329949e86Sstevel static void
fhc_tod_fault(enum tod_fault_type tod_bad)346429949e86Sstevel fhc_tod_fault(enum tod_fault_type tod_bad)
346529949e86Sstevel {
346629949e86Sstevel 	int board_num = 0;
346729949e86Sstevel 	enum ft_class class = FT_SYSTEM;
346829949e86Sstevel 	uint64_t addr;
346929949e86Sstevel 
347029949e86Sstevel 	addr = (va_to_pa((void *)v_eeprom_addr)) >> BOARD_PHYADDR_SHIFT;
347129949e86Sstevel 
347229949e86Sstevel 	if ((addr & CLOCKBOARD_PHYADDR_BITS) != CLOCKBOARD_PHYADDR_BITS) {
347329949e86Sstevel 		/* if tod is not on clock board, */
347429949e86Sstevel 		/* it'd be on one of io boards */
347529949e86Sstevel 		board_num = (addr >> IO_BOARD_NUMBER_SHIFT)
347619397407SSherry Moore 		    & IO_BOARD_NUMBER_MASK;
347729949e86Sstevel 		class = FT_BOARD;
347829949e86Sstevel 	}
347929949e86Sstevel 
348029949e86Sstevel 	switch (tod_bad) {
348129949e86Sstevel 	case TOD_NOFAULT:
348229949e86Sstevel 		clear_fault(board_num, FT_TODFAULT, class);
348329949e86Sstevel 		break;
348429949e86Sstevel 	case TOD_REVERSED:
348529949e86Sstevel 	case TOD_STALLED:
348629949e86Sstevel 	case TOD_JUMPED:
348729949e86Sstevel 	case TOD_RATECHANGED:
348829949e86Sstevel 		reg_fault(board_num, FT_TODFAULT, class);
348929949e86Sstevel 		break;
349029949e86Sstevel 	default:
349129949e86Sstevel 		break;
349229949e86Sstevel 	}
349329949e86Sstevel }
3494