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