xref: /illumos-gate/usr/src/uts/sun4u/lw8/io/lw8.c (revision d1d6926f)
103831d35Sstevel /*
203831d35Sstevel  * CDDL HEADER START
303831d35Sstevel  *
403831d35Sstevel  * The contents of this file are subject to the terms of the
503831d35Sstevel  * Common Development and Distribution License (the "License").
603831d35Sstevel  * You may not use this file except in compliance with the License.
703831d35Sstevel  *
803831d35Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel  * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel  * See the License for the specific language governing permissions
1103831d35Sstevel  * and limitations under the License.
1203831d35Sstevel  *
1303831d35Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel  * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel  *
1903831d35Sstevel  * CDDL HEADER END
2003831d35Sstevel  */
2103831d35Sstevel 
2203831d35Sstevel /*
2307d06da5SSurya Prakki  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2403831d35Sstevel  * Use is subject to license terms.
2503831d35Sstevel  */
2603831d35Sstevel 
2703831d35Sstevel 
2803831d35Sstevel #include <sys/time.h>
2903831d35Sstevel #include <sys/errno.h>
3003831d35Sstevel #include <sys/kmem.h>
3103831d35Sstevel #include <sys/stat.h>
3203831d35Sstevel #include <sys/cmn_err.h>
3303831d35Sstevel 
3403831d35Sstevel #include <sys/conf.h>
3503831d35Sstevel #include <sys/modctl.h>
3603831d35Sstevel #include <sys/devops.h>
3703831d35Sstevel #include <sys/ddi.h>
3803831d35Sstevel #include <sys/sunddi.h>
3903831d35Sstevel #include <sys/callb.h>
4003831d35Sstevel #include <sys/disp.h>
4103831d35Sstevel #include <sys/strlog.h>
4203831d35Sstevel 
4303831d35Sstevel #include <sys/sgevents.h>
4403831d35Sstevel #include <sys/serengeti.h>
4503831d35Sstevel #include <sys/sgsbbc.h>
4603831d35Sstevel #include <sys/sgsbbc_iosram.h>
4703831d35Sstevel #include <sys/sgsbbc_mailbox.h>
4803831d35Sstevel #include <sys/uadmin.h>
4903831d35Sstevel #include <sys/machsystm.h>
5003831d35Sstevel #include <sys/sysevent.h>
5103831d35Sstevel #include <sys/sysevent/dr.h>
5203831d35Sstevel #include <sys/sysevent/eventdefs.h>
5303831d35Sstevel #include <sys/file.h>
5403831d35Sstevel #include <sys/lw8.h>
5503831d35Sstevel #include <sys/lw8_impl.h>
5603831d35Sstevel #include <sys/plat_ecc_unum.h>
5703831d35Sstevel 
5803831d35Sstevel /*
5903831d35Sstevel  * Global Variables - can be patched from Solaris
6003831d35Sstevel  * ==============================================
6103831d35Sstevel  */
6203831d35Sstevel 
6303831d35Sstevel /*
6403831d35Sstevel  * Module Variables
6503831d35Sstevel  * ================
6603831d35Sstevel  */
6703831d35Sstevel 
6803831d35Sstevel /*
6903831d35Sstevel  * functions local to this driver.
7003831d35Sstevel  */
7103831d35Sstevel static int	lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
7203831d35Sstevel static int	lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
7303831d35Sstevel static int	lw8_add_intr_handlers(void);
7403831d35Sstevel static int	lw8_remove_intr_handlers(void);
7503831d35Sstevel static void lw8_wakeup_sleepers(void);
7603831d35Sstevel static uint_t	lw8_fast_shutdown(char *arg);
7703831d35Sstevel static uint_t	lw8_slow_shutdown(char *arg);
7803831d35Sstevel static uint_t	lw8_event_data_handler(char *);
7903831d35Sstevel static uint_t	lw8_dr_data_handler(char *);
8003831d35Sstevel static uint_t	lw8_env_data_handler(char *);
8103831d35Sstevel static uint_t	lw8_cap_ecc_msg_handler(char *);
8203831d35Sstevel static int	lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
8303831d35Sstevel static int	lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
8403831d35Sstevel static int	lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
8503831d35Sstevel     cred_t *cred_p, int *rval_p);
8603831d35Sstevel static void	lw8_logger_start(void);
8703831d35Sstevel static void	lw8_logger_destroy(void);
8803831d35Sstevel static void	lw8_logger_wakeup(void);
8903831d35Sstevel 
9003831d35Sstevel /*
9103831d35Sstevel  * Driver entry points
9203831d35Sstevel  */
9303831d35Sstevel static struct cb_ops lw8_cb_ops = {
9403831d35Sstevel 	lw8_open,	/* open */
9503831d35Sstevel 	lw8_close,	/* close */
9603831d35Sstevel 	nodev,		/* strategy() */
9703831d35Sstevel 	nodev,		/* print() */
9803831d35Sstevel 	nodev,		/* dump() */
9903831d35Sstevel 	nodev,		/* read() */
10003831d35Sstevel 	nodev,		/* write() */
10103831d35Sstevel 	lw8_ioctl,	/* ioctl() */
10203831d35Sstevel 	nodev,		/* devmap() */
10303831d35Sstevel 	nodev,		/* mmap() */
10403831d35Sstevel 	ddi_segmap,	/* segmap() */
10503831d35Sstevel 	nochpoll,	/* poll() */
10603831d35Sstevel 	ddi_prop_op,    /* prop_op() */
10703831d35Sstevel 	NULL,		/* cb_str */
10803831d35Sstevel 	D_NEW | D_MP	/* cb_flag */
10903831d35Sstevel };
11003831d35Sstevel 
11103831d35Sstevel 
11203831d35Sstevel static struct dev_ops lw8_ops = {
11303831d35Sstevel 	DEVO_REV,
11403831d35Sstevel 	0,			/* ref count */
11503831d35Sstevel 	ddi_getinfo_1to1,	/* getinfo() */
11603831d35Sstevel 	nulldev,		/* identify() */
11703831d35Sstevel 	nulldev,		/* probe() */
11803831d35Sstevel 	lw8_attach,		/* attach() */
11903831d35Sstevel 	lw8_detach,		/* detach */
12003831d35Sstevel 	nodev,			/* reset */
12103831d35Sstevel 	&lw8_cb_ops,		/* pointer to cb_ops structure */
12203831d35Sstevel 	(struct bus_ops *)NULL,
12319397407SSherry Moore 	nulldev,		/* power() */
12419397407SSherry Moore 	ddi_quiesce_not_needed,		/* quiesce() */
12503831d35Sstevel };
12603831d35Sstevel 
12703831d35Sstevel /*
12803831d35Sstevel  * Loadable module support.
12903831d35Sstevel  */
13003831d35Sstevel extern struct mod_ops mod_driverops;
13103831d35Sstevel 
13203831d35Sstevel static struct modldrv modldrv = {
13303831d35Sstevel 	&mod_driverops,			/* Type of module. This is a driver */
13419397407SSherry Moore 	"Netra-T12 control driver",	/* Name of the module */
13503831d35Sstevel 	&lw8_ops			/* pointer to the dev_ops structure */
13603831d35Sstevel };
13703831d35Sstevel 
13803831d35Sstevel static struct modlinkage modlinkage = {
13903831d35Sstevel 	MODREV_1,
14003831d35Sstevel 	&modldrv,
14103831d35Sstevel 	NULL
14203831d35Sstevel };
14303831d35Sstevel 
14403831d35Sstevel /*
14503831d35Sstevel  * messages
14603831d35Sstevel  */
14703831d35Sstevel #define	SHUTDOWN_EVENT_MSG		"lw8: system shutdown due to " \
14803831d35Sstevel 					"SC request.\n"
14903831d35Sstevel #define	VOLTAGE_EVENT_MSG		"lw8: system shutdown due to " \
15003831d35Sstevel 					"voltage out of range.\n"
15103831d35Sstevel #define	TEMPERATURE_EVENT_MSG		"lw8: system shutdown due to " \
15203831d35Sstevel 					"temperature exceeding limits.\n"
15303831d35Sstevel #define	FANFAIL_EVENT_MSG		"lw8: system shutdown due to " \
15403831d35Sstevel 					"too many fan failures.\n"
15503831d35Sstevel #define	NO_SCC_EVENT_MSG		"lw8: system shutdown due to " \
15603831d35Sstevel 					"no system configuration card.\n"
15703831d35Sstevel 
15803831d35Sstevel /*
15903831d35Sstevel  * led table - the following provides a cache of the led state - needed
16003831d35Sstevel  * to avoid the overhead of readoing from the SC each time
16103831d35Sstevel  */
16203831d35Sstevel 
16303831d35Sstevel struct led_info {
16403831d35Sstevel 	char	id[MAX_ID_LEN];
16503831d35Sstevel 	int	position;
166*d1d6926fSToomas Soome 	int	status;
16703831d35Sstevel 	char	color[MAX_COLOR_LEN];
16803831d35Sstevel };
16903831d35Sstevel 
17003831d35Sstevel static struct fru_led_info {
17103831d35Sstevel 	char    location[MAX_LOCATION_LEN];
17203831d35Sstevel 	struct led_info led_info[MAX_LEDS_PER_FRU];
17303831d35Sstevel } fru_led_table[MAX_FRUS] = {
17403831d35Sstevel 	"SB0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
17503831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
17603831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
17703831d35Sstevel 	"PS0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
17803831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
17903831d35Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
18003831d35Sstevel 	"SB2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18103831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
18203831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
18303831d35Sstevel 	"PS1", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18403831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
18503831d35Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
18603831d35Sstevel 	"SB4", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
18703831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
18803831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
18903831d35Sstevel 	"PS2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19003831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
19103831d35Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
19203831d35Sstevel 	"IB6", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19303831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
19403831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
19503831d35Sstevel 	"PS3", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19603831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
19703831d35Sstevel 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
19803831d35Sstevel 	"FT0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
19903831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
20003831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
20103831d35Sstevel 	"FAN0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20203831d35Sstevel 	"FAN1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20303831d35Sstevel 	"FAN2", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20403831d35Sstevel 	"FAN3", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20503831d35Sstevel 	"FAN4", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20603831d35Sstevel 	"FAN5", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20703831d35Sstevel 	"FAN6", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20803831d35Sstevel 	"FAN7", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
20903831d35Sstevel 	"FAN8", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
21003831d35Sstevel 	"FAN9", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
21103831d35Sstevel 	"DISK0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
21203831d35Sstevel 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
21303831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
21403831d35Sstevel 	"DISK1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
21503831d35Sstevel 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
21603831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
21703831d35Sstevel 	"RP0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
21803831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
21903831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
22003831d35Sstevel 	"RP2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
22103831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
22203831d35Sstevel 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
22303831d35Sstevel 	"chassis", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
22403831d35Sstevel 		"power", LOM_LED_POSITION_FRU, 0, "green",
22503831d35Sstevel 		"locator", LOM_LED_POSITION_FRU, 0, "white",
22603831d35Sstevel 		"top_access", LOM_LED_POSITION_FRU, 0, "amber",
22703831d35Sstevel 		"alarm1", LOM_LED_POSITION_FRU, 0, "amber",
22803831d35Sstevel 		"alarm2", LOM_LED_POSITION_FRU, 0, "amber",
22903831d35Sstevel 		"system", LOM_LED_POSITION_FRU, 0, "green",
23003831d35Sstevel 		"supplyA", LOM_LED_POSITION_FRU, 0, "green",
23103831d35Sstevel 		"supplyB", LOM_LED_POSITION_FRU, 0, "green"},
23203831d35Sstevel };
23303831d35Sstevel 
23403831d35Sstevel char    *fru_locn[MAX_LOCATION_LEN] = {
23503831d35Sstevel 	"SB0",
23603831d35Sstevel 	"PS0",
23703831d35Sstevel 	"SB2",
23803831d35Sstevel 	"PS1",
23903831d35Sstevel 	"SB4",
24003831d35Sstevel 	"PS2",
24103831d35Sstevel 	"IB6",
24203831d35Sstevel 	"PS3",
24303831d35Sstevel 	"SCC",
24403831d35Sstevel 	"SSC1",
24503831d35Sstevel };
24603831d35Sstevel 
24703831d35Sstevel /*
24803831d35Sstevel  * mutexes which protect the interrupt handlers.
24903831d35Sstevel  */
25003831d35Sstevel static kmutex_t		lw8_shutdown_hdlr_lock;
25103831d35Sstevel static kmutex_t		lw8_dr_hdlr_lock;
25203831d35Sstevel static kmutex_t		lw8_env_hdlr_lock;
25303831d35Sstevel static kmutex_t		lw8_event_mutex;
25403831d35Sstevel static kmutex_t		lw8_logger_lock;
25503831d35Sstevel static kmutex_t		lw8_cap_msg_hdlr_lock;
25603831d35Sstevel static kcondvar_t	lw8_event_cv;
25703831d35Sstevel static kcondvar_t	lw8_logger_sig_cv;
25803831d35Sstevel 
25903831d35Sstevel /*
26003831d35Sstevel  * state booleans
26103831d35Sstevel  */
26203831d35Sstevel static boolean_t	lw8_event_pending = B_FALSE;
26303831d35Sstevel static boolean_t	led_state_cached = B_FALSE;
26403831d35Sstevel 
26503831d35Sstevel /*
26603831d35Sstevel  * Payloads of the event handlers.
26703831d35Sstevel  */
26803831d35Sstevel static lw8_event_t	lw8_shutdown_payload;
26903831d35Sstevel static sbbc_msg_t	lw8_shutdown_payload_msg;
27003831d35Sstevel static sg_system_fru_descriptor_t	lw8_dr_payload;
27103831d35Sstevel static sbbc_msg_t	lw8_dr_payload_msg;
27203831d35Sstevel static sg_event_fan_status_t		lw8_env_payload;
27303831d35Sstevel static sbbc_msg_t	lw8_env_payload_msg;
27403831d35Sstevel static plat_capability_data_t	lw8_cap_payload;
27503831d35Sstevel static sbbc_msg_t	lw8_cap_payload_msg;
27603831d35Sstevel 
27703831d35Sstevel /*
27803831d35Sstevel  * The IDs of the soft interrupts
27903831d35Sstevel  */
28003831d35Sstevel static ddi_softintr_t   lw8_slow_shutdown_softint_id;
28103831d35Sstevel static ddi_softintr_t   lw8_fast_shutdown_softint_id;
28203831d35Sstevel 
28303831d35Sstevel /*
28403831d35Sstevel  * Logger commands..
28503831d35Sstevel  */
28603831d35Sstevel #define	LW8_LOGGER_EXITNOW	-1
28703831d35Sstevel #define	LW8_LOGGER_WAIT	0
28803831d35Sstevel #define	LW8_LOGGER_PROCESSNOW	1
28903831d35Sstevel 
29003831d35Sstevel /*
29103831d35Sstevel  * Logger thread state
29203831d35Sstevel  */
29303831d35Sstevel static int lw8_logger_sig = LW8_LOGGER_WAIT;
29403831d35Sstevel static kt_did_t lw8_logger_tid = 0;
29503831d35Sstevel 
29603831d35Sstevel extern pri_t maxclsyspri;
29703831d35Sstevel 
29803831d35Sstevel int
_init(void)29903831d35Sstevel _init(void)
30003831d35Sstevel {
30103831d35Sstevel 	int	error = 0;
30203831d35Sstevel 
30303831d35Sstevel 	mutex_init(&lw8_shutdown_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30403831d35Sstevel 	mutex_init(&lw8_dr_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30503831d35Sstevel 	mutex_init(&lw8_env_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30603831d35Sstevel 	mutex_init(&lw8_cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
30703831d35Sstevel 	mutex_init(&lw8_event_mutex, NULL, MUTEX_DRIVER, NULL);
30803831d35Sstevel 	mutex_init(&lw8_logger_lock, NULL, MUTEX_DRIVER, NULL);
30903831d35Sstevel 	cv_init(&lw8_event_cv, NULL, CV_DRIVER, NULL);
31003831d35Sstevel 	cv_init(&lw8_logger_sig_cv, NULL, CV_DRIVER, NULL);
31103831d35Sstevel 
31203831d35Sstevel 	error = mod_install(&modlinkage);
31303831d35Sstevel 	if (error) {
31403831d35Sstevel 		cv_destroy(&lw8_logger_sig_cv);
31503831d35Sstevel 		cv_destroy(&lw8_event_cv);
31603831d35Sstevel 		mutex_destroy(&lw8_logger_lock);
31703831d35Sstevel 		mutex_destroy(&lw8_event_mutex);
31803831d35Sstevel 		mutex_destroy(&lw8_env_hdlr_lock);
31903831d35Sstevel 		mutex_destroy(&lw8_cap_msg_hdlr_lock);
32003831d35Sstevel 		mutex_destroy(&lw8_dr_hdlr_lock);
32103831d35Sstevel 		mutex_destroy(&lw8_shutdown_hdlr_lock);
32203831d35Sstevel 	}
32303831d35Sstevel 	return (error);
32403831d35Sstevel }
32503831d35Sstevel 
32603831d35Sstevel 
32703831d35Sstevel int
_info(struct modinfo * modinfop)32803831d35Sstevel _info(struct modinfo *modinfop)
32903831d35Sstevel {
33003831d35Sstevel 	return (mod_info(&modlinkage, modinfop));
33103831d35Sstevel }
33203831d35Sstevel 
33303831d35Sstevel 
33403831d35Sstevel int
_fini(void)33503831d35Sstevel _fini(void)
33603831d35Sstevel {
33703831d35Sstevel 	int	error = 0;
33803831d35Sstevel 
33903831d35Sstevel 	error = mod_remove(&modlinkage);
34003831d35Sstevel 	if (error)
34103831d35Sstevel 		return (error);
34203831d35Sstevel 	cv_destroy(&lw8_logger_sig_cv);
34303831d35Sstevel 	cv_destroy(&lw8_event_cv);
34403831d35Sstevel 	mutex_destroy(&lw8_logger_lock);
34503831d35Sstevel 	mutex_destroy(&lw8_event_mutex);
34603831d35Sstevel 	mutex_destroy(&lw8_env_hdlr_lock);
34703831d35Sstevel 	mutex_destroy(&lw8_cap_msg_hdlr_lock);
34803831d35Sstevel 	mutex_destroy(&lw8_dr_hdlr_lock);
34903831d35Sstevel 	mutex_destroy(&lw8_shutdown_hdlr_lock);
35003831d35Sstevel 	return (error);
35103831d35Sstevel }
35203831d35Sstevel 
35303831d35Sstevel 
35403831d35Sstevel static int
lw8_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)35503831d35Sstevel lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
35603831d35Sstevel {
35703831d35Sstevel 	int			instance;
35803831d35Sstevel 	int			err;
35903831d35Sstevel 
36003831d35Sstevel 	switch (cmd) {
36103831d35Sstevel 	case DDI_ATTACH:
36203831d35Sstevel 		/*
36303831d35Sstevel 		 * only allow one instance
36403831d35Sstevel 		 */
36503831d35Sstevel 		instance = ddi_get_instance(dip);
36603831d35Sstevel 		if (instance != 0)
36703831d35Sstevel 			return (DDI_FAILURE);
36803831d35Sstevel 
36903831d35Sstevel 		err = ddi_create_minor_node(dip, "lw8", S_IFCHR,
370*d1d6926fSToomas Soome 		    instance, DDI_PSEUDO, 0);
37103831d35Sstevel 		if (err != DDI_SUCCESS)
37203831d35Sstevel 			return (DDI_FAILURE);
37303831d35Sstevel 
37403831d35Sstevel 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
37503831d35Sstevel 		    &lw8_slow_shutdown_softint_id, NULL, NULL,
37603831d35Sstevel 		    lw8_slow_shutdown, NULL);
37703831d35Sstevel 		if (err != 0) {
37803831d35Sstevel 			cmn_err(CE_WARN, "Failed to add polling softint"
37903831d35Sstevel 			    "handler for lw8. Err=%d", err);
38003831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
38103831d35Sstevel 			return (DDI_FAILURE);
38203831d35Sstevel 		}
38303831d35Sstevel 
38403831d35Sstevel 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
38503831d35Sstevel 		    &lw8_fast_shutdown_softint_id, NULL, NULL,
38603831d35Sstevel 		    lw8_fast_shutdown, NULL);
38703831d35Sstevel 		if (err != 0) {
38803831d35Sstevel 			cmn_err(CE_WARN, "Failed to add polling softint"
38903831d35Sstevel 			    "handler for lw8. Err=%d", err);
39003831d35Sstevel 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
39103831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
39203831d35Sstevel 			return (DDI_FAILURE);
39303831d35Sstevel 		}
39403831d35Sstevel 
39503831d35Sstevel 		lw8_logger_start();
39603831d35Sstevel 
39703831d35Sstevel 		/*
39803831d35Sstevel 		 * Add the handlers which watch for unsolicited messages
39903831d35Sstevel 		 * and post event to Sysevent Framework.
40003831d35Sstevel 		 */
40103831d35Sstevel 		err = lw8_add_intr_handlers();
40203831d35Sstevel 		if (err != DDI_SUCCESS) {
40303831d35Sstevel 			cmn_err(CE_WARN, "Failed to add event handlers");
40403831d35Sstevel 			lw8_logger_destroy();
40503831d35Sstevel 			ddi_remove_softintr(lw8_fast_shutdown_softint_id);
40603831d35Sstevel 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
40703831d35Sstevel 			ddi_remove_minor_node(dip, NULL);
40803831d35Sstevel 			return (DDI_FAILURE);
40903831d35Sstevel 		}
41003831d35Sstevel 
41103831d35Sstevel 		ddi_report_dev(dip);
41203831d35Sstevel 		return (DDI_SUCCESS);
41303831d35Sstevel 	case DDI_RESUME:
41403831d35Sstevel 		return (DDI_SUCCESS);
41503831d35Sstevel 	default:
41603831d35Sstevel 		return (DDI_FAILURE);
41703831d35Sstevel 	}
41803831d35Sstevel }
41903831d35Sstevel 
42003831d35Sstevel 
42103831d35Sstevel static int
lw8_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)42203831d35Sstevel lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
42303831d35Sstevel {
42403831d35Sstevel 	int	instance;
42503831d35Sstevel 	int	err;
42603831d35Sstevel 
42703831d35Sstevel 	switch (cmd) {
42803831d35Sstevel 	case DDI_DETACH:
42903831d35Sstevel 		instance = ddi_get_instance(dip);
43003831d35Sstevel 		if (instance != 0)
43103831d35Sstevel 			return (DDI_FAILURE);
43203831d35Sstevel 
43303831d35Sstevel 		/*
43403831d35Sstevel 		 * Remove the handlers which watch for unsolicited messages
43503831d35Sstevel 		 * and post event to Sysevent Framework.
43603831d35Sstevel 		 */
43703831d35Sstevel 		err = lw8_remove_intr_handlers();
43803831d35Sstevel 		if (err != DDI_SUCCESS) {
43903831d35Sstevel 			cmn_err(CE_WARN, "Failed to remove event handlers");
44003831d35Sstevel 			return (DDI_FAILURE);
44103831d35Sstevel 		}
44203831d35Sstevel 		lw8_logger_destroy();
44303831d35Sstevel 		ddi_remove_softintr(lw8_slow_shutdown_softint_id);
44403831d35Sstevel 		ddi_remove_softintr(lw8_fast_shutdown_softint_id);
44503831d35Sstevel 		ddi_remove_minor_node(dip, NULL);
44603831d35Sstevel 		return (DDI_SUCCESS);
44703831d35Sstevel 	case DDI_SUSPEND:
44803831d35Sstevel 		return (DDI_SUCCESS);
44903831d35Sstevel 	default:
45003831d35Sstevel 		return (DDI_FAILURE);
45103831d35Sstevel 	}
45203831d35Sstevel }
45303831d35Sstevel 
45403831d35Sstevel static int
lw8_add_intr_handlers()45503831d35Sstevel lw8_add_intr_handlers()
45603831d35Sstevel {
45703831d35Sstevel 	int	err;
45803831d35Sstevel 
45903831d35Sstevel 	lw8_shutdown_payload_msg.msg_buf = (caddr_t)&lw8_shutdown_payload;
46003831d35Sstevel 	lw8_shutdown_payload_msg.msg_len = sizeof (lw8_shutdown_payload);
46103831d35Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, lw8_event_data_handler,
46203831d35Sstevel 	    &lw8_shutdown_payload_msg, NULL, &lw8_shutdown_hdlr_lock);
46303831d35Sstevel 	if (err != 0) {
46403831d35Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_LW8 "
46519397407SSherry Moore 		    " handler. Err=%d", err);
46603831d35Sstevel 		return (DDI_FAILURE);
46703831d35Sstevel 	}
46803831d35Sstevel 
46903831d35Sstevel 	lw8_dr_payload_msg.msg_buf = (caddr_t)&lw8_dr_payload;
47003831d35Sstevel 	lw8_dr_payload_msg.msg_len = sizeof (lw8_dr_payload);
47103831d35Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler,
47219397407SSherry Moore 	    &lw8_dr_payload_msg, NULL, &lw8_dr_hdlr_lock);
47303831d35Sstevel 	if (err != 0) {
47403831d35Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_GENERIC "
47519397407SSherry Moore 		    " handler. Err=%d", err);
47607d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
47707d06da5SSurya Prakki 		    lw8_event_data_handler);
47803831d35Sstevel 		return (DDI_FAILURE);
47903831d35Sstevel 	}
48003831d35Sstevel 
48103831d35Sstevel 	lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
48203831d35Sstevel 	lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
48303831d35Sstevel 	err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
48419397407SSherry Moore 	    &lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
48503831d35Sstevel 	if (err != 0) {
48603831d35Sstevel 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
48719397407SSherry Moore 		    " handler. Err=%d", err);
48807d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
48907d06da5SSurya Prakki 		    lw8_dr_data_handler);
49007d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
49107d06da5SSurya Prakki 		    lw8_event_data_handler);
49203831d35Sstevel 		return (DDI_FAILURE);
49303831d35Sstevel 	}
49403831d35Sstevel 
49503831d35Sstevel 	lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
49603831d35Sstevel 	lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
49703831d35Sstevel 	err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
49803831d35Sstevel 	    &lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
49903831d35Sstevel 	if (err != 0) {
50003831d35Sstevel 		cmn_err(CE_WARN, "Failed to register INFO_MBOX "
50103831d35Sstevel 		    " handler. Err=%d", err);
50207d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
50307d06da5SSurya Prakki 		    lw8_dr_data_handler);
50407d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
50507d06da5SSurya Prakki 		    lw8_event_data_handler);
50607d06da5SSurya Prakki 		(void) sbbc_mbox_unreg_intr(INFO_MBOX,
50707d06da5SSurya Prakki 		    lw8_cap_ecc_msg_handler);
50803831d35Sstevel 		return (DDI_FAILURE);
50903831d35Sstevel 	}
51003831d35Sstevel 
51103831d35Sstevel 	return (DDI_SUCCESS);
51203831d35Sstevel }
51303831d35Sstevel 
51403831d35Sstevel static int
lw8_remove_intr_handlers(void)51503831d35Sstevel lw8_remove_intr_handlers(void)
51603831d35Sstevel {
51703831d35Sstevel 	int	rv = DDI_SUCCESS;
51803831d35Sstevel 	int	err;
51903831d35Sstevel 
52003831d35Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
52103831d35Sstevel 	if (err != 0) {
52203831d35Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
52319397407SSherry Moore 		    "handler. Err=%d", err);
52403831d35Sstevel 		rv = DDI_FAILURE;
52503831d35Sstevel 	}
52603831d35Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
52703831d35Sstevel 	if (err != 0) {
52803831d35Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
52919397407SSherry Moore 		    "handler. Err=%d", err);
53003831d35Sstevel 		rv = DDI_FAILURE;
53103831d35Sstevel 	}
53203831d35Sstevel 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
53303831d35Sstevel 	if (err != 0) {
53403831d35Sstevel 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
53519397407SSherry Moore 		    "handler. Err=%d", err);
53603831d35Sstevel 		rv = DDI_FAILURE;
53703831d35Sstevel 	}
53803831d35Sstevel 	err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
53903831d35Sstevel 	if (err != 0) {
54003831d35Sstevel 		cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
54103831d35Sstevel 		    "handler. Err=%d", err);
54203831d35Sstevel 		rv = DDI_FAILURE;
54303831d35Sstevel 	}
54403831d35Sstevel 	return (rv);
54503831d35Sstevel }
54603831d35Sstevel 
54703831d35Sstevel static uint_t
lw8_dr_data_handler(char * arg)54803831d35Sstevel lw8_dr_data_handler(char *arg)
54903831d35Sstevel {
55003831d35Sstevel 	sg_system_fru_descriptor_t	*payload;
55103831d35Sstevel 	sbbc_msg_t			*msg;
55203831d35Sstevel 	int				hint;
55303831d35Sstevel 	sysevent_t			*ev;
55403831d35Sstevel 	sysevent_id_t			eid;
55503831d35Sstevel 	int				rv = 0;
55603831d35Sstevel 	sysevent_value_t		evnt_val;
55703831d35Sstevel 	sysevent_attr_list_t		*evnt_attr_list = NULL;
55803831d35Sstevel 	char				attach_pnt[MAXPATHLEN];
55903831d35Sstevel 
56003831d35Sstevel 	msg = (sbbc_msg_t *)arg;
56103831d35Sstevel 	if (msg == NULL) {
56203831d35Sstevel 		return (DDI_INTR_CLAIMED);
56303831d35Sstevel 	}
56403831d35Sstevel 	payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
56503831d35Sstevel 	if (payload == NULL) {
56603831d35Sstevel 		return (DDI_INTR_CLAIMED);
56703831d35Sstevel 	}
56803831d35Sstevel 	if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
56903831d35Sstevel 	    sizeof (char *)) {
57003831d35Sstevel 		return (DDI_INTR_CLAIMED);
57103831d35Sstevel 	}
57203831d35Sstevel 
57303831d35Sstevel 	/*
57403831d35Sstevel 	 * if not SB send sysevent (SBs send sysevent from ssm driver)
57503831d35Sstevel 	 */
57603831d35Sstevel 	if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
57703831d35Sstevel 		switch (payload->event_details) {
57803831d35Sstevel 		case SG_EVT_BOARD_ABSENT:
57903831d35Sstevel 			hint = SE_HINT_REMOVE;
58003831d35Sstevel 			break;
58103831d35Sstevel 		case SG_EVT_BOARD_PRESENT:
58203831d35Sstevel 			hint = SE_HINT_INSERT;
58303831d35Sstevel 			break;
58403831d35Sstevel 		default:
58503831d35Sstevel 			hint = SE_NO_HINT;
58603831d35Sstevel 			break;
58703831d35Sstevel 		}
58803831d35Sstevel 		(void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
58903831d35Sstevel 		    fru_locn[payload->slot]);
59003831d35Sstevel 		ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
59103831d35Sstevel 		    KM_NOSLEEP);
59203831d35Sstevel 		if (ev == NULL) {
59303831d35Sstevel 			cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
59403831d35Sstevel 			return (DDI_INTR_CLAIMED);
59503831d35Sstevel 		}
59603831d35Sstevel 		evnt_val.value_type = SE_DATA_TYPE_STRING;
59703831d35Sstevel 		evnt_val.value.sv_string = attach_pnt;
59803831d35Sstevel 		rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
59903831d35Sstevel 		    KM_NOSLEEP);
60003831d35Sstevel 		if (rv != 0) {
60103831d35Sstevel 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
60203831d35Sstevel 			    DR_AP_ID, EC_DR);
60303831d35Sstevel 			sysevent_free(ev);
60403831d35Sstevel 			return (DDI_INTR_CLAIMED);
60503831d35Sstevel 		}
60603831d35Sstevel 
60703831d35Sstevel 		/*
60803831d35Sstevel 		 * Add the hint
60903831d35Sstevel 		 */
61003831d35Sstevel 		evnt_val.value_type = SE_DATA_TYPE_STRING;
61103831d35Sstevel 		evnt_val.value.sv_string = SE_HINT2STR(hint);
61203831d35Sstevel 		rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
61303831d35Sstevel 		    KM_NOSLEEP);
61403831d35Sstevel 		if (rv != 0) {
61503831d35Sstevel 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
61603831d35Sstevel 			    DR_HINT, EC_DR);
61703831d35Sstevel 			sysevent_free_attr(evnt_attr_list);
61803831d35Sstevel 			sysevent_free(ev);
61903831d35Sstevel 			return (DDI_INTR_CLAIMED);
62003831d35Sstevel 		}
62103831d35Sstevel 		if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
62203831d35Sstevel 			cmn_err(CE_WARN, "Failed to attach attr list for %s "
62303831d35Sstevel 			    "event", EC_DR);
62403831d35Sstevel 			sysevent_free_attr(evnt_attr_list);
62503831d35Sstevel 			sysevent_free(ev);
62603831d35Sstevel 			return (DDI_INTR_CLAIMED);
62703831d35Sstevel 		}
62803831d35Sstevel 		rv = log_sysevent(ev, KM_NOSLEEP, &eid);
62903831d35Sstevel 		if (rv != 0) {
63003831d35Sstevel 			cmn_err(CE_WARN,
63103831d35Sstevel 			    "lw8_dr_event_handler: failed to log event");
63203831d35Sstevel 		}
63303831d35Sstevel 		sysevent_free(ev);
63403831d35Sstevel 	}
63503831d35Sstevel 	lw8_wakeup_sleepers();
63603831d35Sstevel 	return (DDI_INTR_CLAIMED);
63703831d35Sstevel }
63803831d35Sstevel 
63903831d35Sstevel static uint_t
lw8_cap_ecc_msg_handler(char * addr)64003831d35Sstevel lw8_cap_ecc_msg_handler(char *addr)
64103831d35Sstevel {
64203831d35Sstevel 	sbbc_msg_t *msg = NULL;
64303831d35Sstevel 	plat_capability_data_t *cap = NULL;
64403831d35Sstevel 
64503831d35Sstevel 	msg = (sbbc_msg_t *)addr;
64603831d35Sstevel 	if (msg == NULL || msg->msg_buf == NULL)
64703831d35Sstevel 		return (DDI_INTR_CLAIMED);
64803831d35Sstevel 
64903831d35Sstevel 	cap = (plat_capability_data_t *)msg->msg_buf;
65003831d35Sstevel 	switch (cap->capd_msg_type) {
65103831d35Sstevel 	case PLAT_ECC_CAPABILITY_MESSAGE:
65203831d35Sstevel 		plat_ecc_capability_sc_set(cap->capd_capability);
65303831d35Sstevel 		break;
65403831d35Sstevel 	default:
65503831d35Sstevel 		break;
65603831d35Sstevel 	}
65703831d35Sstevel 
65803831d35Sstevel 	return (DDI_INTR_CLAIMED);
65903831d35Sstevel }
66003831d35Sstevel 
66103831d35Sstevel /*ARGSUSED*/
66203831d35Sstevel static uint_t
lw8_env_data_handler(char * arg)66303831d35Sstevel lw8_env_data_handler(char *arg)
66403831d35Sstevel {
66503831d35Sstevel 	lw8_wakeup_sleepers();
66603831d35Sstevel 	return (DDI_INTR_CLAIMED);
66703831d35Sstevel }
66803831d35Sstevel 
66903831d35Sstevel /*
67003831d35Sstevel  * wakeup sleepers + mark led cache for this fru as invalid
67103831d35Sstevel  */
67203831d35Sstevel static void
lw8_wakeup_sleepers()67303831d35Sstevel lw8_wakeup_sleepers()
67403831d35Sstevel {
67503831d35Sstevel 	mutex_enter(&lw8_event_mutex);
67603831d35Sstevel 	lw8_event_pending = B_TRUE;
67703831d35Sstevel 	cv_broadcast(&lw8_event_cv);
67803831d35Sstevel 	led_state_cached = B_FALSE;
67903831d35Sstevel 	mutex_exit(&lw8_event_mutex);
68003831d35Sstevel }
68103831d35Sstevel 
68203831d35Sstevel /*
68303831d35Sstevel  * This function is triggered by a soft interrupt and it's purpose is to call
68403831d35Sstevel  * to kadmin() to shutdown the system.
68503831d35Sstevel  */
68603831d35Sstevel /*ARGSUSED*/
68703831d35Sstevel static uint_t
lw8_fast_shutdown(char * arg)68803831d35Sstevel lw8_fast_shutdown(char *arg)
68903831d35Sstevel {
69003831d35Sstevel 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
69103831d35Sstevel 
69203831d35Sstevel 	/*
69303831d35Sstevel 	 * If kadmin fails for some reason then we bring the system down
69403831d35Sstevel 	 * via power_down(), or failing that using halt().
69503831d35Sstevel 	 */
69603831d35Sstevel 	power_down("kadmin() failed, trying power_down()");
69703831d35Sstevel 
69803831d35Sstevel 	halt("power_down() failed, trying halt()");
69903831d35Sstevel 
70003831d35Sstevel 	/*
70103831d35Sstevel 	 * We should never make it this far, so something must have gone
70203831d35Sstevel 	 * horribly, horribly wrong.
70303831d35Sstevel 	 */
70403831d35Sstevel 	/*NOTREACHED*/
705055d7c80Scarlsonj 	return (DDI_INTR_UNCLAIMED);
70603831d35Sstevel }
70703831d35Sstevel 
70803831d35Sstevel /*
70903831d35Sstevel  * This function is triggered by a soft interrupt and it's purpose is to call
71003831d35Sstevel  * to do_shutdown() to shutdown the system.
71103831d35Sstevel  */
71203831d35Sstevel /*ARGSUSED*/
71303831d35Sstevel static uint_t
lw8_slow_shutdown(char * arg)71403831d35Sstevel lw8_slow_shutdown(char *arg)
71503831d35Sstevel {
71603831d35Sstevel 	do_shutdown();
71703831d35Sstevel 	return (DDI_SUCCESS);
71803831d35Sstevel }
71903831d35Sstevel 
72003831d35Sstevel static uint_t
lw8_event_data_handler(char * arg)72103831d35Sstevel lw8_event_data_handler(char *arg)
72203831d35Sstevel {
72303831d35Sstevel 	lw8_event_t	*payload;
72403831d35Sstevel 	sbbc_msg_t	*msg;
72503831d35Sstevel 
72603831d35Sstevel 	if (arg == NULL) {
72703831d35Sstevel 		return (DDI_INTR_CLAIMED);
72803831d35Sstevel 	}
72903831d35Sstevel 
73003831d35Sstevel 	msg = (sbbc_msg_t *)arg;
73103831d35Sstevel 	if (msg->msg_buf == NULL) {
73203831d35Sstevel 		return (DDI_INTR_CLAIMED);
73303831d35Sstevel 	}
73403831d35Sstevel 
73503831d35Sstevel 	payload = (lw8_event_t *)msg->msg_buf;
73603831d35Sstevel 	switch (payload->event_type) {
73703831d35Sstevel 	case LW8_EVENT_REQUESTED_SHUTDOWN:
73803831d35Sstevel 
73903831d35Sstevel 		/*
74003831d35Sstevel 		 * Let the user know why the domain is going down.
74103831d35Sstevel 		 */
74203831d35Sstevel 		cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
74303831d35Sstevel 		ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
74403831d35Sstevel 
74503831d35Sstevel 		/*NOTREACHED*/
74603831d35Sstevel 		break;
74703831d35Sstevel 
74803831d35Sstevel 	case LW8_EVENT_VOLTAGE_SHUTDOWN:
74903831d35Sstevel 
75003831d35Sstevel 		/*
75103831d35Sstevel 		 * Let the user know why the domain is going down.
75203831d35Sstevel 		 */
75303831d35Sstevel 		cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
75403831d35Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
75503831d35Sstevel 
75603831d35Sstevel 		/*NOTREACHED*/
75703831d35Sstevel 		break;
75803831d35Sstevel 
75903831d35Sstevel 	case LW8_EVENT_TEMPERATURE_SHUTDOWN:
76003831d35Sstevel 
76103831d35Sstevel 		/*
76203831d35Sstevel 		 * Let the user know why the domain is going down.
76303831d35Sstevel 		 */
76403831d35Sstevel 		cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
76503831d35Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
76603831d35Sstevel 
76703831d35Sstevel 		/*NOTREACHED*/
76803831d35Sstevel 		break;
76903831d35Sstevel 
77003831d35Sstevel 	case LW8_EVENT_FANFAIL_SHUTDOWN:
77103831d35Sstevel 
77203831d35Sstevel 		/*
77303831d35Sstevel 		 * Let the user know why the domain is going down.
77403831d35Sstevel 		 */
77503831d35Sstevel 		cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
77603831d35Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
77703831d35Sstevel 
77803831d35Sstevel 		/*NOTREACHED*/
77903831d35Sstevel 		break;
78003831d35Sstevel 
78103831d35Sstevel 	case LW8_EVENT_NO_SCC_SHUTDOWN:
78203831d35Sstevel 
78303831d35Sstevel 		/*
78403831d35Sstevel 		 * Let the user know why the domain is going down.
78503831d35Sstevel 		 */
78603831d35Sstevel 		cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
78703831d35Sstevel 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
78803831d35Sstevel 
78903831d35Sstevel 		/*NOTREACHED*/
79003831d35Sstevel 		break;
79103831d35Sstevel 
79203831d35Sstevel 	case LW8_EVENT_NEW_LOG_MSG:
79303831d35Sstevel 
79403831d35Sstevel 		/*
79503831d35Sstevel 		 * Wake up the log retrieval thread.
79603831d35Sstevel 		 */
79703831d35Sstevel 		lw8_logger_wakeup();
79803831d35Sstevel 
79903831d35Sstevel 		break;
80003831d35Sstevel 
80103831d35Sstevel 	default:
80203831d35Sstevel 		return (DDI_INTR_CLAIMED);
80303831d35Sstevel 	}
80403831d35Sstevel 
80503831d35Sstevel 	return (DDI_INTR_CLAIMED);
80603831d35Sstevel }
80703831d35Sstevel 
80803831d35Sstevel /*ARGSUSED*/
80903831d35Sstevel static int
lw8_open(dev_t * dev_p,int flag,int otyp,cred_t * cred_p)81003831d35Sstevel lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
81103831d35Sstevel {
81203831d35Sstevel 	int error = 0;
81303831d35Sstevel 	int instance = getminor(*dev_p);
81403831d35Sstevel 	static fn_t f = "lw8_open";
81503831d35Sstevel 
81603831d35Sstevel 	if (instance != 0)
81703831d35Sstevel 		return (ENXIO);
81803831d35Sstevel 
81903831d35Sstevel 	if ((error = drv_priv(cred_p)) != 0) {
82003831d35Sstevel 		cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
82103831d35Sstevel 		    f, instance);
82203831d35Sstevel 		return (error);
82303831d35Sstevel 	}
82403831d35Sstevel 	return (error);
82503831d35Sstevel }
82603831d35Sstevel 
82703831d35Sstevel /*ARGSUSED*/
82803831d35Sstevel static int
lw8_close(dev_t dev,int flag,int otyp,cred_t * cred_p)82903831d35Sstevel lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
83003831d35Sstevel {
83103831d35Sstevel 	return (DDI_SUCCESS);
83203831d35Sstevel }
83303831d35Sstevel 
83403831d35Sstevel static int
lw8_lomcmd(int cmd,intptr_t arg)83503831d35Sstevel lw8_lomcmd(int cmd, intptr_t arg)
83603831d35Sstevel {
83703831d35Sstevel 	sbbc_msg_t request, *reqp = &request;
83803831d35Sstevel 	sbbc_msg_t response, *resp = &response;
83903831d35Sstevel 	int rv = 0;
84003831d35Sstevel 	lom_eventreq_t *eventreqp;
84103831d35Sstevel 
84203831d35Sstevel 	bzero((caddr_t)&request, sizeof (request));
84303831d35Sstevel 	reqp->msg_type.type = LW8_MBOX;
84403831d35Sstevel 	reqp->msg_type.sub_type = cmd;
84503831d35Sstevel 	bzero((caddr_t)&response, sizeof (response));
84603831d35Sstevel 	resp->msg_type.type = LW8_MBOX;
84703831d35Sstevel 	resp->msg_type.sub_type = cmd;
84803831d35Sstevel 
84903831d35Sstevel 	switch (cmd) {
85003831d35Sstevel 	case LW8_MBOX_GET_INFO:
85103831d35Sstevel 		reqp->msg_len = 0;
85203831d35Sstevel 		reqp->msg_buf = (caddr_t)NULL;
85303831d35Sstevel 		resp->msg_len = sizeof (lom2_info_t);
85403831d35Sstevel 		resp->msg_buf = (caddr_t)arg;
85503831d35Sstevel 		break;
85603831d35Sstevel 	case LW8_MBOX_SET_CTL:
85703831d35Sstevel 		reqp->msg_len = sizeof (lom_ctl2_t);
85803831d35Sstevel 		reqp->msg_buf = (caddr_t)arg;
85903831d35Sstevel 		resp->msg_len = 0;
86003831d35Sstevel 		resp->msg_buf = (caddr_t)NULL;
86103831d35Sstevel 		break;
86203831d35Sstevel 	case LW8_MBOX_UPDATE_FW:
86303831d35Sstevel 		reqp->msg_len = sizeof (lom_prog_t);
86403831d35Sstevel 		reqp->msg_buf = (caddr_t)arg;
86503831d35Sstevel 		resp->msg_len = 0;
86603831d35Sstevel 		resp->msg_buf = (caddr_t)NULL;
86703831d35Sstevel 		break;
86803831d35Sstevel 	case LW8_MBOX_GET_LED:
86903831d35Sstevel 		reqp->msg_len = sizeof (lw8_get_led_payload_t);
87003831d35Sstevel 		reqp->msg_buf = (caddr_t)arg;
87103831d35Sstevel 		resp->msg_len = sizeof (lw8_get_led_payload_t);
87203831d35Sstevel 		resp->msg_buf = (caddr_t)arg;
87303831d35Sstevel 		break;
87403831d35Sstevel 	case LW8_MBOX_SET_LED:
87503831d35Sstevel 		reqp->msg_len = sizeof (lw8_set_led_payload_t);
87603831d35Sstevel 		reqp->msg_buf = (caddr_t)arg;
87703831d35Sstevel 		resp->msg_len = 0;
87803831d35Sstevel 		resp->msg_buf = (caddr_t)NULL;
87903831d35Sstevel 		break;
88003831d35Sstevel 	case LW8_MBOX_GET_EVENTS:
88103831d35Sstevel 		/*
88203831d35Sstevel 		 * cast as lom_eventreq_t to minimise data traffic
88303831d35Sstevel 		 */
88403831d35Sstevel 		eventreqp = (lom_eventreq_t *)arg;
88503831d35Sstevel 		reqp->msg_len = sizeof (lom_eventreq_t);
88603831d35Sstevel 		reqp->msg_buf = (caddr_t)arg;
88703831d35Sstevel 		resp->msg_len = sizeof (lom_eventreq_t) +
88803831d35Sstevel 		    (eventreqp->num * MAX_EVENT_STR);
88903831d35Sstevel 		resp->msg_buf = (caddr_t)arg;
89003831d35Sstevel 		break;
89103831d35Sstevel 	case LW8_MBOX_GET_NEXT_MSG:
89203831d35Sstevel 		reqp->msg_len = 0;
89303831d35Sstevel 		reqp->msg_buf = (caddr_t)NULL;
89403831d35Sstevel 		resp->msg_len = sizeof (lw8_logmsg_t);
89503831d35Sstevel 		resp->msg_buf = (caddr_t)arg;
89603831d35Sstevel 		break;
89703831d35Sstevel 	default:
89803831d35Sstevel 		return (EINVAL);
89903831d35Sstevel 	}
90003831d35Sstevel 
90103831d35Sstevel 	rv = sbbc_mbox_request_response(reqp, resp,
90203831d35Sstevel 	    LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
90303831d35Sstevel 
90403831d35Sstevel 	if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
90503831d35Sstevel 
90603831d35Sstevel 		/* errors from sgsbbc */
90703831d35Sstevel 		if (resp->msg_status > 0) {
90803831d35Sstevel 			return (resp->msg_status);
90903831d35Sstevel 		}
91003831d35Sstevel 
91103831d35Sstevel 		/* errors from SCAPP */
91203831d35Sstevel 		switch (resp->msg_status) {
91303831d35Sstevel 
91403831d35Sstevel 		case SG_MBOX_STATUS_COMMAND_FAILURE:
91503831d35Sstevel 			/* internal SCAPP error */
91603831d35Sstevel 			return (EINTR);
91703831d35Sstevel 
91803831d35Sstevel 		case SG_MBOX_STATUS_HARDWARE_FAILURE:
91903831d35Sstevel 			/* seprom read/write errors */
92003831d35Sstevel 			return (EIO);
92103831d35Sstevel 
92203831d35Sstevel 		case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
92303831d35Sstevel 			/* illegal ioctl parameter */
92403831d35Sstevel 			return (EINVAL);
92503831d35Sstevel 
92603831d35Sstevel 		case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
92703831d35Sstevel 			/* board access denied */
92803831d35Sstevel 			return (EACCES);
92903831d35Sstevel 
93003831d35Sstevel 		case SG_MBOX_STATUS_STALE_CONTENTS:
93103831d35Sstevel 			/* stale contents */
93203831d35Sstevel 			return (ESTALE);
93303831d35Sstevel 
93403831d35Sstevel 		case SG_MBOX_STATUS_STALE_OBJECT:
93503831d35Sstevel 			/* stale handle */
93603831d35Sstevel 			return (ENOENT);
93703831d35Sstevel 
93803831d35Sstevel 		case SG_MBOX_STATUS_NO_SEPROM_SPACE:
93903831d35Sstevel 			/* seprom lacks space */
94003831d35Sstevel 			return (ENOSPC);
94103831d35Sstevel 
94203831d35Sstevel 		case SG_MBOX_STATUS_NO_MEMORY:
94303831d35Sstevel 			/* user prog. lacks space */
94403831d35Sstevel 			return (ENOMEM);
94503831d35Sstevel 
94603831d35Sstevel 		case SG_MBOX_STATUS_NOT_SUPPORTED:
94703831d35Sstevel 			/* unsupported operation */
94803831d35Sstevel 			return (ENOTSUP);
94903831d35Sstevel 
95003831d35Sstevel 		default:
95103831d35Sstevel 			return (EIO);
95203831d35Sstevel 		}
95303831d35Sstevel 	}
95403831d35Sstevel 	return (0);
95503831d35Sstevel }
95603831d35Sstevel 
95703831d35Sstevel /*
95803831d35Sstevel  * set the requested led, and mark cache as empty
95903831d35Sstevel  */
96003831d35Sstevel static int
lw8_setled(lom_set_led_t * set_ledp)96103831d35Sstevel lw8_setled(lom_set_led_t *set_ledp)
96203831d35Sstevel {
96303831d35Sstevel 	int retval;
96403831d35Sstevel 	int i, j;
96503831d35Sstevel 	struct led_info *lip;
96603831d35Sstevel 	lw8_set_led_payload_t lw8_set_led;
96703831d35Sstevel 
96803831d35Sstevel 	for (i = 0; i < MAX_FRUS; i++) {
96903831d35Sstevel 		if (strncmp(set_ledp->location, fru_led_table[i].location,
97003831d35Sstevel 		    MAX_LOCATION_LEN) != 0)
97103831d35Sstevel 			continue;
97203831d35Sstevel 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
97303831d35Sstevel 			lip = &fru_led_table[i].led_info[j];
97403831d35Sstevel 			if (lip->id == NULL)
97503831d35Sstevel 				continue;
97603831d35Sstevel 			if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
97703831d35Sstevel 				continue;
97803831d35Sstevel 			lw8_set_led.value = set_ledp->status;
97903831d35Sstevel 
98003831d35Sstevel 			/*
98103831d35Sstevel 			 * to minimise data transfer, the SC maintains
98203831d35Sstevel 			 * just  3 values per fru - except for
98303831d35Sstevel 			 * the chassis itself at the end which has
98403831d35Sstevel 			 * MAX_LEDS_PER_FRU
98503831d35Sstevel 			 */
98603831d35Sstevel 			lw8_set_led.offset = (i * 3) + j;
98703831d35Sstevel 			retval = lw8_lomcmd(LW8_MBOX_SET_LED,
98803831d35Sstevel 			    (intptr_t)&lw8_set_led);
98903831d35Sstevel 			if (retval != 0)
99003831d35Sstevel 				return (retval);
99103831d35Sstevel 			mutex_enter(&lw8_event_mutex);
99203831d35Sstevel 			led_state_cached = B_FALSE;
99303831d35Sstevel 			mutex_exit(&lw8_event_mutex);
99403831d35Sstevel 			return (0);
99503831d35Sstevel 		}
99603831d35Sstevel 	}
99703831d35Sstevel 	return (EINVAL);
99803831d35Sstevel }
99903831d35Sstevel 
100003831d35Sstevel /*
100103831d35Sstevel  * read led value from cache if possible, otherwise read from sc and
100203831d35Sstevel  * update the cache
100303831d35Sstevel  */
100403831d35Sstevel static int
lw8_getled(lom_get_led_t * get_ledp)100503831d35Sstevel lw8_getled(lom_get_led_t *get_ledp)
100603831d35Sstevel {
100703831d35Sstevel 	int retval;
100803831d35Sstevel 	int i, j, k;
100903831d35Sstevel 	struct led_info *lip;
101003831d35Sstevel 	lw8_get_led_payload_t lw8_get_led;
101103831d35Sstevel 
101203831d35Sstevel 	for (i = 0; i < MAX_FRUS; i++) {
101303831d35Sstevel 		if (strncmp(get_ledp->location, fru_led_table[i].location,
101403831d35Sstevel 		    MAX_LOCATION_LEN) != 0)
101503831d35Sstevel 			continue;
101603831d35Sstevel 		if (get_ledp->id[0] == '\0') {
101703831d35Sstevel 			(void) strncpy(get_ledp->next_id,
101803831d35Sstevel 			    fru_led_table[i].led_info[0].id, MAX_ID_LEN);
101903831d35Sstevel 			return (0);
102003831d35Sstevel 		}
102103831d35Sstevel 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
102203831d35Sstevel 			lip = &fru_led_table[i].led_info[j];
102303831d35Sstevel 			if (lip->id == NULL)
102403831d35Sstevel 				continue;
102503831d35Sstevel 			if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
102603831d35Sstevel 				continue;
102703831d35Sstevel 			mutex_enter(&lw8_event_mutex);
102803831d35Sstevel 			if (!led_state_cached) {
102903831d35Sstevel 				mutex_exit(&lw8_event_mutex);
103003831d35Sstevel 				retval = lw8_lomcmd(LW8_MBOX_GET_LED,
103103831d35Sstevel 				    (intptr_t)&lw8_get_led);
103203831d35Sstevel 				if (retval != 0)
103303831d35Sstevel 					return (retval);
103403831d35Sstevel 				mutex_enter(&lw8_event_mutex);
103503831d35Sstevel 
103603831d35Sstevel 				/*
103703831d35Sstevel 				 * to minimise data transfer, the
103803831d35Sstevel 				 * lw8_get_led_payload_t structure just has 3
103903831d35Sstevel 				 * values per fru - except for the chassis
104003831d35Sstevel 				 * itself at the end which has MAX_LEDS_PER_FRU
104103831d35Sstevel 				 */
104203831d35Sstevel 				for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
104303831d35Sstevel 					fru_led_table[k / 3].led_info[k % 3].
104403831d35Sstevel 					    status = lw8_get_led.value[k];
104503831d35Sstevel 				}
104603831d35Sstevel 				for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
104703831d35Sstevel 					fru_led_table[MAX_FRUS - 1].led_info[k].
104803831d35Sstevel 					    status = lw8_get_led.value[k +
104903831d35Sstevel 					    ((MAX_FRUS - 1) * 3)];
105003831d35Sstevel 				}
105103831d35Sstevel 				led_state_cached = B_TRUE;
105203831d35Sstevel 			}
105303831d35Sstevel 			get_ledp->status = lip->status;
105403831d35Sstevel 			mutex_exit(&lw8_event_mutex);
105503831d35Sstevel 			get_ledp->position = lip->position;
105603831d35Sstevel 			(void) strncpy(get_ledp->color, lip->color,
105703831d35Sstevel 			    MAX_COLOR_LEN);
105803831d35Sstevel 			if (j == MAX_LEDS_PER_FRU - 1) {
105903831d35Sstevel 				get_ledp->next_id[0] = '\0';
106003831d35Sstevel 				return (0);
106103831d35Sstevel 			}
106203831d35Sstevel 			(void) strncpy(get_ledp->next_id,
106303831d35Sstevel 			    fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
106403831d35Sstevel 			return (0);
106503831d35Sstevel 		}
106603831d35Sstevel 	}
106703831d35Sstevel 	if (get_ledp->id[0] == '\0') {
106803831d35Sstevel 		get_ledp->next_id[0] = '\0';
106903831d35Sstevel 		return (0);
107003831d35Sstevel 	}
107103831d35Sstevel 	return (EINVAL);
107203831d35Sstevel }
107303831d35Sstevel 
107403831d35Sstevel /*ARGSUSED*/
107503831d35Sstevel static int
lw8_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)107603831d35Sstevel lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
107703831d35Sstevel     int *rval_p)
107803831d35Sstevel {
107903831d35Sstevel 	int instance = getminor(dev);
108003831d35Sstevel 	lom2_info_t lw8_info2;
108103831d35Sstevel 	lom_ctl_t lw8_ctl;
108203831d35Sstevel 	lom_ctl2_t lw8_ctl2;
108303831d35Sstevel 	lom_mprog_t lw8_mprog;
108403831d35Sstevel 	lom_fled_info_t lw8_fled_info;
108503831d35Sstevel 	lom_info_t lw8_info;
108603831d35Sstevel 	lom_aldata_t lw8_aldata;
108703831d35Sstevel 	lom_get_led_t lw8_get_led;
108803831d35Sstevel 	lom_set_led_t lw8_set_led;
108903831d35Sstevel 	lom_prog_t *lw8_progp;
109003831d35Sstevel 	lom_eventlog2_t *lw8_eventlogp;
109103831d35Sstevel 	lom_eventresp_t *lw8_eventresp;
109203831d35Sstevel 	int retval = 0;
109303831d35Sstevel 	int i, j;
109403831d35Sstevel 
109503831d35Sstevel 	if (instance != 0)
109603831d35Sstevel 		return (ENXIO);
109703831d35Sstevel 
109803831d35Sstevel 	switch (cmd) {
109903831d35Sstevel 	case LOMIOCWTMON:
110003831d35Sstevel 		mutex_enter(&lw8_event_mutex);
110103831d35Sstevel 		if (!lw8_event_pending) {
110203831d35Sstevel 			if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
110303831d35Sstevel 				mutex_exit(&lw8_event_mutex);
110403831d35Sstevel 				retval = EINTR;
110503831d35Sstevel 				break;
110603831d35Sstevel 			}
110703831d35Sstevel 		}
110803831d35Sstevel 		lw8_event_pending = B_FALSE;
110903831d35Sstevel 		mutex_exit(&lw8_event_mutex);
111003831d35Sstevel 		break;
111103831d35Sstevel 	case LOMIOCMREAD:
111203831d35Sstevel 		bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
111303831d35Sstevel 		lw8_mprog.config = 4;
111403831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
111503831d35Sstevel 		    sizeof (lw8_mprog), mode) != 0) {
111603831d35Sstevel 			retval = EFAULT;
111703831d35Sstevel 		}
111803831d35Sstevel 		break;
111903831d35Sstevel 	case LOMIOCCTL2:
112003831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
112103831d35Sstevel 		    sizeof (lw8_ctl2), mode) != 0) {
112203831d35Sstevel 			retval = EFAULT;
112303831d35Sstevel 			break;
112403831d35Sstevel 		}
112503831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
112603831d35Sstevel 		break;
112703831d35Sstevel 	case LOMIOCPROG:
112803831d35Sstevel 		lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
112903831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
113003831d35Sstevel 		    sizeof (*lw8_progp), mode) != 0) {
113103831d35Sstevel 			kmem_free(lw8_progp, sizeof (*lw8_progp));
113203831d35Sstevel 			retval = EFAULT;
113303831d35Sstevel 			break;
113403831d35Sstevel 		}
113503831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
113603831d35Sstevel 		kmem_free(lw8_progp, sizeof (*lw8_progp));
113703831d35Sstevel 		break;
113803831d35Sstevel 	case LOMIOCINFO2:
113903831d35Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
114003831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
114103831d35Sstevel 		if (retval != 0)
114203831d35Sstevel 			break;
114303831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
114403831d35Sstevel 		    sizeof (lw8_info2), mode) != 0) {
114503831d35Sstevel 			retval = EFAULT;
114603831d35Sstevel 		}
114703831d35Sstevel 		break;
114803831d35Sstevel 	case LOMIOCINFO:
114903831d35Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
115003831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
115103831d35Sstevel 		if (retval != 0)
115203831d35Sstevel 			break;
115303831d35Sstevel 		bzero((caddr_t)&lw8_info, sizeof (lw8_info));
115403831d35Sstevel 		lw8_info.ser_char = lw8_info2.escape_chars[0];
115503831d35Sstevel 		lw8_info.fver = lw8_info2.fver;
115603831d35Sstevel 		lw8_info.fchksum = lw8_info2.fchksum;
115703831d35Sstevel 		lw8_info.prod_rev = lw8_info2.prod_rev;
115807d06da5SSurya Prakki 		(void) strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
115903831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
116003831d35Sstevel 		    sizeof (lw8_info), mode) != 0) {
116103831d35Sstevel 			retval = EFAULT;
116203831d35Sstevel 		}
116303831d35Sstevel 		break;
116403831d35Sstevel 	case LOMIOCFLEDSTATE:
116503831d35Sstevel 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
116603831d35Sstevel 		(void) strncpy(lw8_get_led.location, "chassis",
116703831d35Sstevel 		    MAX_LOCATION_LEN);
116803831d35Sstevel 		(void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
116903831d35Sstevel 		retval = lw8_getled(&lw8_get_led);
117003831d35Sstevel 		if (retval != 0)
117103831d35Sstevel 			break;
117203831d35Sstevel 		lw8_fled_info.on = lw8_get_led.status;
117303831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
117403831d35Sstevel 		    sizeof (lw8_fled_info), mode) != 0) {
117503831d35Sstevel 			retval = EFAULT;
117603831d35Sstevel 		}
117703831d35Sstevel 		break;
117803831d35Sstevel 	case LOMIOCALSTATE:
117903831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
118003831d35Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
118103831d35Sstevel 			retval = EFAULT;
118203831d35Sstevel 			break;
118303831d35Sstevel 		}
118403831d35Sstevel 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
118503831d35Sstevel 		(void) strncpy(lw8_get_led.location, "chassis",
118603831d35Sstevel 		    MAX_LOCATION_LEN);
118703831d35Sstevel 		if (lw8_aldata.alarm_no == 3)
118803831d35Sstevel 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
118903831d35Sstevel 		else
119003831d35Sstevel 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
119103831d35Sstevel 			    lw8_aldata.alarm_no);
119203831d35Sstevel 		retval = lw8_getled(&lw8_get_led);
119303831d35Sstevel 		if (retval != 0)
119403831d35Sstevel 			break;
119503831d35Sstevel 		lw8_aldata.state = lw8_get_led.status;
119603831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
119703831d35Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
119803831d35Sstevel 			retval = EFAULT;
119903831d35Sstevel 		}
120003831d35Sstevel 		break;
120103831d35Sstevel 	case LOMIOCGETLED:
120203831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
120303831d35Sstevel 		    sizeof (lw8_get_led), mode) != 0) {
120403831d35Sstevel 			retval = EFAULT;
120503831d35Sstevel 			break;
120603831d35Sstevel 		}
120703831d35Sstevel 		retval = lw8_getled(&lw8_get_led);
120803831d35Sstevel 		if (retval != 0)
120903831d35Sstevel 			break;
121003831d35Sstevel 		if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
121103831d35Sstevel 		    sizeof (lw8_get_led), mode) != 0) {
121203831d35Sstevel 			retval = EFAULT;
121303831d35Sstevel 		}
121403831d35Sstevel 		break;
121503831d35Sstevel 	case LOMIOCEVENTLOG2:
121603831d35Sstevel 		lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
121703831d35Sstevel 		lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
121803831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
121903831d35Sstevel 		    sizeof (*lw8_eventlogp), mode) != 0) {
122003831d35Sstevel 			kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
122103831d35Sstevel 			kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
122203831d35Sstevel 			retval = EFAULT;
122303831d35Sstevel 			break;
122403831d35Sstevel 		}
122503831d35Sstevel 		lw8_eventresp->num = lw8_eventlogp->num;
122603831d35Sstevel 		lw8_eventresp->level = lw8_eventlogp->level;
122703831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
122803831d35Sstevel 		    (intptr_t)lw8_eventresp);
122903831d35Sstevel 		if (retval == 0) {
123003831d35Sstevel 			lw8_eventlogp->num = lw8_eventresp->num;
123103831d35Sstevel 			for (i = 0; i < lw8_eventresp->num; i++) {
123203831d35Sstevel 				for (j = 0; j < MAX_EVENT_STR; j++) {
123303831d35Sstevel 					lw8_eventlogp->string[i][j] =
123403831d35Sstevel 					    lw8_eventresp->string[i][j];
123503831d35Sstevel 				}
123603831d35Sstevel 			}
123703831d35Sstevel 			if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
123803831d35Sstevel 			    sizeof (*lw8_eventlogp), mode) != 0) {
123903831d35Sstevel 				retval = EFAULT;
124003831d35Sstevel 			}
124103831d35Sstevel 		}
124203831d35Sstevel 		kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
124303831d35Sstevel 		kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
124403831d35Sstevel 		break;
124503831d35Sstevel 	case LOMIOCALCTL:
124603831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
124703831d35Sstevel 		    sizeof (lw8_aldata), mode) != 0) {
124803831d35Sstevel 			retval = EFAULT;
124903831d35Sstevel 			break;
125003831d35Sstevel 		}
125103831d35Sstevel 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
125203831d35Sstevel 		(void) strncpy(lw8_set_led.location, "chassis",
125303831d35Sstevel 		    MAX_LOCATION_LEN);
125403831d35Sstevel 		if (lw8_aldata.alarm_no == 3)
125503831d35Sstevel 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
125603831d35Sstevel 		else
125703831d35Sstevel 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
125803831d35Sstevel 			    lw8_aldata.alarm_no);
125903831d35Sstevel 		lw8_set_led.status = lw8_aldata.state;
126003831d35Sstevel 		retval = lw8_setled(&lw8_set_led);
126103831d35Sstevel 		break;
126203831d35Sstevel 	case LOMIOCSETLED:
126303831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
126403831d35Sstevel 		    sizeof (lw8_set_led), mode) != 0) {
126503831d35Sstevel 			retval = EFAULT;
126603831d35Sstevel 			break;
126703831d35Sstevel 		}
126803831d35Sstevel 		retval = lw8_setled(&lw8_set_led);
126903831d35Sstevel 		break;
127003831d35Sstevel 	case LOMIOCCTL:
127103831d35Sstevel 		/*
127203831d35Sstevel 		 * for this ioctl, as well as setting the fault led in the
127303831d35Sstevel 		 * LOMIOCCTL case in lw8_lomcmd(), we also need to set the
127403831d35Sstevel 		 * escape character. To do this we must use LW8_MBOX_SET_CTL,
127503831d35Sstevel 		 * but this also needs the serial_event value which we have
127603831d35Sstevel 		 * to get via LW8_MBOX_GET_INFO
127703831d35Sstevel 		 */
127803831d35Sstevel 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
127903831d35Sstevel 		    sizeof (lw8_ctl), mode) != 0) {
128003831d35Sstevel 			retval = EFAULT;
128103831d35Sstevel 			break;
128203831d35Sstevel 		}
128303831d35Sstevel 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
128403831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
128503831d35Sstevel 		if (retval != 0)
128603831d35Sstevel 			break;
128703831d35Sstevel 		bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
128803831d35Sstevel 		lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
128903831d35Sstevel 		lw8_ctl2.serial_events = lw8_info2.serial_events;
129003831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
129103831d35Sstevel 		if (retval != 0)
129203831d35Sstevel 			break;
129303831d35Sstevel 
129403831d35Sstevel 		/*
129503831d35Sstevel 		 * if fault_led != 0, then set the led
129603831d35Sstevel 		 */
129703831d35Sstevel 		if (lw8_ctl.fault_led == 0)
129803831d35Sstevel 			break;
129903831d35Sstevel 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
130003831d35Sstevel 		(void) strncpy(lw8_set_led.location, "chassis",
130103831d35Sstevel 		    MAX_LOCATION_LEN);
130203831d35Sstevel 		(void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
130303831d35Sstevel 		lw8_set_led.status = lw8_ctl.fault_led - 1;
130403831d35Sstevel 		retval = lw8_setled(&lw8_set_led);
130503831d35Sstevel 		break;
130603831d35Sstevel 	default:
130703831d35Sstevel 		retval = ENOTSUP;
130803831d35Sstevel 		break;
130903831d35Sstevel 	}
131003831d35Sstevel 	return (retval);
131103831d35Sstevel }
131203831d35Sstevel 
131303831d35Sstevel /* ARGSUSED */
131403831d35Sstevel static void
lw8_logger(caddr_t arg)131503831d35Sstevel lw8_logger(caddr_t arg)
131603831d35Sstevel {
131703831d35Sstevel 	callb_cpr_t	cprinfo;
131803831d35Sstevel 	lw8_logmsg_t	*lw8_logmsgp;
131903831d35Sstevel 	boolean_t	more_waiting;
132003831d35Sstevel 	char		level;
132103831d35Sstevel 	int		retval;
132203831d35Sstevel 
132303831d35Sstevel 	CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
132403831d35Sstevel 	    "lw8_logger");
132503831d35Sstevel 
132603831d35Sstevel 	lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
132703831d35Sstevel 	mutex_enter(&lw8_logger_lock);
132803831d35Sstevel 	for (;;) {
132903831d35Sstevel 
133003831d35Sstevel 		/*
133103831d35Sstevel 		 * Wait for someone to tell me to continue.
133203831d35Sstevel 		 */
133303831d35Sstevel 		while (lw8_logger_sig == LW8_LOGGER_WAIT) {
133403831d35Sstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
133503831d35Sstevel 			cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
133603831d35Sstevel 			CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
133703831d35Sstevel 		}
133803831d35Sstevel 
133903831d35Sstevel 		/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
134003831d35Sstevel 		if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
134103831d35Sstevel 			lw8_logger_sig = LW8_LOGGER_WAIT;
134203831d35Sstevel 
134303831d35Sstevel 			kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
134403831d35Sstevel 
134503831d35Sstevel 			/* lw8_logger_lock is held at this point! */
134603831d35Sstevel 			CALLB_CPR_EXIT(&cprinfo);
134703831d35Sstevel 
134803831d35Sstevel 			thread_exit();
134903831d35Sstevel 			/* NOTREACHED */
135003831d35Sstevel 		}
135103831d35Sstevel 
135203831d35Sstevel 		ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
135303831d35Sstevel 		lw8_logger_sig = LW8_LOGGER_WAIT;
135403831d35Sstevel 
135503831d35Sstevel 		mutex_exit(&lw8_logger_lock);
135603831d35Sstevel 
135703831d35Sstevel 		/* Do lw8_event logging */
135803831d35Sstevel 
135903831d35Sstevel 		/*
136003831d35Sstevel 		 * Get one message per iteration. We do not sleep if
136103831d35Sstevel 		 * there are more to process. This makes exit from the
136203831d35Sstevel 		 * routine much more reliable.
136303831d35Sstevel 		 */
136403831d35Sstevel 		more_waiting = B_FALSE;
136503831d35Sstevel 
136603831d35Sstevel 		retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
136703831d35Sstevel 		    (intptr_t)lw8_logmsgp);
136803831d35Sstevel 		if (retval == 0) {
136903831d35Sstevel 			if (lw8_logmsgp->msg_valid) {
137003831d35Sstevel 
137103831d35Sstevel 				switch (lw8_logmsgp->level) {
137203831d35Sstevel 				case 0:	/* LOG_EMERG */
137303831d35Sstevel 					level = SL_FATAL;
137403831d35Sstevel 					break;
137503831d35Sstevel 				case 1:	/* LOG_ALERT */
137603831d35Sstevel 					level = SL_FATAL;
137703831d35Sstevel 					break;
137803831d35Sstevel 				case 2:	/* LOG_CRIT */
137903831d35Sstevel 					level = SL_FATAL;
138003831d35Sstevel 					break;
138103831d35Sstevel 				case 3:	/* LOG_ERR */
138203831d35Sstevel 					level = SL_ERROR;
138303831d35Sstevel 					break;
138403831d35Sstevel 				case 4:	/* LOG_WARNING */
138503831d35Sstevel 					level = SL_WARN;
138603831d35Sstevel 					break;
138703831d35Sstevel 				case 5:	/* LOG_NOTICE */
138803831d35Sstevel 					level = SL_NOTE;
138903831d35Sstevel 					break;
139003831d35Sstevel 				case 6:	/* LOG_INFO */
139103831d35Sstevel 					level = SL_NOTE;
139203831d35Sstevel 					break;
139303831d35Sstevel 				case 7:	/* LOG_DEBUG */
139403831d35Sstevel 					level = SL_TRACE;
139503831d35Sstevel 					break;
139603831d35Sstevel 				default:	/* unknown */
139703831d35Sstevel 					level = SL_NOTE;
139803831d35Sstevel 					break;
139903831d35Sstevel 				}
140003831d35Sstevel 
140103831d35Sstevel 				/* Ensure NUL termination */
140203831d35Sstevel 				lw8_logmsgp->msg[
140319397407SSherry Moore 				    sizeof (lw8_logmsgp->msg) - 1] = '\0';
140407d06da5SSurya Prakki 				(void) strlog(0, 0, 0, SL_CONSOLE | level,
140503831d35Sstevel 				    lw8_logmsgp->msg);
140603831d35Sstevel 			}
140703831d35Sstevel 
140803831d35Sstevel 			if (lw8_logmsgp->num_remaining > 0)
140903831d35Sstevel 				more_waiting = B_TRUE;
141003831d35Sstevel 		}
141103831d35Sstevel 
141203831d35Sstevel 		/*
141303831d35Sstevel 		 * Re-enter the lock to prepare for another iteration.
141403831d35Sstevel 		 * We must have the lock here to protect lw8_logger_sig.
141503831d35Sstevel 		 */
141603831d35Sstevel 		mutex_enter(&lw8_logger_lock);
141703831d35Sstevel 		if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
141803831d35Sstevel 			/* We need to get more events */
141903831d35Sstevel 			lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
142003831d35Sstevel 	}
142103831d35Sstevel }
142203831d35Sstevel 
142303831d35Sstevel static void
lw8_logger_start(void)142403831d35Sstevel lw8_logger_start(void)
142503831d35Sstevel {
142603831d35Sstevel 	kthread_t *tp;
142703831d35Sstevel 
142803831d35Sstevel 	mutex_enter(&lw8_logger_lock);
142903831d35Sstevel 
143003831d35Sstevel 	if (lw8_logger_tid == 0) {
143103831d35Sstevel 		/* Force retrieval of any pending messages */
143203831d35Sstevel 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
143303831d35Sstevel 
143403831d35Sstevel 		tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
143503831d35Sstevel 		    &p0, TS_RUN, maxclsyspri);
143603831d35Sstevel 		lw8_logger_tid = tp->t_did;
143703831d35Sstevel 	}
143803831d35Sstevel 
143903831d35Sstevel 	mutex_exit(&lw8_logger_lock);
144003831d35Sstevel }
144103831d35Sstevel 
144203831d35Sstevel static void
lw8_logger_destroy(void)144303831d35Sstevel lw8_logger_destroy(void)
144403831d35Sstevel {
144503831d35Sstevel 	kt_did_t tid;
144603831d35Sstevel 
144703831d35Sstevel 	mutex_enter(&lw8_logger_lock);
144803831d35Sstevel 	tid = lw8_logger_tid;
144903831d35Sstevel 	if (tid != 0) {
145003831d35Sstevel 		lw8_logger_sig = LW8_LOGGER_EXITNOW;
145103831d35Sstevel 		cv_signal(&lw8_logger_sig_cv);
145203831d35Sstevel 		lw8_logger_tid = 0;
145303831d35Sstevel 	}
145403831d35Sstevel 	mutex_exit(&lw8_logger_lock);
145503831d35Sstevel 
145603831d35Sstevel 	/*
145703831d35Sstevel 	 * Wait for lw8_logger() to finish.
145803831d35Sstevel 	 */
145903831d35Sstevel 	if (tid != 0)
146003831d35Sstevel 		thread_join(tid);
146103831d35Sstevel }
146203831d35Sstevel 
146303831d35Sstevel static void
lw8_logger_wakeup(void)146403831d35Sstevel lw8_logger_wakeup(void)
146503831d35Sstevel {
146603831d35Sstevel 	mutex_enter(&lw8_logger_lock);
146703831d35Sstevel 
146803831d35Sstevel 	if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
146903831d35Sstevel 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
147003831d35Sstevel 	cv_signal(&lw8_logger_sig_cv);
147103831d35Sstevel 
147203831d35Sstevel 	mutex_exit(&lw8_logger_lock);
147303831d35Sstevel }
1474