xref: /onnv-gate/usr/src/uts/sun4u/lw8/io/lw8.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
23*11311SSurya.Prakki@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel 
281708Sstevel #include <sys/time.h>
291708Sstevel #include <sys/errno.h>
301708Sstevel #include <sys/kmem.h>
311708Sstevel #include <sys/stat.h>
321708Sstevel #include <sys/cmn_err.h>
331708Sstevel 
341708Sstevel #include <sys/conf.h>
351708Sstevel #include <sys/modctl.h>
361708Sstevel #include <sys/devops.h>
371708Sstevel #include <sys/ddi.h>
381708Sstevel #include <sys/sunddi.h>
391708Sstevel #include <sys/callb.h>
401708Sstevel #include <sys/disp.h>
411708Sstevel #include <sys/strlog.h>
421708Sstevel 
431708Sstevel #include <sys/sgevents.h>
441708Sstevel #include <sys/serengeti.h>
451708Sstevel #include <sys/sgsbbc.h>
461708Sstevel #include <sys/sgsbbc_iosram.h>
471708Sstevel #include <sys/sgsbbc_mailbox.h>
481708Sstevel #include <sys/uadmin.h>
491708Sstevel #include <sys/machsystm.h>
501708Sstevel #include <sys/sysevent.h>
511708Sstevel #include <sys/sysevent/dr.h>
521708Sstevel #include <sys/sysevent/eventdefs.h>
531708Sstevel #include <sys/file.h>
541708Sstevel #include <sys/lw8.h>
551708Sstevel #include <sys/lw8_impl.h>
561708Sstevel #include <sys/plat_ecc_unum.h>
571708Sstevel 
581708Sstevel /*
591708Sstevel  * Global Variables - can be patched from Solaris
601708Sstevel  * ==============================================
611708Sstevel  */
621708Sstevel 
631708Sstevel /*
641708Sstevel  * Module Variables
651708Sstevel  * ================
661708Sstevel  */
671708Sstevel 
681708Sstevel /*
691708Sstevel  * functions local to this driver.
701708Sstevel  */
711708Sstevel static int	lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
721708Sstevel static int	lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
731708Sstevel static int	lw8_add_intr_handlers(void);
741708Sstevel static int	lw8_remove_intr_handlers(void);
751708Sstevel static void lw8_wakeup_sleepers(void);
761708Sstevel static uint_t	lw8_fast_shutdown(char *arg);
771708Sstevel static uint_t	lw8_slow_shutdown(char *arg);
781708Sstevel static uint_t	lw8_event_data_handler(char *);
791708Sstevel static uint_t	lw8_dr_data_handler(char *);
801708Sstevel static uint_t	lw8_env_data_handler(char *);
811708Sstevel static uint_t	lw8_cap_ecc_msg_handler(char *);
821708Sstevel static int	lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
831708Sstevel static int	lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
841708Sstevel static int	lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
851708Sstevel     cred_t *cred_p, int *rval_p);
861708Sstevel static void	lw8_logger_start(void);
871708Sstevel static void	lw8_logger_destroy(void);
881708Sstevel static void	lw8_logger_wakeup(void);
891708Sstevel 
901708Sstevel /*
911708Sstevel  * Driver entry points
921708Sstevel  */
931708Sstevel static struct cb_ops lw8_cb_ops = {
941708Sstevel 	lw8_open,	/* open */
951708Sstevel 	lw8_close,	/* close */
961708Sstevel 	nodev,		/* strategy() */
971708Sstevel 	nodev,		/* print() */
981708Sstevel 	nodev,		/* dump() */
991708Sstevel 	nodev,		/* read() */
1001708Sstevel 	nodev,		/* write() */
1011708Sstevel 	lw8_ioctl,	/* ioctl() */
1021708Sstevel 	nodev,		/* devmap() */
1031708Sstevel 	nodev,		/* mmap() */
1041708Sstevel 	ddi_segmap,	/* segmap() */
1051708Sstevel 	nochpoll,	/* poll() */
1061708Sstevel 	ddi_prop_op,    /* prop_op() */
1071708Sstevel 	NULL,		/* cb_str */
1081708Sstevel 	D_NEW | D_MP	/* cb_flag */
1091708Sstevel };
1101708Sstevel 
1111708Sstevel 
1121708Sstevel static struct dev_ops lw8_ops = {
1131708Sstevel 	DEVO_REV,
1141708Sstevel 	0,			/* ref count */
1151708Sstevel 	ddi_getinfo_1to1,	/* getinfo() */
1161708Sstevel 	nulldev,		/* identify() */
1171708Sstevel 	nulldev,		/* probe() */
1181708Sstevel 	lw8_attach,		/* attach() */
1191708Sstevel 	lw8_detach,		/* detach */
1201708Sstevel 	nodev,			/* reset */
1211708Sstevel 	&lw8_cb_ops,		/* pointer to cb_ops structure */
1221708Sstevel 	(struct bus_ops *)NULL,
1237656SSherry.Moore@Sun.COM 	nulldev,		/* power() */
1247656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,		/* quiesce() */
1251708Sstevel };
1261708Sstevel 
1271708Sstevel /*
1281708Sstevel  * Loadable module support.
1291708Sstevel  */
1301708Sstevel extern struct mod_ops mod_driverops;
1311708Sstevel 
1321708Sstevel static struct modldrv modldrv = {
1331708Sstevel 	&mod_driverops,			/* Type of module. This is a driver */
1347656SSherry.Moore@Sun.COM 	"Netra-T12 control driver",	/* Name of the module */
1351708Sstevel 	&lw8_ops			/* pointer to the dev_ops structure */
1361708Sstevel };
1371708Sstevel 
1381708Sstevel static struct modlinkage modlinkage = {
1391708Sstevel 	MODREV_1,
1401708Sstevel 	&modldrv,
1411708Sstevel 	NULL
1421708Sstevel };
1431708Sstevel 
1441708Sstevel /*
1451708Sstevel  * messages
1461708Sstevel  */
1471708Sstevel #define	SHUTDOWN_EVENT_MSG		"lw8: system shutdown due to " \
1481708Sstevel 					"SC request.\n"
1491708Sstevel #define	VOLTAGE_EVENT_MSG		"lw8: system shutdown due to " \
1501708Sstevel 					"voltage out of range.\n"
1511708Sstevel #define	TEMPERATURE_EVENT_MSG		"lw8: system shutdown due to " \
1521708Sstevel 					"temperature exceeding limits.\n"
1531708Sstevel #define	FANFAIL_EVENT_MSG		"lw8: system shutdown due to " \
1541708Sstevel 					"too many fan failures.\n"
1551708Sstevel #define	NO_SCC_EVENT_MSG		"lw8: system shutdown due to " \
1561708Sstevel 					"no system configuration card.\n"
1571708Sstevel 
1581708Sstevel /*
1591708Sstevel  * led table - the following provides a cache of the led state - needed
1601708Sstevel  * to avoid the overhead of readoing from the SC each time
1611708Sstevel  */
1621708Sstevel 
1631708Sstevel struct led_info {
1641708Sstevel 	char	id[MAX_ID_LEN];
1651708Sstevel 	int	position;
1661708Sstevel 	int 	status;
1671708Sstevel 	char	color[MAX_COLOR_LEN];
1681708Sstevel };
1691708Sstevel 
1701708Sstevel static struct fru_led_info {
1711708Sstevel 	char    location[MAX_LOCATION_LEN];
1721708Sstevel 	struct led_info led_info[MAX_LEDS_PER_FRU];
1731708Sstevel } fru_led_table[MAX_FRUS] = {
1741708Sstevel 	"SB0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1751708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1761708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
1771708Sstevel 	"PS0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1781708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1791708Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
1801708Sstevel 	"SB2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1811708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1821708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
1831708Sstevel 	"PS1", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1841708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1851708Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
1861708Sstevel 	"SB4", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1871708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1881708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
1891708Sstevel 	"PS2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1901708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1911708Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
1921708Sstevel 	"IB6", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1931708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1941708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
1951708Sstevel 	"PS3", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1961708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
1971708Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
1981708Sstevel 	"FT0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
1991708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
2001708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
2011708Sstevel 	"FAN0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2021708Sstevel 	"FAN1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2031708Sstevel 	"FAN2", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2041708Sstevel 	"FAN3", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2051708Sstevel 	"FAN4", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2061708Sstevel 	"FAN5", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2071708Sstevel 	"FAN6", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2081708Sstevel 	"FAN7", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2091708Sstevel 	"FAN8", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2101708Sstevel 	"FAN9", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
2111708Sstevel 	"DISK0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
2121708Sstevel 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
2131708Sstevel 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
2141708Sstevel 	"DISK1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
2151708Sstevel 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
2161708Sstevel 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
2171708Sstevel 	"RP0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
2181708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
2191708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
2201708Sstevel 	"RP2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
2211708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
2221708Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
2231708Sstevel 	"chassis", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
2241708Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
2251708Sstevel 		"locator", LOM_LED_POSITION_FRU, 0, "white",
2261708Sstevel 		"top_access", LOM_LED_POSITION_FRU, 0, "amber",
2271708Sstevel 		"alarm1", LOM_LED_POSITION_FRU, 0, "amber",
2281708Sstevel 		"alarm2", LOM_LED_POSITION_FRU, 0, "amber",
2291708Sstevel 		"system", LOM_LED_POSITION_FRU, 0, "green",
2301708Sstevel 		"supplyA", LOM_LED_POSITION_FRU, 0, "green",
2311708Sstevel 		"supplyB", LOM_LED_POSITION_FRU, 0, "green"},
2321708Sstevel };
2331708Sstevel 
2341708Sstevel char    *fru_locn[MAX_LOCATION_LEN] = {
2351708Sstevel 	"SB0",
2361708Sstevel 	"PS0",
2371708Sstevel 	"SB2",
2381708Sstevel 	"PS1",
2391708Sstevel 	"SB4",
2401708Sstevel 	"PS2",
2411708Sstevel 	"IB6",
2421708Sstevel 	"PS3",
2431708Sstevel 	"SCC",
2441708Sstevel 	"SSC1",
2451708Sstevel };
2461708Sstevel 
2471708Sstevel /*
2481708Sstevel  * mutexes which protect the interrupt handlers.
2491708Sstevel  */
2501708Sstevel static kmutex_t		lw8_shutdown_hdlr_lock;
2511708Sstevel static kmutex_t		lw8_dr_hdlr_lock;
2521708Sstevel static kmutex_t		lw8_env_hdlr_lock;
2531708Sstevel static kmutex_t		lw8_event_mutex;
2541708Sstevel static kmutex_t		lw8_logger_lock;
2551708Sstevel static kmutex_t		lw8_cap_msg_hdlr_lock;
2561708Sstevel static kcondvar_t	lw8_event_cv;
2571708Sstevel static kcondvar_t	lw8_logger_sig_cv;
2581708Sstevel 
2591708Sstevel /*
2601708Sstevel  * state booleans
2611708Sstevel  */
2621708Sstevel static boolean_t	lw8_event_pending = B_FALSE;
2631708Sstevel static boolean_t	led_state_cached = B_FALSE;
2641708Sstevel 
2651708Sstevel /*
2661708Sstevel  * Payloads of the event handlers.
2671708Sstevel  */
2681708Sstevel static lw8_event_t	lw8_shutdown_payload;
2691708Sstevel static sbbc_msg_t	lw8_shutdown_payload_msg;
2701708Sstevel static sg_system_fru_descriptor_t	lw8_dr_payload;
2711708Sstevel static sbbc_msg_t	lw8_dr_payload_msg;
2721708Sstevel static sg_event_fan_status_t		lw8_env_payload;
2731708Sstevel static sbbc_msg_t	lw8_env_payload_msg;
2741708Sstevel static plat_capability_data_t	lw8_cap_payload;
2751708Sstevel static sbbc_msg_t	lw8_cap_payload_msg;
2761708Sstevel 
2771708Sstevel /*
2781708Sstevel  * The IDs of the soft interrupts
2791708Sstevel  */
2801708Sstevel static ddi_softintr_t   lw8_slow_shutdown_softint_id;
2811708Sstevel static ddi_softintr_t   lw8_fast_shutdown_softint_id;
2821708Sstevel 
2831708Sstevel /*
2841708Sstevel  * Logger commands..
2851708Sstevel  */
2861708Sstevel #define	LW8_LOGGER_EXITNOW	-1
2871708Sstevel #define	LW8_LOGGER_WAIT	0
2881708Sstevel #define	LW8_LOGGER_PROCESSNOW	1
2891708Sstevel 
2901708Sstevel /*
2911708Sstevel  * Logger thread state
2921708Sstevel  */
2931708Sstevel static int lw8_logger_sig = LW8_LOGGER_WAIT;
2941708Sstevel static kt_did_t lw8_logger_tid = 0;
2951708Sstevel 
2961708Sstevel extern pri_t maxclsyspri;
2971708Sstevel 
2981708Sstevel int
_init(void)2991708Sstevel _init(void)
3001708Sstevel {
3011708Sstevel 	int	error = 0;
3021708Sstevel 
3031708Sstevel 	mutex_init(&lw8_shutdown_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
3041708Sstevel 	mutex_init(&lw8_dr_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
3051708Sstevel 	mutex_init(&lw8_env_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
3061708Sstevel 	mutex_init(&lw8_cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
3071708Sstevel 	mutex_init(&lw8_event_mutex, NULL, MUTEX_DRIVER, NULL);
3081708Sstevel 	mutex_init(&lw8_logger_lock, NULL, MUTEX_DRIVER, NULL);
3091708Sstevel 	cv_init(&lw8_event_cv, NULL, CV_DRIVER, NULL);
3101708Sstevel 	cv_init(&lw8_logger_sig_cv, NULL, CV_DRIVER, NULL);
3111708Sstevel 
3121708Sstevel 	error = mod_install(&modlinkage);
3131708Sstevel 	if (error) {
3141708Sstevel 		cv_destroy(&lw8_logger_sig_cv);
3151708Sstevel 		cv_destroy(&lw8_event_cv);
3161708Sstevel 		mutex_destroy(&lw8_logger_lock);
3171708Sstevel 		mutex_destroy(&lw8_event_mutex);
3181708Sstevel 		mutex_destroy(&lw8_env_hdlr_lock);
3191708Sstevel 		mutex_destroy(&lw8_cap_msg_hdlr_lock);
3201708Sstevel 		mutex_destroy(&lw8_dr_hdlr_lock);
3211708Sstevel 		mutex_destroy(&lw8_shutdown_hdlr_lock);
3221708Sstevel 	}
3231708Sstevel 	return (error);
3241708Sstevel }
3251708Sstevel 
3261708Sstevel 
3271708Sstevel int
_info(struct modinfo * modinfop)3281708Sstevel _info(struct modinfo *modinfop)
3291708Sstevel {
3301708Sstevel 	return (mod_info(&modlinkage, modinfop));
3311708Sstevel }
3321708Sstevel 
3331708Sstevel 
3341708Sstevel int
_fini(void)3351708Sstevel _fini(void)
3361708Sstevel {
3371708Sstevel 	int	error = 0;
3381708Sstevel 
3391708Sstevel 	error = mod_remove(&modlinkage);
3401708Sstevel 	if (error)
3411708Sstevel 		return (error);
3421708Sstevel 	cv_destroy(&lw8_logger_sig_cv);
3431708Sstevel 	cv_destroy(&lw8_event_cv);
3441708Sstevel 	mutex_destroy(&lw8_logger_lock);
3451708Sstevel 	mutex_destroy(&lw8_event_mutex);
3461708Sstevel 	mutex_destroy(&lw8_env_hdlr_lock);
3471708Sstevel 	mutex_destroy(&lw8_cap_msg_hdlr_lock);
3481708Sstevel 	mutex_destroy(&lw8_dr_hdlr_lock);
3491708Sstevel 	mutex_destroy(&lw8_shutdown_hdlr_lock);
3501708Sstevel 	return (error);
3511708Sstevel }
3521708Sstevel 
3531708Sstevel 
3541708Sstevel static int
lw8_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3551708Sstevel lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3561708Sstevel {
3571708Sstevel 	int			instance;
3581708Sstevel 	int			err;
3591708Sstevel 
3601708Sstevel 	switch (cmd) {
3611708Sstevel 	case DDI_ATTACH:
3621708Sstevel 		/*
3631708Sstevel 		 * only allow one instance
3641708Sstevel 		 */
3651708Sstevel 		instance = ddi_get_instance(dip);
3661708Sstevel 		if (instance != 0)
3671708Sstevel 			return (DDI_FAILURE);
3681708Sstevel 
3691708Sstevel 		err = ddi_create_minor_node(dip, "lw8", S_IFCHR,
3707656SSherry.Moore@Sun.COM 		    instance, DDI_PSEUDO, NULL);
3711708Sstevel 		if (err != DDI_SUCCESS)
3721708Sstevel 			return (DDI_FAILURE);
3731708Sstevel 
3741708Sstevel 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
3751708Sstevel 		    &lw8_slow_shutdown_softint_id, NULL, NULL,
3761708Sstevel 		    lw8_slow_shutdown, NULL);
3771708Sstevel 		if (err != 0) {
3781708Sstevel 			cmn_err(CE_WARN, "Failed to add polling softint"
3791708Sstevel 			    "handler for lw8. Err=%d", err);
3801708Sstevel 			ddi_remove_minor_node(dip, NULL);
3811708Sstevel 			return (DDI_FAILURE);
3821708Sstevel 		}
3831708Sstevel 
3841708Sstevel 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
3851708Sstevel 		    &lw8_fast_shutdown_softint_id, NULL, NULL,
3861708Sstevel 		    lw8_fast_shutdown, NULL);
3871708Sstevel 		if (err != 0) {
3881708Sstevel 			cmn_err(CE_WARN, "Failed to add polling softint"
3891708Sstevel 			    "handler for lw8. Err=%d", err);
3901708Sstevel 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
3911708Sstevel 			ddi_remove_minor_node(dip, NULL);
3921708Sstevel 			return (DDI_FAILURE);
3931708Sstevel 		}
3941708Sstevel 
3951708Sstevel 		lw8_logger_start();
3961708Sstevel 
3971708Sstevel 		/*
3981708Sstevel 		 * Add the handlers which watch for unsolicited messages
3991708Sstevel 		 * and post event to Sysevent Framework.
4001708Sstevel 		 */
4011708Sstevel 		err = lw8_add_intr_handlers();
4021708Sstevel 		if (err != DDI_SUCCESS) {
4031708Sstevel 			cmn_err(CE_WARN, "Failed to add event handlers");
4041708Sstevel 			lw8_logger_destroy();
4051708Sstevel 			ddi_remove_softintr(lw8_fast_shutdown_softint_id);
4061708Sstevel 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
4071708Sstevel 			ddi_remove_minor_node(dip, NULL);
4081708Sstevel 			return (DDI_FAILURE);
4091708Sstevel 		}
4101708Sstevel 
4111708Sstevel 		ddi_report_dev(dip);
4121708Sstevel 		return (DDI_SUCCESS);
4131708Sstevel 	case DDI_RESUME:
4141708Sstevel 		return (DDI_SUCCESS);
4151708Sstevel 	default:
4161708Sstevel 		return (DDI_FAILURE);
4171708Sstevel 	}
4181708Sstevel }
4191708Sstevel 
4201708Sstevel 
4211708Sstevel static int
lw8_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4221708Sstevel lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4231708Sstevel {
4241708Sstevel 	int	instance;
4251708Sstevel 	int	err;
4261708Sstevel 
4271708Sstevel 	switch (cmd) {
4281708Sstevel 	case DDI_DETACH:
4291708Sstevel 		instance = ddi_get_instance(dip);
4301708Sstevel 		if (instance != 0)
4311708Sstevel 			return (DDI_FAILURE);
4321708Sstevel 
4331708Sstevel 		/*
4341708Sstevel 		 * Remove the handlers which watch for unsolicited messages
4351708Sstevel 		 * and post event to Sysevent Framework.
4361708Sstevel 		 */
4371708Sstevel 		err = lw8_remove_intr_handlers();
4381708Sstevel 		if (err != DDI_SUCCESS) {
4391708Sstevel 			cmn_err(CE_WARN, "Failed to remove event handlers");
4401708Sstevel 			return (DDI_FAILURE);
4411708Sstevel 		}
4421708Sstevel 		lw8_logger_destroy();
4431708Sstevel 		ddi_remove_softintr(lw8_slow_shutdown_softint_id);
4441708Sstevel 		ddi_remove_softintr(lw8_fast_shutdown_softint_id);
4451708Sstevel 		ddi_remove_minor_node(dip, NULL);
4461708Sstevel 		return (DDI_SUCCESS);
4471708Sstevel 	case DDI_SUSPEND:
4481708Sstevel 		return (DDI_SUCCESS);
4491708Sstevel 	default:
4501708Sstevel 		return (DDI_FAILURE);
4511708Sstevel 	}
4521708Sstevel }
4531708Sstevel 
4541708Sstevel static int
lw8_add_intr_handlers()4551708Sstevel lw8_add_intr_handlers()
4561708Sstevel {
4571708Sstevel 	int	err;
4581708Sstevel 
4591708Sstevel 	lw8_shutdown_payload_msg.msg_buf = (caddr_t)&lw8_shutdown_payload;
4601708Sstevel 	lw8_shutdown_payload_msg.msg_len = sizeof (lw8_shutdown_payload);
4611708Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, lw8_event_data_handler,
4621708Sstevel 	    &lw8_shutdown_payload_msg, NULL, &lw8_shutdown_hdlr_lock);
4631708Sstevel 	if (err != 0) {
4641708Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_LW8 "
4657656SSherry.Moore@Sun.COM 		    " handler. Err=%d", err);
4661708Sstevel 		return (DDI_FAILURE);
4671708Sstevel 	}
4681708Sstevel 
4691708Sstevel 	lw8_dr_payload_msg.msg_buf = (caddr_t)&lw8_dr_payload;
4701708Sstevel 	lw8_dr_payload_msg.msg_len = sizeof (lw8_dr_payload);
4711708Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler,
4727656SSherry.Moore@Sun.COM 	    &lw8_dr_payload_msg, NULL, &lw8_dr_hdlr_lock);
4731708Sstevel 	if (err != 0) {
4741708Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_GENERIC "
4757656SSherry.Moore@Sun.COM 		    " handler. Err=%d", err);
476*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
477*11311SSurya.Prakki@Sun.COM 		    lw8_event_data_handler);
4781708Sstevel 		return (DDI_FAILURE);
4791708Sstevel 	}
4801708Sstevel 
4811708Sstevel 	lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
4821708Sstevel 	lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
4831708Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
4847656SSherry.Moore@Sun.COM 	    &lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
4851708Sstevel 	if (err != 0) {
4861708Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
4877656SSherry.Moore@Sun.COM 		    " handler. Err=%d", err);
488*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
489*11311SSurya.Prakki@Sun.COM 		    lw8_dr_data_handler);
490*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
491*11311SSurya.Prakki@Sun.COM 		    lw8_event_data_handler);
4921708Sstevel 		return (DDI_FAILURE);
4931708Sstevel 	}
4941708Sstevel 
4951708Sstevel 	lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
4961708Sstevel 	lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
4971708Sstevel 	err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
4981708Sstevel 	    &lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
4991708Sstevel 	if (err != 0) {
5001708Sstevel 		cmn_err(CE_WARN, "Failed to register INFO_MBOX "
5011708Sstevel 		    " handler. Err=%d", err);
502*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
503*11311SSurya.Prakki@Sun.COM 		    lw8_dr_data_handler);
504*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
505*11311SSurya.Prakki@Sun.COM 		    lw8_event_data_handler);
506*11311SSurya.Prakki@Sun.COM 		(void) sbbc_mbox_unreg_intr(INFO_MBOX,
507*11311SSurya.Prakki@Sun.COM 		    lw8_cap_ecc_msg_handler);
5081708Sstevel 		return (DDI_FAILURE);
5091708Sstevel 	}
5101708Sstevel 
5111708Sstevel 	return (DDI_SUCCESS);
5121708Sstevel }
5131708Sstevel 
5141708Sstevel static int
lw8_remove_intr_handlers(void)5151708Sstevel lw8_remove_intr_handlers(void)
5161708Sstevel {
5171708Sstevel 	int	rv = DDI_SUCCESS;
5181708Sstevel 	int	err;
5191708Sstevel 
5201708Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
5211708Sstevel 	if (err != 0) {
5221708Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
5237656SSherry.Moore@Sun.COM 		    "handler. Err=%d", err);
5241708Sstevel 		rv = DDI_FAILURE;
5251708Sstevel 	}
5261708Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
5271708Sstevel 	if (err != 0) {
5281708Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
5297656SSherry.Moore@Sun.COM 		    "handler. Err=%d", err);
5301708Sstevel 		rv = DDI_FAILURE;
5311708Sstevel 	}
5321708Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
5331708Sstevel 	if (err != 0) {
5341708Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
5357656SSherry.Moore@Sun.COM 		    "handler. Err=%d", err);
5361708Sstevel 		rv = DDI_FAILURE;
5371708Sstevel 	}
5381708Sstevel 	err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
5391708Sstevel 	if (err != 0) {
5401708Sstevel 		cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
5411708Sstevel 		    "handler. Err=%d", err);
5421708Sstevel 		rv = DDI_FAILURE;
5431708Sstevel 	}
5441708Sstevel 	return (rv);
5451708Sstevel }
5461708Sstevel 
5471708Sstevel static uint_t
lw8_dr_data_handler(char * arg)5481708Sstevel lw8_dr_data_handler(char *arg)
5491708Sstevel {
5501708Sstevel 	sg_system_fru_descriptor_t	*payload;
5511708Sstevel 	sbbc_msg_t			*msg;
5521708Sstevel 	int				hint;
5531708Sstevel 	sysevent_t			*ev;
5541708Sstevel 	sysevent_id_t			eid;
5551708Sstevel 	int				rv = 0;
5561708Sstevel 	sysevent_value_t		evnt_val;
5571708Sstevel 	sysevent_attr_list_t		*evnt_attr_list = NULL;
5581708Sstevel 	char				attach_pnt[MAXPATHLEN];
5591708Sstevel 
5601708Sstevel 	msg = (sbbc_msg_t *)arg;
5611708Sstevel 	if (msg == NULL) {
5621708Sstevel 		return (DDI_INTR_CLAIMED);
5631708Sstevel 	}
5641708Sstevel 	payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
5651708Sstevel 	if (payload == NULL) {
5661708Sstevel 		return (DDI_INTR_CLAIMED);
5671708Sstevel 	}
5681708Sstevel 	if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
5691708Sstevel 	    sizeof (char *)) {
5701708Sstevel 		return (DDI_INTR_CLAIMED);
5711708Sstevel 	}
5721708Sstevel 
5731708Sstevel 	/*
5741708Sstevel 	 * if not SB send sysevent (SBs send sysevent from ssm driver)
5751708Sstevel 	 */
5761708Sstevel 	if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
5771708Sstevel 		switch (payload->event_details) {
5781708Sstevel 		case SG_EVT_BOARD_ABSENT:
5791708Sstevel 			hint = SE_HINT_REMOVE;
5801708Sstevel 			break;
5811708Sstevel 		case SG_EVT_BOARD_PRESENT:
5821708Sstevel 			hint = SE_HINT_INSERT;
5831708Sstevel 			break;
5841708Sstevel 		default:
5851708Sstevel 			hint = SE_NO_HINT;
5861708Sstevel 			break;
5871708Sstevel 		}
5881708Sstevel 		(void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
5891708Sstevel 		    fru_locn[payload->slot]);
5901708Sstevel 		ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
5911708Sstevel 		    KM_NOSLEEP);
5921708Sstevel 		if (ev == NULL) {
5931708Sstevel 			cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
5941708Sstevel 			return (DDI_INTR_CLAIMED);
5951708Sstevel 		}
5961708Sstevel 		evnt_val.value_type = SE_DATA_TYPE_STRING;
5971708Sstevel 		evnt_val.value.sv_string = attach_pnt;
5981708Sstevel 		rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
5991708Sstevel 		    KM_NOSLEEP);
6001708Sstevel 		if (rv != 0) {
6011708Sstevel 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
6021708Sstevel 			    DR_AP_ID, EC_DR);
6031708Sstevel 			sysevent_free(ev);
6041708Sstevel 			return (DDI_INTR_CLAIMED);
6051708Sstevel 		}
6061708Sstevel 
6071708Sstevel 		/*
6081708Sstevel 		 * Add the hint
6091708Sstevel 		 */
6101708Sstevel 		evnt_val.value_type = SE_DATA_TYPE_STRING;
6111708Sstevel 		evnt_val.value.sv_string = SE_HINT2STR(hint);
6121708Sstevel 		rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
6131708Sstevel 		    KM_NOSLEEP);
6141708Sstevel 		if (rv != 0) {
6151708Sstevel 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
6161708Sstevel 			    DR_HINT, EC_DR);
6171708Sstevel 			sysevent_free_attr(evnt_attr_list);
6181708Sstevel 			sysevent_free(ev);
6191708Sstevel 			return (DDI_INTR_CLAIMED);
6201708Sstevel 		}
6211708Sstevel 		if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
6221708Sstevel 			cmn_err(CE_WARN, "Failed to attach attr list for %s "
6231708Sstevel 			    "event", EC_DR);
6241708Sstevel 			sysevent_free_attr(evnt_attr_list);
6251708Sstevel 			sysevent_free(ev);
6261708Sstevel 			return (DDI_INTR_CLAIMED);
6271708Sstevel 		}
6281708Sstevel 		rv = log_sysevent(ev, KM_NOSLEEP, &eid);
6291708Sstevel 		if (rv != 0) {
6301708Sstevel 			cmn_err(CE_WARN,
6311708Sstevel 			    "lw8_dr_event_handler: failed to log event");
6321708Sstevel 		}
6331708Sstevel 		sysevent_free(ev);
6341708Sstevel 	}
6351708Sstevel 	lw8_wakeup_sleepers();
6361708Sstevel 	return (DDI_INTR_CLAIMED);
6371708Sstevel }
6381708Sstevel 
6391708Sstevel static uint_t
lw8_cap_ecc_msg_handler(char * addr)6401708Sstevel lw8_cap_ecc_msg_handler(char *addr)
6411708Sstevel {
6421708Sstevel 	sbbc_msg_t *msg = NULL;
6431708Sstevel 	plat_capability_data_t *cap = NULL;
6441708Sstevel 
6451708Sstevel 	msg = (sbbc_msg_t *)addr;
6461708Sstevel 	if (msg == NULL || msg->msg_buf == NULL)
6471708Sstevel 		return (DDI_INTR_CLAIMED);
6481708Sstevel 
6491708Sstevel 	cap = (plat_capability_data_t *)msg->msg_buf;
6501708Sstevel 	switch (cap->capd_msg_type) {
6511708Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
6521708Sstevel 		plat_ecc_capability_sc_set(cap->capd_capability);
6531708Sstevel 		break;
6541708Sstevel 	default:
6551708Sstevel 		break;
6561708Sstevel 	}
6571708Sstevel 
6581708Sstevel 	return (DDI_INTR_CLAIMED);
6591708Sstevel }
6601708Sstevel 
6611708Sstevel /*ARGSUSED*/
6621708Sstevel static uint_t
lw8_env_data_handler(char * arg)6631708Sstevel lw8_env_data_handler(char *arg)
6641708Sstevel {
6651708Sstevel 	lw8_wakeup_sleepers();
6661708Sstevel 	return (DDI_INTR_CLAIMED);
6671708Sstevel }
6681708Sstevel 
6691708Sstevel /*
6701708Sstevel  * wakeup sleepers + mark led cache for this fru as invalid
6711708Sstevel  */
6721708Sstevel static void
lw8_wakeup_sleepers()6731708Sstevel lw8_wakeup_sleepers()
6741708Sstevel {
6751708Sstevel 	mutex_enter(&lw8_event_mutex);
6761708Sstevel 	lw8_event_pending = B_TRUE;
6771708Sstevel 	cv_broadcast(&lw8_event_cv);
6781708Sstevel 	led_state_cached = B_FALSE;
6791708Sstevel 	mutex_exit(&lw8_event_mutex);
6801708Sstevel }
6811708Sstevel 
6821708Sstevel /*
6831708Sstevel  * This function is triggered by a soft interrupt and it's purpose is to call
6841708Sstevel  * to kadmin() to shutdown the system.
6851708Sstevel  */
6861708Sstevel /*ARGSUSED*/
6871708Sstevel static uint_t
lw8_fast_shutdown(char * arg)6881708Sstevel lw8_fast_shutdown(char *arg)
6891708Sstevel {
6901708Sstevel 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
6911708Sstevel 
6921708Sstevel 	/*
6931708Sstevel 	 * If kadmin fails for some reason then we bring the system down
6941708Sstevel 	 * via power_down(), or failing that using halt().
6951708Sstevel 	 */
6961708Sstevel 	power_down("kadmin() failed, trying power_down()");
6971708Sstevel 
6981708Sstevel 	halt("power_down() failed, trying halt()");
6991708Sstevel 
7001708Sstevel 	/*
7011708Sstevel 	 * We should never make it this far, so something must have gone
7021708Sstevel 	 * horribly, horribly wrong.
7031708Sstevel 	 */
7041708Sstevel 	/*NOTREACHED*/
7052840Scarlsonj 	return (DDI_INTR_UNCLAIMED);
7061708Sstevel }
7071708Sstevel 
7081708Sstevel /*
7091708Sstevel  * This function is triggered by a soft interrupt and it's purpose is to call
7101708Sstevel  * to do_shutdown() to shutdown the system.
7111708Sstevel  */
7121708Sstevel /*ARGSUSED*/
7131708Sstevel static uint_t
lw8_slow_shutdown(char * arg)7141708Sstevel lw8_slow_shutdown(char *arg)
7151708Sstevel {
7161708Sstevel 	do_shutdown();
7171708Sstevel 	return (DDI_SUCCESS);
7181708Sstevel }
7191708Sstevel 
7201708Sstevel static uint_t
lw8_event_data_handler(char * arg)7211708Sstevel lw8_event_data_handler(char *arg)
7221708Sstevel {
7231708Sstevel 	lw8_event_t	*payload;
7241708Sstevel 	sbbc_msg_t	*msg;
7251708Sstevel 
7261708Sstevel 	if (arg == NULL) {
7271708Sstevel 		return (DDI_INTR_CLAIMED);
7281708Sstevel 	}
7291708Sstevel 
7301708Sstevel 	msg = (sbbc_msg_t *)arg;
7311708Sstevel 	if (msg->msg_buf == NULL) {
7321708Sstevel 		return (DDI_INTR_CLAIMED);
7331708Sstevel 	}
7341708Sstevel 
7351708Sstevel 	payload = (lw8_event_t *)msg->msg_buf;
7361708Sstevel 	switch (payload->event_type) {
7371708Sstevel 	case LW8_EVENT_REQUESTED_SHUTDOWN:
7381708Sstevel 
7391708Sstevel 		/*
7401708Sstevel 		 * Let the user know why the domain is going down.
7411708Sstevel 		 */
7421708Sstevel 		cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
7431708Sstevel 		ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
7441708Sstevel 
7451708Sstevel 		/*NOTREACHED*/
7461708Sstevel 		break;
7471708Sstevel 
7481708Sstevel 	case LW8_EVENT_VOLTAGE_SHUTDOWN:
7491708Sstevel 
7501708Sstevel 		/*
7511708Sstevel 		 * Let the user know why the domain is going down.
7521708Sstevel 		 */
7531708Sstevel 		cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
7541708Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
7551708Sstevel 
7561708Sstevel 		/*NOTREACHED*/
7571708Sstevel 		break;
7581708Sstevel 
7591708Sstevel 	case LW8_EVENT_TEMPERATURE_SHUTDOWN:
7601708Sstevel 
7611708Sstevel 		/*
7621708Sstevel 		 * Let the user know why the domain is going down.
7631708Sstevel 		 */
7641708Sstevel 		cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
7651708Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
7661708Sstevel 
7671708Sstevel 		/*NOTREACHED*/
7681708Sstevel 		break;
7691708Sstevel 
7701708Sstevel 	case LW8_EVENT_FANFAIL_SHUTDOWN:
7711708Sstevel 
7721708Sstevel 		/*
7731708Sstevel 		 * Let the user know why the domain is going down.
7741708Sstevel 		 */
7751708Sstevel 		cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
7761708Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
7771708Sstevel 
7781708Sstevel 		/*NOTREACHED*/
7791708Sstevel 		break;
7801708Sstevel 
7811708Sstevel 	case LW8_EVENT_NO_SCC_SHUTDOWN:
7821708Sstevel 
7831708Sstevel 		/*
7841708Sstevel 		 * Let the user know why the domain is going down.
7851708Sstevel 		 */
7861708Sstevel 		cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
7871708Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
7881708Sstevel 
7891708Sstevel 		/*NOTREACHED*/
7901708Sstevel 		break;
7911708Sstevel 
7921708Sstevel 	case LW8_EVENT_NEW_LOG_MSG:
7931708Sstevel 
7941708Sstevel 		/*
7951708Sstevel 		 * Wake up the log retrieval thread.
7961708Sstevel 		 */
7971708Sstevel 		lw8_logger_wakeup();
7981708Sstevel 
7991708Sstevel 		break;
8001708Sstevel 
8011708Sstevel 	default:
8021708Sstevel 		return (DDI_INTR_CLAIMED);
8031708Sstevel 	}
8041708Sstevel 
8051708Sstevel 	return (DDI_INTR_CLAIMED);
8061708Sstevel }
8071708Sstevel 
8081708Sstevel /*ARGSUSED*/
8091708Sstevel static int
lw8_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)8101708Sstevel lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
8111708Sstevel {
8121708Sstevel 	int error = 0;
8131708Sstevel 	int instance = getminor(*dev_p);
8141708Sstevel 	static fn_t f = "lw8_open";
8151708Sstevel 
8161708Sstevel 	if (instance != 0)
8171708Sstevel 		return (ENXIO);
8181708Sstevel 
8191708Sstevel 	if ((error = drv_priv(cred_p)) != 0) {
8201708Sstevel 		cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
8211708Sstevel 		    f, instance);
8221708Sstevel 		return (error);
8231708Sstevel 	}
8241708Sstevel 	return (error);
8251708Sstevel }
8261708Sstevel 
8271708Sstevel /*ARGSUSED*/
8281708Sstevel static int
lw8_close(dev_t dev,int flag,int otyp,cred_t * cred_p)8291708Sstevel lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
8301708Sstevel {
8311708Sstevel 	return (DDI_SUCCESS);
8321708Sstevel }
8331708Sstevel 
8341708Sstevel static int
lw8_lomcmd(int cmd,intptr_t arg)8351708Sstevel lw8_lomcmd(int cmd, intptr_t arg)
8361708Sstevel {
8371708Sstevel 	sbbc_msg_t request, *reqp = &request;
8381708Sstevel 	sbbc_msg_t response, *resp = &response;
8391708Sstevel 	int rv = 0;
8401708Sstevel 	lom_eventreq_t *eventreqp;
8411708Sstevel 
8421708Sstevel 	bzero((caddr_t)&request, sizeof (request));
8431708Sstevel 	reqp->msg_type.type = LW8_MBOX;
8441708Sstevel 	reqp->msg_type.sub_type = cmd;
8451708Sstevel 	bzero((caddr_t)&response, sizeof (response));
8461708Sstevel 	resp->msg_type.type = LW8_MBOX;
8471708Sstevel 	resp->msg_type.sub_type = cmd;
8481708Sstevel 
8491708Sstevel 	switch (cmd) {
8501708Sstevel 	case LW8_MBOX_GET_INFO:
8511708Sstevel 		reqp->msg_len = 0;
8521708Sstevel 		reqp->msg_buf = (caddr_t)NULL;
8531708Sstevel 		resp->msg_len = sizeof (lom2_info_t);
8541708Sstevel 		resp->msg_buf = (caddr_t)arg;
8551708Sstevel 		break;
8561708Sstevel 	case LW8_MBOX_SET_CTL:
8571708Sstevel 		reqp->msg_len = sizeof (lom_ctl2_t);
8581708Sstevel 		reqp->msg_buf = (caddr_t)arg;
8591708Sstevel 		resp->msg_len = 0;
8601708Sstevel 		resp->msg_buf = (caddr_t)NULL;
8611708Sstevel 		break;
8621708Sstevel 	case LW8_MBOX_UPDATE_FW:
8631708Sstevel 		reqp->msg_len = sizeof (lom_prog_t);
8641708Sstevel 		reqp->msg_buf = (caddr_t)arg;
8651708Sstevel 		resp->msg_len = 0;
8661708Sstevel 		resp->msg_buf = (caddr_t)NULL;
8671708Sstevel 		break;
8681708Sstevel 	case LW8_MBOX_GET_LED:
8691708Sstevel 		reqp->msg_len = sizeof (lw8_get_led_payload_t);
8701708Sstevel 		reqp->msg_buf = (caddr_t)arg;
8711708Sstevel 		resp->msg_len = sizeof (lw8_get_led_payload_t);
8721708Sstevel 		resp->msg_buf = (caddr_t)arg;
8731708Sstevel 		break;
8741708Sstevel 	case LW8_MBOX_SET_LED:
8751708Sstevel 		reqp->msg_len = sizeof (lw8_set_led_payload_t);
8761708Sstevel 		reqp->msg_buf = (caddr_t)arg;
8771708Sstevel 		resp->msg_len = 0;
8781708Sstevel 		resp->msg_buf = (caddr_t)NULL;
8791708Sstevel 		break;
8801708Sstevel 	case LW8_MBOX_GET_EVENTS:
8811708Sstevel 		/*
8821708Sstevel 		 * cast as lom_eventreq_t to minimise data traffic
8831708Sstevel 		 */
8841708Sstevel 		eventreqp = (lom_eventreq_t *)arg;
8851708Sstevel 		reqp->msg_len = sizeof (lom_eventreq_t);
8861708Sstevel 		reqp->msg_buf = (caddr_t)arg;
8871708Sstevel 		resp->msg_len = sizeof (lom_eventreq_t) +
8881708Sstevel 		    (eventreqp->num * MAX_EVENT_STR);
8891708Sstevel 		resp->msg_buf = (caddr_t)arg;
8901708Sstevel 		break;
8911708Sstevel 	case LW8_MBOX_GET_NEXT_MSG:
8921708Sstevel 		reqp->msg_len = 0;
8931708Sstevel 		reqp->msg_buf = (caddr_t)NULL;
8941708Sstevel 		resp->msg_len = sizeof (lw8_logmsg_t);
8951708Sstevel 		resp->msg_buf = (caddr_t)arg;
8961708Sstevel 		break;
8971708Sstevel 	default:
8981708Sstevel 		return (EINVAL);
8991708Sstevel 	}
9001708Sstevel 
9011708Sstevel 	rv = sbbc_mbox_request_response(reqp, resp,
9021708Sstevel 	    LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
9031708Sstevel 
9041708Sstevel 	if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
9051708Sstevel 
9061708Sstevel 		/* errors from sgsbbc */
9071708Sstevel 		if (resp->msg_status > 0) {
9081708Sstevel 			return (resp->msg_status);
9091708Sstevel 		}
9101708Sstevel 
9111708Sstevel 		/* errors from SCAPP */
9121708Sstevel 		switch (resp->msg_status) {
9131708Sstevel 
9141708Sstevel 		case SG_MBOX_STATUS_COMMAND_FAILURE:
9151708Sstevel 			/* internal SCAPP error */
9161708Sstevel 			return (EINTR);
9171708Sstevel 
9181708Sstevel 		case SG_MBOX_STATUS_HARDWARE_FAILURE:
9191708Sstevel 			/* seprom read/write errors */
9201708Sstevel 			return (EIO);
9211708Sstevel 
9221708Sstevel 		case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
9231708Sstevel 			/* illegal ioctl parameter */
9241708Sstevel 			return (EINVAL);
9251708Sstevel 
9261708Sstevel 		case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
9271708Sstevel 			/* board access denied */
9281708Sstevel 			return (EACCES);
9291708Sstevel 
9301708Sstevel 		case SG_MBOX_STATUS_STALE_CONTENTS:
9311708Sstevel 			/* stale contents */
9321708Sstevel 			return (ESTALE);
9331708Sstevel 
9341708Sstevel 		case SG_MBOX_STATUS_STALE_OBJECT:
9351708Sstevel 			/* stale handle */
9361708Sstevel 			return (ENOENT);
9371708Sstevel 
9381708Sstevel 		case SG_MBOX_STATUS_NO_SEPROM_SPACE:
9391708Sstevel 			/* seprom lacks space */
9401708Sstevel 			return (ENOSPC);
9411708Sstevel 
9421708Sstevel 		case SG_MBOX_STATUS_NO_MEMORY:
9431708Sstevel 			/* user prog. lacks space */
9441708Sstevel 			return (ENOMEM);
9451708Sstevel 
9461708Sstevel 		case SG_MBOX_STATUS_NOT_SUPPORTED:
9471708Sstevel 			/* unsupported operation */
9481708Sstevel 			return (ENOTSUP);
9491708Sstevel 
9501708Sstevel 		default:
9511708Sstevel 			return (EIO);
9521708Sstevel 		}
9531708Sstevel 	}
9541708Sstevel 	return (0);
9551708Sstevel }
9561708Sstevel 
9571708Sstevel /*
9581708Sstevel  * set the requested led, and mark cache as empty
9591708Sstevel  */
9601708Sstevel static int
lw8_setled(lom_set_led_t * set_ledp)9611708Sstevel lw8_setled(lom_set_led_t *set_ledp)
9621708Sstevel {
9631708Sstevel 	int retval;
9641708Sstevel 	int i, j;
9651708Sstevel 	struct led_info *lip;
9661708Sstevel 	lw8_set_led_payload_t lw8_set_led;
9671708Sstevel 
9681708Sstevel 	for (i = 0; i < MAX_FRUS; i++) {
9691708Sstevel 		if (strncmp(set_ledp->location, fru_led_table[i].location,
9701708Sstevel 		    MAX_LOCATION_LEN) != 0)
9711708Sstevel 			continue;
9721708Sstevel 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
9731708Sstevel 			lip = &fru_led_table[i].led_info[j];
9741708Sstevel 			if (lip->id == NULL)
9751708Sstevel 				continue;
9761708Sstevel 			if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
9771708Sstevel 				continue;
9781708Sstevel 			lw8_set_led.value = set_ledp->status;
9791708Sstevel 
9801708Sstevel 			/*
9811708Sstevel 			 * to minimise data transfer, the SC maintains
9821708Sstevel 			 * just  3 values per fru - except for
9831708Sstevel 			 * the chassis itself at the end which has
9841708Sstevel 			 * MAX_LEDS_PER_FRU
9851708Sstevel 			 */
9861708Sstevel 			lw8_set_led.offset = (i * 3) + j;
9871708Sstevel 			retval = lw8_lomcmd(LW8_MBOX_SET_LED,
9881708Sstevel 			    (intptr_t)&lw8_set_led);
9891708Sstevel 			if (retval != 0)
9901708Sstevel 				return (retval);
9911708Sstevel 			mutex_enter(&lw8_event_mutex);
9921708Sstevel 			led_state_cached = B_FALSE;
9931708Sstevel 			mutex_exit(&lw8_event_mutex);
9941708Sstevel 			return (0);
9951708Sstevel 		}
9961708Sstevel 	}
9971708Sstevel 	return (EINVAL);
9981708Sstevel }
9991708Sstevel 
10001708Sstevel /*
10011708Sstevel  * read led value from cache if possible, otherwise read from sc and
10021708Sstevel  * update the cache
10031708Sstevel  */
10041708Sstevel static int
lw8_getled(lom_get_led_t * get_ledp)10051708Sstevel lw8_getled(lom_get_led_t *get_ledp)
10061708Sstevel {
10071708Sstevel 	int retval;
10081708Sstevel 	int i, j, k;
10091708Sstevel 	struct led_info *lip;
10101708Sstevel 	lw8_get_led_payload_t lw8_get_led;
10111708Sstevel 
10121708Sstevel 	for (i = 0; i < MAX_FRUS; i++) {
10131708Sstevel 		if (strncmp(get_ledp->location, fru_led_table[i].location,
10141708Sstevel 		    MAX_LOCATION_LEN) != 0)
10151708Sstevel 			continue;
10161708Sstevel 		if (get_ledp->id[0] == '\0') {
10171708Sstevel 			(void) strncpy(get_ledp->next_id,
10181708Sstevel 			    fru_led_table[i].led_info[0].id, MAX_ID_LEN);
10191708Sstevel 			return (0);
10201708Sstevel 		}
10211708Sstevel 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
10221708Sstevel 			lip = &fru_led_table[i].led_info[j];
10231708Sstevel 			if (lip->id == NULL)
10241708Sstevel 				continue;
10251708Sstevel 			if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
10261708Sstevel 				continue;
10271708Sstevel 			mutex_enter(&lw8_event_mutex);
10281708Sstevel 			if (!led_state_cached) {
10291708Sstevel 				mutex_exit(&lw8_event_mutex);
10301708Sstevel 				retval = lw8_lomcmd(LW8_MBOX_GET_LED,
10311708Sstevel 				    (intptr_t)&lw8_get_led);
10321708Sstevel 				if (retval != 0)
10331708Sstevel 					return (retval);
10341708Sstevel 				mutex_enter(&lw8_event_mutex);
10351708Sstevel 
10361708Sstevel 				/*
10371708Sstevel 				 * to minimise data transfer, the
10381708Sstevel 				 * lw8_get_led_payload_t structure just has 3
10391708Sstevel 				 * values per fru - except for the chassis
10401708Sstevel 				 * itself at the end which has MAX_LEDS_PER_FRU
10411708Sstevel 				 */
10421708Sstevel 				for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
10431708Sstevel 					fru_led_table[k / 3].led_info[k % 3].
10441708Sstevel 					    status = lw8_get_led.value[k];
10451708Sstevel 				}
10461708Sstevel 				for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
10471708Sstevel 					fru_led_table[MAX_FRUS - 1].led_info[k].
10481708Sstevel 					    status = lw8_get_led.value[k +
10491708Sstevel 					    ((MAX_FRUS - 1) * 3)];
10501708Sstevel 				}
10511708Sstevel 				led_state_cached = B_TRUE;
10521708Sstevel 			}
10531708Sstevel 			get_ledp->status = lip->status;
10541708Sstevel 			mutex_exit(&lw8_event_mutex);
10551708Sstevel 			get_ledp->position = lip->position;
10561708Sstevel 			(void) strncpy(get_ledp->color, lip->color,
10571708Sstevel 			    MAX_COLOR_LEN);
10581708Sstevel 			if (j == MAX_LEDS_PER_FRU - 1) {
10591708Sstevel 				get_ledp->next_id[0] = '\0';
10601708Sstevel 				return (0);
10611708Sstevel 			}
10621708Sstevel 			(void) strncpy(get_ledp->next_id,
10631708Sstevel 			    fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
10641708Sstevel 			return (0);
10651708Sstevel 		}
10661708Sstevel 	}
10671708Sstevel 	if (get_ledp->id[0] == '\0') {
10681708Sstevel 		get_ledp->next_id[0] = '\0';
10691708Sstevel 		return (0);
10701708Sstevel 	}
10711708Sstevel 	return (EINVAL);
10721708Sstevel }
10731708Sstevel 
10741708Sstevel /*ARGSUSED*/
10751708Sstevel static int
lw8_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)10761708Sstevel lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
10771708Sstevel     int *rval_p)
10781708Sstevel {
10791708Sstevel 	int instance = getminor(dev);
10801708Sstevel 	lom2_info_t lw8_info2;
10811708Sstevel 	lom_ctl_t lw8_ctl;
10821708Sstevel 	lom_ctl2_t lw8_ctl2;
10831708Sstevel 	lom_mprog_t lw8_mprog;
10841708Sstevel 	lom_fled_info_t lw8_fled_info;
10851708Sstevel 	lom_info_t lw8_info;
10861708Sstevel 	lom_aldata_t lw8_aldata;
10871708Sstevel 	lom_get_led_t lw8_get_led;
10881708Sstevel 	lom_set_led_t lw8_set_led;
10891708Sstevel 	lom_prog_t *lw8_progp;
10901708Sstevel 	lom_eventlog2_t *lw8_eventlogp;
10911708Sstevel 	lom_eventresp_t *lw8_eventresp;
10921708Sstevel 	int retval = 0;
10931708Sstevel 	int i, j;
10941708Sstevel 
10951708Sstevel 	if (instance != 0)
10961708Sstevel 		return (ENXIO);
10971708Sstevel 
10981708Sstevel 	switch (cmd) {
10991708Sstevel 	case LOMIOCWTMON:
11001708Sstevel 		mutex_enter(&lw8_event_mutex);
11011708Sstevel 		if (!lw8_event_pending) {
11021708Sstevel 			if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
11031708Sstevel 				mutex_exit(&lw8_event_mutex);
11041708Sstevel 				retval = EINTR;
11051708Sstevel 				break;
11061708Sstevel 			}
11071708Sstevel 		}
11081708Sstevel 		lw8_event_pending = B_FALSE;
11091708Sstevel 		mutex_exit(&lw8_event_mutex);
11101708Sstevel 		break;
11111708Sstevel 	case LOMIOCMREAD:
11121708Sstevel 		bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
11131708Sstevel 		lw8_mprog.config = 4;
11141708Sstevel 		if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
11151708Sstevel 		    sizeof (lw8_mprog), mode) != 0) {
11161708Sstevel 			retval = EFAULT;
11171708Sstevel 		}
11181708Sstevel 		break;
11191708Sstevel 	case LOMIOCCTL2:
11201708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
11211708Sstevel 		    sizeof (lw8_ctl2), mode) != 0) {
11221708Sstevel 			retval = EFAULT;
11231708Sstevel 			break;
11241708Sstevel 		}
11251708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
11261708Sstevel 		break;
11271708Sstevel 	case LOMIOCPROG:
11281708Sstevel 		lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
11291708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
11301708Sstevel 		    sizeof (*lw8_progp), mode) != 0) {
11311708Sstevel 			kmem_free(lw8_progp, sizeof (*lw8_progp));
11321708Sstevel 			retval = EFAULT;
11331708Sstevel 			break;
11341708Sstevel 		}
11351708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
11361708Sstevel 		kmem_free(lw8_progp, sizeof (*lw8_progp));
11371708Sstevel 		break;
11381708Sstevel 	case LOMIOCINFO2:
11391708Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
11401708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
11411708Sstevel 		if (retval != 0)
11421708Sstevel 			break;
11431708Sstevel 		if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
11441708Sstevel 		    sizeof (lw8_info2), mode) != 0) {
11451708Sstevel 			retval = EFAULT;
11461708Sstevel 		}
11471708Sstevel 		break;
11481708Sstevel 	case LOMIOCINFO:
11491708Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
11501708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
11511708Sstevel 		if (retval != 0)
11521708Sstevel 			break;
11531708Sstevel 		bzero((caddr_t)&lw8_info, sizeof (lw8_info));
11541708Sstevel 		lw8_info.ser_char = lw8_info2.escape_chars[0];
11551708Sstevel 		lw8_info.fver = lw8_info2.fver;
11561708Sstevel 		lw8_info.fchksum = lw8_info2.fchksum;
11571708Sstevel 		lw8_info.prod_rev = lw8_info2.prod_rev;
1158*11311SSurya.Prakki@Sun.COM 		(void) strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
11591708Sstevel 		if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
11601708Sstevel 		    sizeof (lw8_info), mode) != 0) {
11611708Sstevel 			retval = EFAULT;
11621708Sstevel 		}
11631708Sstevel 		break;
11641708Sstevel 	case LOMIOCFLEDSTATE:
11651708Sstevel 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
11661708Sstevel 		(void) strncpy(lw8_get_led.location, "chassis",
11671708Sstevel 		    MAX_LOCATION_LEN);
11681708Sstevel 		(void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
11691708Sstevel 		retval = lw8_getled(&lw8_get_led);
11701708Sstevel 		if (retval != 0)
11711708Sstevel 			break;
11721708Sstevel 		lw8_fled_info.on = lw8_get_led.status;
11731708Sstevel 		if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
11741708Sstevel 		    sizeof (lw8_fled_info), mode) != 0) {
11751708Sstevel 			retval = EFAULT;
11761708Sstevel 		}
11771708Sstevel 		break;
11781708Sstevel 	case LOMIOCALSTATE:
11791708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
11801708Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
11811708Sstevel 			retval = EFAULT;
11821708Sstevel 			break;
11831708Sstevel 		}
11841708Sstevel 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
11851708Sstevel 		(void) strncpy(lw8_get_led.location, "chassis",
11861708Sstevel 		    MAX_LOCATION_LEN);
11871708Sstevel 		if (lw8_aldata.alarm_no == 3)
11881708Sstevel 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
11891708Sstevel 		else
11901708Sstevel 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
11911708Sstevel 			    lw8_aldata.alarm_no);
11921708Sstevel 		retval = lw8_getled(&lw8_get_led);
11931708Sstevel 		if (retval != 0)
11941708Sstevel 			break;
11951708Sstevel 		lw8_aldata.state = lw8_get_led.status;
11961708Sstevel 		if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
11971708Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
11981708Sstevel 			retval = EFAULT;
11991708Sstevel 		}
12001708Sstevel 		break;
12011708Sstevel 	case LOMIOCGETLED:
12021708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
12031708Sstevel 		    sizeof (lw8_get_led), mode) != 0) {
12041708Sstevel 			retval = EFAULT;
12051708Sstevel 			break;
12061708Sstevel 		}
12071708Sstevel 		retval = lw8_getled(&lw8_get_led);
12081708Sstevel 		if (retval != 0)
12091708Sstevel 			break;
12101708Sstevel 		if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
12111708Sstevel 		    sizeof (lw8_get_led), mode) != 0) {
12121708Sstevel 			retval = EFAULT;
12131708Sstevel 		}
12141708Sstevel 		break;
12151708Sstevel 	case LOMIOCEVENTLOG2:
12161708Sstevel 		lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
12171708Sstevel 		lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
12181708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
12191708Sstevel 		    sizeof (*lw8_eventlogp), mode) != 0) {
12201708Sstevel 			kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
12211708Sstevel 			kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
12221708Sstevel 			retval = EFAULT;
12231708Sstevel 			break;
12241708Sstevel 		}
12251708Sstevel 		lw8_eventresp->num = lw8_eventlogp->num;
12261708Sstevel 		lw8_eventresp->level = lw8_eventlogp->level;
12271708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
12281708Sstevel 		    (intptr_t)lw8_eventresp);
12291708Sstevel 		if (retval == 0) {
12301708Sstevel 			lw8_eventlogp->num = lw8_eventresp->num;
12311708Sstevel 			for (i = 0; i < lw8_eventresp->num; i++) {
12321708Sstevel 				for (j = 0; j < MAX_EVENT_STR; j++) {
12331708Sstevel 					lw8_eventlogp->string[i][j] =
12341708Sstevel 					    lw8_eventresp->string[i][j];
12351708Sstevel 				}
12361708Sstevel 			}
12371708Sstevel 			if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
12381708Sstevel 			    sizeof (*lw8_eventlogp), mode) != 0) {
12391708Sstevel 				retval = EFAULT;
12401708Sstevel 			}
12411708Sstevel 		}
12421708Sstevel 		kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
12431708Sstevel 		kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
12441708Sstevel 		break;
12451708Sstevel 	case LOMIOCALCTL:
12461708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
12471708Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
12481708Sstevel 			retval = EFAULT;
12491708Sstevel 			break;
12501708Sstevel 		}
12511708Sstevel 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
12521708Sstevel 		(void) strncpy(lw8_set_led.location, "chassis",
12531708Sstevel 		    MAX_LOCATION_LEN);
12541708Sstevel 		if (lw8_aldata.alarm_no == 3)
12551708Sstevel 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
12561708Sstevel 		else
12571708Sstevel 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
12581708Sstevel 			    lw8_aldata.alarm_no);
12591708Sstevel 		lw8_set_led.status = lw8_aldata.state;
12601708Sstevel 		retval = lw8_setled(&lw8_set_led);
12611708Sstevel 		break;
12621708Sstevel 	case LOMIOCSETLED:
12631708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
12641708Sstevel 		    sizeof (lw8_set_led), mode) != 0) {
12651708Sstevel 			retval = EFAULT;
12661708Sstevel 			break;
12671708Sstevel 		}
12681708Sstevel 		retval = lw8_setled(&lw8_set_led);
12691708Sstevel 		break;
12701708Sstevel 	case LOMIOCCTL:
12711708Sstevel 		/*
12721708Sstevel 		 * for this ioctl, as well as setting the fault led in the
12731708Sstevel 		 * LOMIOCCTL case in lw8_lomcmd(), we also need to set the
12741708Sstevel 		 * escape character. To do this we must use LW8_MBOX_SET_CTL,
12751708Sstevel 		 * but this also needs the serial_event value which we have
12761708Sstevel 		 * to get via LW8_MBOX_GET_INFO
12771708Sstevel 		 */
12781708Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
12791708Sstevel 		    sizeof (lw8_ctl), mode) != 0) {
12801708Sstevel 			retval = EFAULT;
12811708Sstevel 			break;
12821708Sstevel 		}
12831708Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
12841708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
12851708Sstevel 		if (retval != 0)
12861708Sstevel 			break;
12871708Sstevel 		bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
12881708Sstevel 		lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
12891708Sstevel 		lw8_ctl2.serial_events = lw8_info2.serial_events;
12901708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
12911708Sstevel 		if (retval != 0)
12921708Sstevel 			break;
12931708Sstevel 
12941708Sstevel 		/*
12951708Sstevel 		 * if fault_led != 0, then set the led
12961708Sstevel 		 */
12971708Sstevel 		if (lw8_ctl.fault_led == 0)
12981708Sstevel 			break;
12991708Sstevel 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
13001708Sstevel 		(void) strncpy(lw8_set_led.location, "chassis",
13011708Sstevel 		    MAX_LOCATION_LEN);
13021708Sstevel 		(void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
13031708Sstevel 		lw8_set_led.status = lw8_ctl.fault_led - 1;
13041708Sstevel 		retval = lw8_setled(&lw8_set_led);
13051708Sstevel 		break;
13061708Sstevel 	default:
13071708Sstevel 		retval = ENOTSUP;
13081708Sstevel 		break;
13091708Sstevel 	}
13101708Sstevel 	return (retval);
13111708Sstevel }
13121708Sstevel 
13131708Sstevel /* ARGSUSED */
13141708Sstevel static void
lw8_logger(caddr_t arg)13151708Sstevel lw8_logger(caddr_t arg)
13161708Sstevel {
13171708Sstevel 	callb_cpr_t	cprinfo;
13181708Sstevel 	lw8_logmsg_t	*lw8_logmsgp;
13191708Sstevel 	boolean_t	more_waiting;
13201708Sstevel 	char		level;
13211708Sstevel 	int		retval;
13221708Sstevel 
13231708Sstevel 	CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
13241708Sstevel 	    "lw8_logger");
13251708Sstevel 
13261708Sstevel 	lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
13271708Sstevel 	mutex_enter(&lw8_logger_lock);
13281708Sstevel 	for (;;) {
13291708Sstevel 
13301708Sstevel 		/*
13311708Sstevel 		 * Wait for someone to tell me to continue.
13321708Sstevel 		 */
13331708Sstevel 		while (lw8_logger_sig == LW8_LOGGER_WAIT) {
13341708Sstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
13351708Sstevel 			cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
13361708Sstevel 			CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
13371708Sstevel 		}
13381708Sstevel 
13391708Sstevel 		/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
13401708Sstevel 		if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
13411708Sstevel 			lw8_logger_sig = LW8_LOGGER_WAIT;
13421708Sstevel 
13431708Sstevel 			kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
13441708Sstevel 
13451708Sstevel 			/* lw8_logger_lock is held at this point! */
13461708Sstevel 			CALLB_CPR_EXIT(&cprinfo);
13471708Sstevel 
13481708Sstevel 			thread_exit();
13491708Sstevel 			/* NOTREACHED */
13501708Sstevel 		}
13511708Sstevel 
13521708Sstevel 		ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
13531708Sstevel 		lw8_logger_sig = LW8_LOGGER_WAIT;
13541708Sstevel 
13551708Sstevel 		mutex_exit(&lw8_logger_lock);
13561708Sstevel 
13571708Sstevel 		/* Do lw8_event logging */
13581708Sstevel 
13591708Sstevel 		/*
13601708Sstevel 		 * Get one message per iteration. We do not sleep if
13611708Sstevel 		 * there are more to process. This makes exit from the
13621708Sstevel 		 * routine much more reliable.
13631708Sstevel 		 */
13641708Sstevel 		more_waiting = B_FALSE;
13651708Sstevel 
13661708Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
13671708Sstevel 		    (intptr_t)lw8_logmsgp);
13681708Sstevel 		if (retval == 0) {
13691708Sstevel 			if (lw8_logmsgp->msg_valid) {
13701708Sstevel 
13711708Sstevel 				switch (lw8_logmsgp->level) {
13721708Sstevel 				case 0:	/* LOG_EMERG */
13731708Sstevel 					level = SL_FATAL;
13741708Sstevel 					break;
13751708Sstevel 				case 1:	/* LOG_ALERT */
13761708Sstevel 					level = SL_FATAL;
13771708Sstevel 					break;
13781708Sstevel 				case 2:	/* LOG_CRIT */
13791708Sstevel 					level = SL_FATAL;
13801708Sstevel 					break;
13811708Sstevel 				case 3:	/* LOG_ERR */
13821708Sstevel 					level = SL_ERROR;
13831708Sstevel 					break;
13841708Sstevel 				case 4:	/* LOG_WARNING */
13851708Sstevel 					level = SL_WARN;
13861708Sstevel 					break;
13871708Sstevel 				case 5:	/* LOG_NOTICE */
13881708Sstevel 					level = SL_NOTE;
13891708Sstevel 					break;
13901708Sstevel 				case 6:	/* LOG_INFO */
13911708Sstevel 					level = SL_NOTE;
13921708Sstevel 					break;
13931708Sstevel 				case 7:	/* LOG_DEBUG */
13941708Sstevel 					level = SL_TRACE;
13951708Sstevel 					break;
13961708Sstevel 				default:	/* unknown */
13971708Sstevel 					level = SL_NOTE;
13981708Sstevel 					break;
13991708Sstevel 				}
14001708Sstevel 
14011708Sstevel 				/* Ensure NUL termination */
14021708Sstevel 				lw8_logmsgp->msg[
14037656SSherry.Moore@Sun.COM 				    sizeof (lw8_logmsgp->msg) - 1] = '\0';
1404*11311SSurya.Prakki@Sun.COM 				(void) strlog(0, 0, 0, SL_CONSOLE | level,
14051708Sstevel 				    lw8_logmsgp->msg);
14061708Sstevel 			}
14071708Sstevel 
14081708Sstevel 			if (lw8_logmsgp->num_remaining > 0)
14091708Sstevel 				more_waiting = B_TRUE;
14101708Sstevel 		}
14111708Sstevel 
14121708Sstevel 		/*
14131708Sstevel 		 * Re-enter the lock to prepare for another iteration.
14141708Sstevel 		 * We must have the lock here to protect lw8_logger_sig.
14151708Sstevel 		 */
14161708Sstevel 		mutex_enter(&lw8_logger_lock);
14171708Sstevel 		if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
14181708Sstevel 			/* We need to get more events */
14191708Sstevel 			lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
14201708Sstevel 	}
14211708Sstevel }
14221708Sstevel 
14231708Sstevel static void
lw8_logger_start(void)14241708Sstevel lw8_logger_start(void)
14251708Sstevel {
14261708Sstevel 	kthread_t *tp;
14271708Sstevel 
14281708Sstevel 	mutex_enter(&lw8_logger_lock);
14291708Sstevel 
14301708Sstevel 	if (lw8_logger_tid == 0) {
14311708Sstevel 		/* Force retrieval of any pending messages */
14321708Sstevel 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
14331708Sstevel 
14341708Sstevel 		tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
14351708Sstevel 		    &p0, TS_RUN, maxclsyspri);
14361708Sstevel 		lw8_logger_tid = tp->t_did;
14371708Sstevel 	}
14381708Sstevel 
14391708Sstevel 	mutex_exit(&lw8_logger_lock);
14401708Sstevel }
14411708Sstevel 
14421708Sstevel static void
lw8_logger_destroy(void)14431708Sstevel lw8_logger_destroy(void)
14441708Sstevel {
14451708Sstevel 	kt_did_t tid;
14461708Sstevel 
14471708Sstevel 	mutex_enter(&lw8_logger_lock);
14481708Sstevel 	tid = lw8_logger_tid;
14491708Sstevel 	if (tid != 0) {
14501708Sstevel 		lw8_logger_sig = LW8_LOGGER_EXITNOW;
14511708Sstevel 		cv_signal(&lw8_logger_sig_cv);
14521708Sstevel 		lw8_logger_tid = 0;
14531708Sstevel 	}
14541708Sstevel 	mutex_exit(&lw8_logger_lock);
14551708Sstevel 
14561708Sstevel 	/*
14571708Sstevel 	 * Wait for lw8_logger() to finish.
14581708Sstevel 	 */
14591708Sstevel 	if (tid != 0)
14601708Sstevel 		thread_join(tid);
14611708Sstevel }
14621708Sstevel 
14631708Sstevel static void
lw8_logger_wakeup(void)14641708Sstevel lw8_logger_wakeup(void)
14651708Sstevel {
14661708Sstevel 	mutex_enter(&lw8_logger_lock);
14671708Sstevel 
14681708Sstevel 	if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
14691708Sstevel 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
14701708Sstevel 	cv_signal(&lw8_logger_sig_cv);
14711708Sstevel 
14721708Sstevel 	mutex_exit(&lw8_logger_lock);
14731708Sstevel }
1474