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