xref: /illumos-gate/usr/src/uts/sun4u/lw8/io/lw8.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include <sys/time.h>
29 #include <sys/errno.h>
30 #include <sys/kmem.h>
31 #include <sys/stat.h>
32 #include <sys/cmn_err.h>
33 
34 #include <sys/conf.h>
35 #include <sys/modctl.h>
36 #include <sys/devops.h>
37 #include <sys/ddi.h>
38 #include <sys/sunddi.h>
39 #include <sys/callb.h>
40 #include <sys/disp.h>
41 #include <sys/strlog.h>
42 
43 #include <sys/sgevents.h>
44 #include <sys/serengeti.h>
45 #include <sys/sgsbbc.h>
46 #include <sys/sgsbbc_iosram.h>
47 #include <sys/sgsbbc_mailbox.h>
48 #include <sys/uadmin.h>
49 #include <sys/machsystm.h>
50 #include <sys/sysevent.h>
51 #include <sys/sysevent/dr.h>
52 #include <sys/sysevent/eventdefs.h>
53 #include <sys/file.h>
54 #include <sys/lw8.h>
55 #include <sys/lw8_impl.h>
56 #include <sys/plat_ecc_unum.h>
57 
58 /*
59  * Global Variables - can be patched from Solaris
60  * ==============================================
61  */
62 
63 /*
64  * Module Variables
65  * ================
66  */
67 
68 /*
69  * functions local to this driver.
70  */
71 static int	lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
72 static int	lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
73 static int	lw8_add_intr_handlers(void);
74 static int	lw8_remove_intr_handlers(void);
75 static void lw8_wakeup_sleepers(void);
76 static uint_t	lw8_fast_shutdown(char *arg);
77 static uint_t	lw8_slow_shutdown(char *arg);
78 static uint_t	lw8_event_data_handler(char *);
79 static uint_t	lw8_dr_data_handler(char *);
80 static uint_t	lw8_env_data_handler(char *);
81 static uint_t	lw8_cap_ecc_msg_handler(char *);
82 static int	lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p);
83 static int	lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p);
84 static int	lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
85     cred_t *cred_p, int *rval_p);
86 static void	lw8_logger_start(void);
87 static void	lw8_logger_destroy(void);
88 static void	lw8_logger_wakeup(void);
89 
90 /*
91  * Driver entry points
92  */
93 static struct cb_ops lw8_cb_ops = {
94 	lw8_open,	/* open */
95 	lw8_close,	/* close */
96 	nodev,		/* strategy() */
97 	nodev,		/* print() */
98 	nodev,		/* dump() */
99 	nodev,		/* read() */
100 	nodev,		/* write() */
101 	lw8_ioctl,	/* ioctl() */
102 	nodev,		/* devmap() */
103 	nodev,		/* mmap() */
104 	ddi_segmap,	/* segmap() */
105 	nochpoll,	/* poll() */
106 	ddi_prop_op,    /* prop_op() */
107 	NULL,		/* cb_str */
108 	D_NEW | D_MP	/* cb_flag */
109 };
110 
111 
112 static struct dev_ops lw8_ops = {
113 	DEVO_REV,
114 	0,			/* ref count */
115 	ddi_getinfo_1to1,	/* getinfo() */
116 	nulldev,		/* identify() */
117 	nulldev,		/* probe() */
118 	lw8_attach,		/* attach() */
119 	lw8_detach,		/* detach */
120 	nodev,			/* reset */
121 	&lw8_cb_ops,		/* pointer to cb_ops structure */
122 	(struct bus_ops *)NULL,
123 	nulldev,		/* power() */
124 	ddi_quiesce_not_needed,		/* quiesce() */
125 };
126 
127 /*
128  * Loadable module support.
129  */
130 extern struct mod_ops mod_driverops;
131 
132 static struct modldrv modldrv = {
133 	&mod_driverops,			/* Type of module. This is a driver */
134 	"Netra-T12 control driver",	/* Name of the module */
135 	&lw8_ops			/* pointer to the dev_ops structure */
136 };
137 
138 static struct modlinkage modlinkage = {
139 	MODREV_1,
140 	&modldrv,
141 	NULL
142 };
143 
144 /*
145  * messages
146  */
147 #define	SHUTDOWN_EVENT_MSG		"lw8: system shutdown due to " \
148 					"SC request.\n"
149 #define	VOLTAGE_EVENT_MSG		"lw8: system shutdown due to " \
150 					"voltage out of range.\n"
151 #define	TEMPERATURE_EVENT_MSG		"lw8: system shutdown due to " \
152 					"temperature exceeding limits.\n"
153 #define	FANFAIL_EVENT_MSG		"lw8: system shutdown due to " \
154 					"too many fan failures.\n"
155 #define	NO_SCC_EVENT_MSG		"lw8: system shutdown due to " \
156 					"no system configuration card.\n"
157 
158 /*
159  * led table - the following provides a cache of the led state - needed
160  * to avoid the overhead of readoing from the SC each time
161  */
162 
163 struct led_info {
164 	char	id[MAX_ID_LEN];
165 	int	position;
166 	int 	status;
167 	char	color[MAX_COLOR_LEN];
168 };
169 
170 static struct fru_led_info {
171 	char    location[MAX_LOCATION_LEN];
172 	struct led_info led_info[MAX_LEDS_PER_FRU];
173 } fru_led_table[MAX_FRUS] = {
174 	"SB0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
175 		"power", LOM_LED_POSITION_FRU, 0, "green",
176 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
177 	"PS0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
178 		"power", LOM_LED_POSITION_FRU, 0, "green",
179 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
180 	"SB2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
181 		"power", LOM_LED_POSITION_FRU, 0, "green",
182 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
183 	"PS1", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
184 		"power", LOM_LED_POSITION_FRU, 0, "green",
185 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
186 	"SB4", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
187 		"power", LOM_LED_POSITION_FRU, 0, "green",
188 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
189 	"PS2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
190 		"power", LOM_LED_POSITION_FRU, 0, "green",
191 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
192 	"IB6", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
193 		"power", LOM_LED_POSITION_FRU, 0, "green",
194 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
195 	"PS3", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
196 		"power", LOM_LED_POSITION_FRU, 0, "green",
197 		"predicted_fault", LOM_LED_POSITION_FRU, 0, "amber"},
198 	"FT0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
199 		"power", LOM_LED_POSITION_FRU, 0, "green",
200 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
201 	"FAN0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
202 	"FAN1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
203 	"FAN2", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
204 	"FAN3", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
205 	"FAN4", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
206 	"FAN5", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
207 	"FAN6", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
208 	"FAN7", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
209 	"FAN8", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
210 	"FAN9", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber"},
211 	"DISK0", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
212 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
213 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
214 	"DISK1", {"fault", LOM_LED_POSITION_LOCATION, 0, "amber",
215 		"power", LOM_LED_POSITION_LOCATION, 0, "green",
216 		"ok_to_remove", LOM_LED_POSITION_LOCATION, 0, "blue"},
217 	"RP0", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
218 		"power", LOM_LED_POSITION_FRU, 0, "green",
219 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
220 	"RP2", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
221 		"power", LOM_LED_POSITION_FRU, 0, "green",
222 		"ok_to_remove", LOM_LED_POSITION_FRU, 0, "amber"},
223 	"chassis", {"fault", LOM_LED_POSITION_FRU, 0, "amber",
224 		"power", LOM_LED_POSITION_FRU, 0, "green",
225 		"locator", LOM_LED_POSITION_FRU, 0, "white",
226 		"top_access", LOM_LED_POSITION_FRU, 0, "amber",
227 		"alarm1", LOM_LED_POSITION_FRU, 0, "amber",
228 		"alarm2", LOM_LED_POSITION_FRU, 0, "amber",
229 		"system", LOM_LED_POSITION_FRU, 0, "green",
230 		"supplyA", LOM_LED_POSITION_FRU, 0, "green",
231 		"supplyB", LOM_LED_POSITION_FRU, 0, "green"},
232 };
233 
234 char    *fru_locn[MAX_LOCATION_LEN] = {
235 	"SB0",
236 	"PS0",
237 	"SB2",
238 	"PS1",
239 	"SB4",
240 	"PS2",
241 	"IB6",
242 	"PS3",
243 	"SCC",
244 	"SSC1",
245 };
246 
247 /*
248  * mutexes which protect the interrupt handlers.
249  */
250 static kmutex_t		lw8_shutdown_hdlr_lock;
251 static kmutex_t		lw8_dr_hdlr_lock;
252 static kmutex_t		lw8_env_hdlr_lock;
253 static kmutex_t		lw8_event_mutex;
254 static kmutex_t		lw8_logger_lock;
255 static kmutex_t		lw8_cap_msg_hdlr_lock;
256 static kcondvar_t	lw8_event_cv;
257 static kcondvar_t	lw8_logger_sig_cv;
258 
259 /*
260  * state booleans
261  */
262 static boolean_t	lw8_event_pending = B_FALSE;
263 static boolean_t	led_state_cached = B_FALSE;
264 
265 /*
266  * Payloads of the event handlers.
267  */
268 static lw8_event_t	lw8_shutdown_payload;
269 static sbbc_msg_t	lw8_shutdown_payload_msg;
270 static sg_system_fru_descriptor_t	lw8_dr_payload;
271 static sbbc_msg_t	lw8_dr_payload_msg;
272 static sg_event_fan_status_t		lw8_env_payload;
273 static sbbc_msg_t	lw8_env_payload_msg;
274 static plat_capability_data_t	lw8_cap_payload;
275 static sbbc_msg_t	lw8_cap_payload_msg;
276 
277 /*
278  * The IDs of the soft interrupts
279  */
280 static ddi_softintr_t   lw8_slow_shutdown_softint_id;
281 static ddi_softintr_t   lw8_fast_shutdown_softint_id;
282 
283 /*
284  * Logger commands..
285  */
286 #define	LW8_LOGGER_EXITNOW	-1
287 #define	LW8_LOGGER_WAIT	0
288 #define	LW8_LOGGER_PROCESSNOW	1
289 
290 /*
291  * Logger thread state
292  */
293 static int lw8_logger_sig = LW8_LOGGER_WAIT;
294 static kt_did_t lw8_logger_tid = 0;
295 
296 extern pri_t maxclsyspri;
297 
298 int
299 _init(void)
300 {
301 	int	error = 0;
302 
303 	mutex_init(&lw8_shutdown_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
304 	mutex_init(&lw8_dr_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
305 	mutex_init(&lw8_env_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
306 	mutex_init(&lw8_cap_msg_hdlr_lock, NULL, MUTEX_DEFAULT, NULL);
307 	mutex_init(&lw8_event_mutex, NULL, MUTEX_DRIVER, NULL);
308 	mutex_init(&lw8_logger_lock, NULL, MUTEX_DRIVER, NULL);
309 	cv_init(&lw8_event_cv, NULL, CV_DRIVER, NULL);
310 	cv_init(&lw8_logger_sig_cv, NULL, CV_DRIVER, NULL);
311 
312 	error = mod_install(&modlinkage);
313 	if (error) {
314 		cv_destroy(&lw8_logger_sig_cv);
315 		cv_destroy(&lw8_event_cv);
316 		mutex_destroy(&lw8_logger_lock);
317 		mutex_destroy(&lw8_event_mutex);
318 		mutex_destroy(&lw8_env_hdlr_lock);
319 		mutex_destroy(&lw8_cap_msg_hdlr_lock);
320 		mutex_destroy(&lw8_dr_hdlr_lock);
321 		mutex_destroy(&lw8_shutdown_hdlr_lock);
322 	}
323 	return (error);
324 }
325 
326 
327 int
328 _info(struct modinfo *modinfop)
329 {
330 	return (mod_info(&modlinkage, modinfop));
331 }
332 
333 
334 int
335 _fini(void)
336 {
337 	int	error = 0;
338 
339 	error = mod_remove(&modlinkage);
340 	if (error)
341 		return (error);
342 	cv_destroy(&lw8_logger_sig_cv);
343 	cv_destroy(&lw8_event_cv);
344 	mutex_destroy(&lw8_logger_lock);
345 	mutex_destroy(&lw8_event_mutex);
346 	mutex_destroy(&lw8_env_hdlr_lock);
347 	mutex_destroy(&lw8_cap_msg_hdlr_lock);
348 	mutex_destroy(&lw8_dr_hdlr_lock);
349 	mutex_destroy(&lw8_shutdown_hdlr_lock);
350 	return (error);
351 }
352 
353 
354 static int
355 lw8_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
356 {
357 	int			instance;
358 	int			err;
359 
360 	switch (cmd) {
361 	case DDI_ATTACH:
362 		/*
363 		 * only allow one instance
364 		 */
365 		instance = ddi_get_instance(dip);
366 		if (instance != 0)
367 			return (DDI_FAILURE);
368 
369 		err = ddi_create_minor_node(dip, "lw8", S_IFCHR,
370 		    instance, DDI_PSEUDO, NULL);
371 		if (err != DDI_SUCCESS)
372 			return (DDI_FAILURE);
373 
374 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
375 		    &lw8_slow_shutdown_softint_id, NULL, NULL,
376 		    lw8_slow_shutdown, NULL);
377 		if (err != 0) {
378 			cmn_err(CE_WARN, "Failed to add polling softint"
379 			    "handler for lw8. Err=%d", err);
380 			ddi_remove_minor_node(dip, NULL);
381 			return (DDI_FAILURE);
382 		}
383 
384 		err = ddi_add_softintr(dip, DDI_SOFTINT_LOW,
385 		    &lw8_fast_shutdown_softint_id, NULL, NULL,
386 		    lw8_fast_shutdown, NULL);
387 		if (err != 0) {
388 			cmn_err(CE_WARN, "Failed to add polling softint"
389 			    "handler for lw8. Err=%d", err);
390 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
391 			ddi_remove_minor_node(dip, NULL);
392 			return (DDI_FAILURE);
393 		}
394 
395 		lw8_logger_start();
396 
397 		/*
398 		 * Add the handlers which watch for unsolicited messages
399 		 * and post event to Sysevent Framework.
400 		 */
401 		err = lw8_add_intr_handlers();
402 		if (err != DDI_SUCCESS) {
403 			cmn_err(CE_WARN, "Failed to add event handlers");
404 			lw8_logger_destroy();
405 			ddi_remove_softintr(lw8_fast_shutdown_softint_id);
406 			ddi_remove_softintr(lw8_slow_shutdown_softint_id);
407 			ddi_remove_minor_node(dip, NULL);
408 			return (DDI_FAILURE);
409 		}
410 
411 		ddi_report_dev(dip);
412 		return (DDI_SUCCESS);
413 	case DDI_RESUME:
414 		return (DDI_SUCCESS);
415 	default:
416 		return (DDI_FAILURE);
417 	}
418 }
419 
420 
421 static int
422 lw8_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
423 {
424 	int	instance;
425 	int	err;
426 
427 	switch (cmd) {
428 	case DDI_DETACH:
429 		instance = ddi_get_instance(dip);
430 		if (instance != 0)
431 			return (DDI_FAILURE);
432 
433 		/*
434 		 * Remove the handlers which watch for unsolicited messages
435 		 * and post event to Sysevent Framework.
436 		 */
437 		err = lw8_remove_intr_handlers();
438 		if (err != DDI_SUCCESS) {
439 			cmn_err(CE_WARN, "Failed to remove event handlers");
440 			return (DDI_FAILURE);
441 		}
442 		lw8_logger_destroy();
443 		ddi_remove_softintr(lw8_slow_shutdown_softint_id);
444 		ddi_remove_softintr(lw8_fast_shutdown_softint_id);
445 		ddi_remove_minor_node(dip, NULL);
446 		return (DDI_SUCCESS);
447 	case DDI_SUSPEND:
448 		return (DDI_SUCCESS);
449 	default:
450 		return (DDI_FAILURE);
451 	}
452 }
453 
454 static int
455 lw8_add_intr_handlers()
456 {
457 	int	err;
458 
459 	lw8_shutdown_payload_msg.msg_buf = (caddr_t)&lw8_shutdown_payload;
460 	lw8_shutdown_payload_msg.msg_len = sizeof (lw8_shutdown_payload);
461 	err = sbbc_mbox_reg_intr(MBOX_EVENT_LW8, lw8_event_data_handler,
462 	    &lw8_shutdown_payload_msg, NULL, &lw8_shutdown_hdlr_lock);
463 	if (err != 0) {
464 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_LW8 "
465 		    " handler. Err=%d", err);
466 		return (DDI_FAILURE);
467 	}
468 
469 	lw8_dr_payload_msg.msg_buf = (caddr_t)&lw8_dr_payload;
470 	lw8_dr_payload_msg.msg_len = sizeof (lw8_dr_payload);
471 	err = sbbc_mbox_reg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler,
472 	    &lw8_dr_payload_msg, NULL, &lw8_dr_hdlr_lock);
473 	if (err != 0) {
474 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_GENERIC "
475 		    " handler. Err=%d", err);
476 		sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
477 		return (DDI_FAILURE);
478 	}
479 
480 	lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
481 	lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
482 	err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
483 	    &lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
484 	if (err != 0) {
485 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
486 		    " handler. Err=%d", err);
487 		sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
488 		sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
489 		return (DDI_FAILURE);
490 	}
491 
492 	lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
493 	lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
494 	err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
495 	    &lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
496 	if (err != 0) {
497 		cmn_err(CE_WARN, "Failed to register INFO_MBOX "
498 		    " handler. Err=%d", err);
499 		sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
500 		sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
501 		sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
502 		return (DDI_FAILURE);
503 	}
504 
505 	return (DDI_SUCCESS);
506 }
507 
508 static int
509 lw8_remove_intr_handlers(void)
510 {
511 	int	rv = DDI_SUCCESS;
512 	int	err;
513 
514 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
515 	if (err != 0) {
516 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
517 		    "handler. Err=%d", err);
518 		rv = DDI_FAILURE;
519 	}
520 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
521 	if (err != 0) {
522 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
523 		    "handler. Err=%d", err);
524 		rv = DDI_FAILURE;
525 	}
526 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
527 	if (err != 0) {
528 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
529 		    "handler. Err=%d", err);
530 		rv = DDI_FAILURE;
531 	}
532 	err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
533 	if (err != 0) {
534 		cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
535 		    "handler. Err=%d", err);
536 		rv = DDI_FAILURE;
537 	}
538 	return (rv);
539 }
540 
541 static uint_t
542 lw8_dr_data_handler(char *arg)
543 {
544 	sg_system_fru_descriptor_t	*payload;
545 	sbbc_msg_t			*msg;
546 	int				hint;
547 	sysevent_t			*ev;
548 	sysevent_id_t			eid;
549 	int				rv = 0;
550 	sysevent_value_t		evnt_val;
551 	sysevent_attr_list_t		*evnt_attr_list = NULL;
552 	char				attach_pnt[MAXPATHLEN];
553 
554 	msg = (sbbc_msg_t *)arg;
555 	if (msg == NULL) {
556 		return (DDI_INTR_CLAIMED);
557 	}
558 	payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
559 	if (payload == NULL) {
560 		return (DDI_INTR_CLAIMED);
561 	}
562 	if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
563 	    sizeof (char *)) {
564 		return (DDI_INTR_CLAIMED);
565 	}
566 
567 	/*
568 	 * if not SB send sysevent (SBs send sysevent from ssm driver)
569 	 */
570 	if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
571 		switch (payload->event_details) {
572 		case SG_EVT_BOARD_ABSENT:
573 			hint = SE_HINT_REMOVE;
574 			break;
575 		case SG_EVT_BOARD_PRESENT:
576 			hint = SE_HINT_INSERT;
577 			break;
578 		default:
579 			hint = SE_NO_HINT;
580 			break;
581 		}
582 		(void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
583 		    fru_locn[payload->slot]);
584 		ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
585 		    KM_NOSLEEP);
586 		if (ev == NULL) {
587 			cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
588 			return (DDI_INTR_CLAIMED);
589 		}
590 		evnt_val.value_type = SE_DATA_TYPE_STRING;
591 		evnt_val.value.sv_string = attach_pnt;
592 		rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
593 		    KM_NOSLEEP);
594 		if (rv != 0) {
595 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
596 			    DR_AP_ID, EC_DR);
597 			sysevent_free(ev);
598 			return (DDI_INTR_CLAIMED);
599 		}
600 
601 		/*
602 		 * Add the hint
603 		 */
604 		evnt_val.value_type = SE_DATA_TYPE_STRING;
605 		evnt_val.value.sv_string = SE_HINT2STR(hint);
606 		rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
607 		    KM_NOSLEEP);
608 		if (rv != 0) {
609 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
610 			    DR_HINT, EC_DR);
611 			sysevent_free_attr(evnt_attr_list);
612 			sysevent_free(ev);
613 			return (DDI_INTR_CLAIMED);
614 		}
615 		if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
616 			cmn_err(CE_WARN, "Failed to attach attr list for %s "
617 			    "event", EC_DR);
618 			sysevent_free_attr(evnt_attr_list);
619 			sysevent_free(ev);
620 			return (DDI_INTR_CLAIMED);
621 		}
622 		rv = log_sysevent(ev, KM_NOSLEEP, &eid);
623 		if (rv != 0) {
624 			cmn_err(CE_WARN,
625 			    "lw8_dr_event_handler: failed to log event");
626 		}
627 		sysevent_free(ev);
628 	}
629 	lw8_wakeup_sleepers();
630 	return (DDI_INTR_CLAIMED);
631 }
632 
633 static uint_t
634 lw8_cap_ecc_msg_handler(char *addr)
635 {
636 	sbbc_msg_t *msg = NULL;
637 	plat_capability_data_t *cap = NULL;
638 
639 	msg = (sbbc_msg_t *)addr;
640 	if (msg == NULL || msg->msg_buf == NULL)
641 		return (DDI_INTR_CLAIMED);
642 
643 	cap = (plat_capability_data_t *)msg->msg_buf;
644 	switch (cap->capd_msg_type) {
645 	case PLAT_ECC_CAPABILITY_MESSAGE:
646 		plat_ecc_capability_sc_set(cap->capd_capability);
647 		break;
648 	default:
649 		break;
650 	}
651 
652 	return (DDI_INTR_CLAIMED);
653 }
654 
655 /*ARGSUSED*/
656 static uint_t
657 lw8_env_data_handler(char *arg)
658 {
659 	lw8_wakeup_sleepers();
660 	return (DDI_INTR_CLAIMED);
661 }
662 
663 /*
664  * wakeup sleepers + mark led cache for this fru as invalid
665  */
666 static void
667 lw8_wakeup_sleepers()
668 {
669 	mutex_enter(&lw8_event_mutex);
670 	lw8_event_pending = B_TRUE;
671 	cv_broadcast(&lw8_event_cv);
672 	led_state_cached = B_FALSE;
673 	mutex_exit(&lw8_event_mutex);
674 }
675 
676 /*
677  * This function is triggered by a soft interrupt and it's purpose is to call
678  * to kadmin() to shutdown the system.
679  */
680 /*ARGSUSED*/
681 static uint_t
682 lw8_fast_shutdown(char *arg)
683 {
684 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
685 
686 	/*
687 	 * If kadmin fails for some reason then we bring the system down
688 	 * via power_down(), or failing that using halt().
689 	 */
690 	power_down("kadmin() failed, trying power_down()");
691 
692 	halt("power_down() failed, trying halt()");
693 
694 	/*
695 	 * We should never make it this far, so something must have gone
696 	 * horribly, horribly wrong.
697 	 */
698 	/*NOTREACHED*/
699 	return (DDI_INTR_UNCLAIMED);
700 }
701 
702 /*
703  * This function is triggered by a soft interrupt and it's purpose is to call
704  * to do_shutdown() to shutdown the system.
705  */
706 /*ARGSUSED*/
707 static uint_t
708 lw8_slow_shutdown(char *arg)
709 {
710 	do_shutdown();
711 	return (DDI_SUCCESS);
712 }
713 
714 static uint_t
715 lw8_event_data_handler(char *arg)
716 {
717 	lw8_event_t	*payload;
718 	sbbc_msg_t	*msg;
719 
720 	if (arg == NULL) {
721 		return (DDI_INTR_CLAIMED);
722 	}
723 
724 	msg = (sbbc_msg_t *)arg;
725 	if (msg->msg_buf == NULL) {
726 		return (DDI_INTR_CLAIMED);
727 	}
728 
729 	payload = (lw8_event_t *)msg->msg_buf;
730 	switch (payload->event_type) {
731 	case LW8_EVENT_REQUESTED_SHUTDOWN:
732 
733 		/*
734 		 * Let the user know why the domain is going down.
735 		 */
736 		cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
737 		ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
738 
739 		/*NOTREACHED*/
740 		break;
741 
742 	case LW8_EVENT_VOLTAGE_SHUTDOWN:
743 
744 		/*
745 		 * Let the user know why the domain is going down.
746 		 */
747 		cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
748 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
749 
750 		/*NOTREACHED*/
751 		break;
752 
753 	case LW8_EVENT_TEMPERATURE_SHUTDOWN:
754 
755 		/*
756 		 * Let the user know why the domain is going down.
757 		 */
758 		cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
759 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
760 
761 		/*NOTREACHED*/
762 		break;
763 
764 	case LW8_EVENT_FANFAIL_SHUTDOWN:
765 
766 		/*
767 		 * Let the user know why the domain is going down.
768 		 */
769 		cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
770 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
771 
772 		/*NOTREACHED*/
773 		break;
774 
775 	case LW8_EVENT_NO_SCC_SHUTDOWN:
776 
777 		/*
778 		 * Let the user know why the domain is going down.
779 		 */
780 		cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
781 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
782 
783 		/*NOTREACHED*/
784 		break;
785 
786 	case LW8_EVENT_NEW_LOG_MSG:
787 
788 		/*
789 		 * Wake up the log retrieval thread.
790 		 */
791 		lw8_logger_wakeup();
792 
793 		break;
794 
795 	default:
796 		return (DDI_INTR_CLAIMED);
797 	}
798 
799 	return (DDI_INTR_CLAIMED);
800 }
801 
802 /*ARGSUSED*/
803 static int
804 lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
805 {
806 	int error = 0;
807 	int instance = getminor(*dev_p);
808 	static fn_t f = "lw8_open";
809 
810 	if (instance != 0)
811 		return (ENXIO);
812 
813 	if ((error = drv_priv(cred_p)) != 0) {
814 		cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
815 		    f, instance);
816 		return (error);
817 	}
818 	return (error);
819 }
820 
821 /*ARGSUSED*/
822 static int
823 lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
824 {
825 	return (DDI_SUCCESS);
826 }
827 
828 static int
829 lw8_lomcmd(int cmd, intptr_t arg)
830 {
831 	sbbc_msg_t request, *reqp = &request;
832 	sbbc_msg_t response, *resp = &response;
833 	int rv = 0;
834 	lom_eventreq_t *eventreqp;
835 
836 	bzero((caddr_t)&request, sizeof (request));
837 	reqp->msg_type.type = LW8_MBOX;
838 	reqp->msg_type.sub_type = cmd;
839 	bzero((caddr_t)&response, sizeof (response));
840 	resp->msg_type.type = LW8_MBOX;
841 	resp->msg_type.sub_type = cmd;
842 
843 	switch (cmd) {
844 	case LW8_MBOX_GET_INFO:
845 		reqp->msg_len = 0;
846 		reqp->msg_buf = (caddr_t)NULL;
847 		resp->msg_len = sizeof (lom2_info_t);
848 		resp->msg_buf = (caddr_t)arg;
849 		break;
850 	case LW8_MBOX_SET_CTL:
851 		reqp->msg_len = sizeof (lom_ctl2_t);
852 		reqp->msg_buf = (caddr_t)arg;
853 		resp->msg_len = 0;
854 		resp->msg_buf = (caddr_t)NULL;
855 		break;
856 	case LW8_MBOX_UPDATE_FW:
857 		reqp->msg_len = sizeof (lom_prog_t);
858 		reqp->msg_buf = (caddr_t)arg;
859 		resp->msg_len = 0;
860 		resp->msg_buf = (caddr_t)NULL;
861 		break;
862 	case LW8_MBOX_GET_LED:
863 		reqp->msg_len = sizeof (lw8_get_led_payload_t);
864 		reqp->msg_buf = (caddr_t)arg;
865 		resp->msg_len = sizeof (lw8_get_led_payload_t);
866 		resp->msg_buf = (caddr_t)arg;
867 		break;
868 	case LW8_MBOX_SET_LED:
869 		reqp->msg_len = sizeof (lw8_set_led_payload_t);
870 		reqp->msg_buf = (caddr_t)arg;
871 		resp->msg_len = 0;
872 		resp->msg_buf = (caddr_t)NULL;
873 		break;
874 	case LW8_MBOX_GET_EVENTS:
875 		/*
876 		 * cast as lom_eventreq_t to minimise data traffic
877 		 */
878 		eventreqp = (lom_eventreq_t *)arg;
879 		reqp->msg_len = sizeof (lom_eventreq_t);
880 		reqp->msg_buf = (caddr_t)arg;
881 		resp->msg_len = sizeof (lom_eventreq_t) +
882 		    (eventreqp->num * MAX_EVENT_STR);
883 		resp->msg_buf = (caddr_t)arg;
884 		break;
885 	case LW8_MBOX_GET_NEXT_MSG:
886 		reqp->msg_len = 0;
887 		reqp->msg_buf = (caddr_t)NULL;
888 		resp->msg_len = sizeof (lw8_logmsg_t);
889 		resp->msg_buf = (caddr_t)arg;
890 		break;
891 	default:
892 		return (EINVAL);
893 	}
894 
895 	rv = sbbc_mbox_request_response(reqp, resp,
896 	    LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
897 
898 	if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
899 
900 		/* errors from sgsbbc */
901 		if (resp->msg_status > 0) {
902 			return (resp->msg_status);
903 		}
904 
905 		/* errors from SCAPP */
906 		switch (resp->msg_status) {
907 
908 		case SG_MBOX_STATUS_COMMAND_FAILURE:
909 			/* internal SCAPP error */
910 			return (EINTR);
911 
912 		case SG_MBOX_STATUS_HARDWARE_FAILURE:
913 			/* seprom read/write errors */
914 			return (EIO);
915 
916 		case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
917 			/* illegal ioctl parameter */
918 			return (EINVAL);
919 
920 		case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
921 			/* board access denied */
922 			return (EACCES);
923 
924 		case SG_MBOX_STATUS_STALE_CONTENTS:
925 			/* stale contents */
926 			return (ESTALE);
927 
928 		case SG_MBOX_STATUS_STALE_OBJECT:
929 			/* stale handle */
930 			return (ENOENT);
931 
932 		case SG_MBOX_STATUS_NO_SEPROM_SPACE:
933 			/* seprom lacks space */
934 			return (ENOSPC);
935 
936 		case SG_MBOX_STATUS_NO_MEMORY:
937 			/* user prog. lacks space */
938 			return (ENOMEM);
939 
940 		case SG_MBOX_STATUS_NOT_SUPPORTED:
941 			/* unsupported operation */
942 			return (ENOTSUP);
943 
944 		default:
945 			return (EIO);
946 		}
947 	}
948 	return (0);
949 }
950 
951 /*
952  * set the requested led, and mark cache as empty
953  */
954 static int
955 lw8_setled(lom_set_led_t *set_ledp)
956 {
957 	int retval;
958 	int i, j;
959 	struct led_info *lip;
960 	lw8_set_led_payload_t lw8_set_led;
961 
962 	for (i = 0; i < MAX_FRUS; i++) {
963 		if (strncmp(set_ledp->location, fru_led_table[i].location,
964 		    MAX_LOCATION_LEN) != 0)
965 			continue;
966 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
967 			lip = &fru_led_table[i].led_info[j];
968 			if (lip->id == NULL)
969 				continue;
970 			if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
971 				continue;
972 			lw8_set_led.value = set_ledp->status;
973 
974 			/*
975 			 * to minimise data transfer, the SC maintains
976 			 * just  3 values per fru - except for
977 			 * the chassis itself at the end which has
978 			 * MAX_LEDS_PER_FRU
979 			 */
980 			lw8_set_led.offset = (i * 3) + j;
981 			retval = lw8_lomcmd(LW8_MBOX_SET_LED,
982 			    (intptr_t)&lw8_set_led);
983 			if (retval != 0)
984 				return (retval);
985 			mutex_enter(&lw8_event_mutex);
986 			led_state_cached = B_FALSE;
987 			mutex_exit(&lw8_event_mutex);
988 			return (0);
989 		}
990 	}
991 	return (EINVAL);
992 }
993 
994 /*
995  * read led value from cache if possible, otherwise read from sc and
996  * update the cache
997  */
998 static int
999 lw8_getled(lom_get_led_t *get_ledp)
1000 {
1001 	int retval;
1002 	int i, j, k;
1003 	struct led_info *lip;
1004 	lw8_get_led_payload_t lw8_get_led;
1005 
1006 	for (i = 0; i < MAX_FRUS; i++) {
1007 		if (strncmp(get_ledp->location, fru_led_table[i].location,
1008 		    MAX_LOCATION_LEN) != 0)
1009 			continue;
1010 		if (get_ledp->id[0] == '\0') {
1011 			(void) strncpy(get_ledp->next_id,
1012 			    fru_led_table[i].led_info[0].id, MAX_ID_LEN);
1013 			return (0);
1014 		}
1015 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
1016 			lip = &fru_led_table[i].led_info[j];
1017 			if (lip->id == NULL)
1018 				continue;
1019 			if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
1020 				continue;
1021 			mutex_enter(&lw8_event_mutex);
1022 			if (!led_state_cached) {
1023 				mutex_exit(&lw8_event_mutex);
1024 				retval = lw8_lomcmd(LW8_MBOX_GET_LED,
1025 				    (intptr_t)&lw8_get_led);
1026 				if (retval != 0)
1027 					return (retval);
1028 				mutex_enter(&lw8_event_mutex);
1029 
1030 				/*
1031 				 * to minimise data transfer, the
1032 				 * lw8_get_led_payload_t structure just has 3
1033 				 * values per fru - except for the chassis
1034 				 * itself at the end which has MAX_LEDS_PER_FRU
1035 				 */
1036 				for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
1037 					fru_led_table[k / 3].led_info[k % 3].
1038 					    status = lw8_get_led.value[k];
1039 				}
1040 				for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
1041 					fru_led_table[MAX_FRUS - 1].led_info[k].
1042 					    status = lw8_get_led.value[k +
1043 					    ((MAX_FRUS - 1) * 3)];
1044 				}
1045 				led_state_cached = B_TRUE;
1046 			}
1047 			get_ledp->status = lip->status;
1048 			mutex_exit(&lw8_event_mutex);
1049 			get_ledp->position = lip->position;
1050 			(void) strncpy(get_ledp->color, lip->color,
1051 			    MAX_COLOR_LEN);
1052 			if (j == MAX_LEDS_PER_FRU - 1) {
1053 				get_ledp->next_id[0] = '\0';
1054 				return (0);
1055 			}
1056 			(void) strncpy(get_ledp->next_id,
1057 			    fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
1058 			return (0);
1059 		}
1060 	}
1061 	if (get_ledp->id[0] == '\0') {
1062 		get_ledp->next_id[0] = '\0';
1063 		return (0);
1064 	}
1065 	return (EINVAL);
1066 }
1067 
1068 /*ARGSUSED*/
1069 static int
1070 lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
1071     int *rval_p)
1072 {
1073 	int instance = getminor(dev);
1074 	lom2_info_t lw8_info2;
1075 	lom_ctl_t lw8_ctl;
1076 	lom_ctl2_t lw8_ctl2;
1077 	lom_mprog_t lw8_mprog;
1078 	lom_fled_info_t lw8_fled_info;
1079 	lom_info_t lw8_info;
1080 	lom_aldata_t lw8_aldata;
1081 	lom_get_led_t lw8_get_led;
1082 	lom_set_led_t lw8_set_led;
1083 	lom_prog_t *lw8_progp;
1084 	lom_eventlog2_t *lw8_eventlogp;
1085 	lom_eventresp_t *lw8_eventresp;
1086 	int retval = 0;
1087 	int i, j;
1088 
1089 	if (instance != 0)
1090 		return (ENXIO);
1091 
1092 	switch (cmd) {
1093 	case LOMIOCWTMON:
1094 		mutex_enter(&lw8_event_mutex);
1095 		if (!lw8_event_pending) {
1096 			if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
1097 				mutex_exit(&lw8_event_mutex);
1098 				retval = EINTR;
1099 				break;
1100 			}
1101 		}
1102 		lw8_event_pending = B_FALSE;
1103 		mutex_exit(&lw8_event_mutex);
1104 		break;
1105 	case LOMIOCMREAD:
1106 		bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
1107 		lw8_mprog.config = 4;
1108 		if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
1109 		    sizeof (lw8_mprog), mode) != 0) {
1110 			retval = EFAULT;
1111 		}
1112 		break;
1113 	case LOMIOCCTL2:
1114 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
1115 		    sizeof (lw8_ctl2), mode) != 0) {
1116 			retval = EFAULT;
1117 			break;
1118 		}
1119 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
1120 		break;
1121 	case LOMIOCPROG:
1122 		lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
1123 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
1124 		    sizeof (*lw8_progp), mode) != 0) {
1125 			kmem_free(lw8_progp, sizeof (*lw8_progp));
1126 			retval = EFAULT;
1127 			break;
1128 		}
1129 		retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
1130 		kmem_free(lw8_progp, sizeof (*lw8_progp));
1131 		break;
1132 	case LOMIOCINFO2:
1133 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1134 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1135 		if (retval != 0)
1136 			break;
1137 		if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
1138 		    sizeof (lw8_info2), mode) != 0) {
1139 			retval = EFAULT;
1140 		}
1141 		break;
1142 	case LOMIOCINFO:
1143 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1144 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1145 		if (retval != 0)
1146 			break;
1147 		bzero((caddr_t)&lw8_info, sizeof (lw8_info));
1148 		lw8_info.ser_char = lw8_info2.escape_chars[0];
1149 		lw8_info.fver = lw8_info2.fver;
1150 		lw8_info.fchksum = lw8_info2.fchksum;
1151 		lw8_info.prod_rev = lw8_info2.prod_rev;
1152 		strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
1153 		if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
1154 		    sizeof (lw8_info), mode) != 0) {
1155 			retval = EFAULT;
1156 		}
1157 		break;
1158 	case LOMIOCFLEDSTATE:
1159 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
1160 		(void) strncpy(lw8_get_led.location, "chassis",
1161 		    MAX_LOCATION_LEN);
1162 		(void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
1163 		retval = lw8_getled(&lw8_get_led);
1164 		if (retval != 0)
1165 			break;
1166 		lw8_fled_info.on = lw8_get_led.status;
1167 		if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
1168 		    sizeof (lw8_fled_info), mode) != 0) {
1169 			retval = EFAULT;
1170 		}
1171 		break;
1172 	case LOMIOCALSTATE:
1173 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
1174 		    sizeof (lw8_aldata), mode) != 0) {
1175 			retval = EFAULT;
1176 			break;
1177 		}
1178 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
1179 		(void) strncpy(lw8_get_led.location, "chassis",
1180 		    MAX_LOCATION_LEN);
1181 		if (lw8_aldata.alarm_no == 3)
1182 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
1183 		else
1184 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
1185 			    lw8_aldata.alarm_no);
1186 		retval = lw8_getled(&lw8_get_led);
1187 		if (retval != 0)
1188 			break;
1189 		lw8_aldata.state = lw8_get_led.status;
1190 		if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
1191 		    sizeof (lw8_aldata), mode) != 0) {
1192 			retval = EFAULT;
1193 		}
1194 		break;
1195 	case LOMIOCGETLED:
1196 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
1197 		    sizeof (lw8_get_led), mode) != 0) {
1198 			retval = EFAULT;
1199 			break;
1200 		}
1201 		retval = lw8_getled(&lw8_get_led);
1202 		if (retval != 0)
1203 			break;
1204 		if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
1205 		    sizeof (lw8_get_led), mode) != 0) {
1206 			retval = EFAULT;
1207 		}
1208 		break;
1209 	case LOMIOCEVENTLOG2:
1210 		lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
1211 		lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
1212 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
1213 		    sizeof (*lw8_eventlogp), mode) != 0) {
1214 			kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
1215 			kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
1216 			retval = EFAULT;
1217 			break;
1218 		}
1219 		lw8_eventresp->num = lw8_eventlogp->num;
1220 		lw8_eventresp->level = lw8_eventlogp->level;
1221 		retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
1222 		    (intptr_t)lw8_eventresp);
1223 		if (retval == 0) {
1224 			lw8_eventlogp->num = lw8_eventresp->num;
1225 			for (i = 0; i < lw8_eventresp->num; i++) {
1226 				for (j = 0; j < MAX_EVENT_STR; j++) {
1227 					lw8_eventlogp->string[i][j] =
1228 					    lw8_eventresp->string[i][j];
1229 				}
1230 			}
1231 			if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
1232 			    sizeof (*lw8_eventlogp), mode) != 0) {
1233 				retval = EFAULT;
1234 			}
1235 		}
1236 		kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
1237 		kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
1238 		break;
1239 	case LOMIOCALCTL:
1240 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
1241 		    sizeof (lw8_aldata), mode) != 0) {
1242 			retval = EFAULT;
1243 			break;
1244 		}
1245 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
1246 		(void) strncpy(lw8_set_led.location, "chassis",
1247 		    MAX_LOCATION_LEN);
1248 		if (lw8_aldata.alarm_no == 3)
1249 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
1250 		else
1251 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
1252 			    lw8_aldata.alarm_no);
1253 		lw8_set_led.status = lw8_aldata.state;
1254 		retval = lw8_setled(&lw8_set_led);
1255 		break;
1256 	case LOMIOCSETLED:
1257 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
1258 		    sizeof (lw8_set_led), mode) != 0) {
1259 			retval = EFAULT;
1260 			break;
1261 		}
1262 		retval = lw8_setled(&lw8_set_led);
1263 		break;
1264 	case LOMIOCCTL:
1265 		/*
1266 		 * for this ioctl, as well as setting the fault led in the
1267 		 * LOMIOCCTL case in lw8_lomcmd(), we also need to set the
1268 		 * escape character. To do this we must use LW8_MBOX_SET_CTL,
1269 		 * but this also needs the serial_event value which we have
1270 		 * to get via LW8_MBOX_GET_INFO
1271 		 */
1272 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
1273 		    sizeof (lw8_ctl), mode) != 0) {
1274 			retval = EFAULT;
1275 			break;
1276 		}
1277 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1278 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1279 		if (retval != 0)
1280 			break;
1281 		bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
1282 		lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
1283 		lw8_ctl2.serial_events = lw8_info2.serial_events;
1284 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
1285 		if (retval != 0)
1286 			break;
1287 
1288 		/*
1289 		 * if fault_led != 0, then set the led
1290 		 */
1291 		if (lw8_ctl.fault_led == 0)
1292 			break;
1293 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
1294 		(void) strncpy(lw8_set_led.location, "chassis",
1295 		    MAX_LOCATION_LEN);
1296 		(void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
1297 		lw8_set_led.status = lw8_ctl.fault_led - 1;
1298 		retval = lw8_setled(&lw8_set_led);
1299 		break;
1300 	default:
1301 		retval = ENOTSUP;
1302 		break;
1303 	}
1304 	return (retval);
1305 }
1306 
1307 /* ARGSUSED */
1308 static void
1309 lw8_logger(caddr_t arg)
1310 {
1311 	callb_cpr_t	cprinfo;
1312 	lw8_logmsg_t	*lw8_logmsgp;
1313 	boolean_t	more_waiting;
1314 	char		level;
1315 	int		retval;
1316 
1317 	CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
1318 	    "lw8_logger");
1319 
1320 	lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
1321 	mutex_enter(&lw8_logger_lock);
1322 	for (;;) {
1323 
1324 		/*
1325 		 * Wait for someone to tell me to continue.
1326 		 */
1327 		while (lw8_logger_sig == LW8_LOGGER_WAIT) {
1328 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
1329 			cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
1330 			CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
1331 		}
1332 
1333 		/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
1334 		if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
1335 			lw8_logger_sig = LW8_LOGGER_WAIT;
1336 
1337 			kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
1338 
1339 			/* lw8_logger_lock is held at this point! */
1340 			CALLB_CPR_EXIT(&cprinfo);
1341 
1342 			thread_exit();
1343 			/* NOTREACHED */
1344 		}
1345 
1346 		ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
1347 		lw8_logger_sig = LW8_LOGGER_WAIT;
1348 
1349 		mutex_exit(&lw8_logger_lock);
1350 
1351 		/* Do lw8_event logging */
1352 
1353 		/*
1354 		 * Get one message per iteration. We do not sleep if
1355 		 * there are more to process. This makes exit from the
1356 		 * routine much more reliable.
1357 		 */
1358 		more_waiting = B_FALSE;
1359 
1360 		retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
1361 		    (intptr_t)lw8_logmsgp);
1362 		if (retval == 0) {
1363 			if (lw8_logmsgp->msg_valid) {
1364 
1365 				switch (lw8_logmsgp->level) {
1366 				case 0:	/* LOG_EMERG */
1367 					level = SL_FATAL;
1368 					break;
1369 				case 1:	/* LOG_ALERT */
1370 					level = SL_FATAL;
1371 					break;
1372 				case 2:	/* LOG_CRIT */
1373 					level = SL_FATAL;
1374 					break;
1375 				case 3:	/* LOG_ERR */
1376 					level = SL_ERROR;
1377 					break;
1378 				case 4:	/* LOG_WARNING */
1379 					level = SL_WARN;
1380 					break;
1381 				case 5:	/* LOG_NOTICE */
1382 					level = SL_NOTE;
1383 					break;
1384 				case 6:	/* LOG_INFO */
1385 					level = SL_NOTE;
1386 					break;
1387 				case 7:	/* LOG_DEBUG */
1388 					level = SL_TRACE;
1389 					break;
1390 				default:	/* unknown */
1391 					level = SL_NOTE;
1392 					break;
1393 				}
1394 
1395 				/* Ensure NUL termination */
1396 				lw8_logmsgp->msg[
1397 				    sizeof (lw8_logmsgp->msg) - 1] = '\0';
1398 				strlog(0, 0, 0, SL_CONSOLE | level,
1399 				    lw8_logmsgp->msg);
1400 			}
1401 
1402 			if (lw8_logmsgp->num_remaining > 0)
1403 				more_waiting = B_TRUE;
1404 		}
1405 
1406 		/*
1407 		 * Re-enter the lock to prepare for another iteration.
1408 		 * We must have the lock here to protect lw8_logger_sig.
1409 		 */
1410 		mutex_enter(&lw8_logger_lock);
1411 		if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
1412 			/* We need to get more events */
1413 			lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1414 	}
1415 }
1416 
1417 static void
1418 lw8_logger_start(void)
1419 {
1420 	kthread_t *tp;
1421 
1422 	mutex_enter(&lw8_logger_lock);
1423 
1424 	if (lw8_logger_tid == 0) {
1425 		/* Force retrieval of any pending messages */
1426 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1427 
1428 		tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
1429 		    &p0, TS_RUN, maxclsyspri);
1430 		lw8_logger_tid = tp->t_did;
1431 	}
1432 
1433 	mutex_exit(&lw8_logger_lock);
1434 }
1435 
1436 static void
1437 lw8_logger_destroy(void)
1438 {
1439 	kt_did_t tid;
1440 
1441 	mutex_enter(&lw8_logger_lock);
1442 	tid = lw8_logger_tid;
1443 	if (tid != 0) {
1444 		lw8_logger_sig = LW8_LOGGER_EXITNOW;
1445 		cv_signal(&lw8_logger_sig_cv);
1446 		lw8_logger_tid = 0;
1447 	}
1448 	mutex_exit(&lw8_logger_lock);
1449 
1450 	/*
1451 	 * Wait for lw8_logger() to finish.
1452 	 */
1453 	if (tid != 0)
1454 		thread_join(tid);
1455 }
1456 
1457 static void
1458 lw8_logger_wakeup(void)
1459 {
1460 	mutex_enter(&lw8_logger_lock);
1461 
1462 	if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
1463 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1464 	cv_signal(&lw8_logger_sig_cv);
1465 
1466 	mutex_exit(&lw8_logger_lock);
1467 }
1468