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