xref: /onnv-gate/usr/src/uts/sun4u/sunfire/io/sysctrl.c (revision 11311:639e7bc0b42f)
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/sunndi.h>
341341Sstevel #include <sys/ndi_impldefs.h>
351341Sstevel #include <sys/obpdefs.h>
361341Sstevel #include <sys/cmn_err.h>
371341Sstevel #include <sys/errno.h>
381341Sstevel #include <sys/kmem.h>
391341Sstevel #include <sys/debug.h>
401341Sstevel #include <sys/sysmacros.h>
411341Sstevel #include <sys/ivintr.h>
421341Sstevel #include <sys/autoconf.h>
431341Sstevel #include <sys/intreg.h>
441341Sstevel #include <sys/proc.h>
451341Sstevel #include <sys/modctl.h>
461341Sstevel #include <sys/callb.h>
471341Sstevel #include <sys/file.h>
481341Sstevel #include <sys/open.h>
491341Sstevel #include <sys/stat.h>
501341Sstevel #include <sys/fhc.h>
511341Sstevel #include <sys/sysctrl.h>
521341Sstevel #include <sys/jtag.h>
531341Sstevel #include <sys/ac.h>
541341Sstevel #include <sys/simmstat.h>
551341Sstevel #include <sys/clock.h>
561341Sstevel #include <sys/promif.h>
571341Sstevel #include <sys/promimpl.h>
581341Sstevel #include <sys/sunndi.h>
591341Sstevel #include <sys/machsystm.h>
601341Sstevel 
611341Sstevel /* Useful debugging Stuff */
621341Sstevel #ifdef DEBUG
631341Sstevel int sysc_debug_info = 1;
641341Sstevel int sysc_debug_print_level = 0;
651341Sstevel #endif
661341Sstevel 
671341Sstevel /*
681341Sstevel  * Function prototypes
691341Sstevel  */
701341Sstevel static int sysctrl_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
711341Sstevel 		void **result);
721341Sstevel 
731341Sstevel static int sysctrl_attach(dev_info_t *devi, ddi_attach_cmd_t cmd);
741341Sstevel 
751341Sstevel static int sysctrl_detach(dev_info_t *devi, ddi_detach_cmd_t cmd);
761341Sstevel 
771341Sstevel static int sysctrl_open(dev_t *, int, int, cred_t *);
781341Sstevel 
791341Sstevel static int sysctrl_close(dev_t, int, int, cred_t *);
801341Sstevel 
811341Sstevel static int sysctrl_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
821341Sstevel 
831341Sstevel static uint_t system_high_handler(caddr_t arg);
841341Sstevel 
851341Sstevel static uint_t spur_delay(caddr_t arg);
861341Sstevel 
871341Sstevel static void spur_retry(void *);
881341Sstevel 
891341Sstevel static uint_t spur_reenable(caddr_t arg);
901341Sstevel 
911341Sstevel static void spur_long_timeout(void *);
921341Sstevel 
931341Sstevel static uint_t spur_clear_count(caddr_t arg);
941341Sstevel 
951341Sstevel static uint_t ac_fail_handler(caddr_t arg);
961341Sstevel 
971341Sstevel static void ac_fail_retry(void *);
981341Sstevel 
991341Sstevel static uint_t ac_fail_reenable(caddr_t arg);
1001341Sstevel 
1011341Sstevel static uint_t ps_fail_int_handler(caddr_t arg);
1021341Sstevel 
1031341Sstevel static uint_t ps_fail_poll_handler(caddr_t arg);
1041341Sstevel 
1051341Sstevel static uint_t ps_fail_handler(struct sysctrl_soft_state *softsp, int fromint);
1061341Sstevel 
1071341Sstevel enum power_state compute_power_state(struct sysctrl_soft_state *softsp,
1081341Sstevel 					int plus_load);
1091341Sstevel 
1101341Sstevel static void ps_log_state_change(struct sysctrl_soft_state *softsp,
1111341Sstevel 					int index, int present);
1121341Sstevel 
1131341Sstevel static void ps_log_pres_change(struct sysctrl_soft_state *softsp,
1141341Sstevel 					int index, int present);
1151341Sstevel 
1161341Sstevel static void ps_fail_retry(void *);
1171341Sstevel 
1181341Sstevel static uint_t pps_fanfail_handler(caddr_t arg);
1191341Sstevel 
1201341Sstevel static void pps_fanfail_retry(void *);
1211341Sstevel 
1221341Sstevel static uint_t pps_fanfail_reenable(caddr_t arg);
1231341Sstevel 
1241341Sstevel static void pps_fan_poll(void *);
1251341Sstevel 
1261341Sstevel static void pps_fan_state_change(struct sysctrl_soft_state *softsp,
1271341Sstevel 					int index, int fan_ok);
1281341Sstevel 
1291341Sstevel static uint_t bd_insert_handler(caddr_t arg);
1301341Sstevel 
1311341Sstevel static void bd_insert_timeout(void *);
1321341Sstevel 
1331341Sstevel static void bd_remove_timeout(void *);
1341341Sstevel 
1351341Sstevel static uint_t bd_insert_normal(caddr_t arg);
1361341Sstevel 
1371341Sstevel static void sysctrl_add_kstats(struct sysctrl_soft_state *softsp);
1381341Sstevel 
1391341Sstevel static int sysctrl_kstat_update(kstat_t *ksp, int rw);
1401341Sstevel 
1411341Sstevel static int psstat_kstat_update(kstat_t *, int);
1421341Sstevel 
1431341Sstevel static void init_remote_console_uart(struct sysctrl_soft_state *);
1441341Sstevel 
1451341Sstevel static void blink_led_timeout(void *);
1461341Sstevel 
1471341Sstevel static uint_t blink_led_handler(caddr_t arg);
1481341Sstevel 
1491341Sstevel static void sysctrl_thread_wakeup(void *type);
1501341Sstevel 
1511341Sstevel static void sysctrl_overtemp_poll(void);
1521341Sstevel 
1531341Sstevel static void sysctrl_keyswitch_poll(void);
1541341Sstevel 
1551341Sstevel static void update_key_state(struct sysctrl_soft_state *);
1561341Sstevel 
1571341Sstevel static void sysctrl_abort_seq_handler(char *msg);
1581341Sstevel 
1591341Sstevel static void nvram_update_powerfail(struct sysctrl_soft_state *softsp);
1601341Sstevel 
1611341Sstevel static void toggle_board_green_leds(int);
1621341Sstevel 
1631341Sstevel void bd_remove_poll(struct sysctrl_soft_state *);
1641341Sstevel 
1651341Sstevel static void sysc_slot_info(int nslots, int *start, int *limit, int *incr);
1661341Sstevel 
1671341Sstevel extern void sysc_board_connect_supported_init(void);
1681341Sstevel 
1691341Sstevel static void rcons_reinit(struct sysctrl_soft_state *softsp);
1701341Sstevel 
1711341Sstevel /*
1721341Sstevel  * Configuration data structures
1731341Sstevel  */
1741341Sstevel static struct cb_ops sysctrl_cb_ops = {
1751341Sstevel 	sysctrl_open,		/* open */
1761341Sstevel 	sysctrl_close,		/* close */
1771341Sstevel 	nulldev,		/* strategy */
1781341Sstevel 	nulldev,		/* print */
1791341Sstevel 	nulldev,		/* dump */
1801341Sstevel 	nulldev,		/* read */
1811341Sstevel 	nulldev,		/* write */
1821341Sstevel 	sysctrl_ioctl,		/* ioctl */
1831341Sstevel 	nodev,			/* devmap */
1841341Sstevel 	nodev,			/* mmap */
1851341Sstevel 	nodev,			/* segmap */
1861341Sstevel 	nochpoll,		/* poll */
1871341Sstevel 	ddi_prop_op,		/* cb_prop_op */
1881341Sstevel 	0,			/* streamtab */
1891341Sstevel 	D_MP|D_NEW,		/* Driver compatibility flag */
1901341Sstevel 	CB_REV,			/* rev */
1911341Sstevel 	nodev,			/* cb_aread */
1921341Sstevel 	nodev			/* cb_awrite */
1931341Sstevel };
1941341Sstevel 
1951341Sstevel static struct dev_ops sysctrl_ops = {
1961341Sstevel 	DEVO_REV,		/* devo_rev */
1971341Sstevel 	0,			/* refcnt */
1981341Sstevel 	sysctrl_info,		/* getinfo */
1991341Sstevel 	nulldev,		/* identify */
2001341Sstevel 	nulldev,		/* probe */
2011341Sstevel 	sysctrl_attach,		/* attach */
2021341Sstevel 	sysctrl_detach,		/* detach */
2031341Sstevel 	nulldev,		/* reset */
2041341Sstevel 	&sysctrl_cb_ops,	/* cb_ops */
2051341Sstevel 	(struct bus_ops *)0,	/* bus_ops */
2067656SSherry.Moore@Sun.COM 	nulldev,		/* power */
2077656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce */
2081341Sstevel };
2091341Sstevel 
2101341Sstevel void *sysctrlp;				/* sysctrl soft state hook */
2111341Sstevel 
2121341Sstevel /* # of ticks to silence spurious interrupts */
2131341Sstevel static clock_t spur_timeout_hz;
2141341Sstevel 
2151341Sstevel /* # of ticks to count spurious interrupts to print message */
2161341Sstevel static clock_t spur_long_timeout_hz;
2171341Sstevel 
2181341Sstevel /* # of ticks between AC failure polling */
2191341Sstevel static clock_t ac_timeout_hz;
2201341Sstevel 
2211341Sstevel /* # of ticks between Power Supply Failure polling */
2221341Sstevel static clock_t ps_fail_timeout_hz;
2231341Sstevel 
2241341Sstevel /*
2251341Sstevel  * # of ticks between Peripheral Power Supply failure polling
2261341Sstevel  * (used both for interrupt retry timeout and polling function)
2271341Sstevel  */
2281341Sstevel static clock_t pps_fan_timeout_hz;
2291341Sstevel 
2301341Sstevel /* # of ticks delay after board insert interrupt */
2311341Sstevel static clock_t bd_insert_delay_hz;
2321341Sstevel 
2331341Sstevel /* # of secs to wait before restarting poll if we cannot clear interrupts */
2341341Sstevel static clock_t bd_insert_retry_hz;
2351341Sstevel 
2361341Sstevel /* # of secs between Board Removal polling */
2371341Sstevel static clock_t bd_remove_timeout_hz;
2381341Sstevel 
2391341Sstevel /* # of secs between toggle of OS LED */
2401341Sstevel static clock_t blink_led_timeout_hz;
2411341Sstevel 
2421341Sstevel /* overtemp polling routine timeout delay */
2431341Sstevel static clock_t overtemp_timeout_hz;
2441341Sstevel 
2451341Sstevel /* key switch polling routine timeout delay */
2461341Sstevel static clock_t keyswitch_timeout_hz;
2471341Sstevel 
2481341Sstevel /* Specify which system interrupt condition to monitor */
2491341Sstevel int enable_sys_interrupt = SYS_AC_PWR_FAIL_EN | SYS_PPS_FAN_FAIL_EN |
2501341Sstevel 			SYS_PS_FAIL_EN | SYS_SBRD_PRES_EN;
2511341Sstevel 
2521341Sstevel /* Should the overtemp_poll thread be running? */
2531341Sstevel static int sysctrl_do_overtemp_thread = 1;
2541341Sstevel 
2551341Sstevel /* Should the keyswitch_poll thread be running? */
2561341Sstevel static int sysctrl_do_keyswitch_thread = 1;
2571341Sstevel 
2581341Sstevel /*
2591341Sstevel  * This timeout ID is for board remove polling routine. It is
2601341Sstevel  * protected by the fhc_bdlist mutex.
2611341Sstevel  * XXX - This will not work for wildfire. A different scheme must be
2621341Sstevel  * used since there will be multiple sysctrl nodes, each with its
2631341Sstevel  * own list of hotplugged boards to scan.
2641341Sstevel  */
2651341Sstevel static timeout_id_t bd_remove_to_id = 0;
2661341Sstevel 
2671341Sstevel /*
2681341Sstevel  * If this is set, the system will not shutdown when insufficient power
2691341Sstevel  * condition persists.
2701341Sstevel  */
2711341Sstevel int disable_insufficient_power_reboot = 0;
2721341Sstevel 
2731341Sstevel /*
2741341Sstevel  * Set this to enable suspend/resume
2751341Sstevel  */
2761341Sstevel int sysctrl_enable_detach_suspend = 0;
2771341Sstevel 
2781341Sstevel /*
2791341Sstevel  * Set this to reflect the OBP initialized HOTPLUG_DISABLED_PROPERTY and
2801341Sstevel  * during dynamic detection
2811341Sstevel  */
2821341Sstevel int sysctrl_hotplug_disabled = FALSE;
2831341Sstevel 
2841341Sstevel /* Indicates whether or not the overtemp thread has been started */
2851341Sstevel static int sysctrl_overtemp_thread_started = 0;
2861341Sstevel 
2871341Sstevel /* Indicates whether or not the key switch thread has been started */
2881341Sstevel static int sysctrl_keyswitch_thread_started = 0;
2891341Sstevel 
2901341Sstevel /* *Mutex used to protect the soft state list */
2911341Sstevel static kmutex_t sslist_mutex;
2921341Sstevel 
2931341Sstevel /* The CV is used to wakeup the overtemp thread when needed. */
2941341Sstevel static kcondvar_t overtemp_cv;
2951341Sstevel 
2961341Sstevel /* The CV is used to wakeup the key switch thread when needed. */
2971341Sstevel static kcondvar_t keyswitch_cv;
2981341Sstevel 
2991341Sstevel /* This mutex is used to protect the sysctrl_ddi_branch_init variable */
3001341Sstevel static kmutex_t sysctrl_branch_mutex;
3011341Sstevel 
3021341Sstevel /*
3031341Sstevel  * This variable is set after all existing branches in the system have
3041341Sstevel  * been discovered and held via e_ddi_branch_hold(). This happens on
3051341Sstevel  * first open() of any sysctrl minor node.
3061341Sstevel  */
3071341Sstevel static int sysctrl_ddi_branch_init;
3081341Sstevel 
3091341Sstevel /*
3101341Sstevel  * Linked list of all syctrl soft state structures.
3111341Sstevel  * Used for polling sysctrl state changes, i.e. temperature.
3121341Sstevel  */
3131341Sstevel struct sysctrl_soft_state *sys_list = NULL;
3141341Sstevel 
3151341Sstevel extern struct mod_ops mod_driverops;
3161341Sstevel 
3171341Sstevel static struct modldrv modldrv = {
3181341Sstevel 	&mod_driverops,		/* Type of module.  This one is a driver */
3197656SSherry.Moore@Sun.COM 	"Clock Board",		/* name of module */
3201341Sstevel 	&sysctrl_ops,		/* driver ops */
3211341Sstevel };
3221341Sstevel 
3231341Sstevel static struct modlinkage modlinkage = {
3241341Sstevel 	MODREV_1,		/* rev */
3251341Sstevel 	(void *)&modldrv,
3261341Sstevel 	NULL
3271341Sstevel };
3281341Sstevel 
3291341Sstevel #ifndef lint
3301366Spetede char _depends_on[] = "drv/fhc";
3311341Sstevel #endif /* lint */
3321341Sstevel 
3331341Sstevel /*
3341341Sstevel  * These are the module initialization routines.
3351341Sstevel  */
3361341Sstevel 
3371341Sstevel int
_init(void)3381341Sstevel _init(void)
3391341Sstevel {
3401341Sstevel 	int error;
3411341Sstevel 
3421341Sstevel 	if ((error = ddi_soft_state_init(&sysctrlp,
3431341Sstevel 	    sizeof (struct sysctrl_soft_state), 1)) != 0)
3441341Sstevel 		return (error);
3451341Sstevel 
3461341Sstevel 	error = mod_install(&modlinkage);
3471341Sstevel 	if (error != 0) {
3481341Sstevel 		ddi_soft_state_fini(&sysctrlp);
3491341Sstevel 		return (error);
3501341Sstevel 	}
3511341Sstevel 
3521341Sstevel 	mutex_init(&sysctrl_branch_mutex, NULL, MUTEX_DRIVER, NULL);
3531341Sstevel 
3541341Sstevel 	return (0);
3551341Sstevel }
3561341Sstevel 
3571341Sstevel int
_fini(void)3581341Sstevel _fini(void)
3591341Sstevel {
3601341Sstevel 	int error;
3611341Sstevel 
3621341Sstevel 	if ((error = mod_remove(&modlinkage)) != 0)
3631341Sstevel 		return (error);
3641341Sstevel 
3651341Sstevel 	ddi_soft_state_fini(&sysctrlp);
3661341Sstevel 
3671341Sstevel 	mutex_destroy(&sysctrl_branch_mutex);
3681341Sstevel 
3691341Sstevel 	return (0);
3701341Sstevel }
3711341Sstevel 
3721341Sstevel int
_info(struct modinfo * modinfop)3731341Sstevel _info(struct modinfo *modinfop)
3741341Sstevel {
3751341Sstevel 	return (mod_info(&modlinkage, modinfop));
3761341Sstevel }
3771341Sstevel 
3781341Sstevel /* ARGSUSED */
3791341Sstevel static int
sysctrl_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)3801341Sstevel sysctrl_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
3811341Sstevel {
3821341Sstevel 	dev_t	dev;
3831341Sstevel 	int	instance;
3841341Sstevel 
3851341Sstevel 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
3861341Sstevel 		dev = (dev_t)arg;
3871341Sstevel 		instance = GETINSTANCE(dev);
3881341Sstevel 		*result = (void *)(uintptr_t)instance;
3891341Sstevel 		return (DDI_SUCCESS);
3901341Sstevel 	}
3911341Sstevel 	return (DDI_FAILURE);
3921341Sstevel }
3931341Sstevel 
3941341Sstevel static int
sysctrl_attach(dev_info_t * devi,ddi_attach_cmd_t cmd)3951341Sstevel sysctrl_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
3961341Sstevel {
3971341Sstevel 	struct sysctrl_soft_state *softsp;
3981341Sstevel 	int instance;
3991341Sstevel 	uchar_t tmp_reg;
4001341Sstevel 	dev_info_t *dip;
4011341Sstevel 	char *propval;
4021341Sstevel 	int proplen;
4031341Sstevel 	int slot_num;
4041341Sstevel 	int start;		/* start index for scan loop */
4051341Sstevel 	int limit;		/* board number limit for scan loop */
4061341Sstevel 	int incr;		/* amount to incr each pass thru loop */
4071341Sstevel 	void set_clockbrd_info(void);
4081341Sstevel 
4091341Sstevel 
4101341Sstevel 	switch (cmd) {
4111341Sstevel 	case DDI_ATTACH:
4121341Sstevel 		break;
4131341Sstevel 
4141341Sstevel 	case DDI_RESUME:
4151341Sstevel 		/* XXX see sysctrl:DDI_SUSPEND for special h/w treatment */
4161341Sstevel 		return (DDI_SUCCESS);
4171341Sstevel 
4181341Sstevel 	default:
4191341Sstevel 		return (DDI_FAILURE);
4201341Sstevel 	}
4211341Sstevel 
4221341Sstevel 	instance = ddi_get_instance(devi);
4231341Sstevel 
4241341Sstevel 	if (ddi_soft_state_zalloc(sysctrlp, instance) != DDI_SUCCESS)
4251341Sstevel 		return (DDI_FAILURE);
4261341Sstevel 
4271341Sstevel 	softsp = GETSOFTC(instance);
4281341Sstevel 
4291341Sstevel 	/* Set the dip in the soft state */
4301341Sstevel 	softsp->dip = devi;
4311341Sstevel 
4321341Sstevel 	/* Set up the parent dip */
4331341Sstevel 	softsp->pdip = ddi_get_parent(softsp->dip);
4341341Sstevel 
4351341Sstevel 	DPRINTF(SYSCTRL_ATTACH_DEBUG, ("sysctrl: devi= 0x%p\n, softsp=0x%p\n",
436*11311SSurya.Prakki@Sun.COM 	    (void *)devi, (void *)softsp));
4371341Sstevel 
4381341Sstevel 	/* First set all of the timeout values */
4391341Sstevel 	spur_timeout_hz = drv_usectohz(SPUR_TIMEOUT_USEC);
4401341Sstevel 	spur_long_timeout_hz = drv_usectohz(SPUR_LONG_TIMEOUT_USEC);
4411341Sstevel 	ac_timeout_hz = drv_usectohz(AC_TIMEOUT_USEC);
4421341Sstevel 	ps_fail_timeout_hz = drv_usectohz(PS_FAIL_TIMEOUT_USEC);
4431341Sstevel 	pps_fan_timeout_hz = drv_usectohz(PPS_FAN_TIMEOUT_USEC);
4441341Sstevel 	bd_insert_delay_hz = drv_usectohz(BRD_INSERT_DELAY_USEC);
4451341Sstevel 	bd_insert_retry_hz = drv_usectohz(BRD_INSERT_RETRY_USEC);
4461341Sstevel 	bd_remove_timeout_hz = drv_usectohz(BRD_REMOVE_TIMEOUT_USEC);
4471341Sstevel 	blink_led_timeout_hz = drv_usectohz(BLINK_LED_TIMEOUT_USEC);
4481341Sstevel 	overtemp_timeout_hz = drv_usectohz(OVERTEMP_TIMEOUT_SEC * MICROSEC);
4491341Sstevel 	keyswitch_timeout_hz = drv_usectohz(KEYSWITCH_TIMEOUT_USEC);
4501341Sstevel 
4511341Sstevel 	/*
4521341Sstevel 	 * Map in the registers sets that OBP hands us. According
4531341Sstevel 	 * to the sun4u device tree spec., the register sets are as
4541341Sstevel 	 * follows:
4551341Sstevel 	 *
4561341Sstevel 	 *	0	Clock Frequency Registers (contains the bit
4571341Sstevel 	 *		for enabling the remote console reset)
4581341Sstevel 	 *	1	Misc (has all the registers that we need
4591341Sstevel 	 *	2	Clock Version Register
4601341Sstevel 	 */
4611341Sstevel 	if (ddi_map_regs(softsp->dip, 0,
4621341Sstevel 	    (caddr_t *)&softsp->clk_freq1, 0, 0)) {
4631341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: unable to map clock frequency "
4647656SSherry.Moore@Sun.COM 		    "registers", instance);
4651341Sstevel 		goto bad0;
4661341Sstevel 	}
4671341Sstevel 
4681341Sstevel 	if (ddi_map_regs(softsp->dip, 1,
4691341Sstevel 	    (caddr_t *)&softsp->csr, 0, 0)) {
4701341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: unable to map internal"
4717656SSherry.Moore@Sun.COM 		    "registers", instance);
4721341Sstevel 		goto bad1;
4731341Sstevel 	}
4741341Sstevel 
4751341Sstevel 	/*
4761341Sstevel 	 * There is a new register for newer vintage clock board nodes,
4771341Sstevel 	 * OBP register set 2 in the clock board node.
4781341Sstevel 	 *
4791341Sstevel 	 */
4801341Sstevel 	(void) ddi_map_regs(softsp->dip, 2, (caddr_t *)&softsp->clk_ver, 0, 0);
4811341Sstevel 
4821341Sstevel 	/*
4831341Sstevel 	 * Fill in the virtual addresses of the registers in the
4841341Sstevel 	 * sysctrl_soft_state structure. We do not want to calculate
4851341Sstevel 	 * them on the fly. This way we waste a little memory, but
4861341Sstevel 	 * avoid bugs down the road.
4871341Sstevel 	 */
4881341Sstevel 	softsp->clk_freq2 = (uchar_t *)((caddr_t)softsp->clk_freq1 +
4897656SSherry.Moore@Sun.COM 	    SYS_OFF_CLK_FREQ2);
4901341Sstevel 
4911341Sstevel 	softsp->status1 = (uchar_t *)((caddr_t)softsp->csr +
4927656SSherry.Moore@Sun.COM 	    SYS_OFF_STAT1);
4931341Sstevel 
4941341Sstevel 	softsp->status2 = (uchar_t *)((caddr_t)softsp->csr +
4957656SSherry.Moore@Sun.COM 	    SYS_OFF_STAT2);
4961341Sstevel 
4971341Sstevel 	softsp->ps_stat = (uchar_t *)((caddr_t)softsp->csr +
4987656SSherry.Moore@Sun.COM 	    SYS_OFF_PSSTAT);
4991341Sstevel 
5001341Sstevel 	softsp->ps_pres = (uchar_t *)((caddr_t)softsp->csr +
5017656SSherry.Moore@Sun.COM 	    SYS_OFF_PSPRES);
5021341Sstevel 
5031341Sstevel 	softsp->pppsr = (uchar_t *)((caddr_t)softsp->csr +
5047656SSherry.Moore@Sun.COM 	    SYS_OFF_PPPSR);
5051341Sstevel 
5061341Sstevel 	softsp->temp_reg = (uchar_t *)((caddr_t)softsp->csr +
5077656SSherry.Moore@Sun.COM 	    SYS_OFF_TEMP);
5081341Sstevel 
5091341Sstevel 	set_clockbrd_info();
5101341Sstevel 
5111341Sstevel 	/*
5121341Sstevel 	 * Enable the hardware watchdog gate on the clock board if
5131341Sstevel 	 * map_wellknown has detected that watchdog timer is available
5141341Sstevel 	 * and user wants it to be enabled.
5151341Sstevel 	 */
5161341Sstevel 	if (watchdog_available && watchdog_enable)
5171341Sstevel 		*(softsp->clk_freq2) |= TOD_RESET_EN;
5181341Sstevel 	else
5191341Sstevel 		*(softsp->clk_freq2) &= ~TOD_RESET_EN;
5201341Sstevel 
5211341Sstevel 	/* Check for inherited faults from the PROM. */
5221341Sstevel 	if (*softsp->csr & SYS_LED_MID) {
5231341Sstevel 		reg_fault(0, FT_PROM, FT_SYSTEM);
5241341Sstevel 	}
5251341Sstevel 
5261341Sstevel 	/*
5271341Sstevel 	 * calculate and cache the number of slots on this system
5281341Sstevel 	 */
5291341Sstevel 	switch (SYS_TYPE(*softsp->status1)) {
5301341Sstevel 	case SYS_16_SLOT:
5311341Sstevel 		softsp->nslots = 16;
5321341Sstevel 		break;
5331341Sstevel 
5341341Sstevel 	case SYS_8_SLOT:
5351341Sstevel 		softsp->nslots = 8;
5361341Sstevel 		break;
5371341Sstevel 
5381341Sstevel 	case SYS_4_SLOT:
5391341Sstevel 		/* check the clk_version register - if the ptr is valid */
5401341Sstevel 		if ((softsp->clk_ver != NULL) &&
5411341Sstevel 		    (SYS_TYPE2(*softsp->clk_ver) == SYS_PLUS_SYSTEM)) {
5421341Sstevel 			softsp->nslots = 5;
5431341Sstevel 		} else {
5441341Sstevel 			softsp->nslots = 4;
5451341Sstevel 		}
5461341Sstevel 		break;
5471341Sstevel 
5481341Sstevel 	case SYS_TESTBED:
5491341Sstevel 	default:
5501341Sstevel 		softsp->nslots = 0;
5511341Sstevel 		break;
5521341Sstevel 	}
5531341Sstevel 
5541341Sstevel 
5551341Sstevel 	/* create the fault list kstat */
5561341Sstevel 	create_ft_kstats(instance);
5571341Sstevel 
5581341Sstevel 	/*
5591341Sstevel 	 * Do a priming read on the ADC, and throw away the first value
5601341Sstevel 	 * read. This is a feature of the ADC hardware. After a power cycle
5611341Sstevel 	 * it does not contains valid data until a read occurs.
5621341Sstevel 	 */
5631341Sstevel 	tmp_reg = *(softsp->temp_reg);
5641341Sstevel 
5651341Sstevel 	/* Wait 30 usec for ADC hardware to stabilize. */
5661341Sstevel 	DELAY(30);
5671341Sstevel 
5681341Sstevel 	/* shut off all interrupt sources */
5691341Sstevel 	*(softsp->csr) &= ~(SYS_PPS_FAN_FAIL_EN | SYS_PS_FAIL_EN |
5707656SSherry.Moore@Sun.COM 	    SYS_AC_PWR_FAIL_EN | SYS_SBRD_PRES_EN);
5711341Sstevel 	tmp_reg = *(softsp->csr);
5721341Sstevel #ifdef lint
5731341Sstevel 	tmp_reg = tmp_reg;
5741341Sstevel #endif
5751341Sstevel 
5761341Sstevel 	/*
5771341Sstevel 	 * Now register our high interrupt with the system.
5781341Sstevel 	 */
5791341Sstevel 	if (ddi_add_intr(devi, 0, &softsp->iblock,
5801341Sstevel 	    &softsp->idevice, (uint_t (*)(caddr_t))nulldev, NULL) !=
5811341Sstevel 	    DDI_SUCCESS)
5821341Sstevel 		goto bad2;
5831341Sstevel 
5841341Sstevel 	mutex_init(&softsp->csr_mutex, NULL, MUTEX_DRIVER,
5851341Sstevel 	    (void *)softsp->iblock);
5861341Sstevel 
5871341Sstevel 	ddi_remove_intr(devi, 0, softsp->iblock);
5881341Sstevel 
5891341Sstevel 	if (ddi_add_intr(devi, 0, &softsp->iblock,
5901341Sstevel 	    &softsp->idevice, system_high_handler, (caddr_t)softsp) !=
5911341Sstevel 	    DDI_SUCCESS)
5921341Sstevel 		goto bad3;
5931341Sstevel 
5941341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_id,
5951341Sstevel 	    &softsp->spur_int_c, NULL, spur_delay, (caddr_t)softsp) !=
5961341Sstevel 	    DDI_SUCCESS)
5971341Sstevel 		goto bad4;
5981341Sstevel 
5991341Sstevel 	mutex_init(&softsp->spur_int_lock, NULL, MUTEX_DRIVER,
6007656SSherry.Moore@Sun.COM 	    (void *)softsp->spur_int_c);
6011341Sstevel 
6021341Sstevel 
6031341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_high_id,
6041341Sstevel 	    NULL, NULL, spur_reenable, (caddr_t)softsp) != DDI_SUCCESS)
6051341Sstevel 		goto bad5;
6061341Sstevel 
6071341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->spur_long_to_id,
6081341Sstevel 	    NULL, NULL, spur_clear_count, (caddr_t)softsp) != DDI_SUCCESS)
6091341Sstevel 		goto bad6;
6101341Sstevel 
6111341Sstevel 	/*
6121341Sstevel 	 * Now register low-level ac fail handler
6131341Sstevel 	 */
6141341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &softsp->ac_fail_id,
6151341Sstevel 	    NULL, NULL, ac_fail_handler, (caddr_t)softsp) != DDI_SUCCESS)
6161341Sstevel 		goto bad7;
6171341Sstevel 
6181341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->ac_fail_high_id,
6191341Sstevel 	    NULL, NULL, ac_fail_reenable, (caddr_t)softsp) != DDI_SUCCESS)
6201341Sstevel 		goto bad8;
6211341Sstevel 
6221341Sstevel 	/*
6231341Sstevel 	 * Now register low-level ps fail handler
6241341Sstevel 	 */
6251341Sstevel 
6261341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &softsp->ps_fail_int_id,
6271341Sstevel 	    &softsp->ps_fail_c, NULL, ps_fail_int_handler, (caddr_t)softsp) !=
6281341Sstevel 	    DDI_SUCCESS)
6291341Sstevel 		goto bad9;
6301341Sstevel 
6311341Sstevel 	mutex_init(&softsp->ps_fail_lock, NULL, MUTEX_DRIVER,
6327656SSherry.Moore@Sun.COM 	    (void *)softsp->ps_fail_c);
6331341Sstevel 
6341341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->ps_fail_poll_id,
6351341Sstevel 	    NULL, NULL, ps_fail_poll_handler, (caddr_t)softsp) !=
6361341Sstevel 	    DDI_SUCCESS)
6371341Sstevel 		goto bad10;
6381341Sstevel 
6391341Sstevel 	/*
6401341Sstevel 	 * Now register low-level pps fan fail handler
6411341Sstevel 	 */
6421341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->pps_fan_id,
6431341Sstevel 	    NULL, NULL, pps_fanfail_handler, (caddr_t)softsp) !=
6441341Sstevel 	    DDI_SUCCESS)
6451341Sstevel 		goto bad11;
6461341Sstevel 
6471341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->pps_fan_high_id,
6481341Sstevel 	    NULL, NULL, pps_fanfail_reenable, (caddr_t)softsp) !=
6491341Sstevel 	    DDI_SUCCESS)
6501341Sstevel 		goto bad12;
6511341Sstevel 
6521341Sstevel 	/*
6531341Sstevel 	 * Based upon a check for a current share backplane, advise
6541341Sstevel 	 * that system does not support hot plug
6551341Sstevel 	 *
6561341Sstevel 	 */
6571341Sstevel 	if ((*(softsp->pppsr) & SYS_NOT_CURRENT_S) != 0) {
6581341Sstevel 		cmn_err(CE_NOTE, "Hot Plug not supported in this system");
6591341Sstevel 		sysctrl_hotplug_disabled = TRUE;
6601341Sstevel 	}
6611341Sstevel 
6621341Sstevel 	/*
6631341Sstevel 	 * If the trigger circuit is busted or the NOT_BRD_PRES line
6641341Sstevel 	 * is stuck then OBP will publish this property stating that
6651341Sstevel 	 * hot plug is not available.  If this happens we will complain
6661341Sstevel 	 * to the console and register a system fault.  We will also
6671341Sstevel 	 * not enable the board insert interrupt for this session.
6681341Sstevel 	 */
6691341Sstevel 	if (ddi_prop_op(DDI_DEV_T_ANY, softsp->dip, PROP_LEN_AND_VAL_ALLOC,
6701341Sstevel 	    DDI_PROP_DONTPASS, HOTPLUG_DISABLED_PROPERTY,
6711341Sstevel 	    (caddr_t)&propval, &proplen) == DDI_PROP_SUCCESS) {
6721341Sstevel 		cmn_err(CE_WARN, "Hot Plug Unavailable [%s]", propval);
6731341Sstevel 		reg_fault(0, FT_HOT_PLUG, FT_SYSTEM);
6741341Sstevel 		sysctrl_hotplug_disabled = TRUE;
6751341Sstevel 		enable_sys_interrupt &= ~SYS_SBRD_PRES_EN;
6761341Sstevel 		kmem_free(propval, proplen);
6771341Sstevel 	}
6781341Sstevel 
6791341Sstevel 	sysc_board_connect_supported_init();
6801341Sstevel 
6811341Sstevel 	fhc_bd_sc_register(sysc_policy_update, softsp);
6821341Sstevel 
6831341Sstevel 	sysc_slot_info(softsp->nslots, &start, &limit, &incr);
6841341Sstevel 
6851341Sstevel 	/* Prime the board list. */
6861341Sstevel 	fhc_bdlist_prime(start, limit, incr);
6871341Sstevel 
6881341Sstevel 	/*
6891341Sstevel 	 * Set up a board remove timeout call.
6901341Sstevel 	 */
6911341Sstevel 	(void) fhc_bdlist_lock(-1);
6921341Sstevel 
6931341Sstevel 	DPRINTF(SYSCTRL_ATTACH_DEBUG,
6947656SSherry.Moore@Sun.COM 	    ("attach: start bd_remove_poll()..."));
6951341Sstevel 
6961341Sstevel 	bd_remove_poll(softsp);
6971341Sstevel 	fhc_bdlist_unlock();
6981341Sstevel 
6991341Sstevel 	/*
7001341Sstevel 	 * Now register low-level board insert handler
7011341Sstevel 	 */
7021341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->sbrd_pres_id,
7031341Sstevel 	    NULL, NULL, bd_insert_handler, (caddr_t)softsp) != DDI_SUCCESS)
7041341Sstevel 		goto bad13;
7051341Sstevel 
7061341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->sbrd_gone_id,
7071341Sstevel 	    NULL, NULL, bd_insert_normal, (caddr_t)softsp) != DDI_SUCCESS)
7081341Sstevel 		goto bad14;
7091341Sstevel 
7101341Sstevel 	/*
7111341Sstevel 	 * Now register led blink handler (interrupt level)
7121341Sstevel 	 */
7131341Sstevel 	if (ddi_add_softintr(devi, DDI_SOFTINT_LOW, &softsp->blink_led_id,
7141341Sstevel 	    &softsp->sys_led_c, NULL, blink_led_handler, (caddr_t)softsp) !=
7151341Sstevel 	    DDI_SUCCESS)
7161341Sstevel 		goto bad15;
7171341Sstevel 	mutex_init(&softsp->sys_led_lock, NULL, MUTEX_DRIVER,
7187656SSherry.Moore@Sun.COM 	    (void *)softsp->sys_led_c);
7191341Sstevel 
7201341Sstevel 	/* initialize the bit field for all pps fans to assumed good */
7211341Sstevel 	softsp->pps_fan_saved = softsp->pps_fan_external_state =
7227656SSherry.Moore@Sun.COM 	    SYS_AC_FAN_OK | SYS_KEYSW_FAN_OK;
7231341Sstevel 
7241341Sstevel 	/* prime the power supply state machines */
7251341Sstevel 	if (enable_sys_interrupt & SYS_PS_FAIL_EN)
7261341Sstevel 		ddi_trigger_softintr(softsp->ps_fail_poll_id);
7271341Sstevel 
7281341Sstevel 
7291341Sstevel 	/* kick off the OS led blinker */
7301341Sstevel 	softsp->sys_led = FALSE;
7311341Sstevel 	ddi_trigger_softintr(softsp->blink_led_id);
7321341Sstevel 
7331341Sstevel 	/* Now enable selected interrupt sources */
7341341Sstevel 	mutex_enter(&softsp->csr_mutex);
7351341Sstevel 	*(softsp->csr) |= enable_sys_interrupt &
7367656SSherry.Moore@Sun.COM 	    (SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
7377656SSherry.Moore@Sun.COM 	    SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
7381341Sstevel 	tmp_reg = *(softsp->csr);
7391341Sstevel #ifdef lint
7401341Sstevel 	tmp_reg = tmp_reg;
7411341Sstevel #endif
7421341Sstevel 	mutex_exit(&softsp->csr_mutex);
7431341Sstevel 
7441341Sstevel 	/* Initialize the temperature */
7451341Sstevel 	init_temp_arrays(&softsp->tempstat);
7461341Sstevel 
7471341Sstevel 	/*
7481341Sstevel 	 * initialize key switch shadow state
7491341Sstevel 	 */
7501341Sstevel 	softsp->key_shadow = KEY_BOOT;
7511341Sstevel 
7521341Sstevel 	/*
7531341Sstevel 	 * Now add this soft state structure to the front of the linked list
7541341Sstevel 	 * of soft state structures.
7551341Sstevel 	 */
7561341Sstevel 	if (sys_list == (struct sysctrl_soft_state *)NULL) {
7571341Sstevel 		mutex_init(&sslist_mutex, NULL, MUTEX_DEFAULT, NULL);
7581341Sstevel 	}
7591341Sstevel 	mutex_enter(&sslist_mutex);
7601341Sstevel 	softsp->next = sys_list;
7611341Sstevel 	sys_list = softsp;
7621341Sstevel 	mutex_exit(&sslist_mutex);
7631341Sstevel 
7641341Sstevel 	/* Setup the kstats for this device */
7651341Sstevel 	sysctrl_add_kstats(softsp);
7661341Sstevel 
7671341Sstevel 	/* kick off the PPS fan poll routine */
7681341Sstevel 	pps_fan_poll(softsp);
7691341Sstevel 
7701341Sstevel 	if (sysctrl_overtemp_thread_started == 0) {
7711341Sstevel 		/*
7721341Sstevel 		 * set up the overtemp condition variable before
7731341Sstevel 		 * starting the thread.
7741341Sstevel 		 */
7751341Sstevel 		cv_init(&overtemp_cv, NULL, CV_DRIVER, NULL);
7761341Sstevel 
7771341Sstevel 		/*
7781341Sstevel 		 * start up the overtemp polling thread
7791341Sstevel 		 */
7801341Sstevel 		(void) thread_create(NULL, 0, (void (*)())sysctrl_overtemp_poll,
7811341Sstevel 		    NULL, 0, &p0, TS_RUN, minclsyspri);
7821341Sstevel 		sysctrl_overtemp_thread_started++;
7831341Sstevel 	}
7841341Sstevel 
7851341Sstevel 	if (sysctrl_keyswitch_thread_started == 0) {
7861341Sstevel 		extern void (*abort_seq_handler)();
7871341Sstevel 
7881341Sstevel 		/*
7891341Sstevel 		 * interpose sysctrl's abort sequence handler
7901341Sstevel 		 */
7911341Sstevel 		abort_seq_handler = sysctrl_abort_seq_handler;
7921341Sstevel 
7931341Sstevel 		/*
7941341Sstevel 		 * set up the key switch condition variable before
7951341Sstevel 		 * starting the thread
7961341Sstevel 		 */
7971341Sstevel 		cv_init(&keyswitch_cv, NULL, CV_DRIVER, NULL);
7981341Sstevel 
7991341Sstevel 		/*
8001341Sstevel 		 * start up the key switch polling thread
8011341Sstevel 		 */
8021341Sstevel 		(void) thread_create(NULL, 0,
8031341Sstevel 		    (void (*)())sysctrl_keyswitch_poll, NULL, 0, &p0,
8041341Sstevel 		    TS_RUN, minclsyspri);
8051341Sstevel 		sysctrl_keyswitch_thread_started++;
8061341Sstevel 	}
8071341Sstevel 
8081341Sstevel 	/*
8091341Sstevel 	 * perform initialization to allow setting of powerfail-time
8101341Sstevel 	 */
8111341Sstevel 	if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL)
8121341Sstevel 		softsp->options_nodeid = (pnode_t)NULL;
8131341Sstevel 	else
8141341Sstevel 		softsp->options_nodeid = (pnode_t)ddi_get_nodeid(dip);
8151341Sstevel 
8161341Sstevel 	DPRINTF(SYSCTRL_ATTACH_DEBUG,
8177656SSherry.Moore@Sun.COM 	    ("sysctrl: Creating devices start:%d, limit:%d, incr:%d\n",
8187656SSherry.Moore@Sun.COM 	    start, limit, incr));
8191341Sstevel 
8201341Sstevel 	/*
8211341Sstevel 	 * Create minor node for each system attachment points
8221341Sstevel 	 */
8231341Sstevel 	for (slot_num = start; slot_num < limit; slot_num = slot_num + incr) {
8241341Sstevel 		char name[30];
8251341Sstevel 		(void) sprintf(name, "slot%d", slot_num);
8261341Sstevel 		if (ddi_create_minor_node(devi, name, S_IFCHR,
8271341Sstevel 		    (PUTINSTANCE(instance) | slot_num),
8281341Sstevel 		    DDI_NT_ATTACHMENT_POINT, 0) == DDI_FAILURE) {
8291341Sstevel 			cmn_err(CE_WARN, "sysctrl%d: \"%s\" "
8307656SSherry.Moore@Sun.COM 			    "ddi_create_minor_node failed",
8317656SSherry.Moore@Sun.COM 			    instance, name);
8321341Sstevel 			goto bad16;
8331341Sstevel 		}
8341341Sstevel 	}
8351341Sstevel 
8361341Sstevel 	ddi_report_dev(devi);
8371341Sstevel 
8381341Sstevel 	/*
8391341Sstevel 	 * Remote console is inherited from POST
8401341Sstevel 	 */
8411341Sstevel 	if ((*(softsp->clk_freq2) & RCONS_UART_EN) == 0) {
8421341Sstevel 		softsp->enable_rcons_atboot = FALSE;
8431341Sstevel 		cmn_err(CE_WARN, "Remote console not active");
8441341Sstevel 	} else
8451341Sstevel 		softsp->enable_rcons_atboot = TRUE;
8461341Sstevel 
8471341Sstevel 	return (DDI_SUCCESS);
8481341Sstevel 
8491341Sstevel bad16:
8501341Sstevel 	cv_destroy(&keyswitch_cv);
8511341Sstevel 	cv_destroy(&overtemp_cv);
8521341Sstevel 	mutex_destroy(&sslist_mutex);
8531341Sstevel 	mutex_destroy(&softsp->sys_led_lock);
8541341Sstevel 	ddi_remove_softintr(softsp->blink_led_id);
8551341Sstevel bad15:
8561341Sstevel 	ddi_remove_softintr(softsp->sbrd_gone_id);
8571341Sstevel bad14:
8581341Sstevel 	ddi_remove_softintr(softsp->sbrd_pres_id);
8591341Sstevel bad13:
8601341Sstevel 	ddi_remove_softintr(softsp->pps_fan_high_id);
8611341Sstevel bad12:
8621341Sstevel 	ddi_remove_softintr(softsp->pps_fan_id);
8631341Sstevel bad11:
8641341Sstevel 	ddi_remove_softintr(softsp->ps_fail_poll_id);
8651341Sstevel bad10:
8661341Sstevel 	mutex_destroy(&softsp->ps_fail_lock);
8671341Sstevel 	ddi_remove_softintr(softsp->ps_fail_int_id);
8681341Sstevel bad9:
8691341Sstevel 	ddi_remove_softintr(softsp->ac_fail_high_id);
8701341Sstevel bad8:
8711341Sstevel 	ddi_remove_softintr(softsp->ac_fail_id);
8721341Sstevel bad7:
8731341Sstevel 	ddi_remove_softintr(softsp->spur_long_to_id);
8741341Sstevel bad6:
8751341Sstevel 	ddi_remove_softintr(softsp->spur_high_id);
8761341Sstevel bad5:
8771341Sstevel 	mutex_destroy(&softsp->spur_int_lock);
8781341Sstevel 	ddi_remove_softintr(softsp->spur_id);
8791341Sstevel bad4:
8801341Sstevel 	ddi_remove_intr(devi, 0, softsp->iblock);
8811341Sstevel bad3:
8821341Sstevel 	mutex_destroy(&softsp->csr_mutex);
8831341Sstevel bad2:
8841341Sstevel 	ddi_unmap_regs(softsp->dip, 1, (caddr_t *)&softsp->csr, 0, 0);
8851341Sstevel 	if (softsp->clk_ver != NULL)
8861341Sstevel 		ddi_unmap_regs(softsp->dip, 2, (caddr_t *)&softsp->clk_ver,
8871341Sstevel 		    0, 0);
8881341Sstevel bad1:
8891341Sstevel 	ddi_unmap_regs(softsp->dip, 0, (caddr_t *)&softsp->clk_freq1, 0, 0);
8901341Sstevel 
8911341Sstevel bad0:
8921341Sstevel 	ddi_soft_state_free(sysctrlp, instance);
8931341Sstevel 	ddi_remove_minor_node(dip, NULL);
8941341Sstevel 	cmn_err(CE_WARN,
8951341Sstevel 	    "sysctrl%d: Initialization failure. Some system level events,"
8961341Sstevel 	    " {AC Fail, Fan Failure, PS Failure} not detected", instance);
8971341Sstevel 	return (DDI_FAILURE);
8981341Sstevel }
8991341Sstevel 
9001341Sstevel struct sysc_hold {
9011341Sstevel 	int start;
9021341Sstevel 	int limit;
9031341Sstevel 	int incr;
9041341Sstevel 	int hold;
9051341Sstevel };
9061341Sstevel 
9071341Sstevel static int
sysctrl_hold_rele_branches(dev_info_t * dip,void * arg)9081341Sstevel sysctrl_hold_rele_branches(dev_info_t *dip, void *arg)
9091341Sstevel {
9101341Sstevel 	int *rp, len, slot, i;
9111341Sstevel 	struct sysc_hold *ap = (struct sysc_hold *)arg;
9121341Sstevel 
9131341Sstevel 	/*
9141341Sstevel 	 * For Sunfire, top nodes on board are always children of root dip
9151341Sstevel 	 */
9161341Sstevel 	ASSERT(ddi_get_parent(dip) == ddi_root_node());
9171341Sstevel 
9181341Sstevel 	/*
9191341Sstevel 	 * Skip non-PROM and "central" nodes
9201341Sstevel 	 */
9211341Sstevel 	if (!ndi_dev_is_prom_node(dip) ||
9221341Sstevel 	    strcmp(ddi_node_name(dip), "central") == 0)
9231341Sstevel 		return (DDI_WALK_PRUNECHILD);
9241341Sstevel 
9251341Sstevel 	/*
9261341Sstevel 	 * Extract board # from reg property.
9271341Sstevel 	 */
9281341Sstevel 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip,
9291341Sstevel 	    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "reg", (caddr_t)&rp, &len)
9301341Sstevel 	    != DDI_SUCCESS) {
9311341Sstevel 		DPRINTF(SYSC_DEBUG, ("devinfo node %s(%p) has no reg"
9321341Sstevel 		    " property\n", ddi_node_name(dip), (void *)dip));
9331341Sstevel 		return (DDI_WALK_PRUNECHILD);
9341341Sstevel 	}
9351341Sstevel 
9361341Sstevel 	slot = (*rp - 0x1c0) >> 2;
9371341Sstevel 	kmem_free(rp, len);
9381341Sstevel 
9391341Sstevel 	ASSERT(ap->start >= 0 && ap->start < ap->limit);
9401341Sstevel 
9411341Sstevel 	for (i = ap->start; i < ap->limit; i = i + ap->incr) {
9421341Sstevel 		if (i == slot)
9431341Sstevel 			break;
9441341Sstevel 	}
9451341Sstevel 
9461341Sstevel 	if (i >= ap->limit) {
9471341Sstevel 		DPRINTF(SYSC_DEBUG, ("sysctrl_hold_rele: Invalid board # (%d)"
9481341Sstevel 		    " for node %s(%p)\n", slot, ddi_node_name(dip),
9491341Sstevel 		    (void *)dip));
9501341Sstevel 		return (DDI_WALK_PRUNECHILD);
9511341Sstevel 	}
9521341Sstevel 
9531341Sstevel 	if (ap->hold) {
9541341Sstevel 		ASSERT(!e_ddi_branch_held(dip));
9551341Sstevel 		e_ddi_branch_hold(dip);
9561341Sstevel 	} else {
9571341Sstevel 		ASSERT(e_ddi_branch_held(dip));
9581341Sstevel 		e_ddi_branch_rele(dip);
9591341Sstevel 	}
9601341Sstevel 
9611341Sstevel 	return (DDI_WALK_PRUNECHILD);
9621341Sstevel }
9631341Sstevel 
9641341Sstevel /* ARGSUSED */
9651341Sstevel static int
sysctrl_detach(dev_info_t * devi,ddi_detach_cmd_t cmd)9661341Sstevel sysctrl_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
9671341Sstevel {
9681341Sstevel #ifdef	SYSCTRL_SUPPORTS_DETACH
9691341Sstevel 	dev_info_t			*rdip;
9701341Sstevel 	struct sysc_hold		arg = {0};
9711341Sstevel 	struct sysctrl_soft_state	*softsp;
9721341Sstevel #endif	/* SYSCTRL_SUPPORTS_DETACH */
9731341Sstevel 
9741341Sstevel 	if (sysctrl_enable_detach_suspend == FALSE)
9751341Sstevel 		return (DDI_FAILURE);
9761341Sstevel 
9771341Sstevel 	switch (cmd) {
9781341Sstevel 	case DDI_SUSPEND:
9791341Sstevel 		/*
9801341Sstevel 		 * XXX we don't presently save the state of the remote
9811341Sstevel 		 * console because it is a constant function of POST.
9821341Sstevel 		 * XXX we don't deal with the hardware watchdog here
9831341Sstevel 		 * either.  It should be handled in hardclk.
9841341Sstevel 		 */
9851341Sstevel 		return (DDI_SUCCESS);
9861341Sstevel 
9871341Sstevel 	case DDI_DETACH:
9881341Sstevel 		break;
9891341Sstevel 	default:
9901341Sstevel 		return (DDI_FAILURE);
9911341Sstevel 	}
9921341Sstevel 
9931341Sstevel #ifdef	SYSCTRL_SUPPORTS_DETACH
9941341Sstevel 
9951341Sstevel 	/*
9961341Sstevel 	 * XXX If sysctrl ever supports detach, this code should be enabled
9971341Sstevel 	 * This is only the portion of the detach code dealing with
9981341Sstevel 	 * the DDI branch routines. Other parts of detach will need
9991341Sstevel 	 * to be added.
10001341Sstevel 	 */
10011341Sstevel 
10021341Sstevel 	/*
10031341Sstevel 	 * Walk immediate children of root devinfo node, releasing holds
10041341Sstevel 	 * on branches acquired in first sysctrl_open().
10051341Sstevel 	 */
10061341Sstevel 
10071341Sstevel 	instance = ddi_get_instance(dip);
10081341Sstevel 	softsp = GETSOFTC(instance);
10091341Sstevel 
10101341Sstevel 	if (softsp == NULL) {
10111341Sstevel 		cmn_err(CE_WARN, "sysctrl%d device not attached", instance);
10121341Sstevel 		return (DDI_FAILURE);
10131341Sstevel 	}
10141341Sstevel 
10151341Sstevel 	sysc_slot_info(softsp->nslots, &arg.start, &arg.limit, &arg.incr);
10161341Sstevel 
10171341Sstevel 	arg.hold = 0;
10181341Sstevel 
10191341Sstevel 	rdip = ddi_root_node();
10201341Sstevel 
10211341Sstevel 	ndi_devi_enter(rdip, &circ);
10221341Sstevel 	ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches, &arg);
10231341Sstevel 	ndi_devi_exit(rdip, circ);
10241341Sstevel 
10251341Sstevel 	sysctrl_ddi_branch_init = 0;
10261341Sstevel 
10271341Sstevel 	return (DDI_SUCCESS);
10281341Sstevel #endif	/* SYSCTRL_SUPPORTS_DETACH */
10291341Sstevel 
10301341Sstevel 	return (DDI_FAILURE);
10311341Sstevel }
10321341Sstevel 
10331341Sstevel /* ARGSUSED */
10341341Sstevel static int
sysctrl_open(dev_t * devp,int flag,int otyp,cred_t * credp)10351341Sstevel sysctrl_open(dev_t *devp, int flag, int otyp, cred_t *credp)
10361341Sstevel {
10371341Sstevel 	int		instance;
10381341Sstevel 	int		slot;
10391341Sstevel 	dev_t		dev;
10401341Sstevel 	int		circ;
10411341Sstevel 	dev_info_t	*rdip;
10421341Sstevel 	struct sysc_hold arg = {0};
10431341Sstevel 	struct sysctrl_soft_state *softsp;
10441341Sstevel 
10451341Sstevel 	dev = *devp;
10461341Sstevel 
10471341Sstevel 	/*
10481341Sstevel 	 * We checked against the instance softstate structure since there
10491341Sstevel 	 * will only be one instance of sysctrl (clock board) in UEXX00
10501341Sstevel 	 *
10511341Sstevel 	 * Since we only create minor devices for existing slots on a
10521341Sstevel 	 * particular system, we don't need to worry about non-exist slot.
10531341Sstevel 	 */
10541341Sstevel 
10551341Sstevel 	instance = GETINSTANCE(dev);
10561341Sstevel 	slot = GETSLOT(dev);
10571341Sstevel 
10581341Sstevel 	/* Is the instance attached? */
10591341Sstevel 	if ((softsp = GETSOFTC(instance)) == NULL) {
10601341Sstevel 		cmn_err(CE_WARN, "sysctrl%d device not attached", instance);
10611341Sstevel 		return (ENXIO);
10621341Sstevel 	}
10631341Sstevel 
10641341Sstevel 	/* verify that otyp is appropriate */
10651341Sstevel 	if (otyp != OTYP_CHR) {
10661341Sstevel 		return (EINVAL);
10671341Sstevel 	}
10681341Sstevel 
10691341Sstevel 	if (!fhc_bd_valid(slot))
10701341Sstevel 		return (ENXIO);
10711341Sstevel 
10721341Sstevel 	/*
10731341Sstevel 	 * On first open of a sysctrl minor walk immediate children of the
10741341Sstevel 	 * devinfo root node and hold all branches of interest.
10751341Sstevel 	 */
10761341Sstevel 	mutex_enter(&sysctrl_branch_mutex);
10771341Sstevel 	if (!sysctrl_ddi_branch_init) {
10781341Sstevel 
10791341Sstevel 		sysctrl_ddi_branch_init = 1;
10801341Sstevel 
10811341Sstevel 		sysc_slot_info(softsp->nslots, &arg.start, &arg.limit,
10821341Sstevel 		    &arg.incr);
10831341Sstevel 		arg.hold = 1;
10841341Sstevel 
10851341Sstevel 		rdip = ddi_root_node();
10861341Sstevel 
10871341Sstevel 		ndi_devi_enter(rdip, &circ);
10881341Sstevel 		ddi_walk_devs(ddi_get_child(rdip), sysctrl_hold_rele_branches,
10891341Sstevel 		    &arg);
10901341Sstevel 		ndi_devi_exit(rdip, circ);
10911341Sstevel 	}
10921341Sstevel 	mutex_exit(&sysctrl_branch_mutex);
10931341Sstevel 
10941341Sstevel 	return (DDI_SUCCESS);
10951341Sstevel }
10961341Sstevel 
10971341Sstevel /* ARGSUSED */
10981341Sstevel static int
sysctrl_close(dev_t devp,int flag,int otyp,cred_t * credp)10991341Sstevel sysctrl_close(dev_t devp, int flag, int otyp, cred_t *credp)
11001341Sstevel {
11011341Sstevel 	return (DDI_SUCCESS);
11021341Sstevel }
11031341Sstevel 
11041341Sstevel /*
11051341Sstevel  * This function will acquire the lock and set the in_transition
11061341Sstevel  * bit for the specified slot.  If the slot is being used,
11071341Sstevel  * we return FALSE; else set in_transition and return TRUE.
11081341Sstevel  */
11091341Sstevel static int
sysc_enter_transition(int slot)11101341Sstevel sysc_enter_transition(int slot)
11111341Sstevel {
11121341Sstevel 	fhc_bd_t	*list;
11131341Sstevel 	sysc_cfga_stat_t *sysc_stat_lk;
11141341Sstevel 	fhc_bd_t	*glist;
11151341Sstevel 	sysc_cfga_stat_t *sysc_stat_gk;
11161341Sstevel 
11171341Sstevel 	/* mutex lock the structure */
11181341Sstevel 	list = fhc_bdlist_lock(slot);
11191341Sstevel 	if ((slot != -1) && (list == NULL)) {
11201341Sstevel 		fhc_bdlist_unlock();
11211341Sstevel 		return (FALSE);
11221341Sstevel 	}
11231341Sstevel 
11241341Sstevel 	glist = fhc_bd_clock();
11251341Sstevel 	if (slot == -1)
11261341Sstevel 		list = glist;
11271341Sstevel 
11281341Sstevel 	/* change the in_transition bit */
11291341Sstevel 	sysc_stat_lk = &list->sc;
11301341Sstevel 	sysc_stat_gk = &glist->sc;
11311341Sstevel 	if ((sysc_stat_lk->in_transition == TRUE) ||
11321341Sstevel 	    (sysc_stat_gk->in_transition == TRUE)) {
11331341Sstevel 		fhc_bdlist_unlock();
11341341Sstevel 		return (FALSE);
11351341Sstevel 	} else {
11361341Sstevel 		sysc_stat_lk->in_transition = TRUE;
11371341Sstevel 		return (TRUE);
11381341Sstevel 	}
11391341Sstevel }
11401341Sstevel 
11411341Sstevel /*
11421341Sstevel  * This function will release the lock and clear the in_transition
11431341Sstevel  * bit for the specified slot.
11441341Sstevel  */
11451341Sstevel static void
sysc_exit_transition(int slot)11461341Sstevel sysc_exit_transition(int slot)
11471341Sstevel {
11481341Sstevel 	fhc_bd_t	*list;
11491341Sstevel 	sysc_cfga_stat_t *sysc_stat_lk;
11501341Sstevel 
11511341Sstevel 	ASSERT(fhc_bdlist_locked());
11521341Sstevel 
11531341Sstevel 	if (slot == -1)
11541341Sstevel 		list = fhc_bd_clock();
11551341Sstevel 	else
11561341Sstevel 		list = fhc_bd(slot);
11571341Sstevel 	sysc_stat_lk = &list->sc;
11581341Sstevel 	ASSERT(sysc_stat_lk->in_transition == TRUE);
11591341Sstevel 	sysc_stat_lk->in_transition = FALSE;
11601341Sstevel 	fhc_bdlist_unlock();
11611341Sstevel }
11621341Sstevel 
11631341Sstevel static int
sysc_pkt_init(sysc_cfga_pkt_t * pkt,intptr_t arg,int flag)11641341Sstevel sysc_pkt_init(sysc_cfga_pkt_t *pkt, intptr_t arg, int flag)
11651341Sstevel {
11661341Sstevel #ifdef _MULTI_DATAMODEL
11671341Sstevel 	if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
11681341Sstevel 		sysc_cfga_cmd32_t sysc_cmd32;
11691341Sstevel 
11701341Sstevel 		if (ddi_copyin((void *)arg, &sysc_cmd32,
11717656SSherry.Moore@Sun.COM 		    sizeof (sysc_cfga_cmd32_t), flag) != 0) {
11721341Sstevel 			return (EFAULT);
11731341Sstevel 		}
11741341Sstevel 		pkt->cmd_cfga.force = sysc_cmd32.force;
11751341Sstevel 		pkt->cmd_cfga.test = sysc_cmd32.test;
11761341Sstevel 		pkt->cmd_cfga.arg = sysc_cmd32.arg;
11771341Sstevel 		pkt->cmd_cfga.errtype = sysc_cmd32.errtype;
11781341Sstevel 		pkt->cmd_cfga.outputstr =
11797656SSherry.Moore@Sun.COM 		    (char *)(uintptr_t)sysc_cmd32.outputstr;
11801341Sstevel 	} else
11811341Sstevel #endif /* _MULTI_DATAMODEL */
11821341Sstevel 	if (ddi_copyin((void *)arg, &(pkt->cmd_cfga),
11837656SSherry.Moore@Sun.COM 	    sizeof (sysc_cfga_cmd_t), flag) != 0) {
11841341Sstevel 		return (EFAULT);
11851341Sstevel 	}
11861341Sstevel 	pkt->errbuf = kmem_zalloc(SYSC_OUTPUT_LEN, KM_SLEEP);
11871341Sstevel 	return (0);
11881341Sstevel }
11891341Sstevel 
11901341Sstevel static int
sysc_pkt_fini(sysc_cfga_pkt_t * pkt,intptr_t arg,int flag)11911341Sstevel sysc_pkt_fini(sysc_cfga_pkt_t *pkt, intptr_t arg, int flag)
11921341Sstevel {
11931341Sstevel 	int ret = TRUE;
11941341Sstevel 
11951341Sstevel #ifdef _MULTI_DATAMODEL
11961341Sstevel 	if (ddi_model_convert_from(flag & FMODELS) == DDI_MODEL_ILP32) {
11971341Sstevel 
11981341Sstevel 		if (ddi_copyout(&(pkt->cmd_cfga.errtype),
11997656SSherry.Moore@Sun.COM 		    (void *)&(((sysc_cfga_cmd32_t *)arg)->errtype),
12007656SSherry.Moore@Sun.COM 		    sizeof (sysc_err_t), flag) != 0) {
12011341Sstevel 			ret = FALSE;
12021341Sstevel 		}
12031341Sstevel 	} else
12041341Sstevel #endif
12051341Sstevel 	if (ddi_copyout(&(pkt->cmd_cfga.errtype),
12067656SSherry.Moore@Sun.COM 	    (void *)&(((sysc_cfga_cmd_t *)arg)->errtype),
12077656SSherry.Moore@Sun.COM 	    sizeof (sysc_err_t), flag) != 0) {
12081341Sstevel 		ret = FALSE;
12091341Sstevel 	}
12101341Sstevel 
12111341Sstevel 	if ((ret != FALSE) && ((pkt->cmd_cfga.outputstr != NULL) &&
12127656SSherry.Moore@Sun.COM 	    (ddi_copyout(pkt->errbuf, pkt->cmd_cfga.outputstr,
12137656SSherry.Moore@Sun.COM 	    SYSC_OUTPUT_LEN, flag) != 0))) {
12141341Sstevel 			ret = FALSE;
12151341Sstevel 	}
12161341Sstevel 
12171341Sstevel 	kmem_free(pkt->errbuf, SYSC_OUTPUT_LEN);
12181341Sstevel 	return (ret);
12191341Sstevel }
12201341Sstevel 
12211341Sstevel /* ARGSUSED */
12221341Sstevel static int
sysctrl_ioctl(dev_t devt,int cmd,intptr_t arg,int flag,cred_t * cred_p,int * rval_p)12231341Sstevel sysctrl_ioctl(dev_t devt, int cmd, intptr_t arg, int flag, cred_t *cred_p,
12241341Sstevel 		int *rval_p)
12251341Sstevel {
12261341Sstevel 	struct sysctrl_soft_state *softsp;
12271341Sstevel 	sysc_cfga_pkt_t sysc_pkt;
12281341Sstevel 	fhc_bd_t *fhc_list = NULL;
12291341Sstevel 	sysc_cfga_stat_t *sc_list = NULL;
12301341Sstevel 	fhc_bd_t *bdp;
12311341Sstevel 	sysc_cfga_stat_t *sc = NULL;
12321341Sstevel 	int instance;
12331341Sstevel 	int slot;
12341341Sstevel 	int retval = 0;
12351341Sstevel 	int i;
12361341Sstevel 
12371341Sstevel 	instance = GETINSTANCE(devt);
12381341Sstevel 	softsp = GETSOFTC(instance);
12391341Sstevel 	if (softsp == NULL) {
12401341Sstevel 		cmn_err(CE_CONT,
12417656SSherry.Moore@Sun.COM 		    "sysctrl_ioctl(%d): NULL softstate ptr!\n",
12427656SSherry.Moore@Sun.COM 		    (int)GETSLOT(devt));
12431341Sstevel 		return (ENXIO);
12441341Sstevel 	}
12451341Sstevel 
12461341Sstevel 	slot = GETSLOT(devt);
12471341Sstevel 
12481341Sstevel 	/*
12491341Sstevel 	 * First switch is to do correct locking and do ddi_copyin()
12501341Sstevel 	 */
12511341Sstevel 	switch (cmd) {
12521341Sstevel 	case SYSC_CFGA_CMD_GETSTATUS:
12531341Sstevel 		/* mutex lock the whole list */
12541341Sstevel 		if (sysc_enter_transition(-1) != TRUE) {
12551341Sstevel 			retval = EBUSY;
12561341Sstevel 			goto cleanup_exit;
12571341Sstevel 		}
12581341Sstevel 
12591341Sstevel 		/* allocate the memory before acquiring mutex */
12601341Sstevel 		fhc_list = kmem_zalloc(sizeof (fhc_bd_t) * fhc_max_boards(),
12611341Sstevel 		    KM_SLEEP);
12621341Sstevel 
12631341Sstevel 		sc_list = kmem_zalloc(sizeof (sysc_cfga_stat_t) *
12641341Sstevel 		    fhc_max_boards(), KM_SLEEP);
12651341Sstevel 
12661341Sstevel 		break;
12671341Sstevel 
12681341Sstevel 	case SYSC_CFGA_CMD_EJECT:
12691341Sstevel 	case SYSC_CFGA_CMD_INSERT:
12701341Sstevel 		retval = ENOTSUP;
12711341Sstevel 		goto cleanup_exit;
12721341Sstevel 
12731341Sstevel 	case SYSC_CFGA_CMD_CONNECT:
12741341Sstevel 	case SYSC_CFGA_CMD_DISCONNECT:
12751341Sstevel 	case SYSC_CFGA_CMD_UNCONFIGURE:
12761341Sstevel 	case SYSC_CFGA_CMD_CONFIGURE:
12771341Sstevel 	case SYSC_CFGA_CMD_TEST:
12781341Sstevel 	case SYSC_CFGA_CMD_TEST_SET_COND:
12791341Sstevel 	case SYSC_CFGA_CMD_QUIESCE_TEST:
12801341Sstevel 
12811341Sstevel 		/* ioctls allowed if caller has write permission */
12821341Sstevel 		if (!(flag & FWRITE)) {
12831341Sstevel 			retval = EPERM;
12841341Sstevel 			goto cleanup_exit;
12851341Sstevel 		}
12861341Sstevel 
12871341Sstevel 		retval = sysc_pkt_init(&sysc_pkt, arg, flag);
12881341Sstevel 		if (retval != 0)
12891341Sstevel 			goto cleanup_exit;
12901341Sstevel 
12911341Sstevel 		/* grasp lock and set in_transition bit */
12921341Sstevel 		if (sysc_enter_transition(cmd == SYSC_CFGA_CMD_QUIESCE_TEST
12937656SSherry.Moore@Sun.COM 		    ? -1 : slot) != TRUE) {
12941341Sstevel 			retval = EBUSY;
12951341Sstevel 			SYSC_ERR_SET(&sysc_pkt, SYSC_ERR_INTRANS);
12961341Sstevel 			goto cleanup_copyout;
12971341Sstevel 		}
12981341Sstevel 
12991341Sstevel 		/* get the status structure for the slot */
13001341Sstevel 		bdp = fhc_bd(slot);
13011341Sstevel 		sc = &bdp->sc;
13021341Sstevel 		break;
13031341Sstevel 
13041341Sstevel 	/* POSIX definition: return ENOTTY if unsupported command */
13051341Sstevel 	default:
13061341Sstevel 		retval = ENOTTY;
13071341Sstevel 		goto cleanup_exit;
13081341Sstevel 	}
13091341Sstevel 
13101341Sstevel 	/*
13111341Sstevel 	 * Second switch is to call the underlayer workhorse.
13121341Sstevel 	 */
13131341Sstevel 	switch (cmd) {
13141341Sstevel 	case SYSC_CFGA_CMD_GETSTATUS:
13151341Sstevel 		for (i = 0; i < fhc_max_boards(); i++) {
13161341Sstevel 			if (fhc_bd_valid(i)) {
13171341Sstevel 				bdp = fhc_bd(i);
13181341Sstevel 				if (fhc_bd_is_jtag_master(i))
13191341Sstevel 					bdp->sc.no_detach = 1;
13201341Sstevel 				else
13211341Sstevel 					bdp->sc.no_detach = 0;
13221341Sstevel 				bcopy((caddr_t)&bdp->sc,
13237656SSherry.Moore@Sun.COM 				    &sc_list[i], sizeof (sysc_cfga_stat_t));
13241341Sstevel 			} else {
13251341Sstevel 				sc_list[i].board = -1;
13261341Sstevel 				sc_list[i].rstate = SYSC_CFGA_RSTATE_EMPTY;
13271341Sstevel 			}
13281341Sstevel 		}
13291341Sstevel 
13301341Sstevel 		sysc_exit_transition(-1);
13311341Sstevel 
13321341Sstevel 		break;
13331341Sstevel 
13341341Sstevel 	case SYSC_CFGA_CMD_EJECT:
13351341Sstevel 	case SYSC_CFGA_CMD_INSERT:
13361341Sstevel 		retval = ENOTSUP;
13371341Sstevel 		goto cleanup_exit;
13381341Sstevel 
13391341Sstevel 	case SYSC_CFGA_CMD_CONNECT:
13401341Sstevel 		retval = sysc_policy_connect(softsp, &sysc_pkt, sc);
13411341Sstevel 		sysc_exit_transition(slot);
13421341Sstevel 		break;
13431341Sstevel 
13441341Sstevel 	case SYSC_CFGA_CMD_DISCONNECT:
13451341Sstevel 		retval = sysc_policy_disconnect(softsp, &sysc_pkt, sc);
13461341Sstevel 		sysc_exit_transition(slot);
13471341Sstevel 		break;
13481341Sstevel 
13491341Sstevel 	case SYSC_CFGA_CMD_UNCONFIGURE:
13501341Sstevel 		retval = sysc_policy_unconfigure(softsp, &sysc_pkt, sc);
13511341Sstevel 		sysc_exit_transition(slot);
13521341Sstevel 		break;
13531341Sstevel 
13541341Sstevel 	case SYSC_CFGA_CMD_CONFIGURE:
13551341Sstevel 		retval = sysc_policy_configure(softsp, &sysc_pkt, sc);
13561341Sstevel 		sysc_exit_transition(slot);
13571341Sstevel 		break;
13581341Sstevel 
13591341Sstevel 	case SYSC_CFGA_CMD_TEST:
13601341Sstevel 		retval = fhc_bd_test(slot, &sysc_pkt);
13611341Sstevel 		sysc_exit_transition(slot);
13621341Sstevel 		break;
13631341Sstevel 
13641341Sstevel 	case SYSC_CFGA_CMD_TEST_SET_COND:
13651341Sstevel 		retval = fhc_bd_test_set_cond(slot, &sysc_pkt);
13661341Sstevel 		sysc_exit_transition(slot);
13671341Sstevel 		break;
13681341Sstevel 
13691341Sstevel 	case SYSC_CFGA_CMD_QUIESCE_TEST:
13701341Sstevel 		sysctrl_suspend_prepare();
13711341Sstevel 		fhc_bdlist_unlock();
13721341Sstevel 
13731341Sstevel 		if (sysctrl_suspend(&sysc_pkt) == DDI_SUCCESS) {
13741341Sstevel 			sysctrl_resume(&sysc_pkt);
13751341Sstevel 		} else {
13761341Sstevel 			retval = EBUSY;
13771341Sstevel 		}
13781341Sstevel 
13791341Sstevel 		(void) fhc_bdlist_lock(-1);
13801341Sstevel 		sysc_exit_transition(-1);
13811341Sstevel 		break;
13821341Sstevel 
13831341Sstevel 	default:
13841341Sstevel 		retval = ENOTTY;
13851341Sstevel 		goto cleanup_exit;
13861341Sstevel 	}
13871341Sstevel 
13881341Sstevel cleanup_copyout:
13891341Sstevel 	/*
13901341Sstevel 	 * 3rd switch is to do appropriate copyout and reset locks
13911341Sstevel 	 */
13921341Sstevel 	switch (cmd) {
13931341Sstevel 	case SYSC_CFGA_CMD_GETSTATUS:
13941341Sstevel 		if (ddi_copyout(sc_list, (void *)arg,
13957656SSherry.Moore@Sun.COM 		    sizeof (sysc_cfga_stat_t) * fhc_max_boards(),
13967656SSherry.Moore@Sun.COM 		    flag) != 0) {
13971341Sstevel 			retval = EFAULT;
13981341Sstevel 		}
13991341Sstevel 
14001341Sstevel 		/* cleanup memory */
14011341Sstevel 		kmem_free(fhc_list, sizeof (fhc_bd_t) * fhc_max_boards());
14021341Sstevel 		kmem_free(sc_list, sizeof (sysc_cfga_stat_t) *
14031341Sstevel 		    fhc_max_boards());
14041341Sstevel 		break;
14051341Sstevel 
14061341Sstevel 	case SYSC_CFGA_CMD_EJECT:
14071341Sstevel 	case SYSC_CFGA_CMD_INSERT:
14081341Sstevel 		retval = ENOTSUP;
14091341Sstevel 		break;
14101341Sstevel 
14111341Sstevel 	case SYSC_CFGA_CMD_CONNECT:
14121341Sstevel 	case SYSC_CFGA_CMD_DISCONNECT:
14131341Sstevel 	case SYSC_CFGA_CMD_UNCONFIGURE:
14141341Sstevel 	case SYSC_CFGA_CMD_CONFIGURE:
14151341Sstevel 	case SYSC_CFGA_CMD_TEST:
14161341Sstevel 	case SYSC_CFGA_CMD_TEST_SET_COND:
14171341Sstevel 	case SYSC_CFGA_CMD_QUIESCE_TEST:
14181341Sstevel 		if (sysc_pkt_fini(&sysc_pkt, arg, flag) != TRUE)
14191341Sstevel 			return (EFAULT);
14201341Sstevel 		break;
14211341Sstevel 
14221341Sstevel 	default:
14231341Sstevel 		retval = ENOTTY;
14241341Sstevel 		break;
14251341Sstevel 	}
14261341Sstevel 
14271341Sstevel cleanup_exit:
14281341Sstevel 	return (retval);
14291341Sstevel }
14301341Sstevel 
14311341Sstevel /*
14321341Sstevel  * system_high_handler()
14331341Sstevel  * This routine handles system interrupts.
14341341Sstevel  *
14351341Sstevel  * This routine goes through all the interrupt sources and masks
14361341Sstevel  * off the enable bit if interrupting.  Because of the special
14371341Sstevel  * nature of the pps fan source bits, we also cache the state
14381341Sstevel  * of the fan bits for that special case.
14391341Sstevel  *
14401341Sstevel  * The rest of the work is done in the low level handlers
14411341Sstevel  */
14421341Sstevel static uint_t
system_high_handler(caddr_t arg)14431341Sstevel system_high_handler(caddr_t arg)
14441341Sstevel {
14451341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
14461341Sstevel 	uchar_t csr;
14471341Sstevel 	uchar_t status2;
14481341Sstevel 	uchar_t tmp_reg;
14491341Sstevel 	int serviced = 0;
14501341Sstevel 
14511341Sstevel 	ASSERT(softsp);
14521341Sstevel 
14531341Sstevel 	mutex_enter(&softsp->csr_mutex);
14541341Sstevel 
14551341Sstevel 	/* read in the hardware registers */
14561341Sstevel 	csr = *(softsp->csr);
14571341Sstevel 	status2 = *(softsp->status2);
14581341Sstevel 
14591341Sstevel 	if (csr & SYS_AC_PWR_FAIL_EN) {
14601341Sstevel 		if (status2 & SYS_AC_FAIL) {
14611341Sstevel 
14621341Sstevel 			/* save the powerfail state in nvram */
14631341Sstevel 			nvram_update_powerfail(softsp);
14641341Sstevel 
14651341Sstevel 			/* disable this interrupt source */
14661341Sstevel 			csr &= ~SYS_AC_PWR_FAIL_EN;
14671341Sstevel 
14681341Sstevel 			ddi_trigger_softintr(softsp->ac_fail_id);
14691341Sstevel 			serviced++;
14701341Sstevel 		}
14711341Sstevel 	}
14721341Sstevel 
14731341Sstevel 	if (csr & SYS_PS_FAIL_EN) {
14741341Sstevel 		if ((*(softsp->ps_stat) != 0xff) ||
14751341Sstevel 		    ((~status2) & (SYS_PPS0_OK | SYS_CLK_33_OK |
14767656SSherry.Moore@Sun.COM 		    SYS_CLK_50_OK)) ||
14771341Sstevel 		    (~(*(softsp->pppsr)) & SYS_PPPSR_BITS)) {
14781341Sstevel 
14791341Sstevel 			/* disable this interrupt source */
14801341Sstevel 			csr &= ~SYS_PS_FAIL_EN;
14811341Sstevel 
14821341Sstevel 			ddi_trigger_softintr(softsp->ps_fail_int_id);
14831341Sstevel 			serviced++;
14841341Sstevel 		}
14851341Sstevel 	}
14861341Sstevel 
14871341Sstevel 	if (csr & SYS_PPS_FAN_FAIL_EN) {
14881341Sstevel 		if (status2 & SYS_RACK_FANFAIL ||
14891341Sstevel 		    !(status2 & SYS_AC_FAN_OK) ||
14901341Sstevel 		    !(status2 & SYS_KEYSW_FAN_OK)) {
14911341Sstevel 
14921341Sstevel 			/*
14931341Sstevel 			 * we must cache the fan status because it goes
14941341Sstevel 			 * away when we disable interrupts !?!?!
14951341Sstevel 			 */
14961341Sstevel 			softsp->pps_fan_saved = status2;
14971341Sstevel 
14981341Sstevel 			/* disable this interrupt source */
14991341Sstevel 			csr &= ~SYS_PPS_FAN_FAIL_EN;
15001341Sstevel 
15011341Sstevel 			ddi_trigger_softintr(softsp->pps_fan_id);
15021341Sstevel 			serviced++;
15031341Sstevel 		}
15041341Sstevel 	}
15051341Sstevel 
15061341Sstevel 	if (csr & SYS_SBRD_PRES_EN) {
15071341Sstevel 		if (!(*(softsp->status1) & SYS_NOT_BRD_PRES)) {
15081341Sstevel 
15091341Sstevel 			/* disable this interrupt source */
15101341Sstevel 			csr &= ~SYS_SBRD_PRES_EN;
15111341Sstevel 
15121341Sstevel 			ddi_trigger_softintr(softsp->sbrd_pres_id);
15131341Sstevel 			serviced++;
15141341Sstevel 		}
15151341Sstevel 	}
15161341Sstevel 
15171341Sstevel 	if (!serviced) {
15181341Sstevel 
15191341Sstevel 		/*
15201341Sstevel 		 * if we get here than it is likely that contact bounce
15211341Sstevel 		 * is messing with us.  so, we need to shut this interrupt
15221341Sstevel 		 * up for a while to let the contacts settle down.
15231341Sstevel 		 * Then we will re-enable the interrupts that are enabled
15241341Sstevel 		 * right now.  The trick is to disable the appropriate
15251341Sstevel 		 * interrupts and then to re-enable them correctly, even
15261341Sstevel 		 * though intervening handlers might have been working.
15271341Sstevel 		 */
15281341Sstevel 
15291341Sstevel 		/* remember all interrupts that could have caused it */
15301341Sstevel 		softsp->saved_en_state |= csr &
15311341Sstevel 		    (SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
15321341Sstevel 		    SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
15331341Sstevel 
15341341Sstevel 		/* and then turn them off */
15351341Sstevel 		csr &= ~(SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
15367656SSherry.Moore@Sun.COM 		    SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
15371341Sstevel 
15381341Sstevel 		/* and then bump the counter */
15391341Sstevel 		softsp->spur_count++;
15401341Sstevel 
15411341Sstevel 		/* and kick off the timeout */
15421341Sstevel 		ddi_trigger_softintr(softsp->spur_id);
15431341Sstevel 	}
15441341Sstevel 
15451341Sstevel 	/* update the real csr */
15461341Sstevel 	*(softsp->csr) = csr;
15471341Sstevel 	tmp_reg = *(softsp->csr);
15481341Sstevel #ifdef lint
15491341Sstevel 	tmp_reg = tmp_reg;
15501341Sstevel #endif
15511341Sstevel 	mutex_exit(&softsp->csr_mutex);
15521341Sstevel 
15531341Sstevel 	return (DDI_INTR_CLAIMED);
15541341Sstevel }
15551341Sstevel 
15561341Sstevel /*
15571341Sstevel  * we've detected a spurious interrupt.
15581341Sstevel  * determine if we should log a message and if we need another timeout
15591341Sstevel  */
15601341Sstevel static uint_t
spur_delay(caddr_t arg)15611341Sstevel spur_delay(caddr_t arg)
15621341Sstevel {
15631341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
15641341Sstevel 
15651341Sstevel 	ASSERT(softsp);
15661341Sstevel 
15671341Sstevel 	/* do we need to complain? */
15681341Sstevel 	mutex_enter(&softsp->csr_mutex);
15691341Sstevel 
15701341Sstevel 	/* NOTE: this is == because we want one message per long timeout */
15711341Sstevel 	if (softsp->spur_count == MAX_SPUR_COUNT) {
15721341Sstevel 		char buf[128];
15731341Sstevel 
15741341Sstevel 		/* print out the candidates known at this time */
15751341Sstevel 		/* XXX not perfect because of re-entrant nature but close */
15761341Sstevel 		buf[0] = '\0';
15771341Sstevel 		if (softsp->saved_en_state & SYS_AC_PWR_FAIL_EN)
15781341Sstevel 			(void) strcat(buf, "AC FAIL");
15791341Sstevel 		if (softsp->saved_en_state & SYS_PPS_FAN_FAIL_EN)
15801341Sstevel 			(void) strcat(buf, buf[0] ? "|PPS FANS" : "PPS FANS");
15811341Sstevel 		if (softsp->saved_en_state & SYS_PS_FAIL_EN)
15821341Sstevel 			(void) strcat(buf, buf[0] ? "|PS FAIL" : "PS FAIL");
15831341Sstevel 		if (softsp->saved_en_state & SYS_SBRD_PRES_EN)
15841341Sstevel 			(void) strcat(buf,
15857656SSherry.Moore@Sun.COM 			    buf[0] ? "|BOARD INSERT" : "BOARD INSERT");
15861341Sstevel 
15871341Sstevel 		/*
15881341Sstevel 		 * This is a high level mutex, therefore it needs to be
15891341Sstevel 		 * dropped before calling cmn_err.
15901341Sstevel 		 */
15911341Sstevel 		mutex_exit(&softsp->csr_mutex);
15921341Sstevel 
15931341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: unserviced interrupt."
15947656SSherry.Moore@Sun.COM 		    " possible sources [%s].",
15957656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip), buf);
15961341Sstevel 	} else
15971341Sstevel 		mutex_exit(&softsp->csr_mutex);
15981341Sstevel 
15991341Sstevel 	mutex_enter(&softsp->spur_int_lock);
16001341Sstevel 
16011341Sstevel 	/* do we need to start the short timeout? */
16021341Sstevel 	if (softsp->spur_timeout_id == 0) {
16031341Sstevel 		softsp->spur_timeout_id = timeout(spur_retry, softsp,
16041341Sstevel 		    spur_timeout_hz);
16051341Sstevel 	}
16061341Sstevel 
16071341Sstevel 	/* do we need to start the long timeout? */
16081341Sstevel 	if (softsp->spur_long_timeout_id == 0) {
16091341Sstevel 		softsp->spur_long_timeout_id = timeout(spur_long_timeout,
16101341Sstevel 		    softsp, spur_long_timeout_hz);
16111341Sstevel 	}
16121341Sstevel 
16131341Sstevel 	mutex_exit(&softsp->spur_int_lock);
16141341Sstevel 
16151341Sstevel 	return (DDI_INTR_CLAIMED);
16161341Sstevel }
16171341Sstevel 
16181341Sstevel /*
16191341Sstevel  * spur_retry
16201341Sstevel  *
16211341Sstevel  * this routine simply triggers the interrupt which will re-enable
16221341Sstevel  * the interrupts disabled by the spurious int detection.
16231341Sstevel  */
16241341Sstevel static void
spur_retry(void * arg)16251341Sstevel spur_retry(void *arg)
16261341Sstevel {
16271341Sstevel 	struct sysctrl_soft_state *softsp = arg;
16281341Sstevel 
16291341Sstevel 	ASSERT(softsp);
16301341Sstevel 
16311341Sstevel 	ddi_trigger_softintr(softsp->spur_high_id);
16321341Sstevel 
16331341Sstevel 	mutex_enter(&softsp->spur_int_lock);
16341341Sstevel 	softsp->spur_timeout_id = 0;
16351341Sstevel 	mutex_exit(&softsp->spur_int_lock);
16361341Sstevel }
16371341Sstevel 
16381341Sstevel /*
16391341Sstevel  * spur_reenable
16401341Sstevel  *
16411341Sstevel  * OK, we've been slient for a while.   Go ahead and re-enable the
16421341Sstevel  * interrupts that were enabled at the time of the spurious detection.
16431341Sstevel  */
16441341Sstevel static uint_t
spur_reenable(caddr_t arg)16451341Sstevel spur_reenable(caddr_t arg)
16461341Sstevel {
16471341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
16481341Sstevel 	uchar_t tmp_reg;
16491341Sstevel 
16501341Sstevel 	ASSERT(softsp);
16511341Sstevel 
16521341Sstevel 	mutex_enter(&softsp->csr_mutex);
16531341Sstevel 
16541341Sstevel 	/* reenable those who were spurious candidates */
16551341Sstevel 	*(softsp->csr) |= softsp->saved_en_state &
16567656SSherry.Moore@Sun.COM 	    (SYS_AC_PWR_FAIL_EN | SYS_PS_FAIL_EN |
16577656SSherry.Moore@Sun.COM 	    SYS_PPS_FAN_FAIL_EN | SYS_SBRD_PRES_EN);
16581341Sstevel 	tmp_reg = *(softsp->csr);
16591341Sstevel #ifdef lint
16601341Sstevel 	tmp_reg = tmp_reg;
16611341Sstevel #endif
16621341Sstevel 
16631341Sstevel 	/* clear out the saved state */
16641341Sstevel 	softsp->saved_en_state = 0;
16651341Sstevel 
16661341Sstevel 	mutex_exit(&softsp->csr_mutex);
16671341Sstevel 
16681341Sstevel 	return (DDI_INTR_CLAIMED);
16691341Sstevel }
16701341Sstevel 
16711341Sstevel /*
16721341Sstevel  * spur_long_timeout
16731341Sstevel  *
16741341Sstevel  * this routine merely resets the spurious interrupt counter thus ending
16751341Sstevel  * the interval of interest.  of course this is done by triggering a
16761341Sstevel  * softint because the counter is protected by an interrupt mutex.
16771341Sstevel  */
16781341Sstevel static void
spur_long_timeout(void * arg)16791341Sstevel spur_long_timeout(void *arg)
16801341Sstevel {
16811341Sstevel 	struct sysctrl_soft_state *softsp = arg;
16821341Sstevel 
16831341Sstevel 	ASSERT(softsp);
16841341Sstevel 
16851341Sstevel 	ddi_trigger_softintr(softsp->spur_long_to_id);
16861341Sstevel 
16871341Sstevel 	mutex_enter(&softsp->spur_int_lock);
16881341Sstevel 	softsp->spur_long_timeout_id = 0;
16891341Sstevel 	mutex_exit(&softsp->spur_int_lock);
16901341Sstevel }
16911341Sstevel 
16921341Sstevel /*
16931341Sstevel  * spur_clear_count
16941341Sstevel  *
16951341Sstevel  * simply clear out the spurious interrupt counter.
16961341Sstevel  *
16971341Sstevel  * softint level only
16981341Sstevel  */
16991341Sstevel static uint_t
spur_clear_count(caddr_t arg)17001341Sstevel spur_clear_count(caddr_t arg)
17011341Sstevel {
17021341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
17031341Sstevel 
17041341Sstevel 	ASSERT(softsp);
17051341Sstevel 
17061341Sstevel 	mutex_enter(&softsp->csr_mutex);
17071341Sstevel 	softsp->spur_count = 0;
17081341Sstevel 	mutex_exit(&softsp->csr_mutex);
17091341Sstevel 
17101341Sstevel 	return (DDI_INTR_CLAIMED);
17111341Sstevel }
17121341Sstevel 
17131341Sstevel /*
17141341Sstevel  * ac_fail_handler
17151341Sstevel  *
17161341Sstevel  * This routine polls the AC power failure bit in the system status2
17171341Sstevel  * register.  If we get to this routine, then we sensed an ac fail
17181341Sstevel  * condition.  Note the fact and check again in a few.
17191341Sstevel  *
17201341Sstevel  * Called as softint from high interrupt.
17211341Sstevel  */
17221341Sstevel static uint_t
ac_fail_handler(caddr_t arg)17231341Sstevel ac_fail_handler(caddr_t arg)
17241341Sstevel {
17251341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
17261341Sstevel 
17271341Sstevel 	ASSERT(softsp);
17281341Sstevel 
17291341Sstevel 	cmn_err(CE_WARN, "%s failure detected", ft_str_table[FT_AC_PWR]);
17301341Sstevel 	reg_fault(0, FT_AC_PWR, FT_SYSTEM);
17311341Sstevel 	(void) timeout(ac_fail_retry, softsp, ac_timeout_hz);
17321341Sstevel 
17331341Sstevel 	return (DDI_INTR_CLAIMED);
17341341Sstevel }
17351341Sstevel 
17361341Sstevel /*
17371341Sstevel  * The timeout from ac_fail_handler() that checks to see if the
17381341Sstevel  * condition persists.
17391341Sstevel  */
17401341Sstevel static void
ac_fail_retry(void * arg)17411341Sstevel ac_fail_retry(void *arg)
17421341Sstevel {
17431341Sstevel 	struct sysctrl_soft_state *softsp = arg;
17441341Sstevel 
17451341Sstevel 	ASSERT(softsp);
17461341Sstevel 
17471341Sstevel 	if (*softsp->status2 & SYS_AC_FAIL) {	/* still bad? */
17481341Sstevel 		(void) timeout(ac_fail_retry, softsp, ac_timeout_hz);
17491341Sstevel 	} else {
17501341Sstevel 		cmn_err(CE_NOTE, "%s failure no longer detected",
17517656SSherry.Moore@Sun.COM 		    ft_str_table[FT_AC_PWR]);
17521341Sstevel 		clear_fault(0, FT_AC_PWR, FT_SYSTEM);
17531341Sstevel 		ddi_trigger_softintr(softsp->ac_fail_high_id);
17541341Sstevel 	}
17551341Sstevel }
17561341Sstevel 
17571341Sstevel /*
17581341Sstevel  * The interrupt routine that we use to re-enable the interrupt.
17591341Sstevel  * Called from ddi_trigger_softint() in the ac_fail_retry() when
17601341Sstevel  * the AC is better.
17611341Sstevel  */
17621341Sstevel static uint_t
ac_fail_reenable(caddr_t arg)17631341Sstevel ac_fail_reenable(caddr_t arg)
17641341Sstevel {
17651341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
17661341Sstevel 	uchar_t tmp_reg;
17671341Sstevel 
17681341Sstevel 	ASSERT(softsp);
17691341Sstevel 
17701341Sstevel 	mutex_enter(&softsp->csr_mutex);
17711341Sstevel 	*(softsp->csr) |= SYS_AC_PWR_FAIL_EN;
17721341Sstevel 	tmp_reg = *(softsp->csr);
17731341Sstevel #ifdef lint
17741341Sstevel 	tmp_reg = tmp_reg;
17751341Sstevel #endif
17761341Sstevel 	mutex_exit(&softsp->csr_mutex);
17771341Sstevel 
17781341Sstevel 	return (DDI_INTR_CLAIMED);
17791341Sstevel }
17801341Sstevel 
17811341Sstevel /*
17821341Sstevel  * ps_fail_int_handler
17831341Sstevel  *
17841341Sstevel  * Handle power supply failure interrupt.
17851341Sstevel  *
17861341Sstevel  * This wrapper is called as softint from hardware interrupt routine.
17871341Sstevel  */
17881341Sstevel static uint_t
ps_fail_int_handler(caddr_t arg)17891341Sstevel ps_fail_int_handler(caddr_t arg)
17901341Sstevel {
17911341Sstevel 	return (ps_fail_handler((struct sysctrl_soft_state *)arg, 1));
17921341Sstevel }
17931341Sstevel 
17941341Sstevel /*
17951341Sstevel  * ps_fail_poll_handler
17961341Sstevel  *
17971341Sstevel  * Handle power supply failure interrupt.
17981341Sstevel  *
17991341Sstevel  * This wrapper is called as softint from power supply poll routine.
18001341Sstevel  */
18011341Sstevel static uint_t
ps_fail_poll_handler(caddr_t arg)18021341Sstevel ps_fail_poll_handler(caddr_t arg)
18031341Sstevel {
18041341Sstevel 	return (ps_fail_handler((struct sysctrl_soft_state *)arg, 0));
18051341Sstevel }
18061341Sstevel 
18071341Sstevel /*
18081341Sstevel  * ps_fail_handler
18091341Sstevel  *
18101341Sstevel  * This routine checks all eight of the board power supplies that are
18111341Sstevel  * installed plus the Peripheral power supply and the two DC OK. Since the
18121341Sstevel  * hardware bits are not enough to indicate Power Supply failure
18131341Sstevel  * vs. being turned off via software, the driver must maintain a
18141341Sstevel  * shadow state for the Power Supply status and monitor all changes.
18151341Sstevel  *
18161341Sstevel  * Called as a softint only.
18171341Sstevel  */
18181341Sstevel static uint_t
ps_fail_handler(struct sysctrl_soft_state * softsp,int fromint)18191341Sstevel ps_fail_handler(struct sysctrl_soft_state *softsp, int fromint)
18201341Sstevel {
18211341Sstevel 	int i;
18221341Sstevel 	struct ps_state *pstatp;
18231341Sstevel 	int poll_needed = 0;
18241341Sstevel 	uchar_t ps_stat, ps_pres, status1, status2, pppsr;
18251341Sstevel 	uchar_t tmp_reg;
18261341Sstevel 	enum power_state current_power_state;
18271341Sstevel 
18281341Sstevel 	ASSERT(softsp);
18291341Sstevel 
18301341Sstevel 	/* pre-read the hardware state */
18311341Sstevel 	ps_stat = *softsp->ps_stat;
18321341Sstevel 	ps_pres = *softsp->ps_pres;
18331341Sstevel 	status1 = *softsp->status1;
18341341Sstevel 	status2 = *softsp->status2;
18351341Sstevel 	pppsr	= *softsp->pppsr;
18361341Sstevel 
18371341Sstevel 	(void) fhc_bdlist_lock(-1);
18381341Sstevel 
18391341Sstevel 	mutex_enter(&softsp->ps_fail_lock);
18401341Sstevel 
18411341Sstevel 	for (i = 0, pstatp = &softsp->ps_stats[0]; i < SYS_PS_COUNT;
18421341Sstevel 	    i++, pstatp++) {
18431341Sstevel 		int	temp_psok;
18441341Sstevel 		int	temp_pres;
18451341Sstevel 		int	is_precharge = FALSE;
18461341Sstevel 		int	is_fan_assy = FALSE;
18471341Sstevel 
18481341Sstevel 		/*
18491341Sstevel 		 * pre-compute the presence and ok bits for this
18501341Sstevel 		 * power supply from the hardware registers.
18511341Sstevel 		 * NOTE: 4-slot pps1 is the same as core ps 7...
18521341Sstevel 		 */
18531341Sstevel 		switch (i) {
18541341Sstevel 		/* the core power supplies */
18551341Sstevel 		case 0: case 1: case 2: case 3:
18561341Sstevel 		case 4: case 5: case 6: case 7:
18571341Sstevel 			temp_pres = !((ps_pres >> i) & 0x1);
18581341Sstevel 			temp_psok = (ps_stat >> i) & 0x1;
18591341Sstevel 			break;
18601341Sstevel 
18611341Sstevel 		/* the first peripheral power supply */
18621341Sstevel 		case SYS_PPS0_INDEX:
18631341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
18641341Sstevel 			temp_psok = status2 & SYS_PPS0_OK;
18651341Sstevel 			break;
18661341Sstevel 
18671341Sstevel 		/* shared 3.3v clock power */
18681341Sstevel 		case SYS_CLK_33_INDEX:
18691341Sstevel 			temp_pres = TRUE;
18701341Sstevel 			temp_psok = status2 & SYS_CLK_33_OK;
18711341Sstevel 			break;
18721341Sstevel 
18731341Sstevel 		/* shared 5.0v clock power */
18741341Sstevel 		case SYS_CLK_50_INDEX:
18751341Sstevel 			temp_pres = TRUE;
18761341Sstevel 			temp_psok = status2 & SYS_CLK_50_OK;
18771341Sstevel 			break;
18781341Sstevel 
18791341Sstevel 		/* peripheral 5v */
18801341Sstevel 		case SYS_V5_P_INDEX:
18811341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES) ||
18827656SSherry.Moore@Sun.COM 			    ((IS4SLOT(softsp->nslots) ||
18837656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) &&
18847656SSherry.Moore@Sun.COM 			    !(ps_pres & SYS_NOT_PPS1_PRES));
18851341Sstevel 			temp_psok = pppsr & SYS_V5_P_OK;
18861341Sstevel 			break;
18871341Sstevel 
18881341Sstevel 		/* peripheral 12v */
18891341Sstevel 		case SYS_V12_P_INDEX:
18901341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES) ||
18917656SSherry.Moore@Sun.COM 			    ((IS4SLOT(softsp->nslots) ||
18927656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) &&
18937656SSherry.Moore@Sun.COM 			    !(ps_pres & SYS_NOT_PPS1_PRES));
18941341Sstevel 			temp_psok = pppsr & SYS_V12_P_OK;
18951341Sstevel 			break;
18961341Sstevel 
18971341Sstevel 		/* aux 5v */
18981341Sstevel 		case SYS_V5_AUX_INDEX:
18991341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
19001341Sstevel 			temp_psok = pppsr & SYS_V5_AUX_OK;
19011341Sstevel 			break;
19021341Sstevel 
19031341Sstevel 		/* peripheral 5v precharge */
19041341Sstevel 		case SYS_V5_P_PCH_INDEX:
19051341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
19061341Sstevel 			temp_psok = pppsr & SYS_V5_P_PCH_OK;
19071341Sstevel 			is_precharge = TRUE;
19081341Sstevel 			break;
19091341Sstevel 
19101341Sstevel 		/* peripheral 12v precharge */
19111341Sstevel 		case SYS_V12_P_PCH_INDEX:
19121341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
19131341Sstevel 			temp_psok = pppsr & SYS_V12_P_PCH_OK;
19141341Sstevel 			is_precharge = TRUE;
19151341Sstevel 			break;
19161341Sstevel 
19171341Sstevel 		/* 3.3v precharge */
19181341Sstevel 		case SYS_V3_PCH_INDEX:
19191341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
19201341Sstevel 			temp_psok = pppsr & SYS_V3_PCH_OK;
19211341Sstevel 			is_precharge = TRUE;
19221341Sstevel 			break;
19231341Sstevel 
19241341Sstevel 		/* 5v precharge */
19251341Sstevel 		case SYS_V5_PCH_INDEX:
19261341Sstevel 			temp_pres = !(status1 & SYS_NOT_PPS0_PRES);
19271341Sstevel 			temp_psok = pppsr & SYS_V5_PCH_OK;
19281341Sstevel 			is_precharge = TRUE;
19291341Sstevel 			break;
19301341Sstevel 
19311341Sstevel 		/* peripheral fan assy */
19321341Sstevel 		case SYS_P_FAN_INDEX:
19331341Sstevel 			temp_pres = (IS4SLOT(softsp->nslots) ||
19347656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) &&
19357656SSherry.Moore@Sun.COM 			    !(status1 & SYS_NOT_P_FAN_PRES);
19361341Sstevel 			temp_psok = softsp->pps_fan_saved &
19377656SSherry.Moore@Sun.COM 			    SYS_AC_FAN_OK;
19381341Sstevel 			is_fan_assy = TRUE;
19391341Sstevel 			break;
19401341Sstevel 		}
19411341Sstevel 
19421341Sstevel 		/* *** Phase 1 -- power supply presence tests *** */
19431341Sstevel 
19441341Sstevel 		/* do we know the presence status for this power supply? */
19451341Sstevel 		if (pstatp->pshadow == PRES_UNKNOWN) {
19461341Sstevel 			pstatp->pshadow = temp_pres ? PRES_IN : PRES_OUT;
19471341Sstevel 			pstatp->dcshadow = temp_pres ? PS_BOOT : PS_OUT;
19481341Sstevel 		} else {
19491341Sstevel 			/* has the ps presence state changed? */
19501341Sstevel 			if (!temp_pres ^ (pstatp->pshadow == PRES_IN)) {
19511341Sstevel 				pstatp->pctr = 0;
19521341Sstevel 			} else {
19531341Sstevel 				/* a change! are we counting? */
19541341Sstevel 				if (pstatp->pctr == 0) {
19551341Sstevel 					pstatp->pctr = PS_PRES_CHANGE_TICKS;
19561341Sstevel 				} else if (--pstatp->pctr == 0) {
19571341Sstevel 					pstatp->pshadow = temp_pres ?
19587656SSherry.Moore@Sun.COM 					    PRES_IN : PRES_OUT;
19591341Sstevel 					pstatp->dcshadow = temp_pres ?
19607656SSherry.Moore@Sun.COM 					    PS_UNKNOWN : PS_OUT;
19611341Sstevel 
19621341Sstevel 					/*
19631341Sstevel 					 * Now we know the state has
19641341Sstevel 					 * changed, so we should log it.
19651341Sstevel 					 */
19661341Sstevel 					ps_log_pres_change(softsp,
19677656SSherry.Moore@Sun.COM 					    i, temp_pres);
19681341Sstevel 				}
19691341Sstevel 			}
19701341Sstevel 		}
19711341Sstevel 
19721341Sstevel 		/* *** Phase 2 -- power supply status tests *** */
19731341Sstevel 
19741341Sstevel 		/* check if the Power Supply is removed or same as before */
19751341Sstevel 		if ((pstatp->dcshadow == PS_OUT) ||
19761341Sstevel 		    ((pstatp->dcshadow == PS_OK) && temp_psok) ||
19771341Sstevel 		    ((pstatp->dcshadow == PS_FAIL) && !temp_psok)) {
19781341Sstevel 			pstatp->dcctr = 0;
19791341Sstevel 		} else {
19801341Sstevel 
19811341Sstevel 			/* OK, a change, do we start the timer? */
19821341Sstevel 			if (pstatp->dcctr == 0) {
19831341Sstevel 				switch (pstatp->dcshadow) {
19841341Sstevel 				case PS_BOOT:
19851341Sstevel 					pstatp->dcctr = PS_FROM_BOOT_TICKS;
19861341Sstevel 					break;
19871341Sstevel 
19881341Sstevel 				case PS_UNKNOWN:
19891341Sstevel 					pstatp->dcctr = is_fan_assy ?
19907656SSherry.Moore@Sun.COM 					    PS_P_FAN_FROM_UNKNOWN_TICKS :
19917656SSherry.Moore@Sun.COM 					    PS_FROM_UNKNOWN_TICKS;
19921341Sstevel 					break;
19931341Sstevel 
19941341Sstevel 				case PS_OK:
19951341Sstevel 					pstatp->dcctr = is_precharge ?
19967656SSherry.Moore@Sun.COM 					    PS_PCH_FROM_OK_TICKS :
19977656SSherry.Moore@Sun.COM 					    PS_FROM_OK_TICKS;
19981341Sstevel 					break;
19991341Sstevel 
20001341Sstevel 				case PS_FAIL:
20011341Sstevel 					pstatp->dcctr = PS_FROM_FAIL_TICKS;
20021341Sstevel 					break;
20031341Sstevel 
20041341Sstevel 				default:
20051341Sstevel 					panic("sysctrl%d: Unknown Power "
20061341Sstevel 					    "Supply State %d", pstatp->dcshadow,
20071341Sstevel 					    ddi_get_instance(softsp->dip));
20081341Sstevel 				}
20091341Sstevel 			}
20101341Sstevel 
20111341Sstevel 			/* has the ticker expired? */
20121341Sstevel 			if (--pstatp->dcctr == 0) {
20131341Sstevel 
20141341Sstevel 				/* we'll skip OK messages during boot */
20151341Sstevel 				if (!((pstatp->dcshadow == PS_BOOT) &&
20161341Sstevel 				    temp_psok)) {
20171341Sstevel 					ps_log_state_change(softsp,
20187656SSherry.Moore@Sun.COM 					    i, temp_psok);
20191341Sstevel 				}
20201341Sstevel 
20211341Sstevel 				/*
20221341Sstevel 				 * remote console interface has to be
20231341Sstevel 				 * reinitialized on the rising edge V5_AUX
20241341Sstevel 				 * when it is NOT boot. At the boot time an
20251341Sstevel 				 * an error condition exists if it was not
20261341Sstevel 				 * enabled before.
20271341Sstevel 				 */
20281341Sstevel 				if ((i == SYS_V5_AUX_INDEX) &&
20291341Sstevel 				    (pstatp->dcshadow != PS_BOOT) &&
20301341Sstevel 				    (softsp->enable_rcons_atboot)) {
20311341Sstevel 					if (temp_psok)
20321341Sstevel 						rcons_reinit(softsp);
20331341Sstevel 					else
20341341Sstevel 						/* disable rconsole */
20351341Sstevel 						*(softsp->clk_freq2) &=
20361341Sstevel 						    ~RCONS_UART_EN;
20371341Sstevel 					tmp_reg = *(softsp->csr);
20381341Sstevel #ifdef lint
20391341Sstevel 					tmp_reg = tmp_reg;
20401341Sstevel #endif
20411341Sstevel 
20421341Sstevel 				}
20431341Sstevel 
20441341Sstevel 				/* regardless, update the shadow state */
20451341Sstevel 				pstatp->dcshadow = temp_psok ? PS_OK : PS_FAIL;
20461341Sstevel 
20471341Sstevel 				/* always update board condition */
20481341Sstevel 				sysc_policy_update(softsp, NULL,
20497656SSherry.Moore@Sun.COM 				    SYSC_EVT_BD_PS_CHANGE);
20501341Sstevel 
20511341Sstevel 			}
20521341Sstevel 		}
20531341Sstevel 
20541341Sstevel 		/*
20551341Sstevel 		 * We will need to continue polling for three reasons:
20561341Sstevel 		 * - a failing power supply is detected and we haven't yet
20571341Sstevel 		 *   determined the power supplies existence.
20581341Sstevel 		 * - the power supply is just installed and we're waiting
20591341Sstevel 		 *   to give it a change to power up,
20601341Sstevel 		 * - a failed power supply state is recognized
20611341Sstevel 		 *
20621341Sstevel 		 * NOTE: PS_FAIL shadow state is not the same as !temp_psok
20631341Sstevel 		 * because of the persistence of PS_FAIL->PS_OK.
20641341Sstevel 		 */
20651341Sstevel 		if (!temp_psok ||
20661341Sstevel 		    (pstatp->dcshadow == PS_UNKNOWN) ||
20671341Sstevel 		    (pstatp->dcshadow == PS_FAIL)) {
20681341Sstevel 			poll_needed++;
20691341Sstevel 		}
20701341Sstevel 	}
20711341Sstevel 
20721341Sstevel 	/*
20731341Sstevel 	 * Now, get the current power state for this instance.
20741341Sstevel 	 * If the current state is different than what was known, complain.
20751341Sstevel 	 */
20761341Sstevel 	current_power_state = compute_power_state(softsp, 0);
20771341Sstevel 
20781341Sstevel 	if (softsp->power_state != current_power_state) {
20791341Sstevel 		switch (current_power_state) {
20801341Sstevel 		case BELOW_MINIMUM:
20811341Sstevel 			cmn_err(CE_WARN,
20827656SSherry.Moore@Sun.COM 			    "Insufficient power available to system");
20831341Sstevel 			if (!disable_insufficient_power_reboot) {
20841341Sstevel 				cmn_err(CE_WARN, "System reboot in %d seconds",
20857656SSherry.Moore@Sun.COM 				    PS_INSUFFICIENT_COUNTDOWN_SEC);
20861341Sstevel 			}
20871341Sstevel 			reg_fault(1, FT_INSUFFICIENT_POWER, FT_SYSTEM);
20881341Sstevel 			softsp->power_countdown = PS_POWER_COUNTDOWN_TICKS;
20891341Sstevel 			break;
20901341Sstevel 
20911341Sstevel 		case MINIMUM:
20921341Sstevel 			/* If we came from REDUNDANT, complain */
20931341Sstevel 			if (softsp->power_state == REDUNDANT) {
20941341Sstevel 				cmn_err(CE_WARN, "Redundant power lost");
20951341Sstevel 			/* If we came from BELOW_MINIMUM, hurrah! */
20961341Sstevel 			} else if (softsp->power_state == BELOW_MINIMUM) {
20971341Sstevel 				cmn_err(CE_NOTE, "Minimum power available");
20981341Sstevel 				clear_fault(1, FT_INSUFFICIENT_POWER,
20997656SSherry.Moore@Sun.COM 				    FT_SYSTEM);
21001341Sstevel 			}
21011341Sstevel 			break;
21021341Sstevel 
21031341Sstevel 		case REDUNDANT:
21041341Sstevel 			/* If we aren't from boot, spread the good news */
21051341Sstevel 			if (softsp->power_state != BOOT) {
21061341Sstevel 				cmn_err(CE_NOTE, "Redundant power available");
21071341Sstevel 				clear_fault(1, FT_INSUFFICIENT_POWER,
21087656SSherry.Moore@Sun.COM 				    FT_SYSTEM);
21091341Sstevel 			}
21101341Sstevel 			break;
21111341Sstevel 
21121341Sstevel 		default:
21131341Sstevel 			break;
21141341Sstevel 		}
21151341Sstevel 		softsp->power_state = current_power_state;
21161341Sstevel 		sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
21171341Sstevel 	}
21181341Sstevel 
21191341Sstevel 	mutex_exit(&softsp->ps_fail_lock);
21201341Sstevel 
21211341Sstevel 	fhc_bdlist_unlock();
21221341Sstevel 
21231341Sstevel 	/*
21241341Sstevel 	 * Are we in insufficient powerstate?
21251341Sstevel 	 * If so, is it time to take action?
21261341Sstevel 	 */
21271341Sstevel 	if (softsp->power_state == BELOW_MINIMUM &&
21281341Sstevel 	    softsp->power_countdown > 0 && --(softsp->power_countdown) == 0 &&
21291341Sstevel 	    !disable_insufficient_power_reboot) {
21301341Sstevel 		cmn_err(CE_WARN,
21311341Sstevel 		    "Insufficient power. System Reboot Started...");
21321341Sstevel 
21331341Sstevel 		fhc_reboot();
21341341Sstevel 	}
21351341Sstevel 
21361341Sstevel 	/*
21371341Sstevel 	 * If we don't have ps problems that need to be polled for, then
21381341Sstevel 	 * enable interrupts.
21391341Sstevel 	 */
21401341Sstevel 	if (!poll_needed) {
21411341Sstevel 		mutex_enter(&softsp->csr_mutex);
21421341Sstevel 		*(softsp->csr) |= SYS_PS_FAIL_EN;
21431341Sstevel 		tmp_reg = *(softsp->csr);
21441341Sstevel #ifdef lint
21451341Sstevel 		tmp_reg = tmp_reg;
21461341Sstevel #endif
21471341Sstevel 		mutex_exit(&softsp->csr_mutex);
21481341Sstevel 	}
21491341Sstevel 
21501341Sstevel 	/*
21511341Sstevel 	 * Only the polling loop re-triggers the polling loop timeout
21521341Sstevel 	 */
21531341Sstevel 	if (!fromint) {
21541341Sstevel 		(void) timeout(ps_fail_retry, softsp, ps_fail_timeout_hz);
21551341Sstevel 	}
21561341Sstevel 
21571341Sstevel 	return (DDI_INTR_CLAIMED);
21581341Sstevel }
21591341Sstevel 
21601341Sstevel /*
21611341Sstevel  * Compute the current power configuration for this system.
21621341Sstevel  * Disk boards and Clock boards are not counted.
21631341Sstevel  *
21641341Sstevel  * This function must be called with the ps_fail_lock held.
21651341Sstevel  */
21661341Sstevel enum power_state
compute_power_state(struct sysctrl_soft_state * softsp,int plus_load)21671341Sstevel compute_power_state(struct sysctrl_soft_state *softsp, int plus_load)
21681341Sstevel {
21691341Sstevel 	int i;
21701341Sstevel 	int ok_supply_count = 0;
21711341Sstevel 	int load_count = 0;
21721341Sstevel 	int minimum_power_count;
21731341Sstevel 	int pps_ok;
21741341Sstevel 	fhc_bd_t *list;
21751341Sstevel 
21761341Sstevel 	ASSERT(mutex_owned(&softsp->ps_fail_lock));
21771341Sstevel 
21781341Sstevel 	/*
21791341Sstevel 	 * Walk down the interesting power supplies and
21801341Sstevel 	 * count the operational power units
21811341Sstevel 	 */
21821341Sstevel 	for (i = 0; i < 8; i++) {
21831341Sstevel 		/*
21841341Sstevel 		 * power supply id 7 on a 4 or 5 slot system is PPS1.
21851341Sstevel 		 * don't include it in the redundant core power calculation.
21861341Sstevel 		 */
21871341Sstevel 		if (i == 7 &&
21881341Sstevel 		    (IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)))
21891341Sstevel 			continue;
21901341Sstevel 
21911341Sstevel 		if (softsp->ps_stats[i].dcshadow == PS_OK)
21921341Sstevel 			ok_supply_count++;
21931341Sstevel 	}
21941341Sstevel 
21951341Sstevel 	/* Note the state of the PPS... */
21961341Sstevel 	pps_ok = (softsp->ps_stats[SYS_PPS0_INDEX].dcshadow == PS_OK);
21971341Sstevel 
21981341Sstevel 	/*
21991341Sstevel 	 * Dynamically compute the load count in the system.
22001341Sstevel 	 * Don't count disk boards or boards in low power state.
22011341Sstevel 	 */
22021341Sstevel 	for (list = fhc_bd_first(); list; list = fhc_bd_next(list)) {
22031341Sstevel 		ASSERT(list->sc.type != CLOCK_BOARD);
22041341Sstevel 		if (list->sc.rstate == SYSC_CFGA_RSTATE_CONNECTED) {
22051341Sstevel 			load_count++;
22061341Sstevel 		}
22071341Sstevel 	}
22081341Sstevel 
22091341Sstevel 	load_count += plus_load;
22101341Sstevel 	/*
22111341Sstevel 	 * If we are 8 slot and we have 7 or 8 boards, then the PPS
22121341Sstevel 	 * can count as a power supply...
22131341Sstevel 	 */
22141341Sstevel 	if (IS8SLOT(softsp->nslots) && load_count >= 7 && pps_ok)
22151341Sstevel 		ok_supply_count++;
22161341Sstevel 
22171341Sstevel 	/*
22181341Sstevel 	 * This is to cover the corner case of a UE3500 having 5
22191341Sstevel 	 * boards installed and still giving it N+1 power status.
22201341Sstevel 	 */
22211341Sstevel 	if (IS5SLOT(softsp->nslots) && (load_count >= 5))
22221341Sstevel 		ok_supply_count++;
22231341Sstevel 
22241341Sstevel 	/*
22251341Sstevel 	 * Determine our power situation.  This is a simple step
22261341Sstevel 	 * function right now:
22271341Sstevel 	 *
22281341Sstevel 	 * minimum power count = min(7, floor((board count + 1) / 2))
22291341Sstevel 	 */
22301341Sstevel 	minimum_power_count = (load_count + 1) / 2;
22311341Sstevel 	if (minimum_power_count > 7)
22321341Sstevel 		minimum_power_count = 7;
22331341Sstevel 
22341341Sstevel 	if (ok_supply_count > minimum_power_count)
22351341Sstevel 		return (REDUNDANT);
22361341Sstevel 	else if (ok_supply_count == minimum_power_count)
22371341Sstevel 		return (MINIMUM);
22381341Sstevel 	else
22391341Sstevel 		return (BELOW_MINIMUM);
22401341Sstevel }
22411341Sstevel 
22421341Sstevel /*
22431341Sstevel  * log the change of power supply presence
22441341Sstevel  */
22451341Sstevel static void
ps_log_pres_change(struct sysctrl_soft_state * softsp,int index,int present)22461341Sstevel ps_log_pres_change(struct sysctrl_soft_state *softsp, int index, int present)
22471341Sstevel {
22481341Sstevel 	char	*trans = present ? "Installed" : "Removed";
22491341Sstevel 
22501341Sstevel 	switch (index) {
22511341Sstevel 	/* the core power supplies (except for 7) */
22521341Sstevel 	case 0: case 1: case 2: case 3:
22531341Sstevel 	case 4: case 5: case 6:
22541341Sstevel 		cmn_err(CE_NOTE, "%s %d %s", ft_str_table[FT_CORE_PS], index,
22551341Sstevel 		    trans);
22561341Sstevel 		if (!present) {
22577656SSherry.Moore@Sun.COM 			clear_fault(index, FT_CORE_PS, FT_SYSTEM);
22587656SSherry.Moore@Sun.COM 			sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
22591341Sstevel 		}
22601341Sstevel 		break;
22611341Sstevel 
22621341Sstevel 	/* power supply 7 / pps 1 */
22631341Sstevel 	case 7:
22641341Sstevel 		if (IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)) {
22657656SSherry.Moore@Sun.COM 			cmn_err(CE_NOTE, "%s 1 %s", ft_str_table[FT_PPS],
22667656SSherry.Moore@Sun.COM 			    trans);
22677656SSherry.Moore@Sun.COM 			if (!present) {
22681341Sstevel 			clear_fault(1, FT_PPS, FT_SYSTEM);
22697656SSherry.Moore@Sun.COM 			}
22701341Sstevel 		} else {
22717656SSherry.Moore@Sun.COM 			cmn_err(CE_NOTE, "%s %d %s", ft_str_table[FT_CORE_PS],
22727656SSherry.Moore@Sun.COM 			    index, trans);
22737656SSherry.Moore@Sun.COM 			if (!present) {
22741341Sstevel 			clear_fault(7, FT_CORE_PS, FT_SYSTEM);
22751341Sstevel 			sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
22767656SSherry.Moore@Sun.COM 			}
22771341Sstevel 		}
22781341Sstevel 		break;
22791341Sstevel 
22801341Sstevel 	/* the peripheral power supply 0 */
22811341Sstevel 	case SYS_PPS0_INDEX:
22821341Sstevel 		cmn_err(CE_NOTE, "%s 0 %s", ft_str_table[FT_PPS], trans);
22831341Sstevel 		if (!present) {
22841341Sstevel 			clear_fault(0, FT_PPS, FT_SYSTEM);
22851341Sstevel 			sysc_policy_update(softsp, NULL, SYSC_EVT_BD_PS_CHANGE);
22861341Sstevel 		}
22871341Sstevel 		break;
22881341Sstevel 
22891341Sstevel 	/* the peripheral rack fan assy */
22901341Sstevel 	case SYS_P_FAN_INDEX:
22911341Sstevel 		cmn_err(CE_NOTE, "%s %s", ft_str_table[FT_PPS_FAN], trans);
22921341Sstevel 		if (!present) {
22931341Sstevel 			clear_fault(0, FT_PPS_FAN, FT_SYSTEM);
22941341Sstevel 		}
22951341Sstevel 		break;
22961341Sstevel 
22971341Sstevel 	/* we don't mention a change of presence state for any other power */
22981341Sstevel 	}
22991341Sstevel }
23001341Sstevel 
23011341Sstevel /*
23021341Sstevel  * log the change of power supply status
23031341Sstevel  */
23041341Sstevel static void
ps_log_state_change(struct sysctrl_soft_state * softsp,int index,int ps_ok)23051341Sstevel ps_log_state_change(struct sysctrl_soft_state *softsp, int index, int ps_ok)
23061341Sstevel {
23071341Sstevel 	int level = ps_ok ? CE_NOTE : CE_WARN;
23081341Sstevel 	char *s = ps_ok ? "OK" : "Failing";
23091341Sstevel 
23101341Sstevel 	switch (index) {
23111341Sstevel 	/* the core power supplies (except 7) */
23121341Sstevel 	case 0: case 1: case 2: case 3:
23131341Sstevel 	case 4: case 5: case 6:
23141341Sstevel 		cmn_err(level, "%s %d %s", ft_str_table[FT_CORE_PS], index, s);
23151341Sstevel 		if (ps_ok) {
23161341Sstevel 			clear_fault(index, FT_CORE_PS, FT_SYSTEM);
23171341Sstevel 		} else {
23181341Sstevel 			reg_fault(index, FT_CORE_PS, FT_SYSTEM);
23191341Sstevel 		}
23201341Sstevel 		break;
23211341Sstevel 
23221341Sstevel 	/* power supply 7 / pps 1 */
23231341Sstevel 	case 7:
23241341Sstevel 		if (IS4SLOT(softsp->nslots) || IS5SLOT(softsp->nslots)) {
23251341Sstevel 			cmn_err(level, "%s 1 %s", ft_str_table[FT_PPS], s);
23261341Sstevel 			if (ps_ok) {
23271341Sstevel 				clear_fault(1, FT_PPS, FT_SYSTEM);
23281341Sstevel 			} else {
23291341Sstevel 				reg_fault(1, FT_PPS, FT_SYSTEM);
23301341Sstevel 			}
23311341Sstevel 		} else {
23321341Sstevel 			cmn_err(level, "%s %d %s", ft_str_table[FT_CORE_PS],
23337656SSherry.Moore@Sun.COM 			    index, s);
23341341Sstevel 			if (ps_ok) {
23351341Sstevel 				clear_fault(index, FT_CORE_PS, FT_SYSTEM);
23361341Sstevel 			} else {
23371341Sstevel 				reg_fault(index, FT_CORE_PS, FT_SYSTEM);
23381341Sstevel 			}
23391341Sstevel 		}
23401341Sstevel 		break;
23411341Sstevel 
23421341Sstevel 	/* the peripheral power supply */
23431341Sstevel 	case SYS_PPS0_INDEX:
23441341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_PPS], s);
23451341Sstevel 		if (ps_ok) {
23461341Sstevel 			clear_fault(0, FT_PPS, FT_SYSTEM);
23471341Sstevel 		} else {
23481341Sstevel 			reg_fault(0, FT_PPS, FT_SYSTEM);
23491341Sstevel 		}
23501341Sstevel 		break;
23511341Sstevel 
23521341Sstevel 	/* shared 3.3v clock power */
23531341Sstevel 	case SYS_CLK_33_INDEX:
23541341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_CLK_33], s);
23551341Sstevel 		if (ps_ok) {
23561341Sstevel 			clear_fault(0, FT_CLK_33, FT_SYSTEM);
23571341Sstevel 		} else {
23581341Sstevel 			reg_fault(0, FT_CLK_33, FT_SYSTEM);
23591341Sstevel 		}
23601341Sstevel 		break;
23611341Sstevel 
23621341Sstevel 	/* shared 5.0v clock power */
23631341Sstevel 	case SYS_CLK_50_INDEX:
23641341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_CLK_50], s);
23651341Sstevel 		if (ps_ok) {
23661341Sstevel 			clear_fault(0, FT_CLK_50, FT_SYSTEM);
23671341Sstevel 		} else {
23681341Sstevel 			reg_fault(0, FT_CLK_50, FT_SYSTEM);
23691341Sstevel 		}
23701341Sstevel 		break;
23711341Sstevel 
23721341Sstevel 	/* peripheral 5v */
23731341Sstevel 	case SYS_V5_P_INDEX:
23741341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V5_P], s);
23751341Sstevel 		if (ps_ok) {
23761341Sstevel 			clear_fault(0, FT_V5_P, FT_SYSTEM);
23771341Sstevel 		} else {
23781341Sstevel 			reg_fault(0, FT_V5_P, FT_SYSTEM);
23791341Sstevel 		}
23801341Sstevel 		break;
23811341Sstevel 
23821341Sstevel 	/* peripheral 12v */
23831341Sstevel 	case SYS_V12_P_INDEX:
23841341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V12_P], s);
23851341Sstevel 		if (ps_ok) {
23861341Sstevel 			clear_fault(0, FT_V12_P, FT_SYSTEM);
23871341Sstevel 		} else {
23881341Sstevel 			reg_fault(0, FT_V12_P, FT_SYSTEM);
23891341Sstevel 		}
23901341Sstevel 		break;
23911341Sstevel 
23921341Sstevel 	/* aux 5v */
23931341Sstevel 	case SYS_V5_AUX_INDEX:
23941341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V5_AUX], s);
23951341Sstevel 		if (ps_ok) {
23961341Sstevel 			clear_fault(0, FT_V5_AUX, FT_SYSTEM);
23971341Sstevel 		} else {
23981341Sstevel 			reg_fault(0, FT_V5_AUX, FT_SYSTEM);
23991341Sstevel 		}
24001341Sstevel 		break;
24011341Sstevel 
24021341Sstevel 	/* peripheral 5v precharge */
24031341Sstevel 	case SYS_V5_P_PCH_INDEX:
24041341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V5_P_PCH], s);
24051341Sstevel 		if (ps_ok) {
24061341Sstevel 			clear_fault(0, FT_V5_P_PCH, FT_SYSTEM);
24071341Sstevel 		} else {
24081341Sstevel 			reg_fault(0, FT_V5_P_PCH, FT_SYSTEM);
24091341Sstevel 		}
24101341Sstevel 		break;
24111341Sstevel 
24121341Sstevel 	/* peripheral 12v precharge */
24131341Sstevel 	case SYS_V12_P_PCH_INDEX:
24141341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V12_P_PCH], s);
24151341Sstevel 		if (ps_ok) {
24161341Sstevel 			clear_fault(0, FT_V12_P_PCH, FT_SYSTEM);
24171341Sstevel 		} else {
24181341Sstevel 			reg_fault(0, FT_V12_P_PCH, FT_SYSTEM);
24191341Sstevel 		}
24201341Sstevel 		break;
24211341Sstevel 
24221341Sstevel 	/* 3.3v precharge */
24231341Sstevel 	case SYS_V3_PCH_INDEX:
24241341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V3_PCH], s);
24251341Sstevel 		if (ps_ok) {
24261341Sstevel 			clear_fault(0, FT_V3_PCH, FT_SYSTEM);
24271341Sstevel 		} else {
24281341Sstevel 			reg_fault(0, FT_V3_PCH, FT_SYSTEM);
24291341Sstevel 		}
24301341Sstevel 		break;
24311341Sstevel 
24321341Sstevel 	/* 5v precharge */
24331341Sstevel 	case SYS_V5_PCH_INDEX:
24341341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_V5_PCH], s);
24351341Sstevel 		if (ps_ok) {
24361341Sstevel 			clear_fault(0, FT_V5_PCH, FT_SYSTEM);
24371341Sstevel 		} else {
24381341Sstevel 			reg_fault(0, FT_V5_PCH, FT_SYSTEM);
24391341Sstevel 		}
24401341Sstevel 		break;
24411341Sstevel 
24421341Sstevel 	/* peripheral power supply fans */
24431341Sstevel 	case SYS_P_FAN_INDEX:
24441341Sstevel 		cmn_err(level, "%s %s", ft_str_table[FT_PPS_FAN], s);
24451341Sstevel 		if (ps_ok) {
24461341Sstevel 			clear_fault(0, FT_PPS_FAN, FT_SYSTEM);
24471341Sstevel 		} else {
24481341Sstevel 			reg_fault(0, FT_PPS_FAN, FT_SYSTEM);
24491341Sstevel 		}
24501341Sstevel 		break;
24511341Sstevel 	}
24521341Sstevel }
24531341Sstevel 
24541341Sstevel /*
24551341Sstevel  * The timeout from ps_fail_handler() that simply re-triggers a check
24561341Sstevel  * of the ps condition.
24571341Sstevel  */
24581341Sstevel static void
ps_fail_retry(void * arg)24591341Sstevel ps_fail_retry(void *arg)
24601341Sstevel {
24611341Sstevel 	struct sysctrl_soft_state *softsp = arg;
24621341Sstevel 
24631341Sstevel 	ASSERT(softsp);
24641341Sstevel 
24651341Sstevel 	ddi_trigger_softintr(softsp->ps_fail_poll_id);
24661341Sstevel }
24671341Sstevel 
24681341Sstevel /*
24691341Sstevel  * pps_fanfail_handler
24701341Sstevel  *
24711341Sstevel  * This routine is called from the high level handler.
24721341Sstevel  */
24731341Sstevel static uint_t
pps_fanfail_handler(caddr_t arg)24741341Sstevel pps_fanfail_handler(caddr_t arg)
24751341Sstevel {
24761341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
24771341Sstevel 
24781341Sstevel 	ASSERT(softsp);
24791341Sstevel 
24801341Sstevel 	/* always check again in a bit by re-enabling the fan interrupt */
24811341Sstevel 	(void) timeout(pps_fanfail_retry, softsp, pps_fan_timeout_hz);
24821341Sstevel 
24831341Sstevel 	return (DDI_INTR_CLAIMED);
24841341Sstevel }
24851341Sstevel 
24861341Sstevel /*
24871341Sstevel  * After a bit of waiting, we simply re-enable the interrupt to
24881341Sstevel  * see if we get another one.  The softintr triggered routine does
24891341Sstevel  * the dirty work for us since it runs in the interrupt context.
24901341Sstevel  */
24911341Sstevel static void
pps_fanfail_retry(void * arg)24921341Sstevel pps_fanfail_retry(void *arg)
24931341Sstevel {
24941341Sstevel 	struct sysctrl_soft_state *softsp = arg;
24951341Sstevel 
24961341Sstevel 	ASSERT(softsp);
24971341Sstevel 
24981341Sstevel 	ddi_trigger_softintr(softsp->pps_fan_high_id);
24991341Sstevel }
25001341Sstevel 
25011341Sstevel /*
25021341Sstevel  * The other half of the retry handler run from the interrupt context
25031341Sstevel  */
25041341Sstevel static uint_t
pps_fanfail_reenable(caddr_t arg)25051341Sstevel pps_fanfail_reenable(caddr_t arg)
25061341Sstevel {
25071341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
25081341Sstevel 	uchar_t tmp_reg;
25091341Sstevel 
25101341Sstevel 	ASSERT(softsp);
25111341Sstevel 
25121341Sstevel 	mutex_enter(&softsp->csr_mutex);
25131341Sstevel 
25141341Sstevel 	/*
25151341Sstevel 	 * re-initialize the bit field for all pps fans to assumed good.
25161341Sstevel 	 * If the fans are still bad, we're going to get an immediate system
25171341Sstevel 	 * interrupt which will put the correct state back anyway.
25181341Sstevel 	 *
25191341Sstevel 	 * NOTE: the polling routines that use this state understand the
25201341Sstevel 	 * pulse resulting from above...
25211341Sstevel 	 */
25221341Sstevel 	softsp->pps_fan_saved = SYS_AC_FAN_OK | SYS_KEYSW_FAN_OK;
25231341Sstevel 
25241341Sstevel 	*(softsp->csr) |= SYS_PPS_FAN_FAIL_EN;
25251341Sstevel 	tmp_reg = *(softsp->csr);
25261341Sstevel #ifdef lint
25271341Sstevel 	tmp_reg = tmp_reg;
25281341Sstevel #endif
25291341Sstevel 	mutex_exit(&softsp->csr_mutex);
25301341Sstevel 
25311341Sstevel 	return (DDI_INTR_CLAIMED);
25321341Sstevel }
25331341Sstevel 
25341341Sstevel /*
25351341Sstevel  *
25361341Sstevel  * Poll the hardware shadow state to determine the pps fan status.
25371341Sstevel  * The shadow state is maintained by the system_high handler and its
25381341Sstevel  * associated pps_* functions (above).
25391341Sstevel  *
25401341Sstevel  * There is a short time interval where the shadow state is pulsed to
25411341Sstevel  * the OK state even when the fans are bad.  However, this polling
25421341Sstevel  * routine has some built in hysteresis to filter out those _normal_
25431341Sstevel  * events.
25441341Sstevel  */
25451341Sstevel static void
pps_fan_poll(void * arg)25461341Sstevel pps_fan_poll(void *arg)
25471341Sstevel {
25481341Sstevel 	struct sysctrl_soft_state *softsp = arg;
25491341Sstevel 	int i;
25501341Sstevel 
25511341Sstevel 	ASSERT(softsp);
25521341Sstevel 
25531341Sstevel 	for (i = 0; i < SYS_PPS_FAN_COUNT; i++) {
25541341Sstevel 		int fanfail = FALSE;
25551341Sstevel 
25561341Sstevel 		/* determine fan status */
25571341Sstevel 		switch (i) {
25581341Sstevel 		case RACK:
25591341Sstevel 			fanfail = softsp->pps_fan_saved & SYS_RACK_FANFAIL;
25601341Sstevel 			break;
25611341Sstevel 
25621341Sstevel 		case AC:
25631341Sstevel 			/*
25641341Sstevel 			 * Don't bother polling the AC fan on 4 and 5 slot
25651341Sstevel 			 * systems.
25661341Sstevel 			 * Rather, it is handled by the power supply loop.
25671341Sstevel 			 */
25681341Sstevel 			fanfail = !(IS4SLOT(softsp->nslots) ||
25697656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) &&
25707656SSherry.Moore@Sun.COM 			    !(softsp->pps_fan_saved & SYS_AC_FAN_OK);
25711341Sstevel 			break;
25721341Sstevel 
25731341Sstevel 		case KEYSW:
25741341Sstevel 			/*
25751341Sstevel 			 * This signal is not usable if aux5v is missing
25761341Sstevel 			 * so we will synthesize a failed fan when aux5v
25771341Sstevel 			 * fails or when pps0 is out.
25781341Sstevel 			 * The 4 and 5 slot systems behave the same.
25791341Sstevel 			 */
25801341Sstevel 			fanfail = (!(IS4SLOT(softsp->nslots) ||
25817656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) &&
25821341Sstevel 			    (softsp->ps_stats[SYS_V5_AUX_INDEX].dcshadow !=
25837656SSherry.Moore@Sun.COM 			    PS_OK)) ||
25841341Sstevel 			    !(softsp->pps_fan_saved & SYS_KEYSW_FAN_OK);
25851341Sstevel 			break;
25861341Sstevel 
25871341Sstevel 		}
25881341Sstevel 
25891341Sstevel 		/* is the fan bad? */
25901341Sstevel 		if (fanfail) {
25911341Sstevel 
25921341Sstevel 			/* is this condition different than we know? */
25931341Sstevel 			if (softsp->pps_fan_state_count[i] == 0) {
25941341Sstevel 
25951341Sstevel 				/* log the change to failed */
25961341Sstevel 				pps_fan_state_change(softsp, i, FALSE);
25971341Sstevel 			}
25981341Sstevel 
25991341Sstevel 			/* always restart the fan OK counter */
26001341Sstevel 			softsp->pps_fan_state_count[i] = PPS_FROM_FAIL_TICKS;
26011341Sstevel 		} else {
26021341Sstevel 
26031341Sstevel 			/* do we currently know the fan is bad? */
26041341Sstevel 			if (softsp->pps_fan_state_count[i]) {
26051341Sstevel 
26061341Sstevel 				/* yes, but has it been stable? */
26071341Sstevel 				if (--softsp->pps_fan_state_count[i] == 0) {
26081341Sstevel 
26091341Sstevel 					/* log the change to OK */
26101341Sstevel 					pps_fan_state_change(softsp, i, TRUE);
26111341Sstevel 				}
26121341Sstevel 			}
26131341Sstevel 		}
26141341Sstevel 	}
26151341Sstevel 
26161341Sstevel 	/* always check again in a bit by re-enabling the fan interrupt */
26171341Sstevel 	(void) timeout(pps_fan_poll, softsp, pps_fan_timeout_hz);
26181341Sstevel }
26191341Sstevel 
26201341Sstevel /*
26211341Sstevel  * pps_fan_state_change()
26221341Sstevel  *
26231341Sstevel  * Log the changed fan condition and update the external status.
26241341Sstevel  */
26251341Sstevel static void
pps_fan_state_change(struct sysctrl_soft_state * softsp,int index,int fan_ok)26261341Sstevel pps_fan_state_change(struct sysctrl_soft_state *softsp, int index, int fan_ok)
26271341Sstevel {
26281341Sstevel 	char *fan_type;
26291341Sstevel 	char *state = fan_ok ? "fans OK" : "fan failure detected";
26301341Sstevel 
26311341Sstevel 	switch (index) {
26321341Sstevel 	case RACK:
26331341Sstevel 		/* 4 and 5 slot systems behave the same */
26341341Sstevel 		fan_type = (IS4SLOT(softsp->nslots) ||
26357656SSherry.Moore@Sun.COM 		    IS5SLOT(softsp->nslots)) ?
26367656SSherry.Moore@Sun.COM 		    "Disk Drive" : "Rack Exhaust";
26371341Sstevel 		if (fan_ok) {
26381341Sstevel 			softsp->pps_fan_external_state &= ~SYS_RACK_FANFAIL;
26391341Sstevel 			clear_fault(0, (IS4SLOT(softsp->nslots) ||
26407656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) ? FT_DSK_FAN :
26417656SSherry.Moore@Sun.COM 			    FT_RACK_EXH, FT_SYSTEM);
26421341Sstevel 		} else {
26431341Sstevel 			softsp->pps_fan_external_state |= SYS_RACK_FANFAIL;
26441341Sstevel 			reg_fault(0, (IS4SLOT(softsp->nslots) ||
26457656SSherry.Moore@Sun.COM 			    IS5SLOT(softsp->nslots)) ? FT_DSK_FAN :
26467656SSherry.Moore@Sun.COM 			    FT_RACK_EXH, FT_SYSTEM);
26471341Sstevel 		}
26481341Sstevel 		break;
26491341Sstevel 
26501341Sstevel 	case AC:
26511341Sstevel 		fan_type = "AC Box";
26521341Sstevel 		if (fan_ok) {
26531341Sstevel 			softsp->pps_fan_external_state |= SYS_AC_FAN_OK;
26541341Sstevel 			clear_fault(0, FT_AC_FAN, FT_SYSTEM);
26551341Sstevel 		} else {
26561341Sstevel 			softsp->pps_fan_external_state &= ~SYS_AC_FAN_OK;
26571341Sstevel 			reg_fault(0, FT_AC_FAN, FT_SYSTEM);
26581341Sstevel 		}
26591341Sstevel 		break;
26601341Sstevel 
26611341Sstevel 	case KEYSW:
26621341Sstevel 		fan_type = "Keyswitch";
26631341Sstevel 		if (fan_ok) {
26641341Sstevel 			softsp->pps_fan_external_state |= SYS_KEYSW_FAN_OK;
26651341Sstevel 			clear_fault(0, FT_KEYSW_FAN, FT_SYSTEM);
26661341Sstevel 		} else {
26671341Sstevel 			softsp->pps_fan_external_state &= ~SYS_KEYSW_FAN_OK;
26681341Sstevel 			reg_fault(0, FT_KEYSW_FAN, FT_SYSTEM);
26691341Sstevel 		}
26701341Sstevel 		break;
26711341Sstevel 	default:
26721341Sstevel 		fan_type = "[invalid fan id]";
26731341Sstevel 		break;
26741341Sstevel 	}
26751341Sstevel 
26761341Sstevel 	/* now log the state change */
26771341Sstevel 	cmn_err(fan_ok ? CE_NOTE : CE_WARN, "%s %s", fan_type, state);
26781341Sstevel }
26791341Sstevel 
26801341Sstevel static uint_t
bd_insert_handler(caddr_t arg)26811341Sstevel bd_insert_handler(caddr_t arg)
26821341Sstevel {
26831341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
26841341Sstevel 
26851341Sstevel 	ASSERT(softsp);
26861341Sstevel 
26871341Sstevel 	DPRINTF(SYSCTRL_ATTACH_DEBUG, ("bd_insert_handler()"));
26881341Sstevel 
26891341Sstevel 	(void) timeout(bd_insert_timeout, softsp, bd_insert_delay_hz);
26901341Sstevel 
26911341Sstevel 	return (DDI_INTR_CLAIMED);
26921341Sstevel }
26931341Sstevel 
26941341Sstevel void
bd_remove_poll(struct sysctrl_soft_state * softsp)26951341Sstevel bd_remove_poll(struct sysctrl_soft_state *softsp)
26961341Sstevel {
26971341Sstevel 	ASSERT(fhc_bdlist_locked());
26981341Sstevel 
26991341Sstevel 	if (!bd_remove_to_id) {
27001341Sstevel 		bd_remove_to_id = timeout(bd_remove_timeout, softsp,
27017656SSherry.Moore@Sun.COM 		    bd_remove_timeout_hz);
27021341Sstevel 	} else {
27031341Sstevel 		DPRINTF(SYSCTRL_ATTACH_DEBUG,
27047656SSherry.Moore@Sun.COM 		    ("bd_remove_poll ignoring start request"));
27051341Sstevel 	}
27061341Sstevel }
27071341Sstevel 
27081341Sstevel /*
27091341Sstevel  * bd_insert_timeout()
27101341Sstevel  *
27111341Sstevel  * This routine handles the board insert interrupt. It is called from a
27121341Sstevel  * timeout so that it does not run at interrupt level. The main job
27131341Sstevel  * of this routine is to find hotplugged boards and de-assert the
27141341Sstevel  * board insert interrupt coming from the board. For hotplug phase I,
27151341Sstevel  * the routine also powers down the board.
27161341Sstevel  * JTAG scan is used to find boards which have been inserted.
27171341Sstevel  * All other control of the boards is also done by JTAG scan.
27181341Sstevel  */
27191341Sstevel static void
bd_insert_timeout(void * arg)27201341Sstevel bd_insert_timeout(void *arg)
27211341Sstevel {
27221341Sstevel 	struct sysctrl_soft_state *softsp = arg;
27231341Sstevel 	int found;
27241341Sstevel 
27251341Sstevel 	ASSERT(softsp);
27261341Sstevel 
27271341Sstevel 	if (sysctrl_hotplug_disabled) {
27281341Sstevel 		sysc_policy_update(softsp, NULL, SYSC_EVT_BD_HP_DISABLED);
27291341Sstevel 	} else {
27301341Sstevel 		/*
27311341Sstevel 		 * Lock the board list mutex. Keep it locked until all work
27321341Sstevel 		 * is done.
27331341Sstevel 		 */
27341341Sstevel 		(void) fhc_bdlist_lock(-1);
27351341Sstevel 
27361341Sstevel 		found = fhc_bd_insert_scan();
27371341Sstevel 
27381341Sstevel 		if (found) {
27391341Sstevel 			DPRINTF(SYSCTRL_ATTACH_DEBUG,
27401341Sstevel 			    ("bd_insert_timeout starting bd_remove_poll()"));
27411341Sstevel 			bd_remove_poll(softsp);
27421341Sstevel 		}
27431341Sstevel 
27441341Sstevel 		fhc_bdlist_unlock();
27451341Sstevel 	}
27461341Sstevel 
27471341Sstevel 	/*
27481341Sstevel 	 * Enable interrupts.
27491341Sstevel 	 */
27501341Sstevel 	ddi_trigger_softintr(softsp->sbrd_gone_id);
27511341Sstevel }
27521341Sstevel 
27531341Sstevel static void
bd_remove_timeout(void * arg)27541341Sstevel bd_remove_timeout(void *arg)
27551341Sstevel {
27561341Sstevel 	struct sysctrl_soft_state *softsp = arg;
27571341Sstevel 	int keep_polling;
27581341Sstevel 
27591341Sstevel 	ASSERT(softsp);
27601341Sstevel 
27611341Sstevel 	/*
27621341Sstevel 	 * Lock the board list mutex. Keep it locked until all work
27631341Sstevel 	 * is done.
27641341Sstevel 	 */
27651341Sstevel 	(void) fhc_bdlist_lock(-1);
27661341Sstevel 
27671341Sstevel 	bd_remove_to_id = 0;	/* delete our timeout ID */
27681341Sstevel 
27691341Sstevel 	keep_polling = fhc_bd_remove_scan();
27701341Sstevel 
27711341Sstevel 	if (keep_polling) {
27721341Sstevel 		bd_remove_poll(softsp);
27731341Sstevel 	} else {
27741341Sstevel 		DPRINTF(SYSCTRL_ATTACH_DEBUG, ("exiting bd_remove_poll."));
27751341Sstevel 	}
27761341Sstevel 
27771341Sstevel 	fhc_bdlist_unlock();
27781341Sstevel }
27791341Sstevel 
27801341Sstevel static uint_t
bd_insert_normal(caddr_t arg)27811341Sstevel bd_insert_normal(caddr_t arg)
27821341Sstevel {
27831341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
27841341Sstevel 	uchar_t tmp_reg;
27851341Sstevel 
27861341Sstevel 	ASSERT(softsp);
27871341Sstevel 
27881341Sstevel 	/* has the condition been removed? */
27891341Sstevel 	/* XXX add deglitch state machine here */
27901341Sstevel 	if (!(*(softsp->status1) & SYS_NOT_BRD_PRES)) {
27911341Sstevel 		/* check again in a few */
27921341Sstevel 		(void) timeout(bd_insert_timeout, softsp, bd_insert_retry_hz);
27931341Sstevel 	} else {
27941341Sstevel 		/* Turn on the enable bit for this interrupt */
27951341Sstevel 		mutex_enter(&softsp->csr_mutex);
27961341Sstevel 		*(softsp->csr) |= SYS_SBRD_PRES_EN;
27971341Sstevel 		/* flush the hardware store buffer */
27981341Sstevel 		tmp_reg = *(softsp->csr);
27991341Sstevel #ifdef lint
28001341Sstevel 		tmp_reg = tmp_reg;
28011341Sstevel #endif
28021341Sstevel 		mutex_exit(&softsp->csr_mutex);
28031341Sstevel 	}
28041341Sstevel 
28051341Sstevel 	return (DDI_INTR_CLAIMED);
28061341Sstevel }
28071341Sstevel 
28081341Sstevel /*
28091341Sstevel  * blink LED handler.
28101341Sstevel  *
28111341Sstevel  * The actual bit manipulation needs to occur at interrupt level
28121341Sstevel  * because we need access to the CSR with its CSR mutex
28131341Sstevel  */
28141341Sstevel static uint_t
blink_led_handler(caddr_t arg)28151341Sstevel blink_led_handler(caddr_t arg)
28161341Sstevel {
28171341Sstevel 	struct sysctrl_soft_state *softsp = (struct sysctrl_soft_state *)arg;
28181341Sstevel 	uchar_t tmp_reg;
28191341Sstevel 
28201341Sstevel 	ASSERT(softsp);
28211341Sstevel 
28221341Sstevel 	mutex_enter(&softsp->csr_mutex);
28231341Sstevel 
28241341Sstevel 	/*
28251341Sstevel 	 * XXX - The lock for the sys_led is not held here. If more
28261341Sstevel 	 * complicated tasks are done with the System LED, then
28271341Sstevel 	 * locking should be done here.
28281341Sstevel 	 */
28291341Sstevel 
28301341Sstevel 	/* read the hardware register. */
28311341Sstevel 	tmp_reg = *(softsp->csr);
28321341Sstevel 
28331341Sstevel 	/* Only turn on the OS System LED bit if the softsp state is on. */
28341341Sstevel 	if (softsp->sys_led) {
28351341Sstevel 		tmp_reg |= SYS_LED_RIGHT;
28361341Sstevel 	} else {
28371341Sstevel 		tmp_reg &= ~SYS_LED_RIGHT;
28381341Sstevel 	}
28391341Sstevel 
28401341Sstevel 	/* Turn on the yellow LED if system fault status is set. */
28411341Sstevel 	if (softsp->sys_fault) {
28421341Sstevel 		tmp_reg |= SYS_LED_MID;
28431341Sstevel 	} else {
28441341Sstevel 		tmp_reg &= ~SYS_LED_MID;
28451341Sstevel 	}
28461341Sstevel 
28471341Sstevel 	/* write to the hardware register */
28481341Sstevel 	*(softsp->csr) = tmp_reg;
28491341Sstevel 
28501341Sstevel 	/* flush the hardware store buffer */
28511341Sstevel 	tmp_reg = *(softsp->csr);
28521341Sstevel #ifdef lint
28531341Sstevel 	tmp_reg = tmp_reg;
28541341Sstevel #endif
28551341Sstevel 	mutex_exit(&softsp->csr_mutex);
28561341Sstevel 
28571341Sstevel 	(void) timeout(blink_led_timeout, softsp, blink_led_timeout_hz);
28581341Sstevel 
28591341Sstevel 	return (DDI_INTR_CLAIMED);
28601341Sstevel }
28611341Sstevel 
28621341Sstevel /*
28631341Sstevel  * simply re-trigger the interrupt handler on led timeout
28641341Sstevel  */
28651341Sstevel static void
blink_led_timeout(void * arg)28661341Sstevel blink_led_timeout(void *arg)
28671341Sstevel {
28681341Sstevel 	struct sysctrl_soft_state *softsp = arg;
28691341Sstevel 	int led_state;
28701341Sstevel 
28711341Sstevel 	ASSERT(softsp);
28721341Sstevel 
28731341Sstevel 	/*
28741341Sstevel 	 * Process the system fault list here. This is where the driver
28751341Sstevel 	 * must decide what yellow LEDs to turn on if any. The fault
28761341Sstevel 	 * list is walked and each fhc_list entry is updated with it's
28771341Sstevel 	 * yellow LED status. This info is used later by the routine
28781341Sstevel 	 * toggle_board_green_leds().
28791341Sstevel 	 *
28801341Sstevel 	 * The variable system_fault is non-zero if any non-
28811341Sstevel 	 * suppressed faults are found in the system.
28821341Sstevel 	 */
28831341Sstevel 	softsp->sys_fault = process_fault_list();
28841341Sstevel 
28851341Sstevel 	/* blink the system board OS LED */
28861341Sstevel 	mutex_enter(&softsp->sys_led_lock);
28871341Sstevel 	softsp->sys_led = !softsp->sys_led;
28881341Sstevel 	led_state = softsp->sys_led;
28891341Sstevel 	mutex_exit(&softsp->sys_led_lock);
28901341Sstevel 
28911341Sstevel 	toggle_board_green_leds(led_state);
28921341Sstevel 
28931341Sstevel 	ddi_trigger_softintr(softsp->blink_led_id);
28941341Sstevel }
28951341Sstevel 
28961341Sstevel void
toggle_board_green_leds(int led_state)28971341Sstevel toggle_board_green_leds(int led_state)
28981341Sstevel {
28991341Sstevel 	fhc_bd_t *list;
29001341Sstevel 
29011341Sstevel 	(void) fhc_bdlist_lock(-1);
29021341Sstevel 	for (list = fhc_bd_first(); list; list = fhc_bd_next(list)) {
29031341Sstevel 		uint_t value = 0;
29041341Sstevel 
29051341Sstevel 		if (list->sc.in_transition ||
29061341Sstevel 		    (list->sc.rstate != SYSC_CFGA_RSTATE_CONNECTED))
29071341Sstevel 			continue;
29081341Sstevel 
29091341Sstevel 		ASSERT(list->sc.type != CLOCK_BOARD);
29101341Sstevel 		ASSERT(list->sc.type != DISK_BOARD);
29111341Sstevel 		ASSERT(list->softsp);
29121341Sstevel 
29131341Sstevel 		if ((list->sc.ostate == SYSC_CFGA_OSTATE_CONFIGURED) &&
29141341Sstevel 		    led_state)
29151341Sstevel 			value |= FHC_LED_RIGHT;
29161341Sstevel 
29171341Sstevel 		if (list->fault)
29181341Sstevel 			value |= FHC_LED_MID;
29191341Sstevel 		else
29201341Sstevel 			value &= ~FHC_LED_MID;
29211341Sstevel 
29221341Sstevel 		update_board_leds(list, FHC_LED_RIGHT|FHC_LED_MID, value);
29231341Sstevel 	}
29241341Sstevel 	fhc_bdlist_unlock();
29251341Sstevel }
29261341Sstevel 
29271341Sstevel /*
29281341Sstevel  * timestamp an AC power failure in nvram
29291341Sstevel  */
29301341Sstevel static void
nvram_update_powerfail(struct sysctrl_soft_state * softsp)29311341Sstevel nvram_update_powerfail(struct sysctrl_soft_state *softsp)
29321341Sstevel {
29331341Sstevel 	char buf[80];
29341341Sstevel 	int len = 0;
29351341Sstevel 
29361341Sstevel 	numtos(gethrestime_sec(), buf);
29371341Sstevel 
29381341Sstevel 	if (softsp->options_nodeid) {
29391341Sstevel 		len = prom_setprop(softsp->options_nodeid, "powerfail-time",
29407656SSherry.Moore@Sun.COM 		    buf, strlen(buf)+1);
29411341Sstevel 	}
29421341Sstevel 
29431341Sstevel 	if (len <= 0) {
29441341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: failed to set powerfail-time "
29457656SSherry.Moore@Sun.COM 		    "to %s\n", ddi_get_instance(softsp->dip), buf);
29461341Sstevel 	}
29471341Sstevel }
29481341Sstevel 
29491341Sstevel void
sysctrl_add_kstats(struct sysctrl_soft_state * softsp)29501341Sstevel sysctrl_add_kstats(struct sysctrl_soft_state *softsp)
29511341Sstevel {
29521341Sstevel 	struct kstat	*ksp;		/* Generic sysctrl kstats */
29531341Sstevel 	struct kstat	*pksp;		/* Power Supply kstat */
29541341Sstevel 	struct kstat	*tksp;		/* Sysctrl temperatrure kstat */
29551341Sstevel 	struct kstat	*ttsp;		/* Sysctrl temperature test kstat */
29561341Sstevel 
29571341Sstevel 	if ((ksp = kstat_create("unix", ddi_get_instance(softsp->dip),
29581341Sstevel 	    SYSCTRL_KSTAT_NAME, "misc", KSTAT_TYPE_NAMED,
29591341Sstevel 	    sizeof (struct sysctrl_kstat) / sizeof (kstat_named_t),
29601341Sstevel 	    KSTAT_FLAG_PERSISTENT)) == NULL) {
29611341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
29627656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
29631341Sstevel 	} else {
29641341Sstevel 		struct sysctrl_kstat *sysksp;
29651341Sstevel 
29661341Sstevel 		sysksp = (struct sysctrl_kstat *)(ksp->ks_data);
29671341Sstevel 
29681341Sstevel 		/* now init the named kstats */
29691341Sstevel 		kstat_named_init(&sysksp->csr, CSR_KSTAT_NAMED,
29707656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29711341Sstevel 
29721341Sstevel 		kstat_named_init(&sysksp->status1, STAT1_KSTAT_NAMED,
29737656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29741341Sstevel 
29751341Sstevel 		kstat_named_init(&sysksp->status2, STAT2_KSTAT_NAMED,
29767656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29771341Sstevel 
29781341Sstevel 		kstat_named_init(&sysksp->clk_freq2, CLK_FREQ2_KSTAT_NAMED,
29797656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29801341Sstevel 
29811341Sstevel 		kstat_named_init(&sysksp->fan_status, FAN_KSTAT_NAMED,
29827656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29831341Sstevel 
29841341Sstevel 		kstat_named_init(&sysksp->key_status, KEY_KSTAT_NAMED,
29857656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29861341Sstevel 
29871341Sstevel 		kstat_named_init(&sysksp->power_state, POWER_KSTAT_NAMED,
29887656SSherry.Moore@Sun.COM 		    KSTAT_DATA_INT32);
29891341Sstevel 
29901341Sstevel 		kstat_named_init(&sysksp->clk_ver, CLK_VER_KSTAT_NAME,
29917656SSherry.Moore@Sun.COM 		    KSTAT_DATA_CHAR);
29921341Sstevel 
29931341Sstevel 		ksp->ks_update = sysctrl_kstat_update;
29941341Sstevel 		ksp->ks_private = (void *)softsp;
29951341Sstevel 		kstat_install(ksp);
29961341Sstevel 	}
29971341Sstevel 
29981341Sstevel 	if ((tksp = kstat_create("unix", CLOCK_BOARD_INDEX,
29991341Sstevel 	    OVERTEMP_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
30001341Sstevel 	    sizeof (struct temp_stats), KSTAT_FLAG_PERSISTENT)) == NULL) {
30011341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
30021341Sstevel 			ddi_get_instance(softsp->dip));
30031341Sstevel 	} else {
30041341Sstevel 		tksp->ks_update = overtemp_kstat_update;
30051341Sstevel 		tksp->ks_private = (void *)&softsp->tempstat;
30061341Sstevel 		kstat_install(tksp);
30071341Sstevel 	}
30081341Sstevel 
30091341Sstevel 	if ((ttsp = kstat_create("unix", CLOCK_BOARD_INDEX,
30101341Sstevel 	    TEMP_OVERRIDE_KSTAT_NAME, "misc", KSTAT_TYPE_RAW, sizeof (short),
30117656SSherry.Moore@Sun.COM 	    KSTAT_FLAG_PERSISTENT | KSTAT_FLAG_WRITABLE)) == NULL) {
30121341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
30137656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
30141341Sstevel 	} else {
30151341Sstevel 		ttsp->ks_update = temp_override_kstat_update;
30161341Sstevel 		ttsp->ks_private = (void *)&softsp->tempstat.override;
30171341Sstevel 		kstat_install(ttsp);
30181341Sstevel 	}
30191341Sstevel 
30201341Sstevel 	if ((pksp = kstat_create("unix", ddi_get_instance(softsp->dip),
30211341Sstevel 	    PSSHAD_KSTAT_NAME, "misc", KSTAT_TYPE_RAW,
30221341Sstevel 	    SYS_PS_COUNT, KSTAT_FLAG_PERSISTENT)) == NULL) {
30231341Sstevel 		cmn_err(CE_WARN, "sysctrl%d: kstat_create failed",
30247656SSherry.Moore@Sun.COM 		    ddi_get_instance(softsp->dip));
30251341Sstevel 	} else {
30261341Sstevel 		pksp->ks_update = psstat_kstat_update;
30271341Sstevel 		pksp->ks_private = (void *)softsp;
30281341Sstevel 		kstat_install(pksp);
30291341Sstevel 	}
30301341Sstevel }
30311341Sstevel 
30321341Sstevel static int
sysctrl_kstat_update(kstat_t * ksp,int rw)30331341Sstevel sysctrl_kstat_update(kstat_t *ksp, int rw)
30341341Sstevel {
30351341Sstevel 	struct sysctrl_kstat *sysksp;
30361341Sstevel 	struct sysctrl_soft_state *softsp;
30371341Sstevel 
30381341Sstevel 	sysksp = (struct sysctrl_kstat *)(ksp->ks_data);
30391341Sstevel 	softsp = (struct sysctrl_soft_state *)(ksp->ks_private);
30401341Sstevel 
30411341Sstevel 	/* this is a read-only kstat. Exit on a write */
30421341Sstevel 
30431341Sstevel 	if (rw == KSTAT_WRITE) {
30441341Sstevel 		return (EACCES);
30451341Sstevel 	} else {
30461341Sstevel 		/*
30471341Sstevel 		 * copy the current state of the hardware into the
30481341Sstevel 		 * kstat structure.
30491341Sstevel 		 */
30501341Sstevel 		sysksp->csr.value.c[0] = *(softsp->csr);
30511341Sstevel 		sysksp->status1.value.c[0] = *(softsp->status1);
30521341Sstevel 		sysksp->status2.value.c[0] = *(softsp->status2);
30531341Sstevel 		sysksp->clk_freq2.value.c[0] = *(softsp->clk_freq2);
30541341Sstevel 
30551341Sstevel 		sysksp->fan_status.value.c[0] = softsp->pps_fan_external_state;
30561341Sstevel 		sysksp->key_status.value.c[0] = softsp->key_shadow;
30571341Sstevel 		sysksp->power_state.value.i32 = softsp->power_state;
30581341Sstevel 
30591341Sstevel 		/*
30601341Sstevel 		 * non-existence of the clock version register returns the
30611341Sstevel 		 * value 0xff when the hardware register location is read
30621341Sstevel 		 */
30631341Sstevel 		if (softsp->clk_ver != NULL)
30641341Sstevel 			sysksp->clk_ver.value.c[0] = *(softsp->clk_ver);
30651341Sstevel 		else
30661341Sstevel 			sysksp->clk_ver.value.c[0] = (char)0xff;
30671341Sstevel 	}
30681341Sstevel 	return (0);
30691341Sstevel }
30701341Sstevel 
30711341Sstevel static int
psstat_kstat_update(kstat_t * ksp,int rw)30721341Sstevel psstat_kstat_update(kstat_t *ksp, int rw)
30731341Sstevel {
30741341Sstevel 	struct sysctrl_soft_state *softsp;
30751341Sstevel 	uchar_t *ptr = (uchar_t *)(ksp->ks_data);
30761341Sstevel 	int ps;
30771341Sstevel 
30781341Sstevel 	softsp = (struct sysctrl_soft_state *)(ksp->ks_private);
30791341Sstevel 
30801341Sstevel 	if (rw == KSTAT_WRITE) {
30811341Sstevel 		return (EACCES);
30821341Sstevel 	} else {
30831341Sstevel 		for (ps = 0; ps < SYS_PS_COUNT; ps++) {
30841341Sstevel 			*ptr++ = softsp->ps_stats[ps].dcshadow;
30851341Sstevel 		}
30861341Sstevel 	}
30871341Sstevel 	return (0);
30881341Sstevel }
30891341Sstevel 
30901341Sstevel static void
sysctrl_thread_wakeup(void * arg)30911341Sstevel sysctrl_thread_wakeup(void *arg)
30921341Sstevel {
30931341Sstevel 	int type = (int)(uintptr_t)arg;
30941341Sstevel 
30951341Sstevel 	/*
30961341Sstevel 	 * grab mutex to guarantee that our wakeup call
30971341Sstevel 	 * arrives after we go to sleep -- so we can't sleep forever.
30981341Sstevel 	 */
30991341Sstevel 	mutex_enter(&sslist_mutex);
31001341Sstevel 	switch (type) {
31011341Sstevel 	case OVERTEMP_POLL:
31021341Sstevel 		cv_signal(&overtemp_cv);
31031341Sstevel 		break;
31041341Sstevel 	case KEYSWITCH_POLL:
31051341Sstevel 		cv_signal(&keyswitch_cv);
31061341Sstevel 		break;
31071341Sstevel 	default:
31081341Sstevel 		cmn_err(CE_WARN, "sysctrl: invalid type %d to wakeup\n", type);
31091341Sstevel 		break;
31101341Sstevel 	}
31111341Sstevel 	mutex_exit(&sslist_mutex);
31121341Sstevel }
31131341Sstevel 
31141341Sstevel static void
sysctrl_overtemp_poll(void)31151341Sstevel sysctrl_overtemp_poll(void)
31161341Sstevel {
31171341Sstevel 	struct sysctrl_soft_state *list;
31181341Sstevel 	callb_cpr_t cprinfo;
31191341Sstevel 
31201341Sstevel 	CALLB_CPR_INIT(&cprinfo, &sslist_mutex, callb_generic_cpr, "overtemp");
31211341Sstevel 
31221341Sstevel 	/* The overtemp data structures are protected by a mutex. */
31231341Sstevel 	mutex_enter(&sslist_mutex);
31241341Sstevel 
31251341Sstevel 	while (sysctrl_do_overtemp_thread) {
31261341Sstevel 
31271341Sstevel 		for (list = sys_list; list != NULL; list = list->next) {
31281341Sstevel 			if (list->temp_reg != NULL) {
31291341Sstevel 				update_temp(list->pdip, &list->tempstat,
31307656SSherry.Moore@Sun.COM 				    *(list->temp_reg));
31311341Sstevel 			}
31321341Sstevel 		}
31331341Sstevel 
31341341Sstevel 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
31351341Sstevel 
31361341Sstevel 		/* now have this thread sleep for a while */
31371341Sstevel 		(void) timeout(sysctrl_thread_wakeup, (void *)OVERTEMP_POLL,
31387656SSherry.Moore@Sun.COM 		    overtemp_timeout_hz);
31391341Sstevel 
31401341Sstevel 		cv_wait(&overtemp_cv, &sslist_mutex);
31411341Sstevel 
31421341Sstevel 		CALLB_CPR_SAFE_END(&cprinfo, &sslist_mutex);
31431341Sstevel 	}
31441341Sstevel 	CALLB_CPR_EXIT(&cprinfo);
31451341Sstevel 	thread_exit();
31461341Sstevel 	/* NOTREACHED */
31471341Sstevel }
31481341Sstevel 
31491341Sstevel static void
sysctrl_keyswitch_poll(void)31501341Sstevel sysctrl_keyswitch_poll(void)
31511341Sstevel {
31521341Sstevel 	struct sysctrl_soft_state *list;
31531341Sstevel 	callb_cpr_t cprinfo;
31541341Sstevel 
31551341Sstevel 	CALLB_CPR_INIT(&cprinfo, &sslist_mutex, callb_generic_cpr, "keyswitch");
31561341Sstevel 
31571341Sstevel 	/* The keyswitch data strcutures are protected by a mutex. */
31581341Sstevel 	mutex_enter(&sslist_mutex);
31591341Sstevel 
31601341Sstevel 	while (sysctrl_do_keyswitch_thread) {
31611341Sstevel 
31621341Sstevel 		for (list = sys_list; list != NULL; list = list->next) {
31631341Sstevel 			if (list->status1 != NULL)
31641341Sstevel 				update_key_state(list);
31651341Sstevel 		}
31661341Sstevel 
31671341Sstevel 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
31681341Sstevel 
31691341Sstevel 		/* now have this thread sleep for a while */
31701341Sstevel 		(void) timeout(sysctrl_thread_wakeup, (void *)KEYSWITCH_POLL,
31717656SSherry.Moore@Sun.COM 		    keyswitch_timeout_hz);
31721341Sstevel 
31731341Sstevel 		cv_wait(&keyswitch_cv, &sslist_mutex);
31741341Sstevel 
31751341Sstevel 		CALLB_CPR_SAFE_END(&cprinfo, &sslist_mutex);
31761341Sstevel 	}
31771341Sstevel 	CALLB_CPR_EXIT(&cprinfo);
31781341Sstevel 	thread_exit();
31791341Sstevel 	/* NOTREACHED */
31801341Sstevel }
31811341Sstevel 
31821341Sstevel /*
31831341Sstevel  * check the key switch position for state changes
31841341Sstevel  */
31851341Sstevel static void
update_key_state(struct sysctrl_soft_state * list)31861341Sstevel update_key_state(struct sysctrl_soft_state *list)
31871341Sstevel {
31881341Sstevel 	enum keyswitch_state key;
31891341Sstevel 
31901341Sstevel 	/*
31911341Sstevel 	 * snapshot current hardware key position
31921341Sstevel 	 */
31931341Sstevel 	if (*(list->status1) & SYS_NOT_SECURE)
31941341Sstevel 		key = KEY_NOT_SECURE;
31951341Sstevel 	else
31961341Sstevel 		key = KEY_SECURE;
31971341Sstevel 
31981341Sstevel 	/*
31991341Sstevel 	 * check for state transition
32001341Sstevel 	 */
32011341Sstevel 	if (key != list->key_shadow) {
32021341Sstevel 
32031341Sstevel 		/*
32041341Sstevel 		 * handle state transition
32051341Sstevel 		 */
32061341Sstevel 		switch (list->key_shadow) {
32071341Sstevel 		case KEY_BOOT:
32081341Sstevel 			cmn_err(CE_CONT, "?sysctrl%d: Key switch is%sin the "
32091341Sstevel 			    "secure position\n", ddi_get_instance(list->dip),
32101341Sstevel 			    (key == KEY_SECURE) ? " " : " not ");
32111341Sstevel 			list->key_shadow = key;
32121341Sstevel 			break;
32131341Sstevel 		case KEY_SECURE:
32141341Sstevel 		case KEY_NOT_SECURE:
32151341Sstevel 			cmn_err(CE_NOTE, "sysctrl%d: Key switch has changed"
32161341Sstevel 			    " to the %s position",
32171341Sstevel 			    ddi_get_instance(list->dip),
32181341Sstevel 			    (key == KEY_SECURE) ? "secure" : "not-secure");
32191341Sstevel 			list->key_shadow = key;
32201341Sstevel 			break;
32211341Sstevel 		default:
32221341Sstevel 			cmn_err(CE_CONT,
32231341Sstevel 			    "?sysctrl%d: Key switch is in an unknown position,"
32241341Sstevel 			    "treated as being in the %s position\n",
32251341Sstevel 			    ddi_get_instance(list->dip),
32261341Sstevel 			    (list->key_shadow == KEY_SECURE) ?
32271341Sstevel 			    "secure" : "not-secure");
32281341Sstevel 			break;
32291341Sstevel 		}
32301341Sstevel 	}
32311341Sstevel }
32321341Sstevel 
32331341Sstevel /*
32341341Sstevel  * consider key switch position when handling an abort sequence
32351341Sstevel  */
32361341Sstevel static void
sysctrl_abort_seq_handler(char * msg)32371341Sstevel sysctrl_abort_seq_handler(char *msg)
32381341Sstevel {
32391341Sstevel 	struct sysctrl_soft_state *list;
32401341Sstevel 	uint_t secure = 0;
32411341Sstevel 	char buf[64], inst[4];
32421341Sstevel 
32431341Sstevel 
32441341Sstevel 	/*
32451341Sstevel 	 * if any of the key switch positions are secure,
32461341Sstevel 	 * then disallow entry to the prom/debugger
32471341Sstevel 	 */
32481341Sstevel 	mutex_enter(&sslist_mutex);
32491341Sstevel 	buf[0] = (char)0;
32501341Sstevel 	for (list = sys_list; list != NULL; list = list->next) {
32511341Sstevel 		if (!(*(list->status1) & SYS_NOT_SECURE)) {
32521341Sstevel 			if (secure++)
32531341Sstevel 				(void) strcat(buf, ",");
32541341Sstevel 			/*
32551341Sstevel 			 * XXX: later, replace instance number with nodeid
32561341Sstevel 			 */
32571341Sstevel 			(void) sprintf(inst, "%d", ddi_get_instance(list->dip));
32581341Sstevel 			(void) strcat(buf, inst);
32591341Sstevel 		}
32601341Sstevel 	}
32611341Sstevel 	mutex_exit(&sslist_mutex);
32621341Sstevel 
32631341Sstevel 	if (secure) {
32641341Sstevel 		cmn_err(CE_CONT,
32657656SSherry.Moore@Sun.COM 		    "!sysctrl(%s): ignoring debug enter sequence\n", buf);
32661341Sstevel 	} else {
32671341Sstevel 		cmn_err(CE_CONT, "!sysctrl: allowing debug enter\n");
32681341Sstevel 		debug_enter(msg);
32691341Sstevel 	}
32701341Sstevel }
32711341Sstevel 
32721341Sstevel #define	TABLE_END	0xFF
32731341Sstevel 
32741341Sstevel struct uart_cmd {
32751341Sstevel 	uchar_t reg;
32761341Sstevel 	uchar_t data;
32771341Sstevel };
32781341Sstevel 
32791341Sstevel /*
32801341Sstevel  * Time constant defined by this formula:
32811341Sstevel  *	((4915200/32)/(baud) -2)
32821341Sstevel  */
32831341Sstevel 
32841341Sstevel struct uart_cmd uart_table[] = {
32851341Sstevel 	{ 0x09, 0xc0 },	/* Force hardware reset */
32861341Sstevel 	{ 0x04, 0x46 },	/* X16 clock mode, 1 stop bit/char, no parity */
32871341Sstevel 	{ 0x03, 0xc0 },	/* Rx is 8 bits/char */
32881341Sstevel 	{ 0x05, 0xe2 },	/* DTR, Tx is 8 bits/char, RTS */
32891341Sstevel 	{ 0x09, 0x02 },	/* No vector returned on interrupt */
32901341Sstevel 	{ 0x0b, 0x55 },	/* Rx Clock = Tx Clock = BR generator = ~TRxC OUT */
32911341Sstevel 	{ 0x0c, 0x0e },	/* Time Constant = 0x000e for 9600 baud */
32921341Sstevel 	{ 0x0d, 0x00 },	/* High byte of time constant */
32931341Sstevel 	{ 0x0e, 0x02 },	/* BR generator comes from Z-SCC's PCLK input */
32941341Sstevel 	{ 0x03, 0xc1 },	/* Rx is 8 bits/char, Rx is enabled */
32951341Sstevel 	{ 0x05, 0xea },	/* DTR, Tx is 8 bits/char, Tx is enabled, RTS */
32961341Sstevel 	{ 0x0e, 0x03 },	/* BR comes from PCLK, BR generator is enabled */
32971341Sstevel 	{ 0x00, 0x30 },	/* Error reset */
32981341Sstevel 	{ 0x00, 0x30 },	/* Error reset */
32991341Sstevel 	{ 0x00, 0x10 },	/* external status reset */
33001341Sstevel 	{ 0x03, 0xc1 },	/* Rx is 8 bits/char, Rx is enabled */
33011341Sstevel 	{ TABLE_END, 0x0 }
33021341Sstevel };
33031341Sstevel 
33041341Sstevel static void
init_remote_console_uart(struct sysctrl_soft_state * softsp)33051341Sstevel init_remote_console_uart(struct sysctrl_soft_state *softsp)
33061341Sstevel {
33071341Sstevel 	int i = 0;
33081341Sstevel 
33091341Sstevel 	/*
33101341Sstevel 	 * Serial chip expects software to write to the control
33111341Sstevel 	 * register first with the desired register number. Then
33121341Sstevel 	 * write to the control register with the desired data.
33131341Sstevel 	 * So walk thru table writing the register/data pairs to
33141341Sstevel 	 * the serial port chip.
33151341Sstevel 	 */
33161341Sstevel 	while (uart_table[i].reg != TABLE_END) {
33171341Sstevel 		*(softsp->rcons_ctl) = uart_table[i].reg;
33181341Sstevel 		*(softsp->rcons_ctl) = uart_table[i].data;
33191341Sstevel 		i++;
33201341Sstevel 	}
33211341Sstevel }
33221341Sstevel 
33231341Sstevel /*
33241341Sstevel  * return the slot information of the system
33251341Sstevel  *
33261341Sstevel  * function take a sysctrl_soft_state, so it's ready for sunfire+
33271341Sstevel  * change which requires 2 registers to decide the system type.
33281341Sstevel  */
33291341Sstevel static void
sysc_slot_info(int nslots,int * start,int * limit,int * incr)33301341Sstevel sysc_slot_info(int nslots, int *start, int *limit, int *incr)
33311341Sstevel {
33321341Sstevel 	switch (nslots) {
33331341Sstevel 	case 8:
33341341Sstevel 		*start = 0;
33351341Sstevel 		*limit = 8;
33361341Sstevel 		*incr = 1;
33371341Sstevel 		break;
33381341Sstevel 	case 5:
33391341Sstevel 		*start = 1;
33401341Sstevel 		*limit = 10;
33411341Sstevel 		*incr = 2;
33421341Sstevel 		break;
33431341Sstevel 	case 4:
33441341Sstevel 		*start = 1;
33451341Sstevel 		*limit = 8;
33461341Sstevel 		*incr = 2;
33471341Sstevel 		break;
33481341Sstevel 	case 0:
33491341Sstevel 	case 16:
33501341Sstevel 	default:
33511341Sstevel 		*start = 0;
33521341Sstevel 		*limit = 16;
33531341Sstevel 		*incr = 1;
33541341Sstevel 		break;
33551341Sstevel 	}
33561341Sstevel }
33571341Sstevel 
33581341Sstevel /*
33591341Sstevel  * reinitialize the Remote Console on the clock board
33601341Sstevel  *
33611341Sstevel  * with V5_AUX power outage the Remote Console ends up in
33621341Sstevel  * unknown state and has to be reinitilized if it was enabled
33631341Sstevel  * initially.
33641341Sstevel  */
33651341Sstevel static void
rcons_reinit(struct sysctrl_soft_state * softsp)33661341Sstevel rcons_reinit(struct sysctrl_soft_state *softsp)
33671341Sstevel {
33681341Sstevel 	uchar_t tmp_reg;
33691341Sstevel 
33701341Sstevel 	if (!(softsp->rcons_ctl))
33711341Sstevel 		/*
33721341Sstevel 		 * There is no OBP register set for the remote console UART,
33731341Sstevel 		 * so offset from the last register set, the misc register
33741341Sstevel 		 * set, in order to map in the remote console UART.
33751341Sstevel 		 */
33761341Sstevel 		if (ddi_map_regs(softsp->dip, 1, (caddr_t *)&softsp->rcons_ctl,
33771341Sstevel 		    RMT_CONS_OFFSET, RMT_CONS_LEN)) {
33781341Sstevel 			cmn_err(CE_WARN, "Unable to reinitialize "
33797656SSherry.Moore@Sun.COM 			    "remote console.");
33801341Sstevel 			return;
33811341Sstevel 		}
33821341Sstevel 
33831341Sstevel 
33841341Sstevel 	/* Disable the remote console reset control bits. */
33851341Sstevel 	*(softsp->clk_freq2) &= ~RCONS_UART_EN;
33861341Sstevel 
33871341Sstevel 	/* flush the hardware buffers */
33881341Sstevel 	tmp_reg = *(softsp->csr);
33891341Sstevel 
33901341Sstevel 	/*
33911341Sstevel 	 * Program the UART to watch ttya console.
33921341Sstevel 	 */
33931341Sstevel 	init_remote_console_uart(softsp);
33941341Sstevel 
33951341Sstevel 	/* Now enable the remote console reset control bits. */
33961341Sstevel 	*(softsp->clk_freq2) |= RCONS_UART_EN;
33971341Sstevel 
33981341Sstevel 	/* flush the hardware buffers */
33991341Sstevel 	tmp_reg = *(softsp->csr);
34001341Sstevel 
34011341Sstevel 	/* print some info for user to watch */
34021341Sstevel 	cmn_err(CE_NOTE, "Remote console reinitialized");
34031341Sstevel #ifdef lint
34041341Sstevel 	tmp_reg = tmp_reg;
34051341Sstevel #endif
34061341Sstevel }
3407