xref: /onnv-gate/usr/src/uts/sun4u/sunfire/io/fhc.c (revision 11311:639e7bc0b42f)
11341Sstevel /*
21341Sstevel  * CDDL HEADER START
31341Sstevel  *
41341Sstevel  * The contents of this file are subject to the terms of the
51341Sstevel  * Common Development and Distribution License (the "License").
61341Sstevel  * You may not use this file except in compliance with the License.
71341Sstevel  *
81341Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91341Sstevel  * or http://www.opensolaris.org/os/licensing.
101341Sstevel  * See the License for the specific language governing permissions
111341Sstevel  * and limitations under the License.
121341Sstevel  *
131341Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141341Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151341Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161341Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171341Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181341Sstevel  *
191341Sstevel  * CDDL HEADER END
201341Sstevel  */
211341Sstevel 
221341Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241341Sstevel  * Use is subject to license terms.
251341Sstevel  */
261341Sstevel 
271341Sstevel 
281341Sstevel #include <sys/types.h>
291341Sstevel #include <sys/conf.h>
301341Sstevel #include <sys/ddi.h>
311341Sstevel #include <sys/sunddi.h>
321341Sstevel #include <sys/ddi_impldefs.h>
331341Sstevel #include <sys/obpdefs.h>
341341Sstevel #include <sys/promif.h>
351341Sstevel #include <sys/cmn_err.h>
361341Sstevel #include <sys/errno.h>
371341Sstevel #include <sys/kmem.h>
381341Sstevel #include <sys/vmem.h>
391341Sstevel #include <sys/debug.h>
401341Sstevel #include <sys/sysmacros.h>
411341Sstevel #include <sys/intreg.h>
421341Sstevel #include <sys/autoconf.h>
431341Sstevel #include <sys/modctl.h>
441341Sstevel #include <sys/spl.h>
451341Sstevel #include <sys/time.h>
461341Sstevel #include <sys/systm.h>
471341Sstevel #include <sys/machsystm.h>
481341Sstevel #include <sys/cpu.h>
491341Sstevel #include <sys/cpuvar.h>
501341Sstevel #include <sys/x_call.h>		/* xt_one() */
511341Sstevel #include <sys/membar.h>
521341Sstevel #include <sys/vm.h>
531341Sstevel #include <vm/seg_kmem.h>
541341Sstevel #include <vm/hat_sfmmu.h>
551341Sstevel #include <sys/promimpl.h>
561341Sstevel #include <sys/prom_plat.h>
571341Sstevel #include <sys/cpu_module.h>	/* flush_instr_mem() */
581341Sstevel #include <sys/procset.h>
591341Sstevel #include <sys/fhc.h>
601341Sstevel #include <sys/ac.h>
611341Sstevel #include <sys/environ.h>
621341Sstevel #include <sys/jtag.h>
631341Sstevel #include <sys/nexusdebug.h>
641341Sstevel #include <sys/ac.h>
651341Sstevel #include <sys/ddi_subrdefs.h>
661341Sstevel #include <sys/eeprom.h>
671341Sstevel #include <sys/sdt.h>
681341Sstevel #include <sys/ddi_implfuncs.h>
691341Sstevel #include <sys/ontrap.h>
701341Sstevel 
711341Sstevel #ifndef TRUE
721341Sstevel #define	TRUE (1)
731341Sstevel #endif
741341Sstevel #ifndef FALSE
751341Sstevel #define	FALSE (0)
761341Sstevel #endif
771341Sstevel 
781341Sstevel /*
791341Sstevel  * Function to register and deregister callbacks, for sunfire only.
801341Sstevel  */
811341Sstevel extern void plat_register_tod_fault(void (*func)(enum tod_fault_type));
821341Sstevel 
831341Sstevel /*
841341Sstevel  * This table represents the FHC interrupt priorities.  They range from
851341Sstevel  * 1-15, and have been modeled after the sun4d interrupts. The mondo
861341Sstevel  * number anded with 0x7 is used to index into this table. This was
871341Sstevel  * done to save table space.
881341Sstevel  */
891341Sstevel static int fhc_int_priorities[] = {
901341Sstevel 	PIL_15,			/* System interrupt priority */
911341Sstevel 	PIL_12,			/* zs interrupt priority */
921341Sstevel 	PIL_15,			/* TOD interrupt priority */
931341Sstevel 	PIL_15			/* Fan Fail priority */
941341Sstevel };
951341Sstevel 
961341Sstevel static void fhc_tod_fault(enum tod_fault_type tod_bad);
971341Sstevel 
981341Sstevel /*
991341Sstevel  * The dont_calibrate variable is meant to be set to one in /etc/system
1001341Sstevel  * or by boot -h so that the calibration tables are not used. This
1011341Sstevel  * is useful for checking thermistors whose output seems to be incorrect.
1021341Sstevel  */
1031341Sstevel static int dont_calibrate = 0;
1041341Sstevel 
1051341Sstevel /* Only one processor should powerdown the system. */
1061341Sstevel static int powerdown_started = 0;
1071341Sstevel 
1081341Sstevel /* Let user disable overtemp powerdown. */
1091341Sstevel int enable_overtemp_powerdown = 1;
1101341Sstevel 
1111341Sstevel /*
1121341Sstevel  * The following tables correspond to the degress Celcius for each count
1131341Sstevel  * value possible from the 8-bit A/C convertors on each type of system
1141341Sstevel  * board for the UltraSPARC Server systems. To access a temperature,
1151341Sstevel  * just index into the correct table using the count from the A/D convertor
1161341Sstevel  * register, and that is the correct temperature in degress Celsius. These
1171341Sstevel  * values can be negative.
1181341Sstevel  */
1191341Sstevel static short cpu_table[] = {
1201341Sstevel -16,	-14,	-12,	-10,	-8,	-6,	-4,	-2,	/* 0-7 */
1211341Sstevel 1,	4,	6,	8,	10,	12,	13,	15,	/* 8-15 */
1221341Sstevel 16,	18,	19,	20,	22,	23,	24,	25,	/* 16-23 */
1231341Sstevel 26,	27,	28,	29,	30,	31,	32,	33,	/* 24-31 */
1241341Sstevel 34,	35,	35,	36,	37,	38,	39,	39,	/* 32-39 */
1251341Sstevel 40,	41,	41,	42,	43,	44,	44,	45,	/* 40-47 */
1261341Sstevel 46,	46,	47,	47,	48,	49,	49,	50,	/* 48-55 */
1271341Sstevel 51,	51,	52,	53,	53,	54,	54,	55,	/* 56-63 */
1281341Sstevel 55,	56,	56,	57,	57,	58,	58,	59,	/* 64-71 */
1291341Sstevel 60,	60,	61,	61,	62,	62,	63,	63,	/* 72-79 */
1301341Sstevel 64,	64,	65,	65,	66,	66,	67,	67,	/* 80-87 */
1311341Sstevel 68,	68,	69,	69,	70,	70,	71,	71,	/* 88-95 */
1321341Sstevel 72,	72,	73,	73,	74,	74,	75,	75,	/* 96-103 */
1331341Sstevel 76,	76,	77,	77,	78,	78,	79,	79,	/* 104-111 */
1341341Sstevel 80,	80,	81,	81,	82,	82,	83,	83,	/* 112-119 */
1351341Sstevel 84,	84,	85,	85,	86,	86,	87,	87,	/* 120-127 */
1361341Sstevel 88,	88,	89,	89,	90,	90,	91,	91,	/* 128-135 */
1371341Sstevel 92,	92,	93,	93,	94,	94,	95,	95,	/* 136-143 */
1381341Sstevel 96,	96,	97,	98,	98,	99,	99,	100,	/* 144-151 */
1391341Sstevel 100,	101,	101,	102,	103,	103,	104,	104,	/* 152-159 */
1401341Sstevel 105,	106,	106,	107,	107,	108,	109,	109,	/* 160-167 */
1411341Sstevel 110,								/* 168 */
1421341Sstevel };
1431341Sstevel 
1441341Sstevel #define	CPU_MX_CNT	(sizeof (cpu_table)/sizeof (short))
1451341Sstevel 
1461341Sstevel static short cpu2_table[] = {
1471341Sstevel -17,	-16,	-15,	-14,	-13,	-12,	-11,	-10,	/* 0-7 */
1481341Sstevel -9,	-8,	-7,	-6,	-5,	-4,	-3,	-2,	/* 8-15 */
1491341Sstevel -1,	0,	1,	2,	3,	4,	5,	6,	/* 16-23 */
1501341Sstevel 7,	8,	9,	10,	11,	12,	13,	13,	/* 24-31 */
1511341Sstevel 14,	15,	16,	16,	17,	18,	18,	19,	/* 32-39 */
1521341Sstevel 20,	20,	21,	22,	22,	23,	24,	24,	/* 40-47 */
1531341Sstevel 25,	25,	26,	26,	27,	27,	28,	28,	/* 48-55 */
1541341Sstevel 29,	30,	30,	31,	31,	32,	32,	33,	/* 56-63 */
1551341Sstevel 33,	34,	34,	35,	35,	36,	36,	37,	/* 64-71 */
1561341Sstevel 37,	37,	38,	38,	39,	39,	40,	40,	/* 72-79 */
1571341Sstevel 41,	41,	42,	42,	43,	43,	43,	44,	/* 80-87 */
1581341Sstevel 44,	45,	45,	46,	46,	46,	47,	47,	/* 88-95 */
1591341Sstevel 48,	48,	49,	49,	50,	50,	50,	51,	/* 96-103 */
1601341Sstevel 51,	52,	52,	53,	53,	53,	54,	54,	/* 104-111 */
1611341Sstevel 55,	55,	56,	56,	56,	57,	57,	58,	/* 112-119 */
1621341Sstevel 58,	59,	59,	59,	60,	60,	61,	61,	/* 120-127 */
1631341Sstevel 62,	62,	63,	63,	63,	64,	64,	65,	/* 128-135 */
1641341Sstevel 65,	66,	66,	67,	67,	68,	68,	68,	/* 136-143 */
1651341Sstevel 69,	69,	70,	70,	71,	71,	72,	72,	/* 144-151 */
1661341Sstevel 73,	73,	74,	74,	75,	75,	76,	76,	/* 152-159 */
1671341Sstevel 77,	77,	78,	78,	79,	79,	80,	80,	/* 160-167 */
1681341Sstevel 81,	81,	82,	83,	83,	84,	84,	85,	/* 168-175 */
1691341Sstevel 85,	86,	87,	87,	88,	88,	89,	90,	/* 176-183 */
1701341Sstevel 90,	91,	92,	92,	93,	94,	94,	95,	/* 184-191 */
1711341Sstevel 96,	96,	97,	98,	99,	99,	100,	101,	/* 192-199 */
1721341Sstevel 102,	103,	103,	104,	105,	106,	107,	108,	/* 200-207 */
1731341Sstevel 109,	110,							/* 208-209 */
1741341Sstevel };
1751341Sstevel 
1761341Sstevel #define	CPU2_MX_CNT	(sizeof (cpu2_table)/sizeof (short))
1771341Sstevel 
1781341Sstevel static short io_table[] = {
1791341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 0-7 */
1801341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 8-15 */
1811341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 16-23 */
1821341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 24-31 */
1831341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 32-39 */
1841341Sstevel 0,	3,	7,	10,	13,	15,	17,	19,	/* 40-47 */
1851341Sstevel 21,	23,	25,	27,	28,	30,	31,	32,	/* 48-55 */
1861341Sstevel 34,	35,	36,	37,	38,	39,	41,	42,	/* 56-63 */
1871341Sstevel 43,	44,	45,	46,	46,	47,	48,	49,	/* 64-71 */
1881341Sstevel 50,	51,	52,	53,	53,	54,	55,	56,	/* 72-79 */
1891341Sstevel 57,	57,	58,	59,	60,	60,	61,	62,	/* 80-87 */
1901341Sstevel 62,	63,	64,	64,	65,	66,	66,	67,	/* 88-95 */
1911341Sstevel 68,	68,	69,	70,	70,	71,	72,	72,	/* 96-103 */
1921341Sstevel 73,	73,	74,	75,	75,	76,	77,	77,	/* 104-111 */
1931341Sstevel 78,	78,	79,	80,	80,	81,	81,	82,	/* 112-119 */
1941341Sstevel };
1951341Sstevel 
1961341Sstevel #define	IO_MN_CNT	40
1971341Sstevel #define	IO_MX_CNT	(sizeof (io_table)/sizeof (short))
1981341Sstevel 
1991341Sstevel static short clock_table[] = {
2001341Sstevel 0,	0,	0,	0,	0,	0,	0,	0,	/* 0-7 */
2011341Sstevel 0,	0,	0,	0,	1,	2,	4,	5,	/* 8-15 */
2021341Sstevel 7,	8,	10,	11,	12,	13,	14,	15,	/* 16-23 */
2031341Sstevel 17,	18,	19,	20,	21,	22,	23,	24,	/* 24-31 */
2041341Sstevel 24,	25,	26,	27,	28,	29,	29,	30,	/* 32-39 */
2051341Sstevel 31,	32,	32,	33,	34,	35,	35,	36,	/* 40-47 */
2061341Sstevel 37,	38,	38,	39,	40,	40,	41,	42,	/* 48-55 */
2071341Sstevel 42,	43,	44,	44,	45,	46,	46,	47,	/* 56-63 */
2081341Sstevel 48,	48,	49,	50,	50,	51,	52,	52,	/* 64-71 */
2091341Sstevel 53,	54,	54,	55,	56,	57,	57,	58,	/* 72-79 */
2101341Sstevel 59,	59,	60,	60,	61,	62,	63,	63,	/* 80-87 */
2111341Sstevel 64,	65,	65,	66,	67,	68,	68,	69,	/* 88-95 */
2121341Sstevel 70,	70,	71,	72,	73,	74,	74,	75,	/* 96-103 */
2131341Sstevel 76,	77,	78,	78,	79,	80,	81,	82,	/* 104-111 */
2141341Sstevel };
2151341Sstevel 
2161341Sstevel #define	CLK_MN_CNT	11
2171341Sstevel #define	CLK_MX_CNT	(sizeof (clock_table)/sizeof (short))
2181341Sstevel 
2191341Sstevel /*
2201341Sstevel  * System temperature limits.
2211341Sstevel  *
2221341Sstevel  * The following variables are the warning and danger limits for the
2231341Sstevel  * different types of system boards. The limits are different because
2241341Sstevel  * the various boards reach different nominal temperatures because
2251341Sstevel  * of the different components that they contain.
2261341Sstevel  *
2271341Sstevel  * The warning limit is the temperature at which the user is warned.
2281341Sstevel  * The danger limit is the temperature at which the system is shutdown.
2291341Sstevel  * In the case of CPU/Memory system boards, the system will attempt
2301341Sstevel  * to offline and power down processors on a board in an attempt to
2311341Sstevel  * bring the board back into the nominal temperature range before
2321341Sstevel  * shutting down the system.
2331341Sstevel  *
2341341Sstevel  * These values can be tuned via /etc/system or boot -h.
2351341Sstevel  */
2361341Sstevel short cpu_warn_temp = 73;	/* CPU/Memory Warning Temperature */
2371341Sstevel short cpu_danger_temp = 83;	/* CPU/Memory Danger Temperature */
2381341Sstevel short io_warn_temp = 60;	/* IO Board Warning Temperature */
2391341Sstevel short io_danger_temp = 68;	/* IO Board Danger Temperature */
2401341Sstevel short clk_warn_temp = 60;	/* Clock Board Warning Temperature */
2411341Sstevel short clk_danger_temp = 68;	/* Clock Board Danger Temperature */
2421341Sstevel 
2431341Sstevel short dft_warn_temp = 60;	/* default warning temp value */
2441341Sstevel short dft_danger_temp = 68;	/* default danger temp value */
2451341Sstevel 
2461341Sstevel short cpu_warn_temp_4x = 60; 	/* CPU/Memory warning temp for 400 MHZ */
2471341Sstevel short cpu_danger_temp_4x = 68;	/* CPU/Memory danger temp for 400 MHZ */
2481341Sstevel 
2491341Sstevel /*
2501341Sstevel  * This variable tells us if we are in a heat chamber. It is set
2511341Sstevel  * early on in boot, after we check the OBP 'mfg-mode' property in
2521341Sstevel  * the options node.
2531341Sstevel  */
2541341Sstevel static int temperature_chamber = -1;
2551341Sstevel 
2561341Sstevel /*
2571341Sstevel  * The fhc memloc structure is protected under the bdlist lock
2581341Sstevel  */
2591341Sstevel static struct fhc_memloc *fhc_base_memloc = NULL;
2601341Sstevel 
2611341Sstevel /*
2621341Sstevel  * Driver global fault list mutex and list head pointer. The list is
2631341Sstevel  * protected by the mutex and contains a record of all known faults.
2641341Sstevel  * Faults can be inherited from the PROM or detected by the kernel.
2651341Sstevel  */
2661341Sstevel static kmutex_t ftlist_mutex;
2671341Sstevel static struct ft_link_list *ft_list = NULL;
2681341Sstevel static int ft_nfaults = 0;
2691341Sstevel 
2701341Sstevel /*
2711341Sstevel  * Table of all known fault strings. This table is indexed by the fault
2721341Sstevel  * type. Do not change the ordering of the table without redefining the
2731341Sstevel  * fault type enum list on fhc.h.
2741341Sstevel  */
2751341Sstevel char *ft_str_table[] = {
2761341Sstevel 	"Core Power Supply",		/* FT_CORE_PS */
2771341Sstevel 	"Overtemp",			/* FT_OVERTEMP */
2781341Sstevel 	"AC Power",			/* FT_AC_PWR */
2791341Sstevel 	"Peripheral Power Supply",	/* FT_PPS */
2801341Sstevel 	"System 3.3 Volt Power",	/* FT_CLK_33 */
2811341Sstevel 	"System 5.0 Volt Power",	/* FT_CLK_50 */
2821341Sstevel 	"Peripheral 5.0 Volt Power",	/* FT_V5_P */
2831341Sstevel 	"Peripheral 12 Volt Power",	/* FT_V12_P */
2841341Sstevel 	"Auxiliary 5.0 Volt Power",	/* FT_V5_AUX */
2851341Sstevel 	"Peripheral 5.0 Volt Precharge", /* FT_V5_P_PCH */
2861341Sstevel 	"Peripheral 12 Volt Precharge",	/* FT_V12_P_PCH */
2871341Sstevel 	"System 3.3 Volt Precharge",	/* FT_V3_PCH */
2881341Sstevel 	"System 5.0 Volt Precharge",	/* FT_V5_PCH */
2891341Sstevel 	"Peripheral Power Supply Fans",	/* FT_PPS_FAN */
2901341Sstevel 	"Rack Exhaust Fan",		/* FT_RACK_EXH */
2911341Sstevel 	"Disk Drive Fan",		/* FT_DSK_FAN */
2921341Sstevel 	"AC Box Fan",			/* FT_AC_FAN */
2931341Sstevel 	"Key Switch Fan",		/* FT_KEYSW_FAN */
2941341Sstevel 	"Minimum Power",		/* FT_INSUFFICIENT_POWER */
2951341Sstevel 	"PROM detected",		/* FT_PROM */
2961341Sstevel 	"Hot Plug Support System",	/* FT_HOT_PLUG */
2971341Sstevel 	"TOD"				/* FT_TODFAULT */
2981341Sstevel };
2991341Sstevel 
3001341Sstevel static int ft_max_index = (sizeof (ft_str_table) / sizeof (char *));
3011341Sstevel 
3021341Sstevel /*
3031341Sstevel  * Function prototypes
3041341Sstevel  */
3051341Sstevel static int fhc_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t,
3061341Sstevel 	void *, void *);
3071341Sstevel static int fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip,
3081341Sstevel 	ddi_intr_op_t intr_op, ddi_intr_handle_impl_t *hdlp, void *result);
3091341Sstevel 
3101341Sstevel static int fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
3111341Sstevel 	ddi_intr_handle_impl_t *hdlp);
3121341Sstevel static void fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
3131341Sstevel 	ddi_intr_handle_impl_t *hdlp);
3141341Sstevel 
3151341Sstevel static int fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
3161341Sstevel static int fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
3171341Sstevel static int fhc_init(struct fhc_soft_state *softsp);
3181341Sstevel static void fhc_unmap_regs(struct fhc_soft_state *softsp);
3191341Sstevel static enum board_type fhc_board_type(struct fhc_soft_state *, int);
3201341Sstevel 
3211341Sstevel static void
3221341Sstevel fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign);
3231341Sstevel 
3241341Sstevel static int
3251341Sstevel fhc_ctlops_peekpoke(ddi_ctl_enum_t, peekpoke_ctlops_t *, void *result);
3261341Sstevel 
3271341Sstevel static void fhc_add_kstats(struct fhc_soft_state *);
3281341Sstevel static int fhc_kstat_update(kstat_t *, int);
3291341Sstevel static int check_for_chamber(void);
3301341Sstevel static int ft_ks_snapshot(struct kstat *, void *, int);
3311341Sstevel static int ft_ks_update(struct kstat *, int);
3321341Sstevel static int check_central(int board);
3331341Sstevel 
3341341Sstevel /*
3351341Sstevel  * board type and A/D convertor output passed in and real temperature
3361341Sstevel  * is returned.
3371341Sstevel  */
3381341Sstevel static short calibrate_temp(enum board_type, uchar_t, uint_t);
3391341Sstevel static enum temp_state get_temp_state(enum board_type, short, int);
3401341Sstevel 
3411341Sstevel /* Routine to determine if there are CPUs on this board. */
3421341Sstevel static int cpu_on_board(int);
3431341Sstevel 
3441341Sstevel static void build_bd_display_str(char *, enum board_type, int);
3451341Sstevel 
3461341Sstevel /* Interrupt distribution callback function. */
3471341Sstevel static void fhc_intrdist(void *);
3481341Sstevel 
3491341Sstevel /* CPU power control */
3501341Sstevel int fhc_cpu_poweroff(struct cpu *);	/* cpu_poweroff()->platform */
3511341Sstevel int fhc_cpu_poweron(struct cpu *);	/* cpu_poweron()->platform */
3521341Sstevel 
3531341Sstevel extern struct cpu_node cpunodes[];
3541341Sstevel extern void halt(char *);
3551341Sstevel 
3561341Sstevel /*
3571341Sstevel  * Configuration data structures
3581341Sstevel  */
3591341Sstevel static struct bus_ops fhc_bus_ops = {
3601341Sstevel 	BUSO_REV,
3611341Sstevel 	ddi_bus_map,		/* map */
3621341Sstevel 	0,			/* get_intrspec */
3631341Sstevel 	0,			/* add_intrspec */
3641341Sstevel 	0,			/* remove_intrspec */
3651341Sstevel 	i_ddi_map_fault,	/* map_fault */
3661341Sstevel 	ddi_no_dma_map,		/* dma_map */
3671341Sstevel 	ddi_no_dma_allochdl,
3681341Sstevel 	ddi_no_dma_freehdl,
3691341Sstevel 	ddi_no_dma_bindhdl,
3701341Sstevel 	ddi_no_dma_unbindhdl,
3711341Sstevel 	ddi_no_dma_flush,
3721341Sstevel 	ddi_no_dma_win,
3731341Sstevel 	ddi_dma_mctl,		/* dma_ctl */
3741341Sstevel 	fhc_ctlops,		/* ctl */
3751341Sstevel 	ddi_bus_prop_op,	/* prop_op */
3761341Sstevel 	0,			/* (*bus_get_eventcookie)();	*/
3771341Sstevel 	0,			/* (*bus_add_eventcall)();	*/
3781341Sstevel 	0,			/* (*bus_remove_eventcall)();	*/
3791341Sstevel 	0,			/* (*bus_post_event)();		*/
3801341Sstevel 	0,			/* (*bus_intr_control)();	*/
3811341Sstevel 	0,			/* (*bus_config)();		*/
3821341Sstevel 	0,			/* (*bus_unconfig)();		*/
3831341Sstevel 	0,			/* (*bus_fm_init)();		*/
3841341Sstevel 	0,			/* (*bus_fm_fini)();		*/
3851341Sstevel 	0,			/* (*bus_fm_access_enter)();	*/
3861341Sstevel 	0,			/* (*bus_fm_access_exit)();	*/
3871341Sstevel 	0,			/* (*bus_power)();		*/
3881341Sstevel 	fhc_intr_ops		/* (*bus_intr_op)();		*/
3891341Sstevel };
3901341Sstevel 
3911341Sstevel static struct cb_ops fhc_cb_ops = {
3921341Sstevel 	nulldev,		/* open */
3931341Sstevel 	nulldev,		/* close */
3941341Sstevel 	nulldev,		/* strategy */
3951341Sstevel 	nulldev,		/* print */
3961341Sstevel 	nulldev,		/* dump */
3971341Sstevel 	nulldev,		/* read */
3981341Sstevel 	nulldev,		/* write */
3991341Sstevel 	nulldev, 		/* ioctl */
4001341Sstevel 	nodev,			/* devmap */
4011341Sstevel 	nodev,			/* mmap */
4021341Sstevel 	nodev,			/* segmap */
4031341Sstevel 	nochpoll,		/* poll */
4041341Sstevel 	ddi_prop_op,		/* cb_prop_op */
4051341Sstevel 	0,			/* streamtab */
4061341Sstevel 	D_MP|D_NEW|D_HOTPLUG,	/* Driver compatibility flag */
4071341Sstevel 	CB_REV,			/* rev */
4081341Sstevel 	nodev,			/* cb_aread */
4091341Sstevel 	nodev			/* cb_awrite */
4101341Sstevel };
4111341Sstevel 
4121341Sstevel static struct dev_ops fhc_ops = {
4131341Sstevel 	DEVO_REV,		/* rev */
4141341Sstevel 	0,			/* refcnt  */
4151341Sstevel 	ddi_no_info,		/* getinfo */
4161341Sstevel 	nulldev,		/* identify */
4171341Sstevel 	nulldev,		/* probe */
4181341Sstevel 	fhc_attach,		/* attach */
4191341Sstevel 	fhc_detach,		/* detach */
4201341Sstevel 	nulldev,		/* reset */
4211341Sstevel 	&fhc_cb_ops,		/* cb_ops */
4221341Sstevel 	&fhc_bus_ops,		/* bus_ops */
4237656SSherry.Moore@Sun.COM 	nulldev,		/* power */
4247656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce */
4251341Sstevel };
4261341Sstevel 
4271341Sstevel /*
4281341Sstevel  * Driver globals
4291341Sstevel  * TODO - We need to investigate what locking needs to be done here.
4301341Sstevel  */
4311341Sstevel void *fhcp;				/* fhc soft state hook */
4321341Sstevel 
4331341Sstevel extern struct mod_ops mod_driverops;
4341341Sstevel 
4351341Sstevel static struct modldrv modldrv = {
4361341Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
4377656SSherry.Moore@Sun.COM 	"FHC Nexus",		/* Name of module. */
4381341Sstevel 	&fhc_ops,		/* driver ops */
4391341Sstevel };
4401341Sstevel 
4411341Sstevel static struct modlinkage modlinkage = {
4421341Sstevel 	MODREV_1,		/* rev */
4431341Sstevel 	(void *)&modldrv,
4441341Sstevel 	NULL
4451341Sstevel };
4461341Sstevel 
4471341Sstevel 
4481341Sstevel /*
4491341Sstevel  * These are the module initialization routines.
4501341Sstevel  */
4511341Sstevel 
4521341Sstevel static caddr_t shutdown_va;
4531341Sstevel 
4541341Sstevel int
_init(void)4551341Sstevel _init(void)
4561341Sstevel {
4571341Sstevel 	int error;
4581341Sstevel 
4591341Sstevel 	if ((error = ddi_soft_state_init(&fhcp,
4601341Sstevel 	    sizeof (struct fhc_soft_state), 1)) != 0)
4611341Sstevel 		return (error);
4621341Sstevel 
4631341Sstevel 	fhc_bdlist_init();
4641341Sstevel 	mutex_init(&ftlist_mutex, NULL, MUTEX_DEFAULT, NULL);
4651341Sstevel 
4661341Sstevel 	shutdown_va = vmem_alloc(heap_arena, PAGESIZE, VM_SLEEP);
4671341Sstevel 	ASSERT(shutdown_va != NULL);
4681341Sstevel 
4691341Sstevel 	plat_register_tod_fault(fhc_tod_fault);
4701341Sstevel 
4711341Sstevel 	return (mod_install(&modlinkage));
4721341Sstevel }
4731341Sstevel 
4741341Sstevel int
_fini(void)4751341Sstevel _fini(void)
4761341Sstevel {
4771341Sstevel 	int error;
4781341Sstevel 
4791341Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
4801341Sstevel 		return (error);
4811341Sstevel 
4821341Sstevel 	plat_register_tod_fault(NULL);
4831341Sstevel 
4841341Sstevel 	mutex_destroy(&ftlist_mutex);
4851341Sstevel 
4861341Sstevel 	fhc_bdlist_fini();
4871341Sstevel 
4881341Sstevel 	ddi_soft_state_fini(&fhcp);
4891341Sstevel 
4901341Sstevel 	return (0);
4911341Sstevel }
4921341Sstevel 
4931341Sstevel int
_info(struct modinfo * modinfop)4941341Sstevel _info(struct modinfo *modinfop)
4951341Sstevel {
4961341Sstevel 	return (mod_info(&modlinkage, modinfop));
4971341Sstevel }
4981341Sstevel 
4991341Sstevel /*
5001341Sstevel  * Reset the interrupt mapping registers.
5011341Sstevel  * This function resets the values during DDI_RESUME.
5021341Sstevel  *
5031341Sstevel  * NOTE: This function will not work for a full CPR cycle
5041341Sstevel  * and is currently designed to handle the RESUME after a connect.
5051341Sstevel  *
5061341Sstevel  * Note about the PROM handling of moving CENTRAL to another board:
5071341Sstevel  * The PROM moves the IGN identity (igr register) from the
5081341Sstevel  * original CENTRAL to the new one. This means that we do not
5091341Sstevel  * duplicate the fhc_attach code that sets it to (board number * 2).
5101341Sstevel  * We rely on only using FHC interrupts from one board only
5111341Sstevel  * (the UART and SYS interrupts) so that the values of the other IGNs
5121341Sstevel  * are irrelevant. The benefit of this approach is that we don't
5131341Sstevel  * have to have to tear down and rebuild the interrupt records
5141341Sstevel  * for UART and SYS. It is also why we don't try to change the
5151341Sstevel  * board number in the fhc instance for the clock board.
5161341Sstevel  */
5171341Sstevel static void
fhc_handle_imr(struct fhc_soft_state * softsp)5181341Sstevel fhc_handle_imr(struct fhc_soft_state *softsp)
5191341Sstevel {
5201341Sstevel 	int i;
5211341Sstevel 	int cent;
5221341Sstevel 	uint_t tmp_reg;
5231341Sstevel 
5241341Sstevel 
5251341Sstevel 	if (softsp->is_central) {
5261341Sstevel 		uint_t want_igr, act_igr;
5271341Sstevel 
5281341Sstevel 		want_igr = softsp->list->sc.board << 1;
5291341Sstevel 		act_igr = *softsp->igr & 0x1f;
5301341Sstevel 		if (want_igr != act_igr) {
5311341Sstevel 			*softsp->igr = want_igr;
5321341Sstevel 			tmp_reg = *softsp->igr;
5331341Sstevel #ifdef lint
5341341Sstevel 			tmp_reg = tmp_reg;
5351341Sstevel #endif
5361341Sstevel 			/* We must now re-issue any pending interrupts. */
5371341Sstevel 			for (i = 0; i < FHC_MAX_INO; i++) {
5381341Sstevel 				if (*(softsp->intr_regs[i].clear_reg) == 3) {
5391341Sstevel 					*(softsp->intr_regs[i].clear_reg) =
5401341Sstevel 					    ISM_IDLE;
5411341Sstevel 
5421341Sstevel 					tmp_reg =
5431341Sstevel 					    *(softsp->intr_regs[i].clear_reg);
5441341Sstevel #ifdef lint
5451341Sstevel 					tmp_reg = tmp_reg;
5461341Sstevel #endif
5471341Sstevel 				}
5481341Sstevel 			}
5491341Sstevel 			cmn_err(CE_NOTE, "central IGN corruption fixed: "
5507656SSherry.Moore@Sun.COM 			    "got %x wanted %x", act_igr, want_igr);
5511341Sstevel 		}
5521341Sstevel 		return;
5531341Sstevel 	}
5541341Sstevel 
5551341Sstevel 	ASSERT(softsp->list->sc.board == FHC_BSR_TO_BD(*(softsp->bsr)));
5561341Sstevel 	cent = check_central(softsp->list->sc.board);
5571341Sstevel 
5581341Sstevel 	/* Loop through all 4 FHC interrupt mapping registers */
5591341Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
5601341Sstevel 
5611341Sstevel 		if (i == FHC_SYS_INO &&
5621341Sstevel 		    *(softsp->intr_regs[i].clear_reg) == 3) {
5631341Sstevel 			cmn_err(CE_NOTE,
5641341Sstevel 			    "found lost system interrupt, resetting..");
5651341Sstevel 
5661341Sstevel 			*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
5671341Sstevel 
5681341Sstevel 			/*
5691341Sstevel 			 * ensure atomic write with this read.
5701341Sstevel 			 */
5711341Sstevel 			tmp_reg = *(softsp->intr_regs[i].clear_reg);
5721341Sstevel #ifdef lint
5731341Sstevel 			tmp_reg = tmp_reg;
5741341Sstevel #endif
5751341Sstevel 		}
5761341Sstevel 
5771341Sstevel 		/*
5781341Sstevel 		 * The mapping registers on the board with the "central" bit
5791341Sstevel 		 * set should not be touched as it has been taken care by POST.
5801341Sstevel 		 */
5811341Sstevel 
5821341Sstevel 		if (cent)
5831341Sstevel 			continue;
5841341Sstevel 
5851341Sstevel 		*(softsp->intr_regs[i].mapping_reg) = 0;
5861341Sstevel 
5871341Sstevel 		/*
5881341Sstevel 		 * ensure atomic write with this read.
5891341Sstevel 		 */
5901341Sstevel 		tmp_reg = *(softsp->intr_regs[i].mapping_reg);
5911341Sstevel #ifdef lint
5921341Sstevel 		tmp_reg = tmp_reg;
5931341Sstevel #endif
5941341Sstevel 
5951341Sstevel 	}
5961341Sstevel }
5971341Sstevel 
5981341Sstevel static int
check_central(int board)5991341Sstevel check_central(int board)
6001341Sstevel {
6011341Sstevel 	uint_t cs_value;
6021341Sstevel 
6031341Sstevel 	/*
6041341Sstevel 	 * This is the value of AC configuration and status reg
6051341Sstevel 	 * in the Local Devices space. We access it as a physical
6061341Sstevel 	 * address.
6071341Sstevel 	 */
6081341Sstevel 	cs_value = ldphysio(AC_BCSR(board));
6091341Sstevel 	if (cs_value & AC_CENTRAL)
6101341Sstevel 		return (TRUE);
6111341Sstevel 	else
6121341Sstevel 		return (FALSE);
6131341Sstevel }
6141341Sstevel 
6151341Sstevel static int
fhc_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)6161341Sstevel fhc_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
6171341Sstevel {
6181341Sstevel 	struct fhc_soft_state *softsp;
6191341Sstevel 	int instance;
6201341Sstevel 
6211341Sstevel 	instance = ddi_get_instance(devi);
6221341Sstevel 
6231341Sstevel 	switch (cmd) {
6241341Sstevel 	case DDI_ATTACH:
6251341Sstevel 		break;
6261341Sstevel 
6271341Sstevel 	case DDI_RESUME:
6281341Sstevel 		softsp = ddi_get_soft_state(fhcp, instance);
6291341Sstevel 		/* IGR, NOT_BRD_PRES handled by prom */
6301341Sstevel 		/* reset interrupt mapping registers */
6311341Sstevel 		fhc_handle_imr(softsp);
6321341Sstevel 
6331341Sstevel 		return (DDI_SUCCESS);
6341341Sstevel 
6351341Sstevel 	default:
6361341Sstevel 		return (DDI_FAILURE);
6371341Sstevel 	}
6381341Sstevel 
6391341Sstevel 
6401341Sstevel 	if (ddi_soft_state_zalloc(fhcp, instance) != DDI_SUCCESS)
6411341Sstevel 		return (DDI_FAILURE);
6421341Sstevel 
6431341Sstevel 	softsp = ddi_get_soft_state(fhcp, instance);
6441341Sstevel 
6451341Sstevel 	/* Set the dip in the soft state */
6461341Sstevel 	softsp->dip = devi;
6471341Sstevel 
6481341Sstevel 	if (fhc_init(softsp) != DDI_SUCCESS)
6491341Sstevel 		goto bad;
6501341Sstevel 
6511341Sstevel 	ddi_report_dev(devi);
6521341Sstevel 
6531341Sstevel 	return (DDI_SUCCESS);
6541341Sstevel 
6551341Sstevel bad:
6561341Sstevel 	ddi_soft_state_free(fhcp, instance);
6571341Sstevel 	return (DDI_FAILURE);
6581341Sstevel }
6591341Sstevel 
6601341Sstevel static int
fhc_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)6611341Sstevel fhc_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
6621341Sstevel {
6631341Sstevel 	int board;
6641341Sstevel 	int instance;
6651341Sstevel 	struct fhc_soft_state *softsp;
6661341Sstevel 	fhc_bd_t *list = NULL;
6671341Sstevel 
6681341Sstevel 	/* get the instance of this devi */
6691341Sstevel 	instance = ddi_get_instance(devi);
6701341Sstevel 
6711341Sstevel 	/* get the soft state pointer for this device node */
6721341Sstevel 	softsp = ddi_get_soft_state(fhcp, instance);
6731341Sstevel 
6741341Sstevel 	board = softsp->list->sc.board;
6751341Sstevel 
6761341Sstevel 	switch (cmd) {
6771341Sstevel 	case DDI_SUSPEND:
6781341Sstevel 
6791341Sstevel 		return (DDI_SUCCESS);
6801341Sstevel 
6811341Sstevel 	case DDI_DETACH:
6821341Sstevel 		/* grab the lock on the board list */
6831341Sstevel 		list = fhc_bdlist_lock(board);
6841341Sstevel 
6851341Sstevel 		if (fhc_bd_detachable(board) &&
6861341Sstevel 		    !fhc_bd_is_jtag_master(board))
6871341Sstevel 			break;
6881341Sstevel 		else
6891341Sstevel 			fhc_bdlist_unlock();
6901341Sstevel 		/* FALLTHROUGH */
6911341Sstevel 
6921341Sstevel 	default:
6931341Sstevel 		return (DDI_FAILURE);
6941341Sstevel 	}
6951341Sstevel 
6961341Sstevel 	/* Remove the interrupt redistribution callback. */
6971341Sstevel 	intr_dist_rem(fhc_intrdist, (void *)devi);
6981341Sstevel 
6991341Sstevel 	/* remove the soft state pointer from the board list */
7001341Sstevel 	list->softsp = NULL;
7011341Sstevel 
7021341Sstevel 	/* clear inherited faults from the PROM. */
7031341Sstevel 	clear_fault(list->sc.board, FT_PROM, FT_BOARD);
7041341Sstevel 
7051341Sstevel 	/* remove the kstat for this board */
7061341Sstevel 	kstat_delete(softsp->fhc_ksp);
7071341Sstevel 
7081341Sstevel 	/* destroy the mutexes in this soft state structure */
7091341Sstevel 	mutex_destroy(&softsp->poll_list_lock);
7101341Sstevel 	mutex_destroy(&softsp->ctrl_lock);
7111341Sstevel 
7121341Sstevel 	/* unmap all the register sets */
7131341Sstevel 	fhc_unmap_regs(softsp);
7141341Sstevel 
7151341Sstevel 	/* release the board list lock now */
7161341Sstevel 	fhc_bdlist_unlock();
7171341Sstevel 
7181341Sstevel 	/* free the soft state structure */
7191341Sstevel 	ddi_soft_state_free(fhcp, instance);
7201341Sstevel 
7211341Sstevel 	return (DDI_SUCCESS);
7221341Sstevel }
7231341Sstevel 
7241341Sstevel static enum board_type
fhc_board_type(struct fhc_soft_state * softsp,int board)7251341Sstevel fhc_board_type(struct fhc_soft_state *softsp, int board)
7261341Sstevel {
7271341Sstevel 	int proplen;
7281341Sstevel 	char *board_type;
7291341Sstevel 	enum board_type type;
7301341Sstevel 
7311341Sstevel 	if (softsp->is_central)
7321341Sstevel 		type = CLOCK_BOARD;
7331341Sstevel 	else if (ddi_getlongprop(DDI_DEV_T_ANY, softsp->dip,
7341341Sstevel 	    DDI_PROP_DONTPASS, "board-type", (caddr_t)&board_type,
7351341Sstevel 	    &proplen) == DDI_PROP_SUCCESS) {
7361341Sstevel 		/* match the board-type string */
7371341Sstevel 		if (strcmp(CPU_BD_NAME, board_type) == 0) {
7381341Sstevel 			type = CPU_BOARD;
7391341Sstevel 		} else if (strcmp(MEM_BD_NAME, board_type) == 0) {
7401341Sstevel 			type = MEM_BOARD;
7411341Sstevel 		} else if (strcmp(IO_2SBUS_BD_NAME, board_type) == 0) {
7421341Sstevel 			type = IO_2SBUS_BOARD;
7431341Sstevel 		} else if (strcmp(IO_SBUS_FFB_BD_NAME, board_type) == 0) {
7441341Sstevel 			type = IO_SBUS_FFB_BOARD;
7451341Sstevel 		} else if (strcmp(IO_2SBUS_SOCPLUS_BD_NAME, board_type) == 0) {
7461341Sstevel 			type = IO_2SBUS_SOCPLUS_BOARD;
7471341Sstevel 		} else if (strcmp(IO_SBUS_FFB_SOCPLUS_BD_NAME, board_type)
7481341Sstevel 		    == 0) {
7491341Sstevel 			type = IO_SBUS_FFB_SOCPLUS_BOARD;
7501341Sstevel 		} else if (strcmp(IO_PCI_BD_NAME, board_type) == 0) {
7511341Sstevel 			type = IO_PCI_BOARD;
7521341Sstevel 		} else {
7531341Sstevel 			type = UNKNOWN_BOARD;
7541341Sstevel 		}
7551341Sstevel 		kmem_free(board_type, proplen);
7561341Sstevel 	} else
7571341Sstevel 		type = UNKNOWN_BOARD;
7581341Sstevel 
7591341Sstevel 	/*
7601341Sstevel 	 * if the board type is indeterminate, it must be determined.
7611341Sstevel 	 */
7621341Sstevel 	if (type == UNKNOWN_BOARD) {
7631341Sstevel 		/*
7641341Sstevel 		 * Use the UPA64 bits from the FHC.
7651341Sstevel 		 * This is not the best solution since we
7661341Sstevel 		 * cannot fully type the IO boards.
7671341Sstevel 		 */
7681341Sstevel 		if (cpu_on_board(board))
7691341Sstevel 			type = CPU_BOARD;
7701341Sstevel 		else if ((*(softsp->bsr) & FHC_UPADATA64A) ||
7717656SSherry.Moore@Sun.COM 		    (*(softsp->bsr) & FHC_UPADATA64B))
7721341Sstevel 			type = IO_2SBUS_BOARD;
7731341Sstevel 		else
7741341Sstevel 			type = MEM_BOARD;
7751341Sstevel 	}
7761341Sstevel 
7771341Sstevel 	return (type);
7781341Sstevel }
7791341Sstevel 
7801341Sstevel static void
fhc_unmap_regs(struct fhc_soft_state * softsp)7811341Sstevel fhc_unmap_regs(struct fhc_soft_state *softsp)
7821341Sstevel {
7831341Sstevel 	dev_info_t *dip = softsp->dip;
7841341Sstevel 
7851341Sstevel 	if (softsp->id) {
7861341Sstevel 		ddi_unmap_regs(dip, 0, (caddr_t *)&softsp->id, 0, 0);
7871341Sstevel 		softsp->id = NULL;
7881341Sstevel 	}
7891341Sstevel 	if (softsp->igr) {
7901341Sstevel 		ddi_unmap_regs(dip, 1, (caddr_t *)&softsp->igr, 0, 0);
7911341Sstevel 		softsp->igr = NULL;
7921341Sstevel 	}
7931341Sstevel 	if (softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg) {
7941341Sstevel 		ddi_unmap_regs(dip, 2,
7957656SSherry.Moore@Sun.COM 		    (caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
7967656SSherry.Moore@Sun.COM 		    0, 0);
7971341Sstevel 		softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg = NULL;
7981341Sstevel 	}
7991341Sstevel 	if (softsp->intr_regs[FHC_SYS_INO].mapping_reg) {
8001341Sstevel 		ddi_unmap_regs(dip, 3,
8017656SSherry.Moore@Sun.COM 		    (caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
8027656SSherry.Moore@Sun.COM 		    0, 0);
8031341Sstevel 		softsp->intr_regs[FHC_SYS_INO].mapping_reg = NULL;
8041341Sstevel 	}
8051341Sstevel 	if (softsp->intr_regs[FHC_UART_INO].mapping_reg) {
8061341Sstevel 		ddi_unmap_regs(dip, 4,
8077656SSherry.Moore@Sun.COM 		    (caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
8087656SSherry.Moore@Sun.COM 		    0, 0);
8091341Sstevel 		softsp->intr_regs[FHC_UART_INO].mapping_reg = NULL;
8101341Sstevel 	}
8111341Sstevel 	if (softsp->intr_regs[FHC_TOD_INO].mapping_reg) {
8121341Sstevel 		ddi_unmap_regs(dip, 5,
8137656SSherry.Moore@Sun.COM 		    (caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
8147656SSherry.Moore@Sun.COM 		    0, 0);
8151341Sstevel 		softsp->intr_regs[FHC_TOD_INO].mapping_reg = NULL;
8161341Sstevel 	}
8171341Sstevel }
8181341Sstevel 
8191341Sstevel static int
fhc_init(struct fhc_soft_state * softsp)8201341Sstevel fhc_init(struct fhc_soft_state *softsp)
8211341Sstevel {
8221341Sstevel 	int i;
8231341Sstevel 	uint_t tmp_reg;
8241341Sstevel 	int board;
8251341Sstevel 
8261341Sstevel 	/*
8271341Sstevel 	 * Map in the FHC registers. Specifying length and offset of
8281341Sstevel 	 * zero maps in the entire OBP register set.
8291341Sstevel 	 */
8301341Sstevel 
8311341Sstevel 	/* map in register set 0 */
8321341Sstevel 	if (ddi_map_regs(softsp->dip, 0,
8331341Sstevel 	    (caddr_t *)&softsp->id, 0, 0)) {
8341341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map internal "
8357656SSherry.Moore@Sun.COM 		    "registers", ddi_get_instance(softsp->dip));
8361341Sstevel 		goto bad;
8371341Sstevel 	}
8381341Sstevel 
8391341Sstevel 	/*
8401341Sstevel 	 * Fill in the virtual addresses of the registers in the
8411341Sstevel 	 * fhc_soft_state structure.
8421341Sstevel 	 */
8431341Sstevel 	softsp->rctrl = (uint_t *)((char *)(softsp->id) +
8447656SSherry.Moore@Sun.COM 	    FHC_OFF_RCTRL);
8451341Sstevel 	softsp->ctrl = (uint_t *)((char *)(softsp->id) +
8467656SSherry.Moore@Sun.COM 	    FHC_OFF_CTRL);
8471341Sstevel 	softsp->bsr = (uint_t *)((char *)(softsp->id) +
8487656SSherry.Moore@Sun.COM 	    FHC_OFF_BSR);
8491341Sstevel 	softsp->jtag_ctrl = (uint_t *)((char *)(softsp->id) +
8507656SSherry.Moore@Sun.COM 	    FHC_OFF_JTAG_CTRL);
8511341Sstevel 	softsp->jt_master.jtag_cmd = (uint_t *)((char *)(softsp->id) +
8527656SSherry.Moore@Sun.COM 	    FHC_OFF_JTAG_CMD);
8531341Sstevel 
8541341Sstevel 	/* map in register set 1 */
8551341Sstevel 	if (ddi_map_regs(softsp->dip, 1,
8561341Sstevel 	    (caddr_t *)&softsp->igr, 0, 0)) {
8571341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map IGR "
8587656SSherry.Moore@Sun.COM 		    "register", ddi_get_instance(softsp->dip));
8591341Sstevel 		goto bad;
8601341Sstevel 	}
8611341Sstevel 
8621341Sstevel 	/*
8631341Sstevel 	 * map in register set 2
8641341Sstevel 	 * XXX this can never be used as an interrupt generator
8651341Sstevel 	 * (hardware queue overflow in fhc)
8661341Sstevel 	 */
8671341Sstevel 	if (ddi_map_regs(softsp->dip, 2,
8681341Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_FANFAIL_INO].mapping_reg,
8691341Sstevel 	    0, 0)) {
8701341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map Fan Fail "
8717656SSherry.Moore@Sun.COM 		    "IMR register", ddi_get_instance(softsp->dip));
8721341Sstevel 		goto bad;
8731341Sstevel 	}
8741341Sstevel 
8751341Sstevel 	/* map in register set 3 */
8761341Sstevel 	if (ddi_map_regs(softsp->dip, 3,
8771341Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_SYS_INO].mapping_reg,
8781341Sstevel 	    0, 0)) {
8791341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map System "
8807656SSherry.Moore@Sun.COM 		    "IMR register\n", ddi_get_instance(softsp->dip));
8811341Sstevel 		goto bad;
8821341Sstevel 	}
8831341Sstevel 
8841341Sstevel 	/* map in register set 4 */
8851341Sstevel 	if (ddi_map_regs(softsp->dip, 4,
8861341Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_UART_INO].mapping_reg,
8871341Sstevel 	    0, 0)) {
8881341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map UART "
8897656SSherry.Moore@Sun.COM 		    "IMR register\n", ddi_get_instance(softsp->dip));
8901341Sstevel 		goto bad;
8911341Sstevel 	}
8921341Sstevel 
8931341Sstevel 	/* map in register set 5 */
8941341Sstevel 	if (ddi_map_regs(softsp->dip, 5,
8951341Sstevel 	    (caddr_t *)&softsp->intr_regs[FHC_TOD_INO].mapping_reg,
8961341Sstevel 	    0, 0)) {
8971341Sstevel 		cmn_err(CE_WARN, "fhc%d: unable to map FHC TOD "
8987656SSherry.Moore@Sun.COM 		    "IMR register", ddi_get_instance(softsp->dip));
8991341Sstevel 		goto bad;
9001341Sstevel 	}
9011341Sstevel 
9021341Sstevel 	/* Loop over all intr sets and setup the VAs for the ISMR */
9031341Sstevel 	/* TODO - Make sure we are calculating the ISMR correctly. */
9041341Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
9051341Sstevel 		softsp->intr_regs[i].clear_reg =
9067656SSherry.Moore@Sun.COM 		    (uint_t *)((char *)(softsp->intr_regs[i].mapping_reg) +
9077656SSherry.Moore@Sun.COM 		    FHC_OFF_ISMR);
9081341Sstevel 
9091341Sstevel 		/* Now clear the state machines to idle */
9101341Sstevel 		*(softsp->intr_regs[i].clear_reg) = ISM_IDLE;
9111341Sstevel 	}
9121341Sstevel 
9131341Sstevel 	/*
9141341Sstevel 	 * It is OK to not have a OBP_BOARDNUM property. This happens for
9151341Sstevel 	 * the board which is a child of central. However this FHC
9161341Sstevel 	 * still needs a proper Interrupt Group Number programmed
9171341Sstevel 	 * into the Interrupt Group register, because the other
9181341Sstevel 	 * instance of FHC, which is not under central, will properly
9191341Sstevel 	 * program the IGR. The numbers from the two settings of the
9201341Sstevel 	 * IGR need to be the same. One driver cannot wait for the
9211341Sstevel 	 * other to program the IGR, because there is no guarantee
9221341Sstevel 	 * which instance of FHC will get attached first.
9231341Sstevel 	 */
9241341Sstevel 	if ((board = (int)ddi_getprop(DDI_DEV_T_ANY, softsp->dip,
9251341Sstevel 	    DDI_PROP_DONTPASS, OBP_BOARDNUM, -1)) == -1) {
9261341Sstevel 		/*
9271341Sstevel 		 * Now determine the board number by reading the
9281341Sstevel 		 * hardware register.
9291341Sstevel 		 */
9301341Sstevel 		board = FHC_BSR_TO_BD(*(softsp->bsr));
9311341Sstevel 		softsp->is_central = 1;
9321341Sstevel 	}
9331341Sstevel 
9341341Sstevel 	/*
9351341Sstevel 	 * If this fhc holds JTAG master line, and is not the central fhc,
9361341Sstevel 	 * (this avoids two JTAG master nodes) then initialize the
9371341Sstevel 	 * mutex and set the flag in the structure.
9381341Sstevel 	 */
9391341Sstevel 	if ((*(softsp->jtag_ctrl) & JTAG_MASTER_EN) && !softsp->is_central) {
9401341Sstevel 		mutex_init(&(softsp->jt_master.lock), NULL, MUTEX_DEFAULT,
9411341Sstevel 		    NULL);
9421341Sstevel 		softsp->jt_master.is_master = 1;
9431341Sstevel 	} else {
9441341Sstevel 		softsp->jt_master.is_master = 0;
9451341Sstevel 	}
9461341Sstevel 
9471341Sstevel 	fhc_bd_init(softsp, board, fhc_board_type(softsp, board));
9481341Sstevel 
9491341Sstevel 	/* Initialize the mutex guarding the poll_list. */
9501341Sstevel 	mutex_init(&softsp->poll_list_lock, NULL, MUTEX_DRIVER, NULL);
9511341Sstevel 
9521341Sstevel 	/* Initialize the mutex guarding the FHC CSR */
9531341Sstevel 	mutex_init(&softsp->ctrl_lock, NULL, MUTEX_DRIVER, NULL);
9541341Sstevel 
9551341Sstevel 	/* Initialize the poll_list to be empty */
9561341Sstevel 	for (i = 0; i < MAX_ZS_CNT; i++) {
9571341Sstevel 		softsp->poll_list[i].funcp = NULL;
9581341Sstevel 	}
9591341Sstevel 
9601341Sstevel 	/* Modify the various registers in the FHC now */
9611341Sstevel 
9621341Sstevel 	/*
9631341Sstevel 	 * We know this board to be present now, record that state and
9641341Sstevel 	 * remove the NOT_BRD_PRES condition
9651341Sstevel 	 */
9661341Sstevel 	if (!(softsp->is_central)) {
9671341Sstevel 		mutex_enter(&softsp->ctrl_lock);
9681341Sstevel 		*(softsp->ctrl) |= FHC_NOT_BRD_PRES;
9691341Sstevel 		/* Now flush the hardware store buffers. */
9701341Sstevel 		tmp_reg = *(softsp->ctrl);
9711341Sstevel #ifdef lint
9721341Sstevel 		tmp_reg = tmp_reg;
9731341Sstevel #endif
9741341Sstevel 		/* XXX record the board state in global space */
9751341Sstevel 		mutex_exit(&softsp->ctrl_lock);
9761341Sstevel 
9771341Sstevel 		/* Add kstats for all non-central instances of the FHC. */
9781341Sstevel 		fhc_add_kstats(softsp);
9791341Sstevel 	}
9801341Sstevel 
9811341Sstevel 	/*
9821341Sstevel 	 * Read the device tree to see if this system is in an environmental
9831341Sstevel 	 * chamber.
9841341Sstevel 	 */
9851341Sstevel 	if (temperature_chamber == -1) {
9861341Sstevel 		temperature_chamber = check_for_chamber();
9871341Sstevel 	}
9881341Sstevel 
9891341Sstevel 	/* Check for inherited faults from the PROM. */
9901341Sstevel 	if (*softsp->ctrl & FHC_LED_MID) {
9911341Sstevel 		reg_fault(softsp->list->sc.board, FT_PROM, FT_BOARD);
9921341Sstevel 	}
9931341Sstevel 
9941341Sstevel 	/*
9951341Sstevel 	 * setup the IGR. Shift the board number over by one to get
9961341Sstevel 	 * the UPA MID.
9971341Sstevel 	 */
9981341Sstevel 	*(softsp->igr) = (softsp->list->sc.board) << 1;
9991341Sstevel 
10001341Sstevel 	/* Now flush the hardware store buffers. */
10011341Sstevel 	tmp_reg = *(softsp->id);
10021341Sstevel #ifdef lint
10031341Sstevel 	tmp_reg = tmp_reg;
10041341Sstevel #endif
10051341Sstevel 
10061341Sstevel 	/* Add the interrupt redistribution callback. */
10071341Sstevel 	intr_dist_add(fhc_intrdist, (void *)softsp->dip);
10081341Sstevel 
10091341Sstevel 	return (DDI_SUCCESS);
10101341Sstevel bad:
10111341Sstevel 	fhc_unmap_regs(softsp);
10121341Sstevel 	return (DDI_FAILURE);
10131341Sstevel }
10141341Sstevel 
10151341Sstevel static uint_t
fhc_intr_wrapper(caddr_t arg)10161341Sstevel fhc_intr_wrapper(caddr_t arg)
10171341Sstevel {
10181341Sstevel 	uint_t intr_return;
10191341Sstevel 	uint_t tmpreg;
10201341Sstevel 	struct fhc_wrapper_arg *intr_info = (struct fhc_wrapper_arg *)arg;
10211341Sstevel 	uint_t (*funcp)(caddr_t, caddr_t) = intr_info->funcp;
10221341Sstevel 	caddr_t iarg1 = intr_info->arg1;
10231341Sstevel 	caddr_t iarg2 = intr_info->arg2;
10241341Sstevel 	dev_info_t *dip = intr_info->child;
10251341Sstevel 
10261341Sstevel 	tmpreg = ISM_IDLE;
10271341Sstevel 
10281341Sstevel 	DTRACE_PROBE4(interrupt__start, dev_info_t, dip,
10291341Sstevel 	    void *, funcp, caddr_t, iarg1, caddr_t, iarg2);
10301341Sstevel 
10311341Sstevel 	intr_return = (*funcp)(iarg1, iarg2);
10321341Sstevel 
10331341Sstevel 	DTRACE_PROBE4(interrupt__complete, dev_info_t, dip,
10341341Sstevel 	    void *, funcp, caddr_t, iarg1, int, intr_return);
10351341Sstevel 
10361341Sstevel 	/* Idle the state machine. */
10371341Sstevel 	*(intr_info->clear_reg) = tmpreg;
10381341Sstevel 
10391341Sstevel 	/* Flush the hardware store buffers. */
10401341Sstevel 	tmpreg = *(intr_info->clear_reg);
10411341Sstevel #ifdef lint
10421341Sstevel 	tmpreg = tmpreg;
10431341Sstevel #endif	/* lint */
10441341Sstevel 
10451341Sstevel 	return (intr_return);
10461341Sstevel }
10471341Sstevel 
10481341Sstevel /*
10491341Sstevel  * fhc_zs_intr_wrapper
10501341Sstevel  *
10511341Sstevel  * This function handles intrerrupts where more than one device may interupt
10521341Sstevel  * the fhc with the same mondo.
10531341Sstevel  */
10541341Sstevel 
10551341Sstevel #define	MAX_INTR_CNT 10
10561341Sstevel 
10571341Sstevel static uint_t
fhc_zs_intr_wrapper(caddr_t arg)10581341Sstevel fhc_zs_intr_wrapper(caddr_t arg)
10591341Sstevel {
10601341Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)arg;
10611341Sstevel 	uint_t (*funcp0)(caddr_t, caddr_t);
10621341Sstevel 	uint_t (*funcp1)(caddr_t, caddr_t);
10631341Sstevel 	caddr_t funcp0_arg1, funcp0_arg2, funcp1_arg1, funcp1_arg2;
10641341Sstevel 	uint_t tmp_reg;
10651341Sstevel 	uint_t result = DDI_INTR_UNCLAIMED;
10661341Sstevel 	volatile uint_t *clear_reg;
10671341Sstevel 	uchar_t *spurious_cntr = &softsp->spurious_zs_cntr;
10681341Sstevel 
10691341Sstevel 	funcp0 = softsp->poll_list[0].funcp;
10701341Sstevel 	funcp1 = softsp->poll_list[1].funcp;
10711341Sstevel 	funcp0_arg1 = softsp->poll_list[0].arg1;
10721341Sstevel 	funcp0_arg2 = softsp->poll_list[0].arg2;
10731341Sstevel 	funcp1_arg1 = softsp->poll_list[1].arg1;
10741341Sstevel 	funcp1_arg2 = softsp->poll_list[1].arg2;
10751341Sstevel 	clear_reg = softsp->intr_regs[FHC_UART_INO].clear_reg;
10761341Sstevel 
10771341Sstevel 	if (funcp0 != NULL) {
10781341Sstevel 		if ((funcp0)(funcp0_arg1, funcp0_arg2) == DDI_INTR_CLAIMED) {
10791341Sstevel 			result = DDI_INTR_CLAIMED;
10801341Sstevel 		}
10811341Sstevel 	}
10821341Sstevel 
10831341Sstevel 	if (funcp1 != NULL) {
10841341Sstevel 		if ((funcp1)(funcp1_arg1, funcp1_arg2) == DDI_INTR_CLAIMED) {
10851341Sstevel 			result = DDI_INTR_CLAIMED;
10861341Sstevel 		}
10871341Sstevel 	}
10881341Sstevel 
10891341Sstevel 	if (result == DDI_INTR_UNCLAIMED) {
10901341Sstevel 		(*spurious_cntr)++;
10911341Sstevel 
10921341Sstevel 		if (*spurious_cntr < MAX_INTR_CNT) {
10931341Sstevel 			result = DDI_INTR_CLAIMED;
10941341Sstevel 		} else {
10951341Sstevel 			*spurious_cntr = (uchar_t)0;
10961341Sstevel 		}
10971341Sstevel 	} else {
10981341Sstevel 		*spurious_cntr = (uchar_t)0;
10991341Sstevel 	}
11001341Sstevel 
11011341Sstevel 	/* Idle the state machine. */
11021341Sstevel 	*(clear_reg) = ISM_IDLE;
11031341Sstevel 
11041341Sstevel 	/* flush the store buffers. */
11051341Sstevel 	tmp_reg = *(clear_reg);
11061341Sstevel #ifdef lint
11071341Sstevel 	tmp_reg = tmp_reg;
11081341Sstevel #endif
11091341Sstevel 
11101341Sstevel 	return (result);
11111341Sstevel }
11121341Sstevel 
11131341Sstevel 
11141341Sstevel /*
11151341Sstevel  * add_intrspec - Add an interrupt specification.
11161341Sstevel  */
11171341Sstevel static int
fhc_add_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)11181341Sstevel fhc_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
11191341Sstevel     ddi_intr_handle_impl_t *hdlp)
11201341Sstevel {
11211341Sstevel 	int ino;
11221341Sstevel 	struct fhc_wrapper_arg *fhc_arg;
11231341Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)
11241341Sstevel 	    ddi_get_soft_state(fhcp, ddi_get_instance(dip));
11251341Sstevel 	volatile uint_t *mondo_vec_reg;
11261341Sstevel 	uint_t tmp_mondo_vec;
11271341Sstevel 	uint_t tmpreg; /* HW flush reg */
11281341Sstevel 	uint_t cpu_id;
11291341Sstevel 	int ret = DDI_SUCCESS;
11301341Sstevel 
11311341Sstevel 	/* Xlate the interrupt */
11321341Sstevel 	fhc_xlate_intrs(hdlp,
11331341Sstevel 	    (softsp->list->sc.board << BD_IVINTR_SHFT));
11341341Sstevel 
11351341Sstevel 	/* get the mondo number */
11361341Sstevel 	ino = FHC_INO(hdlp->ih_vector);
11371341Sstevel 	mondo_vec_reg = softsp->intr_regs[ino].mapping_reg;
11381341Sstevel 
11391341Sstevel 	ASSERT(ino < FHC_MAX_INO);
11401341Sstevel 
11411341Sstevel 	/* We don't use the two spare interrupts. */
11421341Sstevel 	if (ino >= FHC_MAX_INO) {
11431341Sstevel 		cmn_err(CE_WARN, "fhc%d: Spare interrupt %d not usable",
11441341Sstevel 		    ddi_get_instance(dip), ino);
11451341Sstevel 		return (DDI_FAILURE);
11461341Sstevel 	}
11471341Sstevel 
11481341Sstevel 	/* TOD and Fan Fail interrupts are not usable */
11491341Sstevel 	if (ino == FHC_TOD_INO) {
11501341Sstevel 		cmn_err(CE_WARN, "fhc%d: TOD interrupt not usable",
11511341Sstevel 		    ddi_get_instance(dip));
11521341Sstevel 		return (DDI_FAILURE);
11531341Sstevel 	}
11541341Sstevel 	if (ino == FHC_FANFAIL_INO) {
11551341Sstevel 		cmn_err(CE_WARN, "fhc%d: Fan fail interrupt not usable",
11561341Sstevel 		    ddi_get_instance(dip));
11571341Sstevel 		return (DDI_FAILURE);
11581341Sstevel 	}
11591341Sstevel 
11601341Sstevel 	/*
11611341Sstevel 	 * If the interrupt is for the zs chips, use the vector
11621341Sstevel 	 * polling lists. Otherwise use a straight handler.
11631341Sstevel 	 */
11641341Sstevel 	if (ino == FHC_UART_INO) {
11651341Sstevel 		int32_t zs_inst;
11661341Sstevel 		/* First lock the mutex for this poll_list */
11671341Sstevel 		mutex_enter(&softsp->poll_list_lock);
11681341Sstevel 
11691341Sstevel 		/*
11701341Sstevel 		 * Add this interrupt to the polling list.
11711341Sstevel 		 */
11721341Sstevel 
11731341Sstevel 		/* figure out where to add this item in the list */
11741341Sstevel 		for (zs_inst = 0; zs_inst < MAX_ZS_CNT; zs_inst++) {
11751341Sstevel 			if (softsp->poll_list[zs_inst].funcp == NULL) {
11761341Sstevel 				softsp->poll_list[zs_inst].arg1 =
11771341Sstevel 				    hdlp->ih_cb_arg1;
11781341Sstevel 				softsp->poll_list[zs_inst].arg2 =
11791341Sstevel 				    hdlp->ih_cb_arg2;
11801341Sstevel 				softsp->poll_list[zs_inst].funcp =
11811341Sstevel 				    (ddi_intr_handler_t *)
11821341Sstevel 				    hdlp->ih_cb_func;
11831341Sstevel 				softsp->poll_list[zs_inst].inum =
11841341Sstevel 				    hdlp->ih_inum;
11851341Sstevel 				softsp->poll_list[zs_inst].child = rdip;
11861341Sstevel 
11871341Sstevel 				break;
11881341Sstevel 			}
11891341Sstevel 		}
11901341Sstevel 
11911341Sstevel 		if (zs_inst >= MAX_ZS_CNT) {
11921341Sstevel 			cmn_err(CE_WARN,
11931341Sstevel 			    "fhc%d: poll list overflow",
11941341Sstevel 			    ddi_get_instance(dip));
11951341Sstevel 			mutex_exit(&softsp->poll_list_lock);
11961341Sstevel 			ret = DDI_FAILURE;
11971341Sstevel 			goto done;
11981341Sstevel 		}
11991341Sstevel 
12001341Sstevel 		/*
12011341Sstevel 		 * If polling list is empty, then install handler
12021341Sstevel 		 * and enable interrupts for this ino.
12031341Sstevel 		 */
12041341Sstevel 		if (zs_inst == 0) {
12051341Sstevel 			DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
12061341Sstevel 			    (ddi_intr_handler_t *)fhc_zs_intr_wrapper,
12071341Sstevel 			    (caddr_t)softsp, NULL);
12081341Sstevel 
12091341Sstevel 			ret = i_ddi_add_ivintr(hdlp);
12101341Sstevel 
12111341Sstevel 			DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
12121341Sstevel 			    softsp->poll_list[zs_inst].funcp,
12131341Sstevel 			    softsp->poll_list[zs_inst].arg1,
12141341Sstevel 			    softsp->poll_list[zs_inst].arg2);
12151341Sstevel 
12161341Sstevel 			if (ret != DDI_SUCCESS)
12171341Sstevel 				goto done;
12181341Sstevel 		}
12191341Sstevel 
12201341Sstevel 		/*
12211341Sstevel 		 * If both zs handlers are active, then this is the
12221341Sstevel 		 * second add_intrspec called, so do not enable
12231341Sstevel 		 * the IMR_VALID bit, it is already on.
12241341Sstevel 		 */
12251341Sstevel 		if (zs_inst > 0) {
12261341Sstevel 				/* now release the mutex and return */
12271341Sstevel 			mutex_exit(&softsp->poll_list_lock);
12281341Sstevel 
12291341Sstevel 			goto done;
12301341Sstevel 		} else {
12311341Sstevel 			/* just release the mutex */
12321341Sstevel 			mutex_exit(&softsp->poll_list_lock);
12331341Sstevel 		}
12341341Sstevel 	} else {	/* normal interrupt installation */
12351341Sstevel 		int32_t i;
12361341Sstevel 
12371341Sstevel 		/* Allocate a nexus interrupt data structure */
12381341Sstevel 		fhc_arg = kmem_alloc(sizeof (struct fhc_wrapper_arg), KM_SLEEP);
12391341Sstevel 		fhc_arg->child = rdip;
12401341Sstevel 		fhc_arg->mapping_reg = mondo_vec_reg;
12411341Sstevel 		fhc_arg->clear_reg = (softsp->intr_regs[ino].clear_reg);
12421341Sstevel 		fhc_arg->softsp = softsp;
12431341Sstevel 		fhc_arg->funcp =
12441341Sstevel 		    (ddi_intr_handler_t *)hdlp->ih_cb_func;
12451341Sstevel 		fhc_arg->arg1 = hdlp->ih_cb_arg1;
12461341Sstevel 		fhc_arg->arg2 = hdlp->ih_cb_arg2;
12471341Sstevel 		fhc_arg->inum = hdlp->ih_inum;
12481341Sstevel 
12491341Sstevel 		for (i = 0; i < FHC_MAX_INO; i++) {
12501341Sstevel 			if (softsp->intr_list[i] == 0) {
12511341Sstevel 				softsp->intr_list[i] = fhc_arg;
12521341Sstevel 				break;
12531341Sstevel 			}
12541341Sstevel 		}
12551341Sstevel 
12561341Sstevel 		/*
12571341Sstevel 		 * Save the fhc_arg in the ispec so we can use this info
12581341Sstevel 		 * later to uninstall this interrupt spec.
12591341Sstevel 		 */
12601341Sstevel 		DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
12611341Sstevel 		    (ddi_intr_handler_t *)fhc_intr_wrapper,
12621341Sstevel 		    (caddr_t)fhc_arg, NULL);
12631341Sstevel 
12641341Sstevel 		ret = i_ddi_add_ivintr(hdlp);
12651341Sstevel 
12661341Sstevel 		DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, fhc_arg->funcp,
12671341Sstevel 		    fhc_arg->arg1, fhc_arg->arg2);
12681341Sstevel 
12691341Sstevel 		if (ret != DDI_SUCCESS)
12701341Sstevel 			goto done;
12711341Sstevel 	}
12721341Sstevel 
12731341Sstevel 	/*
12741341Sstevel 	 * Clear out a stale 'pending' or 'transmit' state in
12751341Sstevel 	 * this device's ISM that might have been left from a
12761341Sstevel 	 * previous session.
12771341Sstevel 	 *
12781341Sstevel 	 * Since all FHC interrupts are level interrupts, any
12791341Sstevel 	 * real interrupting condition will immediately transition
12801341Sstevel 	 * the ISM back to pending.
12811341Sstevel 	 */
12821341Sstevel 	*(softsp->intr_regs[ino].clear_reg) = ISM_IDLE;
12831341Sstevel 
12841341Sstevel 	/*
12851341Sstevel 	 * Program the mondo vector accordingly.  This MUST be the
12861341Sstevel 	 * last thing we do.  Once we program the ino, the device
12871341Sstevel 	 * may begin to interrupt.
12881341Sstevel 	 */
12891341Sstevel 	cpu_id = intr_dist_cpuid();
12901341Sstevel 
12911341Sstevel 	tmp_mondo_vec = cpu_id << INR_PID_SHIFT;
12921341Sstevel 
12931341Sstevel 	/* don't do this for fan because fan has a special control */
12941341Sstevel 	if (ino == FHC_FANFAIL_INO)
12951341Sstevel 		panic("fhc%d: enabling fanfail interrupt",
12961341Sstevel 		    ddi_get_instance(dip));
12971341Sstevel 	else
12981341Sstevel 		tmp_mondo_vec |= IMR_VALID;
12991341Sstevel 
13001341Sstevel 	DPRINTF(FHC_INTERRUPT_DEBUG,
1301*11311SSurya.Prakki@Sun.COM 	    ("Mondo 0x%x mapping reg: 0x%p", hdlp->ih_vector,
1302*11311SSurya.Prakki@Sun.COM 	    (void *)mondo_vec_reg));
13031341Sstevel 
13041341Sstevel 	/* Store it in the hardware reg. */
13051341Sstevel 	*mondo_vec_reg = tmp_mondo_vec;
13061341Sstevel 
13071341Sstevel 	/* Read a FHC register to flush store buffers */
13081341Sstevel 	tmpreg = *(softsp->id);
13091341Sstevel #ifdef lint
13101341Sstevel 	tmpreg = tmpreg;
13111341Sstevel #endif
13121341Sstevel 
13131341Sstevel done:
13141341Sstevel 	return (ret);
13151341Sstevel }
13161341Sstevel 
13171341Sstevel /*
13181341Sstevel  * remove_intrspec - Remove an interrupt specification.
13191341Sstevel  */
13201341Sstevel static void
fhc_remove_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)13211341Sstevel fhc_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
13221341Sstevel 	ddi_intr_handle_impl_t *hdlp)
13231341Sstevel {
13241341Sstevel 	volatile uint_t *mondo_vec_reg;
13251341Sstevel 	volatile uint_t tmpreg;
13261341Sstevel 	int i;
13271341Sstevel 	struct fhc_soft_state *softsp = (struct fhc_soft_state *)
13287656SSherry.Moore@Sun.COM 	    ddi_get_soft_state(fhcp, ddi_get_instance(dip));
13291341Sstevel 	int ino;
13301341Sstevel 
13311341Sstevel 	/* Xlate the interrupt */
13321341Sstevel 	fhc_xlate_intrs(hdlp,
13331341Sstevel 	    (softsp->list->sc.board << BD_IVINTR_SHFT));
13341341Sstevel 
13351341Sstevel 	/* get the mondo number */
13361341Sstevel 	ino = FHC_INO(hdlp->ih_vector);
13371341Sstevel 
13381341Sstevel 	if (ino == FHC_UART_INO) {
13391341Sstevel 		int intr_found = 0;
13401341Sstevel 
13411341Sstevel 		/* Lock the poll_list first */
13421341Sstevel 		mutex_enter(&softsp->poll_list_lock);
13431341Sstevel 
13441341Sstevel 		/*
13451341Sstevel 		 * Find which entry in the poll list belongs to this
13461341Sstevel 		 * intrspec.
13471341Sstevel 		 */
13481341Sstevel 		for (i = 0; i < MAX_ZS_CNT; i++) {
13491341Sstevel 			if (softsp->poll_list[i].child == rdip &&
13501341Sstevel 			    softsp->poll_list[i].inum == hdlp->ih_inum) {
13511341Sstevel 				softsp->poll_list[i].funcp = NULL;
13521341Sstevel 				intr_found++;
13531341Sstevel 			}
13541341Sstevel 		}
13551341Sstevel 
13561341Sstevel 		/* If we did not find an entry, then we have a problem */
13571341Sstevel 		if (!intr_found) {
13581341Sstevel 			cmn_err(CE_WARN, "fhc%d: Intrspec not found in"
13597656SSherry.Moore@Sun.COM 			    " poll list", ddi_get_instance(dip));
13601341Sstevel 			mutex_exit(&softsp->poll_list_lock);
13611341Sstevel 			goto done;
13621341Sstevel 		}
13631341Sstevel 
13641341Sstevel 		/*
13651341Sstevel 		 * If we have removed all active entries for the poll
13661341Sstevel 		 * list, then we have to disable interupts at this point.
13671341Sstevel 		 */
13681341Sstevel 		if ((softsp->poll_list[0].funcp == NULL) &&
13691341Sstevel 		    (softsp->poll_list[1].funcp == NULL)) {
13701341Sstevel 			mondo_vec_reg =
13711341Sstevel 			    softsp->intr_regs[FHC_UART_INO].mapping_reg;
13721341Sstevel 			*mondo_vec_reg &= ~IMR_VALID;
13731341Sstevel 
13741341Sstevel 			/* flush the hardware buffers */
13751341Sstevel 			tmpreg = *(softsp->ctrl);
13761341Sstevel 
13771341Sstevel 			/* Eliminate the particular handler from the system. */
13781341Sstevel 			i_ddi_rem_ivintr(hdlp);
13791341Sstevel 		}
13801341Sstevel 
13811341Sstevel 		mutex_exit(&softsp->poll_list_lock);
13821341Sstevel 	} else {
13831341Sstevel 		int32_t i;
13841341Sstevel 
13851341Sstevel 
13861341Sstevel 		for (i = 0; i < FHC_MAX_INO; i++)
13871341Sstevel 			if (softsp->intr_list[i]->child == rdip &&
13881341Sstevel 			    softsp->intr_list[i]->inum == hdlp->ih_inum)
13891341Sstevel 				break;
13901341Sstevel 
13911341Sstevel 		if (i >= FHC_MAX_INO)
13921341Sstevel 			goto done;
13931341Sstevel 
13941341Sstevel 		mondo_vec_reg = softsp->intr_list[i]->mapping_reg;
13951341Sstevel 
13961341Sstevel 		/* Turn off the valid bit in the mapping register. */
13971341Sstevel 		/* XXX what about FHC_FANFAIL owned imr? */
13981341Sstevel 		*mondo_vec_reg &= ~IMR_VALID;
13991341Sstevel 
14001341Sstevel 		/* flush the hardware store buffers */
14011341Sstevel 		tmpreg = *(softsp->id);
14021341Sstevel #ifdef lint
14031341Sstevel 		tmpreg = tmpreg;
14041341Sstevel #endif
14051341Sstevel 
14061341Sstevel 		/* Eliminate the particular handler from the system. */
14071341Sstevel 		i_ddi_rem_ivintr(hdlp);
14081341Sstevel 
14091341Sstevel 		kmem_free(softsp->intr_list[i],
14101341Sstevel 		    sizeof (struct fhc_wrapper_arg));
14111341Sstevel 		softsp->intr_list[i] = 0;
14121341Sstevel 	}
14131341Sstevel 
14141341Sstevel done:
14151341Sstevel 	;
14161341Sstevel }
14171341Sstevel 
14181341Sstevel /* new intr_ops structure */
14191341Sstevel 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)14201341Sstevel fhc_intr_ops(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t intr_op,
14211341Sstevel     ddi_intr_handle_impl_t *hdlp, void *result)
14221341Sstevel {
14231341Sstevel 	int	ret = DDI_SUCCESS;
14241341Sstevel 
14251341Sstevel 	switch (intr_op) {
14261341Sstevel 	case DDI_INTROP_GETCAP:
14271341Sstevel 		*(int *)result = DDI_INTR_FLAG_LEVEL;
14281341Sstevel 		break;
14291341Sstevel 	case DDI_INTROP_ALLOC:
14301341Sstevel 		*(int *)result = hdlp->ih_scratch1;
14311341Sstevel 		break;
14321341Sstevel 	case DDI_INTROP_FREE:
14331341Sstevel 		break;
14341341Sstevel 	case DDI_INTROP_GETPRI:
14351341Sstevel 		if (hdlp->ih_pri == 0) {
14361341Sstevel 			struct fhc_soft_state *softsp =
14371341Sstevel 			    (struct fhc_soft_state *)ddi_get_soft_state(fhcp,
14381341Sstevel 			    ddi_get_instance(dip));
14391341Sstevel 
14401341Sstevel 			/* Xlate the interrupt */
14411341Sstevel 			fhc_xlate_intrs(hdlp,
14421341Sstevel 			    (softsp->list->sc.board << BD_IVINTR_SHFT));
14431341Sstevel 		}
14441341Sstevel 
14451341Sstevel 		*(int *)result = hdlp->ih_pri;
14461341Sstevel 		break;
14471341Sstevel 	case DDI_INTROP_SETPRI:
14481341Sstevel 		break;
14491341Sstevel 	case DDI_INTROP_ADDISR:
14501341Sstevel 		ret = fhc_add_intr_impl(dip, rdip, hdlp);
14511341Sstevel 		break;
14521341Sstevel 	case DDI_INTROP_REMISR:
14531341Sstevel 		fhc_remove_intr_impl(dip, rdip, hdlp);
14541341Sstevel 		break;
14551341Sstevel 	case DDI_INTROP_ENABLE:
14561341Sstevel 	case DDI_INTROP_DISABLE:
14571341Sstevel 		break;
14581341Sstevel 	case DDI_INTROP_NINTRS:
14591341Sstevel 	case DDI_INTROP_NAVAIL:
14602580Sanish 		*(int *)result = i_ddi_get_intx_nintrs(rdip);
14611341Sstevel 		break;
14621341Sstevel 	case DDI_INTROP_SETCAP:
14631341Sstevel 	case DDI_INTROP_SETMASK:
14641341Sstevel 	case DDI_INTROP_CLRMASK:
14651341Sstevel 	case DDI_INTROP_GETPENDING:
14661341Sstevel 		ret = DDI_ENOTSUP;
14671341Sstevel 		break;
14681341Sstevel 	case DDI_INTROP_SUPPORTED_TYPES:
14691341Sstevel 		/* only support fixed interrupts */
14702580Sanish 		*(int *)result = i_ddi_get_intx_nintrs(rdip) ?
14711341Sstevel 		    DDI_INTR_TYPE_FIXED : 0;
14721341Sstevel 		break;
14731341Sstevel 	default:
14741341Sstevel 		ret = i_ddi_intr_ops(dip, rdip, intr_op, hdlp, result);
14751341Sstevel 		break;
14761341Sstevel 	}
14771341Sstevel 
14781341Sstevel 	return (ret);
14791341Sstevel }
14801341Sstevel 
14811341Sstevel /*
14821341Sstevel  * FHC Control Ops routine
14831341Sstevel  *
14841341Sstevel  * Requests handled here:
14851341Sstevel  *	DDI_CTLOPS_INITCHILD	see impl_ddi_sunbus_initchild() for details
14861341Sstevel  *	DDI_CTLOPS_UNINITCHILD	see fhc_uninit_child() for details
14871341Sstevel  *	DDI_CTLOPS_REPORTDEV	TODO - need to implement this.
14881341Sstevel  */
14891341Sstevel static int
fhc_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)14901341Sstevel fhc_ctlops(dev_info_t *dip, dev_info_t *rdip,
14911341Sstevel 	ddi_ctl_enum_t op, void *arg, void *result)
14921341Sstevel {
14931341Sstevel 
14941341Sstevel 	switch (op) {
14951341Sstevel 	case DDI_CTLOPS_INITCHILD:
14961341Sstevel 		DPRINTF(FHC_CTLOPS_DEBUG, ("DDI_CTLOPS_INITCHILD\n"));
14971341Sstevel 		return (impl_ddi_sunbus_initchild((dev_info_t *)arg));
14981341Sstevel 
14991341Sstevel 	case DDI_CTLOPS_UNINITCHILD:
15001341Sstevel 		impl_ddi_sunbus_removechild((dev_info_t *)arg);
15011341Sstevel 		return (DDI_SUCCESS);
15021341Sstevel 
15031341Sstevel 	case DDI_CTLOPS_REPORTDEV:
15041341Sstevel 		/*
15051341Sstevel 		 * TODO - Figure out what makes sense to report here.
15061341Sstevel 		 */
15071341Sstevel 		return (DDI_SUCCESS);
15081341Sstevel 
15091341Sstevel 	case DDI_CTLOPS_POKE:
15101341Sstevel 	case DDI_CTLOPS_PEEK:
15111341Sstevel 		return (fhc_ctlops_peekpoke(op, (peekpoke_ctlops_t *)arg,
15121341Sstevel 		    result));
15131341Sstevel 
15141341Sstevel 	default:
15151341Sstevel 		return (ddi_ctlops(dip, rdip, op, arg, result));
15161341Sstevel 	}
15171341Sstevel }
15181341Sstevel 
15191341Sstevel 
15201341Sstevel /*
15211341Sstevel  * We're prepared to claim that the interrupt string is in
15221341Sstevel  * the form of a list of <FHCintr> specifications, or we're dealing
15231341Sstevel  * with on-board devices and we have an interrupt_number property which
15241341Sstevel  * gives us our mondo number.
15251341Sstevel  * Translate the mondos into fhcintrspecs.
15261341Sstevel  */
15271341Sstevel /* ARGSUSED */
15281341Sstevel static void
fhc_xlate_intrs(ddi_intr_handle_impl_t * hdlp,uint32_t ign)15291341Sstevel fhc_xlate_intrs(ddi_intr_handle_impl_t *hdlp, uint32_t ign)
15301341Sstevel 
15311341Sstevel {
15321341Sstevel 	uint32_t mondo;
15331341Sstevel 
15341341Sstevel 	mondo = hdlp->ih_vector;
15351341Sstevel 
15361341Sstevel 	hdlp->ih_vector = (mondo | ign);
15371341Sstevel 	if (hdlp->ih_pri == 0)
15381341Sstevel 		hdlp->ih_pri = fhc_int_priorities[FHC_INO(mondo)];
15391341Sstevel }
15401341Sstevel 
15411341Sstevel static int
fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd,peekpoke_ctlops_t * in_args,void * result)15421341Sstevel fhc_ctlops_peekpoke(ddi_ctl_enum_t cmd, peekpoke_ctlops_t *in_args,
15431341Sstevel     void *result)
15441341Sstevel {
15451341Sstevel 	int err = DDI_SUCCESS;
15461341Sstevel 	on_trap_data_t otd;
15471341Sstevel 
15481341Sstevel 	/* No safe access except for peek/poke is supported. */
15491341Sstevel 	if (in_args->handle != NULL)
15501341Sstevel 		return (DDI_FAILURE);
15511341Sstevel 
15521341Sstevel 	/* Set up protected environment. */
15531341Sstevel 	if (!on_trap(&otd, OT_DATA_ACCESS)) {
15541341Sstevel 		uintptr_t tramp = otd.ot_trampoline;
15551341Sstevel 
15561341Sstevel 		if (cmd == DDI_CTLOPS_POKE) {
15571341Sstevel 			otd.ot_trampoline = (uintptr_t)&poke_fault;
15581341Sstevel 			err = do_poke(in_args->size, (void *)in_args->dev_addr,
15591341Sstevel 			    (void *)in_args->host_addr);
15601341Sstevel 		} else {
15611341Sstevel 			otd.ot_trampoline = (uintptr_t)&peek_fault;
15621341Sstevel 			err = do_peek(in_args->size, (void *)in_args->dev_addr,
15631341Sstevel 			    (void *)in_args->host_addr);
15641341Sstevel 			result = (void *)in_args->host_addr;
15651341Sstevel 		}
15661341Sstevel 		otd.ot_trampoline = tramp;
15671341Sstevel 	} else
15681341Sstevel 		err = DDI_FAILURE;
15691341Sstevel 
15701341Sstevel 	/* Take down protected environment. */
15711341Sstevel 	no_trap();
15721341Sstevel 
15731341Sstevel 	return (err);
15741341Sstevel }
15751341Sstevel 
15761341Sstevel /*
15771341Sstevel  * This function initializes the temperature arrays for use. All
15781341Sstevel  * temperatures are set in to invalid value to start.
15791341Sstevel  */
15801341Sstevel void
init_temp_arrays(struct temp_stats * envstat)15811341Sstevel init_temp_arrays(struct temp_stats *envstat)
15821341Sstevel {
15831341Sstevel 	int i;
15841341Sstevel 
15851341Sstevel 	envstat->index = 0;
15861341Sstevel 
15871341Sstevel 	for (i = 0; i < L1_SZ; i++) {
15881341Sstevel 		envstat->l1[i] = NA_TEMP;
15891341Sstevel 	}
15901341Sstevel 
15911341Sstevel 	for (i = 0; i < L2_SZ; i++) {
15921341Sstevel 		envstat->l2[i] = NA_TEMP;
15931341Sstevel 	}
15941341Sstevel 
15951341Sstevel 	for (i = 0; i < L3_SZ; i++) {
15961341Sstevel 		envstat->l3[i] = NA_TEMP;
15971341Sstevel 	}
15981341Sstevel 
15991341Sstevel 	for (i = 0; i < L4_SZ; i++) {
16001341Sstevel 		envstat->l4[i] = NA_TEMP;
16011341Sstevel 	}
16021341Sstevel 
16031341Sstevel 	for (i = 0; i < L5_SZ; i++) {
16041341Sstevel 		envstat->l5[i] = NA_TEMP;
16051341Sstevel 	}
16061341Sstevel 
16071341Sstevel 	envstat->max = NA_TEMP;
16081341Sstevel 	envstat->min = NA_TEMP;
16091341Sstevel 	envstat->trend = TREND_UNKNOWN;
16101341Sstevel 	envstat->version = TEMP_KSTAT_VERSION;
16111341Sstevel 	envstat->override = NA_TEMP;
16121341Sstevel }
16131341Sstevel 
16141341Sstevel /* Inhibit warning messages below this temperature, eg for CPU poweron. */
16151341Sstevel static uint_t fhc_cpu_warning_temp_threshold = FHC_CPU_WARNING_TEMP_THRESHOLD;
16161341Sstevel 
16171341Sstevel /*
16181341Sstevel  * This function manages the temperature history in the temperature
16191341Sstevel  * statistics buffer passed in. It calls the temperature calibration
16201341Sstevel  * routines and maintains the time averaged temperature data.
16211341Sstevel  */
16221341Sstevel void
update_temp(dev_info_t * pdip,struct temp_stats * envstat,uchar_t value)16231341Sstevel update_temp(dev_info_t *pdip, struct temp_stats *envstat, uchar_t value)
16241341Sstevel {
16251341Sstevel 	uint_t index;		    /* The absolute temperature counter */
16261341Sstevel 	uint_t tmp_index;	    /* temp index into upper level array */
16271341Sstevel 	int count;		    /* Count of non-zero values in array */
16281341Sstevel 	int total;		    /* sum total of non-zero values in array */
16291341Sstevel 	short real_temp;	    /* calibrated temperature */
16301341Sstevel 	int i;
16311341Sstevel 	struct fhc_soft_state *softsp;
16321341Sstevel 	char buffer[256];	    /* buffer for warning of overtemp */
16331341Sstevel 	enum temp_state temp_state; /* Temperature state */
16341341Sstevel 
16351341Sstevel 	/*
16361341Sstevel 	 * NOTE: This global counter is not protected since we're called
16371341Sstevel 	 * serially for each board.
16381341Sstevel 	 */
16391341Sstevel 	static int shutdown_msg = 0; /* Flag if shutdown warning issued */
16401341Sstevel 
16411341Sstevel 	/* determine soft state pointer of parent */
16421341Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(pdip));
16431341Sstevel 
16441341Sstevel 	envstat->index++;
16451341Sstevel 	index = envstat->index;
16461341Sstevel 
16471341Sstevel 	/*
16481341Sstevel 	 * You need to update the level 5 intervals first, since
16491341Sstevel 	 * they are based on the data from the level 4 intervals,
16501341Sstevel 	 * and so on, down to the level 1 intervals.
16511341Sstevel 	 */
16521341Sstevel 
16531341Sstevel 	/* update the level 5 intervals if it is time */
16541341Sstevel 	if (((tmp_index = L5_INDEX(index)) > 0) && (L5_REM(index) == 0)) {
16551341Sstevel 		/* Generate the index within the level 5 array */
16561341Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
16571341Sstevel 		tmp_index = tmp_index % L5_SZ;
16581341Sstevel 
16591341Sstevel 		/* take an average of the level 4 array */
16601341Sstevel 		for (i = 0, count = 0, total = 0; i < L4_SZ; i++) {
16611341Sstevel 			/* Do not include zero values in average */
16621341Sstevel 			if (envstat->l4[i] != NA_TEMP) {
16631341Sstevel 				total += (int)envstat->l4[i];
16641341Sstevel 				count++;
16651341Sstevel 			}
16661341Sstevel 		}
16671341Sstevel 
16681341Sstevel 		/*
16691341Sstevel 		 * If there were any level 4 data points to average,
16701341Sstevel 		 * do so.
16711341Sstevel 		 */
16721341Sstevel 		if (count != 0) {
16731341Sstevel 			envstat->l5[tmp_index] = total/count;
16741341Sstevel 		} else {
16751341Sstevel 			envstat->l5[tmp_index] = NA_TEMP;
16761341Sstevel 		}
16771341Sstevel 	}
16781341Sstevel 
16791341Sstevel 	/* update the level 4 intervals if it is time */
16801341Sstevel 	if (((tmp_index = L4_INDEX(index)) > 0) && (L4_REM(index) == 0)) {
16811341Sstevel 		/* Generate the index within the level 4 array */
16821341Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
16831341Sstevel 		tmp_index = tmp_index % L4_SZ;
16841341Sstevel 
16851341Sstevel 		/* take an average of the level 3 array */
16861341Sstevel 		for (i = 0, count = 0, total = 0; i < L3_SZ; i++) {
16871341Sstevel 			/* Do not include zero values in average */
16881341Sstevel 			if (envstat->l3[i] != NA_TEMP) {
16891341Sstevel 				total += (int)envstat->l3[i];
16901341Sstevel 				count++;
16911341Sstevel 			}
16921341Sstevel 		}
16931341Sstevel 
16941341Sstevel 		/*
16951341Sstevel 		 * If there were any level 3 data points to average,
16961341Sstevel 		 * do so.
16971341Sstevel 		 */
16981341Sstevel 		if (count != 0) {
16991341Sstevel 			envstat->l4[tmp_index] = total/count;
17001341Sstevel 		} else {
17011341Sstevel 			envstat->l4[tmp_index] = NA_TEMP;
17021341Sstevel 		}
17031341Sstevel 	}
17041341Sstevel 
17051341Sstevel 	/* update the level 3 intervals if it is time */
17061341Sstevel 	if (((tmp_index = L3_INDEX(index)) > 0) && (L3_REM(index) == 0)) {
17071341Sstevel 		/* Generate the index within the level 3 array */
17081341Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
17091341Sstevel 		tmp_index = tmp_index % L3_SZ;
17101341Sstevel 
17111341Sstevel 		/* take an average of the level 2 array */
17121341Sstevel 		for (i = 0, count = 0, total = 0; i < L2_SZ; i++) {
17131341Sstevel 			/* Do not include zero values in average */
17141341Sstevel 			if (envstat->l2[i] != NA_TEMP) {
17151341Sstevel 				total += (int)envstat->l2[i];
17161341Sstevel 				count++;
17171341Sstevel 			}
17181341Sstevel 		}
17191341Sstevel 
17201341Sstevel 		/*
17211341Sstevel 		 * If there were any level 2 data points to average,
17221341Sstevel 		 * do so.
17231341Sstevel 		 */
17241341Sstevel 		if (count != 0) {
17251341Sstevel 			envstat->l3[tmp_index] = total/count;
17261341Sstevel 		} else {
17271341Sstevel 			envstat->l3[tmp_index] = NA_TEMP;
17281341Sstevel 		}
17291341Sstevel 	}
17301341Sstevel 
17311341Sstevel 	/* update the level 2 intervals if it is time */
17321341Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0)) {
17331341Sstevel 		/* Generate the index within the level 2 array */
17341341Sstevel 		tmp_index -= 1;		/* decrement by 1 for indexing */
17351341Sstevel 		tmp_index = tmp_index % L2_SZ;
17361341Sstevel 
17371341Sstevel 		/* take an average of the level 1 array */
17381341Sstevel 		for (i = 0, count = 0, total = 0; i < L1_SZ; i++) {
17391341Sstevel 			/* Do not include zero values in average */
17401341Sstevel 			if (envstat->l1[i] != NA_TEMP) {
17411341Sstevel 				total += (int)envstat->l1[i];
17421341Sstevel 				count++;
17431341Sstevel 			}
17441341Sstevel 		}
17451341Sstevel 
17461341Sstevel 		/*
17471341Sstevel 		 * If there were any level 1 data points to average,
17481341Sstevel 		 * do so.
17491341Sstevel 		 */
17501341Sstevel 		if (count != 0) {
17511341Sstevel 			envstat->l2[tmp_index] = total/count;
17521341Sstevel 		} else {
17531341Sstevel 			envstat->l2[tmp_index] = NA_TEMP;
17541341Sstevel 		}
17551341Sstevel 	}
17561341Sstevel 
17571341Sstevel 	/* determine the current temperature in degrees Celcius */
17581341Sstevel 	if (envstat->override != NA_TEMP) {
17591341Sstevel 		/* use override temperature for this board */
17601341Sstevel 		real_temp = envstat->override;
17611341Sstevel 	} else {
17621341Sstevel 		/* Run the calibration function using this board type */
17631341Sstevel 		real_temp = calibrate_temp(softsp->list->sc.type, value,
17647656SSherry.Moore@Sun.COM 		    softsp->list->sc.ac_compid);
17651341Sstevel 	}
17661341Sstevel 
17671341Sstevel 	envstat->l1[index % L1_SZ] = real_temp;
17681341Sstevel 
17691341Sstevel 	/* check if the temperature state for this device needs to change */
17701341Sstevel 	temp_state = get_temp_state(softsp->list->sc.type, real_temp,
17717656SSherry.Moore@Sun.COM 	    softsp->list->sc.board);
17721341Sstevel 
17731341Sstevel 	/* has the state changed? Then get the board string ready */
17741341Sstevel 	if (temp_state != envstat->state) {
17751341Sstevel 		int board = softsp->list->sc.board;
17761341Sstevel 		enum board_type type = softsp->list->sc.type;
17771341Sstevel 
17781341Sstevel 		build_bd_display_str(buffer, type, board);
17791341Sstevel 
17801341Sstevel 		if (temp_state > envstat->state) {
17811341Sstevel 			if (envstat->state == TEMP_OK) {
17821341Sstevel 				if (type == CLOCK_BOARD) {
17831341Sstevel 					reg_fault(0, FT_OVERTEMP, FT_SYSTEM);
17841341Sstevel 				} else {
17851341Sstevel 					reg_fault(board, FT_OVERTEMP,
17867656SSherry.Moore@Sun.COM 					    FT_BOARD);
17871341Sstevel 				}
17881341Sstevel 			}
17891341Sstevel 
17901341Sstevel 			/* heating up, change state now */
17911341Sstevel 			envstat->temp_cnt = 0;
17921341Sstevel 			envstat->state = temp_state;
17931341Sstevel 
17941341Sstevel 			if (temp_state == TEMP_WARN) {
17951341Sstevel 				/* now warn the user of the problem */
17961341Sstevel 				cmn_err(CE_WARN,
17977656SSherry.Moore@Sun.COM 				    "%s is warm (temperature: %dC). "
17987656SSherry.Moore@Sun.COM 				    "Please check system cooling", buffer,
17997656SSherry.Moore@Sun.COM 				    real_temp);
18001341Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_OVERTEMP);
18011341Sstevel 				if (temperature_chamber == -1)
18021341Sstevel 					temperature_chamber =
18031341Sstevel 					    check_for_chamber();
18041341Sstevel 			} else if (temp_state == TEMP_DANGER) {
18051341Sstevel 				cmn_err(CE_WARN,
18067656SSherry.Moore@Sun.COM 				    "%s is very hot (temperature: %dC)",
18077656SSherry.Moore@Sun.COM 				    buffer, real_temp);
18081341Sstevel 
18091341Sstevel 				envstat->shutdown_cnt = 1;
18101341Sstevel 				if (temperature_chamber == -1)
18111341Sstevel 					temperature_chamber =
18127656SSherry.Moore@Sun.COM 					    check_for_chamber();
18131341Sstevel 				if ((temperature_chamber == 0) &&
18141341Sstevel 				    enable_overtemp_powerdown) {
18151341Sstevel 					/*
18161341Sstevel 					 * NOTE: The "%d seconds" is not
18171341Sstevel 					 * necessarily accurate in the case
18181341Sstevel 					 * where we have multiple boards
18191341Sstevel 					 * overheating and subsequently cooling
18201341Sstevel 					 * down.
18211341Sstevel 					 */
18221341Sstevel 					if (shutdown_msg == 0) {
18231341Sstevel 						cmn_err(CE_WARN, "System "
18247656SSherry.Moore@Sun.COM 						    "shutdown scheduled "
18257656SSherry.Moore@Sun.COM 						    "in %d seconds due to "
18267656SSherry.Moore@Sun.COM 						    "over-temperature "
18277656SSherry.Moore@Sun.COM 						    "condition on %s",
18287656SSherry.Moore@Sun.COM 						    SHUTDOWN_TIMEOUT_SEC,
18297656SSherry.Moore@Sun.COM 						    buffer);
18301341Sstevel 					}
18311341Sstevel 					shutdown_msg++;
18321341Sstevel 				}
18331341Sstevel 			}
18341341Sstevel 
18351341Sstevel 			/*
18361341Sstevel 			 * If this is a cpu board, power them off.
18371341Sstevel 			 */
18381341Sstevel 			if (temperature_chamber == 0) {
18391341Sstevel 				mutex_enter(&cpu_lock);
18401341Sstevel 				(void) fhc_board_poweroffcpus(board, NULL,
18411341Sstevel 				    CPU_FORCED);
18421341Sstevel 				mutex_exit(&cpu_lock);
18431341Sstevel 			}
18441341Sstevel 		} else if (temp_state < envstat->state) {
18451341Sstevel 			/*
18461341Sstevel 			 * Avert the sigpower that would
18471341Sstevel 			 * otherwise be sent to init.
18481341Sstevel 			 */
18491341Sstevel 			envstat->shutdown_cnt = 0;
18501341Sstevel 
18511341Sstevel 			/* cooling down, use state counter */
18521341Sstevel 			if (envstat->temp_cnt == 0) {
18531341Sstevel 				envstat->temp_cnt = TEMP_STATE_COUNT;
18541341Sstevel 			} else if (--envstat->temp_cnt == 0) {
18551341Sstevel 				if (temp_state == TEMP_WARN) {
18561341Sstevel 					cmn_err(CE_NOTE,
18577656SSherry.Moore@Sun.COM 					    "%s is cooling "
18587656SSherry.Moore@Sun.COM 					    "(temperature: %dC)", buffer,
18597656SSherry.Moore@Sun.COM 					    real_temp);
18601341Sstevel 
18611341Sstevel 				} else if (temp_state == TEMP_OK) {
18621341Sstevel 					cmn_err(CE_NOTE,
18637656SSherry.Moore@Sun.COM 					    "%s has cooled down "
18647656SSherry.Moore@Sun.COM 					    "(temperature: %dC), system OK",
18657656SSherry.Moore@Sun.COM 					    buffer, real_temp);
18661341Sstevel 
18671341Sstevel 					if (type == CLOCK_BOARD) {
18681341Sstevel 						clear_fault(0, FT_OVERTEMP,
18697656SSherry.Moore@Sun.COM 						    FT_SYSTEM);
18701341Sstevel 					} else {
18711341Sstevel 						clear_fault(board, FT_OVERTEMP,
18727656SSherry.Moore@Sun.COM 						    FT_BOARD);
18731341Sstevel 					}
18741341Sstevel 				}
18751341Sstevel 
18761341Sstevel 				/*
18771341Sstevel 				 * If we just came out of TEMP_DANGER, and
18781341Sstevel 				 * a warning was issued about shutting down,
18791341Sstevel 				 * let the user know it's been cancelled
18801341Sstevel 				 */
18811341Sstevel 				if (envstat->state == TEMP_DANGER &&
18821341Sstevel 				    (temperature_chamber == 0) &&
18831341Sstevel 				    enable_overtemp_powerdown &&
18841341Sstevel 				    (powerdown_started == 0) &&
18851341Sstevel 				    (--shutdown_msg == 0)) {
18861341Sstevel 					cmn_err(CE_NOTE, "System "
18877656SSherry.Moore@Sun.COM 					    "shutdown due to over-"
18887656SSherry.Moore@Sun.COM 					    "temperature "
18897656SSherry.Moore@Sun.COM 					    "condition cancelled");
18901341Sstevel 				}
18911341Sstevel 				envstat->state = temp_state;
18921341Sstevel 
18931341Sstevel 				fhc_bd_update(board, SYSC_EVT_BD_TEMP_OK);
18941341Sstevel 			}
18951341Sstevel 		}
18961341Sstevel 	} else {
18971341Sstevel 		envstat->temp_cnt = 0;
18981341Sstevel 
18991341Sstevel 		if (temp_state == TEMP_DANGER) {
19001341Sstevel 			if (temperature_chamber == -1) {
19011341Sstevel 				temperature_chamber = check_for_chamber();
19021341Sstevel 			}
19031341Sstevel 
19041341Sstevel 			if ((envstat->shutdown_cnt++ >= SHUTDOWN_COUNT) &&
19051341Sstevel 			    (temperature_chamber == 0) &&
19061341Sstevel 			    enable_overtemp_powerdown &&
19071341Sstevel 			    (powerdown_started == 0)) {
19081341Sstevel 				powerdown_started = 1;
19091341Sstevel 
19101341Sstevel 				/* the system is still too hot */
19111341Sstevel 				build_bd_display_str(buffer,
19127656SSherry.Moore@Sun.COM 				    softsp->list->sc.type,
19137656SSherry.Moore@Sun.COM 				    softsp->list->sc.board);
19141341Sstevel 
19151341Sstevel 				cmn_err(CE_WARN, "%s still too hot "
19167656SSherry.Moore@Sun.COM 				    "(temperature: %dC)."
19177656SSherry.Moore@Sun.COM 				    " Overtemp shutdown started", buffer,
19187656SSherry.Moore@Sun.COM 				    real_temp);
19191341Sstevel 
19201341Sstevel 				fhc_reboot();
19211341Sstevel 			}
19221341Sstevel 		}
19231341Sstevel 	}
19241341Sstevel 
19251341Sstevel 	/* update the maximum and minimum temperatures if necessary */
19261341Sstevel 	if ((envstat->max == NA_TEMP) || (real_temp > envstat->max)) {
19271341Sstevel 		envstat->max = real_temp;
19281341Sstevel 	}
19291341Sstevel 
19301341Sstevel 	if ((envstat->min == NA_TEMP) || (real_temp < envstat->min)) {
19311341Sstevel 		envstat->min = real_temp;
19321341Sstevel 	}
19331341Sstevel 
19341341Sstevel 	/*
19351341Sstevel 	 * Update the temperature trend.  Currently, the temperature
19361341Sstevel 	 * trend algorithm is based on the level 2 stats.  So, we
19371341Sstevel 	 * only need to run every time the level 2 stats get updated.
19381341Sstevel 	 */
19391341Sstevel 	if (((tmp_index = L2_INDEX(index)) > 0) && (L2_REM(index) == 0))  {
19401341Sstevel 		enum board_type type = softsp->list->sc.type;
19411341Sstevel 
19421341Sstevel 		envstat->trend = temp_trend(envstat);
19431341Sstevel 
19441341Sstevel 		/* Issue a warning if the temperature is rising rapidly. */
19451341Sstevel 		/* For CPU boards, don't warn if CPUs just powered on. */
19461341Sstevel 		if (envstat->trend == TREND_RAPID_RISE &&
19471341Sstevel 		    (type != CPU_BOARD || real_temp >
19481341Sstevel 		    fhc_cpu_warning_temp_threshold))  {
19491341Sstevel 			int board = softsp->list->sc.board;
19501341Sstevel 
19511341Sstevel 			build_bd_display_str(buffer, type, board);
19521341Sstevel 			cmn_err(CE_WARN, "%s temperature is rising rapidly!  "
19537656SSherry.Moore@Sun.COM 			    "Current temperature is %dC", buffer,
19547656SSherry.Moore@Sun.COM 			    real_temp);
19551341Sstevel 		}
19561341Sstevel 	}
19571341Sstevel }
19581341Sstevel 
19591341Sstevel #define	PREV_L2_INDEX(x)    ((x) ? ((x) - 1) : (L2_SZ - 1))
19601341Sstevel 
19611341Sstevel /*
19621341Sstevel  * This routine determines if the temp of the device passed in is heating
19631341Sstevel  * up, cooling down, or staying stable.
19641341Sstevel  */
19651341Sstevel enum temp_trend
temp_trend(struct temp_stats * tempstat)19661341Sstevel temp_trend(struct temp_stats *tempstat)
19671341Sstevel {
19681341Sstevel 	int		ii;
19691341Sstevel 	uint_t		curr_index;
19701341Sstevel 	int		curr_temp;
19711341Sstevel 	uint_t		prev_index;
19721341Sstevel 	int		prev_temp;
19731341Sstevel 	int		trail_temp;
19741341Sstevel 	int		delta;
19751341Sstevel 	int		read_cnt;
19761341Sstevel 	enum temp_trend	result = TREND_STABLE;
19771341Sstevel 
19781341Sstevel 	if (tempstat == NULL)
19791341Sstevel 		return (TREND_UNKNOWN);
19801341Sstevel 
19811341Sstevel 	curr_index = (L2_INDEX(tempstat->index) - 1) % L2_SZ;
19821341Sstevel 	curr_temp = tempstat->l2[curr_index];
19831341Sstevel 
19841341Sstevel 	/* Count how many temperature readings are available */
19851341Sstevel 	prev_index = curr_index;
19861341Sstevel 	for (read_cnt = 0; read_cnt < L2_SZ - 1; read_cnt++) {
19871341Sstevel 		if (tempstat->l2[prev_index] == NA_TEMP)
19881341Sstevel 			break;
19891341Sstevel 		prev_index = PREV_L2_INDEX(prev_index);
19901341Sstevel 	}
19911341Sstevel 
19921341Sstevel 	switch (read_cnt) {
19931341Sstevel 	case 0:
19941341Sstevel 	case 1:
19951341Sstevel 		result = TREND_UNKNOWN;
19961341Sstevel 		break;
19971341Sstevel 
19981341Sstevel 	default:
19991341Sstevel 		delta = curr_temp - tempstat->l2[PREV_L2_INDEX(curr_index)];
20001341Sstevel 		prev_index = curr_index;
20011341Sstevel 		trail_temp = prev_temp = curr_temp;
20021341Sstevel 		if (delta >= RAPID_RISE_THRESH) {	    /* rapid rise? */
20031341Sstevel 			result = TREND_RAPID_RISE;
20041341Sstevel 		} else if (delta > 0) {			    /* rise? */
20051341Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
20061341Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
20071341Sstevel 				prev_temp = tempstat->l2[prev_index];
20081341Sstevel 				if (prev_temp > trail_temp) {
20091341Sstevel 					break;
20101341Sstevel 				}
20111341Sstevel 				trail_temp = prev_temp;
20121341Sstevel 				if (prev_temp <= curr_temp - NOISE_THRESH) {
20131341Sstevel 					result = TREND_RISE;
20141341Sstevel 					break;
20151341Sstevel 				}
20161341Sstevel 			}
20171341Sstevel 		} else if (delta <= -RAPID_FALL_THRESH) {   /* rapid fall? */
20181341Sstevel 			result = TREND_RAPID_FALL;
20191341Sstevel 		} else if (delta < 0) {			    /* fall? */
20201341Sstevel 			for (ii = 1; ii < read_cnt; ii++) {
20211341Sstevel 				prev_index = PREV_L2_INDEX(prev_index);
20221341Sstevel 				prev_temp = tempstat->l2[prev_index];
20231341Sstevel 				if (prev_temp < trail_temp) {
20241341Sstevel 					break;
20251341Sstevel 				}
20261341Sstevel 				trail_temp = prev_temp;
20271341Sstevel 				if (prev_temp >= curr_temp + NOISE_THRESH) {
20281341Sstevel 					result = TREND_FALL;
20291341Sstevel 					break;
20301341Sstevel 				}
20311341Sstevel 			}
20321341Sstevel 		}
20331341Sstevel 	}
20341341Sstevel 	return (result);
20351341Sstevel }
20361341Sstevel 
20371341Sstevel /*
20381341Sstevel  * Reboot the system if we can, otherwise attempt a power down
20391341Sstevel  */
20401341Sstevel void
fhc_reboot(void)20411341Sstevel fhc_reboot(void)
20421341Sstevel {
20431341Sstevel 	proc_t *initpp;
20441341Sstevel 
20451341Sstevel 	/* send a SIGPWR to init process */
20461341Sstevel 	mutex_enter(&pidlock);
20471341Sstevel 	initpp = prfind(P_INITPID);
20481341Sstevel 	mutex_exit(&pidlock);
20491341Sstevel 
20501341Sstevel 	/*
20511341Sstevel 	 * If we're still booting and init(1) isn't
20521341Sstevel 	 * set up yet, simply halt.
20531341Sstevel 	 */
20541341Sstevel 	if (initpp != NULL) {
20551341Sstevel 		psignal(initpp, SIGFPE);	/* init 6 */
20561341Sstevel 	} else {
20571341Sstevel 		power_down("Environmental Shutdown");
20581341Sstevel 		halt("Power off the System");
20591341Sstevel 	}
20601341Sstevel }
20611341Sstevel 
20621341Sstevel int
overtemp_kstat_update(kstat_t * ksp,int rw)20631341Sstevel overtemp_kstat_update(kstat_t *ksp, int rw)
20641341Sstevel {
20651341Sstevel 	struct temp_stats *tempstat;
20661341Sstevel 	char *kstatp;
20671341Sstevel 	int i;
20681341Sstevel 
20691341Sstevel 	kstatp = (char *)ksp->ks_data;
20701341Sstevel 	tempstat = (struct temp_stats *)ksp->ks_private;
20711341Sstevel 
20721341Sstevel 	/*
20731341Sstevel 	 * Kstat reads are used to retrieve the current system temperature
20741341Sstevel 	 * history. Kstat writes are used to reset the max and min
20751341Sstevel 	 * temperatures.
20761341Sstevel 	 */
20771341Sstevel 	if (rw == KSTAT_WRITE) {
20781341Sstevel 		short max;	/* temporary copy of max temperature */
20791341Sstevel 		short min;	/* temporary copy of min temperature */
20801341Sstevel 
20811341Sstevel 		/*
20821341Sstevel 		 * search for and reset the max and min to the current
20831341Sstevel 		 * array contents. Old max and min values will get
20841341Sstevel 		 * averaged out as they move into the higher level arrays.
20851341Sstevel 		 */
20861341Sstevel 		max = tempstat->l1[0];
20871341Sstevel 		min = tempstat->l1[0];
20881341Sstevel 
20891341Sstevel 		/* Pull the max and min from Level 1 array */
20901341Sstevel 		for (i = 0; i < L1_SZ; i++) {
20911341Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
20921341Sstevel 			    (tempstat->l1[i] > max)) {
20931341Sstevel 				max = tempstat->l1[i];
20941341Sstevel 			}
20951341Sstevel 
20961341Sstevel 			if ((tempstat->l1[i] != NA_TEMP) &&
20971341Sstevel 			    (tempstat->l1[i] < min)) {
20981341Sstevel 				min = tempstat->l1[i];
20991341Sstevel 			}
21001341Sstevel 		}
21011341Sstevel 
21021341Sstevel 		/* Pull the max and min from Level 2 array */
21031341Sstevel 		for (i = 0; i < L2_SZ; i++) {
21041341Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
21051341Sstevel 			    (tempstat->l2[i] > max)) {
21061341Sstevel 				max = tempstat->l2[i];
21071341Sstevel 			}
21081341Sstevel 
21091341Sstevel 			if ((tempstat->l2[i] != NA_TEMP) &&
21101341Sstevel 			    (tempstat->l2[i] < min)) {
21111341Sstevel 				min = tempstat->l2[i];
21121341Sstevel 			}
21131341Sstevel 		}
21141341Sstevel 
21151341Sstevel 		/* Pull the max and min from Level 3 array */
21161341Sstevel 		for (i = 0; i < L3_SZ; i++) {
21171341Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
21181341Sstevel 			    (tempstat->l3[i] > max)) {
21191341Sstevel 				max = tempstat->l3[i];
21201341Sstevel 			}
21211341Sstevel 
21221341Sstevel 			if ((tempstat->l3[i] != NA_TEMP) &&
21231341Sstevel 			    (tempstat->l3[i] < min)) {
21241341Sstevel 				min = tempstat->l3[i];
21251341Sstevel 			}
21261341Sstevel 		}
21271341Sstevel 
21281341Sstevel 		/* Pull the max and min from Level 4 array */
21291341Sstevel 		for (i = 0; i < L4_SZ; i++) {
21301341Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
21311341Sstevel 			    (tempstat->l4[i] > max)) {
21321341Sstevel 				max = tempstat->l4[i];
21331341Sstevel 			}
21341341Sstevel 
21351341Sstevel 			if ((tempstat->l4[i] != NA_TEMP) &&
21361341Sstevel 			    (tempstat->l4[i] < min)) {
21371341Sstevel 				min = tempstat->l4[i];
21381341Sstevel 			}
21391341Sstevel 		}
21401341Sstevel 
21411341Sstevel 		/* Pull the max and min from Level 5 array */
21421341Sstevel 		for (i = 0; i < L5_SZ; i++) {
21431341Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
21441341Sstevel 			    (tempstat->l5[i] > max)) {
21451341Sstevel 				max = tempstat->l5[i];
21461341Sstevel 			}
21471341Sstevel 
21481341Sstevel 			if ((tempstat->l5[i] != NA_TEMP) &&
21491341Sstevel 			    (tempstat->l5[i] < min)) {
21501341Sstevel 				min = tempstat->l5[i];
21511341Sstevel 			}
21521341Sstevel 		}
21531341Sstevel 	} else {
21541341Sstevel 		/*
21551341Sstevel 		 * copy the temperature history buffer into the
21561341Sstevel 		 * kstat structure.
21571341Sstevel 		 */
21581341Sstevel 		bcopy(tempstat, kstatp, sizeof (struct temp_stats));
21591341Sstevel 	}
21601341Sstevel 	return (0);
21611341Sstevel }
21621341Sstevel 
21631341Sstevel int
temp_override_kstat_update(kstat_t * ksp,int rw)21641341Sstevel temp_override_kstat_update(kstat_t *ksp, int rw)
21651341Sstevel {
21661341Sstevel 	short *over;
21671341Sstevel 	short *kstatp;
21681341Sstevel 
21691341Sstevel 	kstatp = (short *)ksp->ks_data;
21701341Sstevel 	over = (short *)ksp->ks_private;
21711341Sstevel 
21721341Sstevel 	/*
21731341Sstevel 	 * Kstat reads are used to get the temperature override setting.
21741341Sstevel 	 * Kstat writes are used to set the temperature override setting.
21751341Sstevel 	 */
21761341Sstevel 	if (rw == KSTAT_WRITE) {
21771341Sstevel 		*over = *kstatp;
21781341Sstevel 	} else {
21791341Sstevel 		*kstatp = *over;
21801341Sstevel 	}
21811341Sstevel 	return (0);
21821341Sstevel }
21831341Sstevel 
21841341Sstevel /*
21851341Sstevel  * This function uses the calibration tables at the beginning of this file
21861341Sstevel  * to lookup the actual temperature of the thermistor in degrees Celcius.
21871341Sstevel  * If the measurement is out of the bounds of the acceptable values, the
21881341Sstevel  * closest boundary value is used instead.
21891341Sstevel  */
21901341Sstevel static short
calibrate_temp(enum board_type type,uchar_t temp,uint_t ac_comp)21911341Sstevel calibrate_temp(enum board_type type, uchar_t temp, uint_t ac_comp)
21921341Sstevel {
21931341Sstevel 	short result = NA_TEMP;
21941341Sstevel 
21951341Sstevel 	if (dont_calibrate == 1) {
21961341Sstevel 		return ((short)temp);
21971341Sstevel 	}
21981341Sstevel 
21991341Sstevel 	switch (type) {
22001341Sstevel 	case CPU_BOARD:
22011341Sstevel 		/*
22021341Sstevel 		 * If AC chip revision is >= 4 or if it is unitialized,
22031341Sstevel 		 * then use the new calibration tables.
22041341Sstevel 		 */
22051341Sstevel 		if ((CHIP_REV(ac_comp) >= 4) || (CHIP_REV(ac_comp) == 0)) {
22061341Sstevel 			if (temp >= CPU2_MX_CNT) {
22071341Sstevel 				result = cpu2_table[CPU2_MX_CNT-1];
22081341Sstevel 			} else {
22091341Sstevel 				result = cpu2_table[temp];
22101341Sstevel 			}
22111341Sstevel 		} else {
22121341Sstevel 			if (temp >= CPU_MX_CNT) {
22131341Sstevel 				result = cpu_table[CPU_MX_CNT-1];
22141341Sstevel 			} else {
22151341Sstevel 				result = cpu_table[temp];
22161341Sstevel 			}
22171341Sstevel 		}
22181341Sstevel 		break;
22191341Sstevel 
22201341Sstevel 	case IO_2SBUS_BOARD:
22211341Sstevel 	case IO_SBUS_FFB_BOARD:
22221341Sstevel 	case IO_PCI_BOARD:
22231341Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
22241341Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
22251341Sstevel 		if (temp < IO_MN_CNT) {
22261341Sstevel 			result = io_table[IO_MN_CNT];
22271341Sstevel 		} else if (temp >= IO_MX_CNT) {
22281341Sstevel 			result = io_table[IO_MX_CNT-1];
22291341Sstevel 		} else {
22301341Sstevel 			result = io_table[temp];
22311341Sstevel 		}
22321341Sstevel 		break;
22331341Sstevel 
22341341Sstevel 	case CLOCK_BOARD:
22351341Sstevel 		if (temp < CLK_MN_CNT) {
22361341Sstevel 			result = clock_table[CLK_MN_CNT];
22371341Sstevel 		} else if (temp >= CLK_MX_CNT) {
22381341Sstevel 			result = clock_table[CLK_MX_CNT-1];
22391341Sstevel 		} else {
22401341Sstevel 			result = clock_table[temp];
22411341Sstevel 		}
22421341Sstevel 		break;
22431341Sstevel 
22441341Sstevel 	default:
22451341Sstevel 		break;
22461341Sstevel 	}
22471341Sstevel 
22481341Sstevel 	return (result);
22491341Sstevel }
22501341Sstevel 
22511341Sstevel /*
22521341Sstevel  * Determine the temperature state of this board based on its type and
22531341Sstevel  * the actual temperature in degrees Celcius.
22541341Sstevel  */
22551341Sstevel static enum temp_state
get_temp_state(enum board_type type,short temp,int board)22561341Sstevel get_temp_state(enum board_type type, short temp, int board)
22571341Sstevel {
22581341Sstevel 	enum temp_state state = TEMP_OK;
22591341Sstevel 	short warn_limit;
22601341Sstevel 	short danger_limit;
22611341Sstevel 	struct cpu *cpa, *cpb;
22621341Sstevel 
22631341Sstevel 	switch (type) {
22641341Sstevel 	case CPU_BOARD:
22651341Sstevel 		warn_limit = cpu_warn_temp;
22661341Sstevel 		danger_limit = cpu_danger_temp;
22671341Sstevel 
22681341Sstevel 		/*
22691341Sstevel 		 * For CPU boards with frequency >= 400 MHZ,
22701341Sstevel 		 * temperature zones are different.
22711341Sstevel 		 */
22721341Sstevel 
22731341Sstevel 		mutex_enter(&cpu_lock);
22741341Sstevel 
22751341Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL) {
22761341Sstevel 			if ((cpa->cpu_type_info.pi_clock) >= 400) {
22771341Sstevel 				warn_limit = cpu_warn_temp_4x;
22781341Sstevel 				danger_limit = cpu_danger_temp_4x;
22791341Sstevel 			}
22801341Sstevel 		}
22811341Sstevel 		if ((cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL) {
22821341Sstevel 			if ((cpb->cpu_type_info.pi_clock) >= 400) {
22831341Sstevel 				warn_limit = cpu_warn_temp_4x;
22841341Sstevel 				danger_limit = cpu_danger_temp_4x;
22851341Sstevel 			}
22861341Sstevel 		}
22871341Sstevel 
22881341Sstevel 		mutex_exit(&cpu_lock);
22891341Sstevel 
22901341Sstevel 		break;
22911341Sstevel 
22921341Sstevel 	case IO_2SBUS_BOARD:
22931341Sstevel 	case IO_SBUS_FFB_BOARD:
22941341Sstevel 	case IO_PCI_BOARD:
22951341Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
22961341Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
22971341Sstevel 		warn_limit = io_warn_temp;
22981341Sstevel 		danger_limit = io_danger_temp;
22991341Sstevel 		break;
23001341Sstevel 
23011341Sstevel 	case CLOCK_BOARD:
23021341Sstevel 		warn_limit = clk_warn_temp;
23031341Sstevel 		danger_limit = clk_danger_temp;
23041341Sstevel 		break;
23051341Sstevel 
23061341Sstevel 	case UNINIT_BOARD:
23071341Sstevel 	case UNKNOWN_BOARD:
23081341Sstevel 	case MEM_BOARD:
23091341Sstevel 	default:
23101341Sstevel 		warn_limit = dft_warn_temp;
23111341Sstevel 		danger_limit = dft_danger_temp;
23121341Sstevel 		break;
23131341Sstevel 	}
23141341Sstevel 
23151341Sstevel 	if (temp >= danger_limit) {
23161341Sstevel 		state = TEMP_DANGER;
23171341Sstevel 	} else if (temp >= warn_limit) {
23181341Sstevel 		state = TEMP_WARN;
23191341Sstevel 	}
23201341Sstevel 
23211341Sstevel 	return (state);
23221341Sstevel }
23231341Sstevel 
23241341Sstevel static void
fhc_add_kstats(struct fhc_soft_state * softsp)23251341Sstevel fhc_add_kstats(struct fhc_soft_state *softsp)
23261341Sstevel {
23271341Sstevel 	struct kstat *fhc_ksp;
23281341Sstevel 	struct fhc_kstat *fhc_named_ksp;
23291341Sstevel 
23301341Sstevel 	if ((fhc_ksp = kstat_create("unix", softsp->list->sc.board,
23311341Sstevel 	    FHC_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
23321341Sstevel 	    sizeof (struct fhc_kstat) / sizeof (kstat_named_t),
23331341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
23341341Sstevel 		cmn_err(CE_WARN, "fhc%d kstat_create failed",
23357656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
23361341Sstevel 		return;
23371341Sstevel 	}
23381341Sstevel 
23391341Sstevel 	fhc_named_ksp = (struct fhc_kstat *)(fhc_ksp->ks_data);
23401341Sstevel 
23411341Sstevel 	/* initialize the named kstats */
23421341Sstevel 	kstat_named_init(&fhc_named_ksp->csr,
23437656SSherry.Moore@Sun.COM 	    CSR_KSTAT_NAMED,
23447656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT32);
23451341Sstevel 
23461341Sstevel 	kstat_named_init(&fhc_named_ksp->bsr,
23477656SSherry.Moore@Sun.COM 	    BSR_KSTAT_NAMED,
23487656SSherry.Moore@Sun.COM 	    KSTAT_DATA_UINT32);
23491341Sstevel 
23501341Sstevel 	fhc_ksp->ks_update = fhc_kstat_update;
23511341Sstevel 	fhc_ksp->ks_private = (void *)softsp;
23521341Sstevel 	softsp->fhc_ksp = fhc_ksp;
23531341Sstevel 	kstat_install(fhc_ksp);
23541341Sstevel }
23551341Sstevel 
23561341Sstevel static int
fhc_kstat_update(kstat_t * ksp,int rw)23571341Sstevel fhc_kstat_update(kstat_t *ksp, int rw)
23581341Sstevel {
23591341Sstevel 	struct fhc_kstat *fhcksp;
23601341Sstevel 	struct fhc_soft_state *softsp;
23611341Sstevel 
23621341Sstevel 	fhcksp = (struct fhc_kstat *)ksp->ks_data;
23631341Sstevel 	softsp = (struct fhc_soft_state *)ksp->ks_private;
23641341Sstevel 
23651341Sstevel 	/* this is a read-only kstat. Bail out on a write */
23661341Sstevel 	if (rw == KSTAT_WRITE) {
23671341Sstevel 		return (EACCES);
23681341Sstevel 	} else {
23691341Sstevel 		/*
23701341Sstevel 		 * copy the current state of the hardware into the
23711341Sstevel 		 * kstat structure.
23721341Sstevel 		 */
23731341Sstevel 		fhcksp->csr.value.ui32 = *softsp->ctrl;
23741341Sstevel 		fhcksp->bsr.value.ui32 = *softsp->bsr;
23751341Sstevel 	}
23761341Sstevel 	return (0);
23771341Sstevel }
23781341Sstevel 
23791341Sstevel static int
cpu_on_board(int board)23801341Sstevel cpu_on_board(int board)
23811341Sstevel {
23821341Sstevel 	int upa_a = board << 1;
23831341Sstevel 	int upa_b = (board << 1) + 1;
23841341Sstevel 
23851341Sstevel 	if ((cpunodes[upa_a].nodeid != NULL) ||
23861341Sstevel 	    (cpunodes[upa_b].nodeid != NULL)) {
23871341Sstevel 		return (1);
23881341Sstevel 	} else {
23891341Sstevel 		return (0);
23901341Sstevel 	}
23911341Sstevel }
23921341Sstevel 
23931341Sstevel /*
23941341Sstevel  * This function uses the board list and toggles the OS green board
23951341Sstevel  * LED. The mask input tells which bit fields are being modified,
23961341Sstevel  * and the value input tells the states of the bits.
23971341Sstevel  */
23981341Sstevel void
update_board_leds(fhc_bd_t * board,uint_t mask,uint_t value)23991341Sstevel update_board_leds(fhc_bd_t *board, uint_t mask, uint_t value)
24001341Sstevel {
24011341Sstevel 	volatile uint_t temp;
24021341Sstevel 
24031341Sstevel 	ASSERT(fhc_bdlist_locked());
24041341Sstevel 
24051341Sstevel 	/* mask off mask and value for only the LED bits */
24061341Sstevel 	mask &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
24071341Sstevel 	value &= (FHC_LED_LEFT|FHC_LED_MID|FHC_LED_RIGHT);
24081341Sstevel 
24091341Sstevel 	if (board != NULL) {
24101341Sstevel 		mutex_enter(&board->softsp->ctrl_lock);
24111341Sstevel 
24121341Sstevel 		/* read the current register state */
24131341Sstevel 		temp = *board->softsp->ctrl;
24141341Sstevel 
24151341Sstevel 		/*
24161341Sstevel 		 * The EPDA bits are special since the register is
24171341Sstevel 		 * special.  We don't want to set them, since setting
24181341Sstevel 		 * the bits on a shutdown cpu keeps the cpu permanently
24191341Sstevel 		 * powered off.  Also, the CSR_SYNC bit must always be
24201341Sstevel 		 * set to 0 as it is an OBP semaphore that is expected to
24211341Sstevel 		 * be clear for cpu restart.
24221341Sstevel 		 */
24231341Sstevel 		temp &= ~(FHC_CSR_SYNC | FHC_EPDA_OFF | FHC_EPDB_OFF);
24241341Sstevel 
24251341Sstevel 		/* mask off the bits to change */
24261341Sstevel 		temp &= ~mask;
24271341Sstevel 
24281341Sstevel 		/* or in the new values of the bits. */
24291341Sstevel 		temp |= value;
24301341Sstevel 
24311341Sstevel 		/* update the register */
24321341Sstevel 		*board->softsp->ctrl = temp;
24331341Sstevel 
24341341Sstevel 		/* flush the hardware registers */
24351341Sstevel 		temp = *board->softsp->ctrl;
24361341Sstevel #ifdef lint
24371341Sstevel 		temp = temp;
24381341Sstevel #endif
24391341Sstevel 
24401341Sstevel 		mutex_exit(&board->softsp->ctrl_lock);
24411341Sstevel 	}
24421341Sstevel }
24431341Sstevel 
24441341Sstevel static int
check_for_chamber(void)24451341Sstevel check_for_chamber(void)
24461341Sstevel {
24471341Sstevel 	int chamber = 0;
24481341Sstevel 	dev_info_t *options_dip;
24491341Sstevel 	pnode_t options_node_id;
24501341Sstevel 	int mfgmode_len;
24511341Sstevel 	int retval;
24521341Sstevel 	char *mfgmode;
24531341Sstevel 
24541341Sstevel 	/*
24551341Sstevel 	 * The operator can disable overtemp powerdown from /etc/system or
24561341Sstevel 	 * boot -h.
24571341Sstevel 	 */
24581341Sstevel 	if (!enable_overtemp_powerdown) {
24591341Sstevel 		cmn_err(CE_WARN, "Operator has disabled overtemp powerdown");
24601341Sstevel 		return (1);
24611341Sstevel 	}
24621341Sstevel 
24631341Sstevel 	/*
24641341Sstevel 	 * An OBP option, 'mfg-mode' is being used to inform us as to
24651341Sstevel 	 * whether we are in an enviromental chamber. It exists in
24661341Sstevel 	 * the 'options' node. This is where all OBP 'setenv' (eeprom)
24671341Sstevel 	 * parameters live.
24681341Sstevel 	 */
24691341Sstevel 	if ((options_dip = ddi_find_devinfo("options", -1, 0)) != NULL) {
24701341Sstevel 		options_node_id = (pnode_t)ddi_get_nodeid(options_dip);
24711341Sstevel 		mfgmode_len = prom_getproplen(options_node_id, "mfg-mode");
24721341Sstevel 		if (mfgmode_len == -1) {
24731341Sstevel 			return (chamber);
24741341Sstevel 		}
24751341Sstevel 		mfgmode = kmem_alloc(mfgmode_len+1, KM_SLEEP);
24761341Sstevel 
24771341Sstevel 		retval = prom_getprop(options_node_id, "mfg-mode", mfgmode);
24781341Sstevel 		if (retval != -1) {
24791341Sstevel 			mfgmode[retval] = 0;
24801341Sstevel 			if (strcmp(mfgmode, CHAMBER_VALUE) == 0) {
24811341Sstevel 				chamber = 1;
24821341Sstevel 				cmn_err(CE_WARN, "System in Temperature"
24837656SSherry.Moore@Sun.COM 				    " Chamber Mode. Overtemperature"
24847656SSherry.Moore@Sun.COM 				    " Shutdown disabled");
24851341Sstevel 			}
24861341Sstevel 		}
24871341Sstevel 		kmem_free(mfgmode, mfgmode_len+1);
24881341Sstevel 	}
24891341Sstevel 	return (chamber);
24901341Sstevel }
24911341Sstevel 
24921341Sstevel static void
build_bd_display_str(char * buffer,enum board_type type,int board)24931341Sstevel build_bd_display_str(char *buffer, enum board_type type, int board)
24941341Sstevel {
24951341Sstevel 	if (buffer == NULL) {
24961341Sstevel 		return;
24971341Sstevel 	}
24981341Sstevel 
24991341Sstevel 	/* fill in board type to display */
25001341Sstevel 	switch (type) {
25011341Sstevel 	case UNINIT_BOARD:
25021341Sstevel 		(void) sprintf(buffer, "Uninitialized Board type board %d",
25037656SSherry.Moore@Sun.COM 		    board);
25041341Sstevel 		break;
25051341Sstevel 
25061341Sstevel 	case UNKNOWN_BOARD:
25071341Sstevel 		(void) sprintf(buffer, "Unknown Board type board %d", board);
25081341Sstevel 		break;
25091341Sstevel 
25101341Sstevel 	case CPU_BOARD:
25111341Sstevel 	case MEM_BOARD:
25121341Sstevel 		(void) sprintf(buffer, "CPU/Memory board %d", board);
25131341Sstevel 		break;
25141341Sstevel 
25151341Sstevel 	case IO_2SBUS_BOARD:
25161341Sstevel 		(void) sprintf(buffer, "2 SBus IO board %d", board);
25171341Sstevel 		break;
25181341Sstevel 
25191341Sstevel 	case IO_SBUS_FFB_BOARD:
25201341Sstevel 		(void) sprintf(buffer, "SBus FFB IO board %d", board);
25211341Sstevel 		break;
25221341Sstevel 
25231341Sstevel 	case IO_PCI_BOARD:
25241341Sstevel 		(void) sprintf(buffer, "PCI IO board %d", board);
25251341Sstevel 		break;
25261341Sstevel 
25271341Sstevel 	case CLOCK_BOARD:
25281341Sstevel 		(void) sprintf(buffer, "Clock board");
25291341Sstevel 		break;
25301341Sstevel 
25311341Sstevel 	case IO_2SBUS_SOCPLUS_BOARD:
25321341Sstevel 		(void) sprintf(buffer, "2 SBus SOC+ IO board %d", board);
25331341Sstevel 		break;
25341341Sstevel 
25351341Sstevel 	case IO_SBUS_FFB_SOCPLUS_BOARD:
25361341Sstevel 		(void) sprintf(buffer, "SBus FFB SOC+ IO board %d", board);
25371341Sstevel 		break;
25381341Sstevel 
25391341Sstevel 	default:
25401341Sstevel 		(void) sprintf(buffer, "Unrecognized board type board %d",
25417656SSherry.Moore@Sun.COM 		    board);
25421341Sstevel 		break;
25431341Sstevel 	}
25441341Sstevel }
25451341Sstevel 
25461341Sstevel void
fhc_intrdist(void * arg)25471341Sstevel fhc_intrdist(void *arg)
25481341Sstevel {
25491341Sstevel 	struct fhc_soft_state *softsp;
25501341Sstevel 	dev_info_t *dip = (dev_info_t *)arg;
25511341Sstevel 	volatile uint_t *mondo_vec_reg;
25521341Sstevel 	volatile uint_t *intr_state_reg;
25531341Sstevel 	uint_t mondo_vec;
25541341Sstevel 	uint_t tmp_reg;
25551341Sstevel 	uint_t cpu_id;
25561341Sstevel 	uint_t i;
25571341Sstevel 
25581341Sstevel 	/* extract the soft state pointer */
25591341Sstevel 	softsp = ddi_get_soft_state(fhcp, ddi_get_instance(dip));
25601341Sstevel 
25611341Sstevel 	/*
25621341Sstevel 	 * Loop through all the interrupt mapping registers and reprogram
25631341Sstevel 	 * the target CPU for all valid registers.
25641341Sstevel 	 */
25651341Sstevel 	for (i = 0; i < FHC_MAX_INO; i++) {
25661341Sstevel 		mondo_vec_reg = softsp->intr_regs[i].mapping_reg;
25671341Sstevel 		intr_state_reg = softsp->intr_regs[i].clear_reg;
25681341Sstevel 
25691341Sstevel 		if ((*mondo_vec_reg & IMR_VALID) == 0)
25701341Sstevel 			continue;
25711341Sstevel 
25721341Sstevel 		cpu_id = intr_dist_cpuid();
25731341Sstevel 
25741341Sstevel 		/* Check the current target of the mondo */
25751341Sstevel 		if (((*mondo_vec_reg & INR_PID_MASK) >> INR_PID_SHIFT) ==
25761341Sstevel 		    cpu_id) {
25771341Sstevel 			/* It is the same, don't reprogram */
25781341Sstevel 			return;
25791341Sstevel 		}
25801341Sstevel 
25811341Sstevel 		/* So it's OK to reprogram the CPU target */
25821341Sstevel 
25831341Sstevel 		/* turn off the valid bit */
25841341Sstevel 		*mondo_vec_reg &= ~IMR_VALID;
25851341Sstevel 
25861341Sstevel 		/* flush the hardware registers */
25871341Sstevel 		tmp_reg = *softsp->id;
25881341Sstevel 
25891341Sstevel 		/*
25901341Sstevel 		 * wait for the state machine to idle. Do not loop on panic, so
25911341Sstevel 		 * that system does not hang.
25921341Sstevel 		 */
25931341Sstevel 		while (((*intr_state_reg & INT_PENDING) == INT_PENDING) &&
25941341Sstevel 		    !panicstr)
25951341Sstevel 			;
25961341Sstevel 
25971341Sstevel 		/* re-target the mondo and turn it on */
25981341Sstevel 		mondo_vec = (cpu_id << INR_PID_SHIFT) | IMR_VALID;
25991341Sstevel 
26001341Sstevel 		/* write it back to the hardware. */
26011341Sstevel 		*mondo_vec_reg = mondo_vec;
26021341Sstevel 
26031341Sstevel 		/* flush the hardware buffers. */
26041341Sstevel 		tmp_reg = *(softsp->id);
26051341Sstevel 
26061341Sstevel #ifdef	lint
26071341Sstevel 		tmp_reg = tmp_reg;
26081341Sstevel #endif	/* lint */
26091341Sstevel 	}
26101341Sstevel }
26111341Sstevel 
26121341Sstevel /*
26131341Sstevel  * reg_fault
26141341Sstevel  *
26151341Sstevel  * This routine registers a fault in the fault list. If the fault
26161341Sstevel  * is unique (does not exist in fault list) then a new fault is
26171341Sstevel  * added to the fault list, with the appropriate structure elements
26181341Sstevel  * filled in.
26191341Sstevel  */
26201341Sstevel void
reg_fault(int unit,enum ft_type type,enum ft_class fclass)26211341Sstevel reg_fault(int unit, enum ft_type type, enum ft_class fclass)
26221341Sstevel {
26231341Sstevel 	struct ft_link_list *list;	/* temporary list pointer */
26241341Sstevel 
26251341Sstevel 	if (type >= ft_max_index) {
26261341Sstevel 		cmn_err(CE_WARN, "Illegal Fault type %x", type);
26271341Sstevel 		return;
26281341Sstevel 	}
26291341Sstevel 
26301341Sstevel 	mutex_enter(&ftlist_mutex);
26311341Sstevel 
26321341Sstevel 	/* Search for the requested fault. If it already exists, return. */
26331341Sstevel 	for (list = ft_list; list != NULL; list = list->next) {
26341341Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
26351341Sstevel 		    (list->f.fclass == fclass)) {
26361341Sstevel 			mutex_exit(&ftlist_mutex);
26371341Sstevel 			return;
26381341Sstevel 		}
26391341Sstevel 	}
26401341Sstevel 
26411341Sstevel 	/* Allocate a new fault structure. */
26421341Sstevel 	list = kmem_zalloc(sizeof (struct ft_link_list), KM_SLEEP);
26431341Sstevel 
26441341Sstevel 	/* fill in the fault list elements */
26451341Sstevel 	list->f.unit = unit;
26461341Sstevel 	list->f.type = type;
26471341Sstevel 	list->f.fclass = fclass;
26481341Sstevel 	list->f.create_time = (time32_t)gethrestime_sec(); /* XX64 */
26491341Sstevel 	(void) strncpy(list->f.msg, ft_str_table[type], MAX_FT_DESC);
26501341Sstevel 
26511341Sstevel 	/* link it into the list. */
26521341Sstevel 	list->next = ft_list;
26531341Sstevel 	ft_list = list;
26541341Sstevel 
26551341Sstevel 	/* Update the total fault count */
26561341Sstevel 	ft_nfaults++;
26571341Sstevel 
26581341Sstevel 	mutex_exit(&ftlist_mutex);
26591341Sstevel }
26601341Sstevel 
26611341Sstevel /*
26621341Sstevel  * clear_fault
26631341Sstevel  *
26641341Sstevel  * This routine finds the fault list entry specified by the caller,
26651341Sstevel  * deletes it from the fault list, and frees up the memory used for
26661341Sstevel  * the entry. If the requested fault is not found, it exits silently.
26671341Sstevel  */
26681341Sstevel void
clear_fault(int unit,enum ft_type type,enum ft_class fclass)26691341Sstevel clear_fault(int unit, enum ft_type type, enum ft_class fclass)
26701341Sstevel {
26711341Sstevel 	struct ft_link_list *list;		/* temporary list pointer */
26721341Sstevel 	struct ft_link_list **vect;
26731341Sstevel 
26741341Sstevel 	mutex_enter(&ftlist_mutex);
26751341Sstevel 
26761341Sstevel 	list = ft_list;
26771341Sstevel 	vect = &ft_list;
26781341Sstevel 
26791341Sstevel 	/*
26801341Sstevel 	 * Search for the requested fault. If it exists, delete it
26811341Sstevel 	 * and relink the fault list.
26821341Sstevel 	 */
26831341Sstevel 	for (; list != NULL; vect = &list->next, list = list->next) {
26841341Sstevel 		if ((list->f.unit == unit) && (list->f.type == type) &&
26851341Sstevel 		    (list->f.fclass == fclass)) {
26861341Sstevel 			/* remove the item from the list */
26871341Sstevel 			*vect = list->next;
26881341Sstevel 
26891341Sstevel 			/* free the memory allocated */
26901341Sstevel 			kmem_free(list, sizeof (struct ft_link_list));
26911341Sstevel 
26921341Sstevel 			/* Update the total fault count */
26931341Sstevel 			ft_nfaults--;
26941341Sstevel 			break;
26951341Sstevel 		}
26961341Sstevel 	}
26971341Sstevel 	mutex_exit(&ftlist_mutex);
26981341Sstevel }
26991341Sstevel 
27001341Sstevel /*
27011341Sstevel  * process_fault_list
27021341Sstevel  *
27031341Sstevel  * This routine walks the global fault list and updates the board list
27041341Sstevel  * with the current status of each Yellow LED. If any faults are found
27051341Sstevel  * in the system, then a non-zero value is returned. Else zero is returned.
27061341Sstevel  */
27071341Sstevel int
process_fault_list(void)27081341Sstevel process_fault_list(void)
27091341Sstevel {
27101341Sstevel 	int fault = 0;
27111341Sstevel 	struct ft_link_list *ftlist;	/* fault list pointer */
27121341Sstevel 	fhc_bd_t *bdlist;		/* board list pointer */
27131341Sstevel 
27141341Sstevel 	/*
27151341Sstevel 	 * Note on locking. The bdlist mutex is always acquired and
27161341Sstevel 	 * held around the ftlist mutex when both are needed for an
27171341Sstevel 	 * operation. This is to avoid deadlock.
27181341Sstevel 	 */
27191341Sstevel 
27201341Sstevel 	/* First lock the board list */
27211341Sstevel 	(void) fhc_bdlist_lock(-1);
27221341Sstevel 
27231341Sstevel 	/* Grab the fault list lock first */
27241341Sstevel 	mutex_enter(&ftlist_mutex);
27251341Sstevel 
27261341Sstevel 	/* clear the board list of all faults first */
27271341Sstevel 	for (bdlist = fhc_bd_first(); bdlist; bdlist = fhc_bd_next(bdlist))
27281341Sstevel 		bdlist->fault = 0;
27291341Sstevel 
27301341Sstevel 	/* walk the fault list here */
27311341Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
27321341Sstevel 		fault++;
27331341Sstevel 
27341341Sstevel 		/*
27351341Sstevel 		 * If this is a board level fault, find the board, The
27361341Sstevel 		 * unit number for all board class faults must be the
27371341Sstevel 		 * actual board number. The caller of reg_fault must
27381341Sstevel 		 * ensure this for FT_BOARD class faults.
27391341Sstevel 		 */
27401341Sstevel 		if (ftlist->f.fclass == FT_BOARD) {
27411341Sstevel 			/* Sanity check the board first */
27421341Sstevel 			if (fhc_bd_valid(ftlist->f.unit)) {
27431341Sstevel 				bdlist = fhc_bd(ftlist->f.unit);
27441341Sstevel 				bdlist->fault = 1;
27451341Sstevel 			} else {
27461341Sstevel 				cmn_err(CE_WARN, "No board %d list entry found",
27477656SSherry.Moore@Sun.COM 				    ftlist->f.unit);
27481341Sstevel 			}
27491341Sstevel 		}
27501341Sstevel 	}
27511341Sstevel 
27521341Sstevel 	/* now unlock the fault list */
27531341Sstevel 	mutex_exit(&ftlist_mutex);
27541341Sstevel 
27551341Sstevel 	/* unlock the board list before leaving */
27561341Sstevel 	fhc_bdlist_unlock();
27571341Sstevel 
27581341Sstevel 	return (fault);
27591341Sstevel }
27601341Sstevel 
27611341Sstevel /*
27621341Sstevel  * Add a new memloc to the database (and keep 'em sorted by PA)
27631341Sstevel  */
27641341Sstevel void
fhc_add_memloc(int board,uint64_t pa,uint_t size)27651341Sstevel fhc_add_memloc(int board, uint64_t pa, uint_t size)
27661341Sstevel {
27671341Sstevel 	struct fhc_memloc *p, **pp;
27681341Sstevel 	uint_t ipa = pa >> FHC_MEMLOC_SHIFT;
27691341Sstevel 
27701341Sstevel 	ASSERT(fhc_bdlist_locked());
27711341Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
27721341Sstevel 
27731341Sstevel 	/* look for a comparable memloc (as long as new PA smaller) */
27741341Sstevel 	for (p = fhc_base_memloc, pp = &fhc_base_memloc;
27751341Sstevel 	    p != NULL; pp = &p->next, p = p->next) {
27761341Sstevel 		/* have we passed our place in the sort? */
27771341Sstevel 		if (ipa < p->pa) {
27781341Sstevel 			break;
27791341Sstevel 		}
27801341Sstevel 	}
27811341Sstevel 	p = kmem_alloc(sizeof (struct fhc_memloc), KM_SLEEP);
27821341Sstevel 	p->next = *pp;
27831341Sstevel 	p->board = board;
27841341Sstevel 	p->pa = ipa;
27851341Sstevel 	p->size = size;
27861341Sstevel #ifdef DEBUG_MEMDEC
27871341Sstevel 	cmn_err(CE_NOTE, "fhc_add_memloc: adding %d 0x%x 0x%x",
27887656SSherry.Moore@Sun.COM 	    p->board, p->pa, p->size);
27891341Sstevel #endif /* DEBUG_MEMDEC */
27901341Sstevel 	*pp = p;
27911341Sstevel }
27921341Sstevel 
27931341Sstevel /*
27941341Sstevel  * Delete all memloc records for a board from the database
27951341Sstevel  */
27961341Sstevel void
fhc_del_memloc(int board)27971341Sstevel fhc_del_memloc(int board)
27981341Sstevel {
27991341Sstevel 	struct fhc_memloc *p, **pp;
28001341Sstevel 
28011341Sstevel 	ASSERT(fhc_bdlist_locked());
28021341Sstevel 
28031341Sstevel 	/* delete all entries that match board */
28041341Sstevel 	pp = &fhc_base_memloc;
28051341Sstevel 	while ((p = *pp) != NULL) {
28061341Sstevel 		if (p->board == board) {
28071341Sstevel #ifdef DEBUG_MEMDEC
28081341Sstevel 			cmn_err(CE_NOTE, "fhc_del_memloc: removing %d "
28091341Sstevel 			    "0x%x 0x%x", board, p->pa, p->size);
28101341Sstevel #endif /* DEBUG_MEMDEC */
28111341Sstevel 			*pp = p->next;
28121341Sstevel 			kmem_free(p, sizeof (struct fhc_memloc));
28131341Sstevel 		} else {
28141341Sstevel 			pp = &(p->next);
28151341Sstevel 		}
28161341Sstevel 	}
28171341Sstevel }
28181341Sstevel 
28191341Sstevel /*
28201341Sstevel  * Find a physical address range of sufficient size and return a starting PA
28211341Sstevel  */
28221341Sstevel uint64_t
fhc_find_memloc_gap(uint_t size)28231341Sstevel fhc_find_memloc_gap(uint_t size)
28241341Sstevel {
28251341Sstevel 	struct fhc_memloc *p;
28261341Sstevel 	uint_t base_pa = 0;
28271341Sstevel 	uint_t mask = ~(size-1);
28281341Sstevel 
28291341Sstevel 	ASSERT(fhc_bdlist_locked());
28301341Sstevel 	ASSERT((size & (size-1)) == 0);		/* size must be power of 2 */
28311341Sstevel 
28321341Sstevel 	/*
28331341Sstevel 	 * walk the list of known memlocs and measure the 'gaps'.
28341341Sstevel 	 * we will need a hole that can align the 'size' requested.
28351341Sstevel 	 * (e.g. a 256mb bank needs to be on a 256mb boundary).
28361341Sstevel 	 */
28371341Sstevel 	for (p = fhc_base_memloc; p != NULL; p = p->next) {
28381341Sstevel 		if (base_pa != (base_pa & mask))
28391341Sstevel 			base_pa = (base_pa + size) & mask;
28401341Sstevel 		if (base_pa + size <= p->pa)
28411341Sstevel 			break;
28421341Sstevel 		base_pa = p->pa + p->size;
28431341Sstevel 	}
28441341Sstevel 
28451341Sstevel 	/*
28461341Sstevel 	 * At this point, we assume that base_pa is good enough.
28471341Sstevel 	 */
28481341Sstevel 	ASSERT((base_pa + size) <= FHC_MEMLOC_MAX);
28491341Sstevel 	if (base_pa != (base_pa & mask))
28501341Sstevel 		base_pa = (base_pa + size) & mask;	/* align */
28511341Sstevel 	return ((uint64_t)base_pa << FHC_MEMLOC_SHIFT);
28521341Sstevel }
28531341Sstevel 
28541341Sstevel /*
28551341Sstevel  * This simple function to write the MCRs can only be used when
28561341Sstevel  * the contents of memory are not valid as there is a bug in the AC
28571341Sstevel  * ASIC concerning refresh.
28581341Sstevel  */
28591341Sstevel static void
fhc_write_mcrs(uint64_t cpa,uint64_t dpa0,uint64_t dpa1,uint64_t c,uint64_t d0,uint64_t d1)28601341Sstevel fhc_write_mcrs(
28611341Sstevel 	uint64_t cpa,
28621341Sstevel 	uint64_t dpa0,
28631341Sstevel 	uint64_t dpa1,
28641341Sstevel 	uint64_t c,
28651341Sstevel 	uint64_t d0,
28661341Sstevel 	uint64_t d1)
28671341Sstevel {
28681341Sstevel 	stdphysio(cpa, c & ~AC_CSR_REFEN);
28691341Sstevel 	(void) lddphysio(cpa);
28701341Sstevel 	if (GRP_SIZE_IS_SET(d0)) {
28711341Sstevel 		stdphysio(dpa0, d0);
28721341Sstevel 		(void) lddphysio(dpa0);
28731341Sstevel 	}
28741341Sstevel 	if (GRP_SIZE_IS_SET(d1)) {
28751341Sstevel 		stdphysio(dpa1, d1);
28761341Sstevel 		(void) lddphysio(dpa1);
28771341Sstevel 	}
28781341Sstevel 	stdphysio(cpa, c);
28791341Sstevel 	(void) lddphysio(cpa);
28801341Sstevel }
28811341Sstevel 
28821341Sstevel /* compute the appropriate RASIZE for bank size */
28831341Sstevel static uint_t
fhc_cvt_size(uint64_t bsz)28841341Sstevel fhc_cvt_size(uint64_t bsz)
28851341Sstevel {
28861341Sstevel 	uint_t csz;
28871341Sstevel 
28881341Sstevel 	csz = 0;
28891341Sstevel 	bsz /= 64;
28901341Sstevel 	while (bsz) {
28911341Sstevel 		csz++;
28921341Sstevel 		bsz /= 2;
28931341Sstevel 	}
28941341Sstevel 	csz /= 2;
28951341Sstevel 
28961341Sstevel 	return (csz);
28971341Sstevel }
28981341Sstevel 
28991341Sstevel void
fhc_program_memory(int board,uint64_t pa)29001341Sstevel fhc_program_memory(int board, uint64_t pa)
29011341Sstevel {
29021341Sstevel 	uint64_t cpa, dpa0, dpa1;
29031341Sstevel 	uint64_t c, d0, d1;
29041341Sstevel 	uint64_t b0_pa, b1_pa;
29051341Sstevel 	uint64_t memdec0, memdec1;
29061341Sstevel 	uint_t b0_size, b1_size;
29071341Sstevel 
29081341Sstevel 	/* XXX gross hack to get to board via board number */
29091341Sstevel 	cpa = 0x1c0f9000060ull + (board * 0x400000000ull);
29101341Sstevel #ifdef DEBUG_MEMDEC
29111341Sstevel 	prom_printf("cpa = 0x%llx\n", cpa);
29121341Sstevel #endif /* DEBUG_MEMDEC */
29131341Sstevel 	dpa0 = cpa + 0x10;
29141341Sstevel 	dpa1 = cpa + 0x20;
29151341Sstevel 
29161341Sstevel /* assume size is set by connect */
29171341Sstevel 	memdec0 = lddphysio(dpa0);
29181341Sstevel #ifdef DEBUG_MEMDEC
29191341Sstevel 	prom_printf("memdec0 = 0x%llx\n", memdec0);
29201341Sstevel #endif /* DEBUG_MEMDEC */
29211341Sstevel 	memdec1 = lddphysio(dpa1);
29221341Sstevel #ifdef DEBUG_MEMDEC
29231341Sstevel 	prom_printf("memdec1 = 0x%llx\n", memdec1);
29241341Sstevel #endif /* DEBUG_MEMDEC */
29251341Sstevel 	if (GRP_SIZE_IS_SET(memdec0)) {
29261341Sstevel 		b0_size = GRP_SPANMB(memdec0);
29271341Sstevel 	} else {
29281341Sstevel 		b0_size = 0;
29291341Sstevel 	}
29301341Sstevel 	if (GRP_SIZE_IS_SET(memdec1)) {
29311341Sstevel 		b1_size = GRP_SPANMB(memdec1);
29321341Sstevel 	} else {
29331341Sstevel 		b1_size = 0;
29341341Sstevel 	}
29351341Sstevel 
29361341Sstevel 	c = lddphysio(cpa);
29371341Sstevel #ifdef DEBUG_MEMDEC
29381341Sstevel 	prom_printf("c = 0x%llx\n", c);
29391341Sstevel #endif /* DEBUG_MEMDEC */
29401341Sstevel 	if (b0_size) {
29411341Sstevel 		b0_pa = pa;
29421341Sstevel 		d0 = SETUP_DECODE(b0_pa, b0_size, 0, 0);
29431341Sstevel 		d0 |= AC_MEM_VALID;
29441341Sstevel 
29451341Sstevel 		c &= ~0x7;
29461341Sstevel 		c |= 0;
29471341Sstevel 		c &= ~(0x7 << 8);
29481341Sstevel 		c |= (fhc_cvt_size(b0_size) << 8);  /* match row size */
29491341Sstevel 	} else {
29501341Sstevel 		d0 = memdec0;
29511341Sstevel 	}
29521341Sstevel 	if (b1_size) {
29531341Sstevel 		b1_pa = pa + 0x80000000ull; /* XXX 2gb */
29541341Sstevel 		d1 = SETUP_DECODE(b1_pa, b1_size, 0, 0);
29551341Sstevel 		d1 |= AC_MEM_VALID;
29561341Sstevel 
29571341Sstevel 		c &= ~(0x7 << 3);
29581341Sstevel 		c |= (0 << 3);
29591341Sstevel 		c &= ~(0x7 << 11);
29601341Sstevel 		c |= (fhc_cvt_size(b1_size) << 11); /* match row size */
29611341Sstevel 	} else {
29621341Sstevel 		d1 = memdec1;
29631341Sstevel 	}
29641341Sstevel #ifdef DEBUG_MEMDEC
29651341Sstevel 	prom_printf("c 0x%llx, d0 0x%llx, d1 0x%llx\n", c, d0, d1);
29661341Sstevel #endif /* DEBUG_MEMDEC */
29671341Sstevel 	fhc_write_mcrs(cpa, dpa0, dpa1, c, d0, d1);
29681341Sstevel }
29691341Sstevel 
29701341Sstevel /*
29711341Sstevel  * Creates a variable sized virtual kstat with a snapshot routine in order
29721341Sstevel  * to pass the linked list fault list up to userland. Also creates a
29731341Sstevel  * virtual kstat to pass up the string table for faults.
29741341Sstevel  */
29751341Sstevel void
create_ft_kstats(int instance)29761341Sstevel create_ft_kstats(int instance)
29771341Sstevel {
29781341Sstevel 	struct kstat *ksp;
29791341Sstevel 
29801341Sstevel 	ksp = kstat_create("unix", instance, FT_LIST_KSTAT_NAME, "misc",
29817656SSherry.Moore@Sun.COM 	    KSTAT_TYPE_RAW, 1, KSTAT_FLAG_VIRTUAL|KSTAT_FLAG_VAR_SIZE);
29821341Sstevel 
29831341Sstevel 	if (ksp != NULL) {
29841341Sstevel 		ksp->ks_data = NULL;
29851341Sstevel 		ksp->ks_update = ft_ks_update;
29861341Sstevel 		ksp->ks_snapshot = ft_ks_snapshot;
29871341Sstevel 		ksp->ks_data_size = 1;
29881341Sstevel 		ksp->ks_lock = &ftlist_mutex;
29891341Sstevel 		kstat_install(ksp);
29901341Sstevel 	}
29911341Sstevel }
29921341Sstevel 
29931341Sstevel /*
29941341Sstevel  * This routine creates a snapshot of all the fault list data. It is
29951341Sstevel  * called by the kstat framework when a kstat read is done.
29961341Sstevel  */
29971341Sstevel static int
ft_ks_snapshot(struct kstat * ksp,void * buf,int rw)29981341Sstevel ft_ks_snapshot(struct kstat *ksp, void *buf, int rw)
29991341Sstevel {
30001341Sstevel 	struct ft_link_list *ftlist;
30011341Sstevel 
30021341Sstevel 	if (rw == KSTAT_WRITE) {
30031341Sstevel 		return (EACCES);
30041341Sstevel 	}
30051341Sstevel 
30061341Sstevel 	ksp->ks_snaptime = gethrtime();
30071341Sstevel 
30081341Sstevel 	for (ftlist = ft_list; ftlist != NULL; ftlist = ftlist->next) {
30091341Sstevel 		bcopy(&ftlist->f, buf, sizeof (struct ft_list));
30101341Sstevel 		buf = ((struct ft_list *)buf) + 1;
30111341Sstevel 	}
30121341Sstevel 	return (0);
30131341Sstevel }
30141341Sstevel 
30151341Sstevel /*
30161341Sstevel  * Setup the kstat data size for the kstat framework. This is used in
30171341Sstevel  * conjunction with the ks_snapshot routine. This routine sets the size,
30181341Sstevel  * the kstat framework allocates the memory, and ks_shapshot does the
30191341Sstevel  * data transfer.
30201341Sstevel  */
30211341Sstevel static int
ft_ks_update(struct kstat * ksp,int rw)30221341Sstevel ft_ks_update(struct kstat *ksp, int rw)
30231341Sstevel {
30241341Sstevel 	if (rw == KSTAT_WRITE) {
30251341Sstevel 		return (EACCES);
30261341Sstevel 	} else {
30271341Sstevel 		if (ft_nfaults) {
30281341Sstevel 			ksp->ks_data_size = ft_nfaults *
30297656SSherry.Moore@Sun.COM 			    sizeof (struct ft_list);
30301341Sstevel 		} else {
30311341Sstevel 			ksp->ks_data_size = 1;
30321341Sstevel 		}
30331341Sstevel 	}
30341341Sstevel 
30351341Sstevel 	return (0);
30361341Sstevel }
30371341Sstevel 
30381341Sstevel /*
30391341Sstevel  * Power off any cpus on the board.
30401341Sstevel  */
30411341Sstevel int
fhc_board_poweroffcpus(int board,char * errbuf,int cpu_flags)30421341Sstevel fhc_board_poweroffcpus(int board, char *errbuf, int cpu_flags)
30431341Sstevel {
30441341Sstevel 	cpu_t *cpa, *cpb;
30451341Sstevel 	enum board_type type;
30461341Sstevel 	int error = 0;
30471341Sstevel 
30481341Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
30491341Sstevel 
30501341Sstevel 	/*
30511341Sstevel 	 * what type of board are we dealing with?
30521341Sstevel 	 */
30531341Sstevel 	type = fhc_bd_type(board);
30541341Sstevel 
30551341Sstevel 	switch (type) {
30561341Sstevel 	case CPU_BOARD:
30571341Sstevel 
30581341Sstevel 		/*
30591341Sstevel 		 * the shutdown sequence will be:
30601341Sstevel 		 *
30611341Sstevel 		 * idle both cpus then shut them off.
30621341Sstevel 		 * it looks like the hardware gets corrupted if one
30631341Sstevel 		 * cpu is busy while the other is shutting down...
30641341Sstevel 		 */
30651341Sstevel 
30661341Sstevel 		if ((cpa = cpu_get(FHC_BOARD2CPU_A(board))) != NULL &&
30671341Sstevel 		    cpu_is_active(cpa)) {
30681341Sstevel 			if (!cpu_intr_on(cpa)) {
30691341Sstevel 				cpu_intr_enable(cpa);
30701341Sstevel 			}
30711341Sstevel 			if ((error = cpu_offline(cpa, cpu_flags)) != 0) {
30721341Sstevel 				cmn_err(CE_WARN,
30731341Sstevel 				    "Processor %d failed to offline.",
30741341Sstevel 				    cpa->cpu_id);
30751341Sstevel 				if (errbuf != NULL) {
30761341Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
30771341Sstevel 					    "processor %d failed to offline",
30781341Sstevel 					    cpa->cpu_id);
30791341Sstevel 				}
30801341Sstevel 			}
30811341Sstevel 		}
30821341Sstevel 
30831341Sstevel 		if (error == 0 &&
30841341Sstevel 		    (cpb = cpu_get(FHC_BOARD2CPU_B(board))) != NULL &&
30851341Sstevel 		    cpu_is_active(cpb)) {
30861341Sstevel 			if (!cpu_intr_on(cpb)) {
30871341Sstevel 				cpu_intr_enable(cpb);
30881341Sstevel 			}
30891341Sstevel 			if ((error = cpu_offline(cpb, cpu_flags)) != 0) {
30901341Sstevel 				cmn_err(CE_WARN,
30911341Sstevel 				    "Processor %d failed to offline.",
30921341Sstevel 				    cpb->cpu_id);
30931341Sstevel 
30941341Sstevel 				if (errbuf != NULL) {
30951341Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
30961341Sstevel 					    "processor %d failed to offline",
30971341Sstevel 					    cpb->cpu_id);
30981341Sstevel 				}
30991341Sstevel 			}
31001341Sstevel 		}
31011341Sstevel 
31021341Sstevel 		if (error == 0 && cpa != NULL && cpu_is_offline(cpa)) {
31031341Sstevel 			if ((error = cpu_poweroff(cpa)) != 0) {
31041341Sstevel 				cmn_err(CE_WARN,
31051341Sstevel 				    "Processor %d failed to power off.",
31061341Sstevel 				    cpa->cpu_id);
31071341Sstevel 				if (errbuf != NULL) {
31081341Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
31091341Sstevel 					    "processor %d failed to power off",
31101341Sstevel 					    cpa->cpu_id);
31111341Sstevel 				}
31121341Sstevel 			} else {
31131341Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
31141341Sstevel 				    cpa->cpu_id);
31151341Sstevel 			}
31161341Sstevel 		}
31171341Sstevel 
31181341Sstevel 		if (error == 0 && cpb != NULL && cpu_is_offline(cpb)) {
31191341Sstevel 			if ((error = cpu_poweroff(cpb)) != 0) {
31201341Sstevel 				cmn_err(CE_WARN,
31211341Sstevel 				    "Processor %d failed to power off.",
31221341Sstevel 				    cpb->cpu_id);
31231341Sstevel 
31241341Sstevel 				if (errbuf != NULL) {
31251341Sstevel 					(void) snprintf(errbuf, SYSC_OUTPUT_LEN,
31261341Sstevel 					    "processor %d failed to power off",
31271341Sstevel 					    cpb->cpu_id);
31281341Sstevel 				}
31291341Sstevel 			} else {
31301341Sstevel 				cmn_err(CE_NOTE, "Processor %d powered off.",
31311341Sstevel 				    cpb->cpu_id);
31321341Sstevel 			}
31331341Sstevel 		}
31341341Sstevel 
31351341Sstevel 		/*
31361341Sstevel 		 * If all the shutdowns completed, ONLY THEN, clear the
31371341Sstevel 		 * incorrectly valid dtags...
31381341Sstevel 		 *
31391341Sstevel 		 * IMPORTANT: it is an error to read or write dtags while
31401341Sstevel 		 * they are 'active'
31411341Sstevel 		 */
31421341Sstevel 		if (error == 0 && (cpa != NULL || cpb != NULL)) {
31431341Sstevel 			u_longlong_t base = 0;
31441341Sstevel 			int i;
31451341Sstevel #ifdef DEBUG
31461341Sstevel 			int nonz0 = 0;
31471341Sstevel 			int nonz1 = 0;
31481341Sstevel #endif
31491341Sstevel 			if (cpa != NULL)
31501341Sstevel 				base = FHC_DTAG_BASE(cpa->cpu_id);
31511341Sstevel 			if (cpb != NULL)
31521341Sstevel 				base = FHC_DTAG_BASE(cpb->cpu_id);
31531341Sstevel 			ASSERT(base != 0);
31541341Sstevel 
31551341Sstevel 			for (i = 0; i < FHC_DTAG_SIZE; i += FHC_DTAG_SKIP) {
31561341Sstevel 				u_longlong_t value = lddphysio(base+i);
31571341Sstevel #ifdef lint
31581341Sstevel 				value = value;
31591341Sstevel #endif
31601341Sstevel #ifdef DEBUG
31611341Sstevel 				if (cpa != NULL && (value & FHC_DTAG_LOW))
31621341Sstevel 					nonz0++;
31631341Sstevel 				if (cpb != NULL && (value & FHC_DTAG_HIGH))
31641341Sstevel 					nonz1++;
31651341Sstevel #endif
31661341Sstevel 				/* always clear the dtags */
31671341Sstevel 				stdphysio(base + i, 0ull);
31681341Sstevel 			}
31691341Sstevel #ifdef DEBUG
31701341Sstevel 			if (nonz0 || nonz1) {
31711341Sstevel 				cmn_err(CE_NOTE, "!dtag results: "
31721341Sstevel 				    "cpua valid %d, cpub valid %d",
31731341Sstevel 				    nonz0, nonz1);
31741341Sstevel 			}
31751341Sstevel #endif
31761341Sstevel 		}
31771341Sstevel 
31781341Sstevel 		break;
31791341Sstevel 
31801341Sstevel 	default:
31811341Sstevel 		break;
31821341Sstevel 	}
31831341Sstevel 
31841341Sstevel 	return (error);
31851341Sstevel }
31861341Sstevel 
31871341Sstevel /*
31881341Sstevel  * platform code for shutting down cpus.
31891341Sstevel  */
31901341Sstevel int
fhc_cpu_poweroff(struct cpu * cp)31911341Sstevel fhc_cpu_poweroff(struct cpu *cp)
31921341Sstevel {
31931341Sstevel 	int board;
31941341Sstevel 	fhc_bd_t *bd_list;
31951341Sstevel 	int delays;
31961341Sstevel 	extern void idle_stop_xcall(void);
31971341Sstevel 	static void fhc_cpu_shutdown_self(void);
31981341Sstevel 
31991341Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
32001341Sstevel 	ASSERT((cp->cpu_flags & (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED)) ==
32017656SSherry.Moore@Sun.COM 	    (CPU_EXISTS | CPU_OFFLINE | CPU_QUIESCED));
32021341Sstevel 
32031341Sstevel 	/*
32041341Sstevel 	 * Lock the board so that we can safely access the
32051341Sstevel 	 * registers. This cannot be done inside the pause_cpus().
32061341Sstevel 	 */
32071341Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
32081341Sstevel 	bd_list = fhc_bdlist_lock(board);
32091341Sstevel 	ASSERT(fhc_bd_valid(board) && (bd_list->sc.type == CPU_BOARD));
32101341Sstevel 
32111341Sstevel 	/*
32121341Sstevel 	 * Capture all CPUs (except for detaching proc) to prevent
32131341Sstevel 	 * crosscalls to the detaching proc until it has cleared its
32141341Sstevel 	 * bit in cpu_ready_set.
32151341Sstevel 	 *
32161341Sstevel 	 * The CPU's remain paused and the prom_mutex is known to be free.
32171341Sstevel 	 * This prevents the x-trap victim from blocking when doing prom
32181341Sstevel 	 * IEEE-1275 calls at a high PIL level.
32191341Sstevel 	 */
32201341Sstevel 	promsafe_pause_cpus();
32211341Sstevel 
32221341Sstevel 	/*
32231341Sstevel 	 * Quiesce interrupts on the target CPU. We do this by setting
32241341Sstevel 	 * the CPU 'not ready'- (i.e. removing the CPU from cpu_ready_set) to
32251341Sstevel 	 * prevent it from receiving cross calls and cross traps.
32261341Sstevel 	 * This prevents the processor from receiving any new soft interrupts.
32271341Sstevel 	 */
32281341Sstevel 	mp_cpu_quiesce(cp);
32291341Sstevel 
32301341Sstevel 	xt_one_unchecked(cp->cpu_id, (xcfunc_t *)idle_stop_xcall,
32317656SSherry.Moore@Sun.COM 	    (uint64_t)fhc_cpu_shutdown_self, (uint64_t)NULL);
32321341Sstevel 
32331341Sstevel 	/*
32341341Sstevel 	 * Wait for slave cpu to shutdown.
32351341Sstevel 	 * Sense this by watching the hardware EPDx bit.
32361341Sstevel 	 */
32371341Sstevel 	for (delays = FHC_SHUTDOWN_WAIT_MSEC; delays != 0; delays--) {
32381341Sstevel 		uint_t temp;
32391341Sstevel 
32401341Sstevel 		DELAY(1000);
32411341Sstevel 
32421341Sstevel 		/* get the current cpu power status */
32431341Sstevel 		temp = *bd_list->softsp->ctrl;
32441341Sstevel 
32451341Sstevel 		/* has the cpu actually signalled shutdown? */
32461341Sstevel 		if (FHC_CPU_IS_A(cp->cpu_id)) {
32471341Sstevel 			if (temp & FHC_EPDA_OFF)
32481341Sstevel 				break;
32491341Sstevel 		} else {
32501341Sstevel 			if (temp & FHC_EPDB_OFF)
32511341Sstevel 				break;
32521341Sstevel 		}
32531341Sstevel 	}
32541341Sstevel 
32551341Sstevel 	start_cpus();
32561341Sstevel 
32571341Sstevel 	fhc_bdlist_unlock();
32581341Sstevel 
32591341Sstevel 	/* A timeout means we've lost control of the cpu. */
32601341Sstevel 	if (delays == 0)
32611341Sstevel 		panic("Processor %d failed during shutdown", cp->cpu_id);
32621341Sstevel 
32631341Sstevel 	return (0);
32641341Sstevel }
32651341Sstevel 
32661341Sstevel /*
32671341Sstevel  * shutdown_self
32681341Sstevel  * slave side shutdown.  clean up and execute the shutdown sequence.
32691341Sstevel  */
32701341Sstevel static void
fhc_cpu_shutdown_self(void)32711341Sstevel fhc_cpu_shutdown_self(void)
32721341Sstevel {
32731341Sstevel 	extern void flush_windows(void);
32741341Sstevel 	static void os_completes_shutdown(void);
32751341Sstevel 
32761341Sstevel 	flush_windows();
32771341Sstevel 
32781341Sstevel 	ASSERT(CPU->cpu_intr_actv == 0);
32791341Sstevel 	ASSERT(CPU->cpu_thread == CPU->cpu_idle_thread ||
32801341Sstevel 	    CPU->cpu_thread == CPU->cpu_startup_thread);
32811341Sstevel 
32821341Sstevel 	CPU->cpu_flags = CPU_POWEROFF | CPU_OFFLINE | CPU_QUIESCED;
32831341Sstevel 
32841341Sstevel 	(void) prom_sunfire_cpu_off();	/* inform Ultra Enterprise prom */
32851341Sstevel 
32861341Sstevel 	os_completes_shutdown();
32871341Sstevel 
32881341Sstevel 	panic("fhc_cpu_shutdown_self: cannot return");
32891341Sstevel 	/*NOTREACHED*/
32901341Sstevel }
32911341Sstevel 
32921341Sstevel /*
32931341Sstevel  * Warm start CPU.
32941341Sstevel  */
32951341Sstevel static int
fhc_cpu_start(struct cpu * cp)32961341Sstevel fhc_cpu_start(struct cpu *cp)
32971341Sstevel {
32981341Sstevel 	int rv;
32991341Sstevel 	int cpuid = cp->cpu_id;
33001341Sstevel 	pnode_t nodeid;
33011341Sstevel 	extern void restart_other_cpu(int);
33021341Sstevel 
33031341Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
33041341Sstevel 
33051341Sstevel 	/* power on cpu */
33061341Sstevel 	nodeid = cpunodes[cpuid].nodeid;
33071341Sstevel 	ASSERT(nodeid != (pnode_t)0);
33081341Sstevel 	rv = prom_wakeupcpu(nodeid);
33091341Sstevel 	if (rv != 0) {
33101341Sstevel 		cmn_err(CE_WARN, "Processor %d failed to power on.", cpuid);
33111341Sstevel 		return (EBUSY);
33121341Sstevel 	}
33131341Sstevel 
33141341Sstevel 	cp->cpu_flags &= ~CPU_POWEROFF;
33151341Sstevel 
33161341Sstevel 	/*
33171341Sstevel 	 * NOTE: restart_other_cpu pauses cpus during the slave cpu start.
33181341Sstevel 	 * This helps to quiesce the bus traffic a bit which makes
33191341Sstevel 	 * the tick sync routine in the prom more robust.
33201341Sstevel 	 */
33211341Sstevel 	restart_other_cpu(cpuid);
33221341Sstevel 
33231341Sstevel 	return (0);
33241341Sstevel }
33251341Sstevel 
33261341Sstevel /*
33271341Sstevel  * Power on CPU.
33281341Sstevel  */
33291341Sstevel int
fhc_cpu_poweron(struct cpu * cp)33301341Sstevel fhc_cpu_poweron(struct cpu *cp)
33311341Sstevel {
33321341Sstevel 	fhc_bd_t *bd_list;
33331341Sstevel 	enum temp_state state;
33341341Sstevel 	int board;
33351341Sstevel 	int status;
33361341Sstevel 	int status_other;
33371341Sstevel 	struct cpu *cp_other;
33381341Sstevel 
33391341Sstevel 	ASSERT(MUTEX_HELD(&cpu_lock));
33401341Sstevel 	ASSERT(cpu_is_poweredoff(cp));
33411341Sstevel 
33421341Sstevel 	/* do not power on overtemperature cpu */
33431341Sstevel 	board = FHC_CPU2BOARD(cp->cpu_id);
33441341Sstevel 	bd_list = fhc_bdlist_lock(board);
33451341Sstevel 
33461341Sstevel 	ASSERT(bd_list != NULL);
33471341Sstevel 	ASSERT(bd_list->sc.type == CPU_BOARD);
33481341Sstevel 	ASSERT(bd_list->dev_softsp != NULL);
33491341Sstevel 
33501341Sstevel 	state = ((struct environ_soft_state *)
33517656SSherry.Moore@Sun.COM 	    bd_list->dev_softsp)->tempstat.state;
33521341Sstevel 
33531341Sstevel 	fhc_bdlist_unlock();
33541341Sstevel 	if ((state == TEMP_WARN) || (state == TEMP_DANGER))
33551341Sstevel 		return (EBUSY);
33561341Sstevel 
33571341Sstevel 	status = fhc_cpu_start(cp);
33581341Sstevel 
33591341Sstevel 	/* policy for dual cpu boards */
33601341Sstevel 
33611341Sstevel 	if ((status == 0) &&
33621341Sstevel 	    ((cp_other = cpu_get(FHC_OTHER_CPU_ID(cp->cpu_id))) != NULL)) {
33631341Sstevel 		/*
33641341Sstevel 		 * Do not leave board's other cpu idling in the prom.
33651341Sstevel 		 * Start the other cpu and set its state to P_OFFLINE.
33661341Sstevel 		 */
33671341Sstevel 		status_other = fhc_cpu_start(cp_other);
33681341Sstevel 		if (status_other != 0) {
33691341Sstevel 			panic("fhc: failed to start second CPU"
33701341Sstevel 			    " in pair %d & %d, error %d",
33711341Sstevel 			    cp->cpu_id, cp_other->cpu_id, status_other);
33721341Sstevel 		}
33731341Sstevel 	}
33741341Sstevel 
33751341Sstevel 	return (status);
33761341Sstevel }
33771341Sstevel 
33781341Sstevel /*
33791341Sstevel  * complete the shutdown sequence in case the firmware doesn't.
33801341Sstevel  *
33811341Sstevel  * If the firmware returns, then complete the shutdown code.
33821341Sstevel  * (sunfire firmware presently only updates its status.  the
33831341Sstevel  * OS must flush the D-tags and execute the shutdown instruction.)
33841341Sstevel  */
33851341Sstevel static void
os_completes_shutdown(void)33861341Sstevel os_completes_shutdown(void)
33871341Sstevel {
33881341Sstevel 	pfn_t 			pfn;
33891341Sstevel 	tte_t			tte;
33901341Sstevel 	volatile uint_t		*src;
33911341Sstevel 	volatile uint_t		*dst;
33921341Sstevel 	caddr_t			copy_addr;
33931341Sstevel 	extern void fhc_shutdown_asm(u_longlong_t, int);
33941341Sstevel 	extern void fhc_shutdown_asm_end(void);
33951341Sstevel 
33961341Sstevel 	copy_addr = shutdown_va + FHC_SRAM_OS_OFFSET;
33971341Sstevel 
33981341Sstevel 	/* compute sram global address for this operation */
33991341Sstevel 	pfn = FHC_LOCAL_OS_PAGEBASE >> MMU_PAGESHIFT;
34001341Sstevel 
34011341Sstevel 	/* force load i and d translations */
34021341Sstevel 	tte.tte_inthi = TTE_VALID_INT | TTE_SZ_INT(TTE8K) |
34037656SSherry.Moore@Sun.COM 	    TTE_PFN_INTHI(pfn);
34041341Sstevel 	tte.tte_intlo = TTE_PFN_INTLO(pfn) |
34057656SSherry.Moore@Sun.COM 	    TTE_HWWR_INT | TTE_PRIV_INT | TTE_LCK_INT; /* un$ */
34062241Shuah 	sfmmu_dtlb_ld_kva(shutdown_va, &tte);	/* load dtlb */
34072241Shuah 	sfmmu_itlb_ld_kva(shutdown_va, &tte);	/* load itlb */
34081341Sstevel 
34091341Sstevel 	/*
34101341Sstevel 	 * copy the special shutdown function to sram
34111341Sstevel 	 * (this is a special integer copy that synchronizes with localspace
34121341Sstevel 	 * accesses.  we need special throttling to ensure copy integrity)
34131341Sstevel 	 */
34141341Sstevel 	for (src = (uint_t *)fhc_shutdown_asm, dst = (uint_t *)copy_addr;
34151341Sstevel 	    src < (uint_t *)fhc_shutdown_asm_end;
34161341Sstevel 	    src++, dst++) {
34171341Sstevel 		volatile uint_t dummy;
34181341Sstevel 
34191341Sstevel 		*dst = *src;
34201341Sstevel 		/*
34211341Sstevel 		 * ensure non corrupting single write operations to
34221341Sstevel 		 * localspace sram by interleaving reads with writes.
34231341Sstevel 		 */
34241341Sstevel 		dummy = *dst;
34251341Sstevel #ifdef lint
34261341Sstevel 		dummy = dummy;
34271341Sstevel #endif
34281341Sstevel 	}
34291341Sstevel 
34301341Sstevel 	/*
34311341Sstevel 	 * Call the shutdown sequencer.
34321341Sstevel 	 * NOTE: the base flush address must be unique for each MID.
34331341Sstevel 	 */
34341341Sstevel 	((void (*)(u_longlong_t, int))copy_addr)(
34357656SSherry.Moore@Sun.COM 	    FHC_BASE_NOMEM + CPU->cpu_id * FHC_MAX_ECACHE_SIZE,
34367656SSherry.Moore@Sun.COM 	    cpunodes[CPU->cpu_id].ecache_size);
34371341Sstevel }
34381341Sstevel 
34391341Sstevel enum temp_state
fhc_env_temp_state(int board)34401341Sstevel fhc_env_temp_state(int board)
34411341Sstevel {
34421341Sstevel 	fhc_bd_t *bdp;
34431341Sstevel 	struct environ_soft_state *envp;
34441341Sstevel 
34451341Sstevel 	ASSERT(fhc_bd_valid(board));
34461341Sstevel 
34471341Sstevel 	bdp = fhc_bd(board);
34481341Sstevel 
34491341Sstevel 	/*
34501341Sstevel 	 * Due to asynchronous attach of environ, environ may
34511341Sstevel 	 * not be attached by the time we start calling this routine
34521341Sstevel 	 * to check the temperature state.  Environ not attaching is
34531341Sstevel 	 * pathological so this will only cover the time between
34541341Sstevel 	 * board connect and environ attach.
34551341Sstevel 	 */
34561341Sstevel 	if (!bdp->dev_softsp) {
34571341Sstevel 		return (TEMP_OK);
34581341Sstevel 	}
34591341Sstevel 	envp = (struct environ_soft_state *)bdp->dev_softsp;
34601341Sstevel 
34611341Sstevel 	return (envp->tempstat.state);
34621341Sstevel }
34631341Sstevel 
34641341Sstevel static void
fhc_tod_fault(enum tod_fault_type tod_bad)34651341Sstevel fhc_tod_fault(enum tod_fault_type tod_bad)
34661341Sstevel {
34671341Sstevel 	int board_num = 0;
34681341Sstevel 	enum ft_class class = FT_SYSTEM;
34691341Sstevel 	uint64_t addr;
34701341Sstevel 
34711341Sstevel 	addr = (va_to_pa((void *)v_eeprom_addr)) >> BOARD_PHYADDR_SHIFT;
34721341Sstevel 
34731341Sstevel 	if ((addr & CLOCKBOARD_PHYADDR_BITS) != CLOCKBOARD_PHYADDR_BITS) {
34741341Sstevel 		/* if tod is not on clock board, */
34751341Sstevel 		/* it'd be on one of io boards */
34761341Sstevel 		board_num = (addr >> IO_BOARD_NUMBER_SHIFT)
34777656SSherry.Moore@Sun.COM 		    & IO_BOARD_NUMBER_MASK;
34781341Sstevel 		class = FT_BOARD;
34791341Sstevel 	}
34801341Sstevel 
34811341Sstevel 	switch (tod_bad) {
34821341Sstevel 	case TOD_NOFAULT:
34831341Sstevel 		clear_fault(board_num, FT_TODFAULT, class);
34841341Sstevel 		break;
34851341Sstevel 	case TOD_REVERSED:
34861341Sstevel 	case TOD_STALLED:
34871341Sstevel 	case TOD_JUMPED:
34881341Sstevel 	case TOD_RATECHANGED:
34891341Sstevel 		reg_fault(board_num, FT_TODFAULT, class);
34901341Sstevel 		break;
34911341Sstevel 	default:
34921341Sstevel 		break;
34931341Sstevel 	}
34941341Sstevel }
3495