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