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