xref: /illumos-gate/usr/src/uts/sun4u/lw8/io/lw8.c (revision e6d6c189fa3a95d7aa27bbe0aeacf7c1a6b57c8c)
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 2009 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 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
477 		    lw8_event_data_handler);
478 		return (DDI_FAILURE);
479 	}
480 
481 	lw8_env_payload_msg.msg_buf = (caddr_t)&lw8_env_payload;
482 	lw8_env_payload_msg.msg_len = sizeof (lw8_env_payload);
483 	err = sbbc_mbox_reg_intr(MBOX_EVENT_ENV, lw8_env_data_handler,
484 	    &lw8_env_payload_msg, NULL, &lw8_env_hdlr_lock);
485 	if (err != 0) {
486 		cmn_err(CE_WARN, "Failed to register MBOX_EVENT_ENV "
487 		    " handler. Err=%d", err);
488 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
489 		    lw8_dr_data_handler);
490 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
491 		    lw8_event_data_handler);
492 		return (DDI_FAILURE);
493 	}
494 
495 	lw8_cap_payload_msg.msg_buf = (caddr_t)&lw8_cap_payload;
496 	lw8_cap_payload_msg.msg_len = sizeof (lw8_cap_payload);
497 	err = sbbc_mbox_reg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler,
498 	    &lw8_cap_payload_msg, NULL, &lw8_cap_msg_hdlr_lock);
499 	if (err != 0) {
500 		cmn_err(CE_WARN, "Failed to register INFO_MBOX "
501 		    " handler. Err=%d", err);
502 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC,
503 		    lw8_dr_data_handler);
504 		(void) sbbc_mbox_unreg_intr(MBOX_EVENT_LW8,
505 		    lw8_event_data_handler);
506 		(void) sbbc_mbox_unreg_intr(INFO_MBOX,
507 		    lw8_cap_ecc_msg_handler);
508 		return (DDI_FAILURE);
509 	}
510 
511 	return (DDI_SUCCESS);
512 }
513 
514 static int
515 lw8_remove_intr_handlers(void)
516 {
517 	int	rv = DDI_SUCCESS;
518 	int	err;
519 
520 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_LW8, lw8_event_data_handler);
521 	if (err != 0) {
522 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_LW8 "
523 		    "handler. Err=%d", err);
524 		rv = DDI_FAILURE;
525 	}
526 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_GENERIC, lw8_dr_data_handler);
527 	if (err != 0) {
528 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_GENERIC "
529 		    "handler. Err=%d", err);
530 		rv = DDI_FAILURE;
531 	}
532 	err = sbbc_mbox_unreg_intr(MBOX_EVENT_ENV, lw8_env_data_handler);
533 	if (err != 0) {
534 		cmn_err(CE_WARN, "Failed to unregister MBOX_EVENT_ENV "
535 		    "handler. Err=%d", err);
536 		rv = DDI_FAILURE;
537 	}
538 	err = sbbc_mbox_unreg_intr(INFO_MBOX, lw8_cap_ecc_msg_handler);
539 	if (err != 0) {
540 		cmn_err(CE_WARN, "Failed to unregister INFO_MBOX "
541 		    "handler. Err=%d", err);
542 		rv = DDI_FAILURE;
543 	}
544 	return (rv);
545 }
546 
547 static uint_t
548 lw8_dr_data_handler(char *arg)
549 {
550 	sg_system_fru_descriptor_t	*payload;
551 	sbbc_msg_t			*msg;
552 	int				hint;
553 	sysevent_t			*ev;
554 	sysevent_id_t			eid;
555 	int				rv = 0;
556 	sysevent_value_t		evnt_val;
557 	sysevent_attr_list_t		*evnt_attr_list = NULL;
558 	char				attach_pnt[MAXPATHLEN];
559 
560 	msg = (sbbc_msg_t *)arg;
561 	if (msg == NULL) {
562 		return (DDI_INTR_CLAIMED);
563 	}
564 	payload = (sg_system_fru_descriptor_t *)msg->msg_buf;
565 	if (payload == NULL) {
566 		return (DDI_INTR_CLAIMED);
567 	}
568 	if (payload->slot < 0 || payload->slot >= sizeof (fru_locn) /
569 	    sizeof (char *)) {
570 		return (DDI_INTR_CLAIMED);
571 	}
572 
573 	/*
574 	 * if not SB send sysevent (SBs send sysevent from ssm driver)
575 	 */
576 	if (strncmp(fru_locn[payload->slot], "SB", 2) != 0) {
577 		switch (payload->event_details) {
578 		case SG_EVT_BOARD_ABSENT:
579 			hint = SE_HINT_REMOVE;
580 			break;
581 		case SG_EVT_BOARD_PRESENT:
582 			hint = SE_HINT_INSERT;
583 			break;
584 		default:
585 			hint = SE_NO_HINT;
586 			break;
587 		}
588 		(void) snprintf(attach_pnt, sizeof (attach_pnt), "ssm0:N0.%s",
589 		    fru_locn[payload->slot]);
590 		ev = sysevent_alloc(EC_DR, ESC_DR_AP_STATE_CHANGE, EP_DDI,
591 		    KM_NOSLEEP);
592 		if (ev == NULL) {
593 			cmn_err(CE_WARN, "Failed to allocate %s event", EC_DR);
594 			return (DDI_INTR_CLAIMED);
595 		}
596 		evnt_val.value_type = SE_DATA_TYPE_STRING;
597 		evnt_val.value.sv_string = attach_pnt;
598 		rv = sysevent_add_attr(&evnt_attr_list, DR_AP_ID, &evnt_val,
599 		    KM_NOSLEEP);
600 		if (rv != 0) {
601 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
602 			    DR_AP_ID, EC_DR);
603 			sysevent_free(ev);
604 			return (DDI_INTR_CLAIMED);
605 		}
606 
607 		/*
608 		 * Add the hint
609 		 */
610 		evnt_val.value_type = SE_DATA_TYPE_STRING;
611 		evnt_val.value.sv_string = SE_HINT2STR(hint);
612 		rv = sysevent_add_attr(&evnt_attr_list, DR_HINT, &evnt_val,
613 		    KM_NOSLEEP);
614 		if (rv != 0) {
615 			cmn_err(CE_WARN, "Failed to add attr [%s] for %s event",
616 			    DR_HINT, EC_DR);
617 			sysevent_free_attr(evnt_attr_list);
618 			sysevent_free(ev);
619 			return (DDI_INTR_CLAIMED);
620 		}
621 		if (sysevent_attach_attributes(ev, evnt_attr_list) != 0) {
622 			cmn_err(CE_WARN, "Failed to attach attr list for %s "
623 			    "event", EC_DR);
624 			sysevent_free_attr(evnt_attr_list);
625 			sysevent_free(ev);
626 			return (DDI_INTR_CLAIMED);
627 		}
628 		rv = log_sysevent(ev, KM_NOSLEEP, &eid);
629 		if (rv != 0) {
630 			cmn_err(CE_WARN,
631 			    "lw8_dr_event_handler: failed to log event");
632 		}
633 		sysevent_free(ev);
634 	}
635 	lw8_wakeup_sleepers();
636 	return (DDI_INTR_CLAIMED);
637 }
638 
639 static uint_t
640 lw8_cap_ecc_msg_handler(char *addr)
641 {
642 	sbbc_msg_t *msg = NULL;
643 	plat_capability_data_t *cap = NULL;
644 
645 	msg = (sbbc_msg_t *)addr;
646 	if (msg == NULL || msg->msg_buf == NULL)
647 		return (DDI_INTR_CLAIMED);
648 
649 	cap = (plat_capability_data_t *)msg->msg_buf;
650 	switch (cap->capd_msg_type) {
651 	case PLAT_ECC_CAPABILITY_MESSAGE:
652 		plat_ecc_capability_sc_set(cap->capd_capability);
653 		break;
654 	default:
655 		break;
656 	}
657 
658 	return (DDI_INTR_CLAIMED);
659 }
660 
661 /*ARGSUSED*/
662 static uint_t
663 lw8_env_data_handler(char *arg)
664 {
665 	lw8_wakeup_sleepers();
666 	return (DDI_INTR_CLAIMED);
667 }
668 
669 /*
670  * wakeup sleepers + mark led cache for this fru as invalid
671  */
672 static void
673 lw8_wakeup_sleepers()
674 {
675 	mutex_enter(&lw8_event_mutex);
676 	lw8_event_pending = B_TRUE;
677 	cv_broadcast(&lw8_event_cv);
678 	led_state_cached = B_FALSE;
679 	mutex_exit(&lw8_event_mutex);
680 }
681 
682 /*
683  * This function is triggered by a soft interrupt and it's purpose is to call
684  * to kadmin() to shutdown the system.
685  */
686 /*ARGSUSED*/
687 static uint_t
688 lw8_fast_shutdown(char *arg)
689 {
690 	(void) kadmin(A_SHUTDOWN, AD_POWEROFF, NULL, kcred);
691 
692 	/*
693 	 * If kadmin fails for some reason then we bring the system down
694 	 * via power_down(), or failing that using halt().
695 	 */
696 	power_down("kadmin() failed, trying power_down()");
697 
698 	halt("power_down() failed, trying halt()");
699 
700 	/*
701 	 * We should never make it this far, so something must have gone
702 	 * horribly, horribly wrong.
703 	 */
704 	/*NOTREACHED*/
705 	return (DDI_INTR_UNCLAIMED);
706 }
707 
708 /*
709  * This function is triggered by a soft interrupt and it's purpose is to call
710  * to do_shutdown() to shutdown the system.
711  */
712 /*ARGSUSED*/
713 static uint_t
714 lw8_slow_shutdown(char *arg)
715 {
716 	do_shutdown();
717 	return (DDI_SUCCESS);
718 }
719 
720 static uint_t
721 lw8_event_data_handler(char *arg)
722 {
723 	lw8_event_t	*payload;
724 	sbbc_msg_t	*msg;
725 
726 	if (arg == NULL) {
727 		return (DDI_INTR_CLAIMED);
728 	}
729 
730 	msg = (sbbc_msg_t *)arg;
731 	if (msg->msg_buf == NULL) {
732 		return (DDI_INTR_CLAIMED);
733 	}
734 
735 	payload = (lw8_event_t *)msg->msg_buf;
736 	switch (payload->event_type) {
737 	case LW8_EVENT_REQUESTED_SHUTDOWN:
738 
739 		/*
740 		 * Let the user know why the domain is going down.
741 		 */
742 		cmn_err(CE_WARN, "%s", SHUTDOWN_EVENT_MSG);
743 		ddi_trigger_softintr(lw8_slow_shutdown_softint_id);
744 
745 		/*NOTREACHED*/
746 		break;
747 
748 	case LW8_EVENT_VOLTAGE_SHUTDOWN:
749 
750 		/*
751 		 * Let the user know why the domain is going down.
752 		 */
753 		cmn_err(CE_WARN, "%s", VOLTAGE_EVENT_MSG);
754 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
755 
756 		/*NOTREACHED*/
757 		break;
758 
759 	case LW8_EVENT_TEMPERATURE_SHUTDOWN:
760 
761 		/*
762 		 * Let the user know why the domain is going down.
763 		 */
764 		cmn_err(CE_WARN, "%s", TEMPERATURE_EVENT_MSG);
765 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
766 
767 		/*NOTREACHED*/
768 		break;
769 
770 	case LW8_EVENT_FANFAIL_SHUTDOWN:
771 
772 		/*
773 		 * Let the user know why the domain is going down.
774 		 */
775 		cmn_err(CE_WARN, "%s", FANFAIL_EVENT_MSG);
776 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
777 
778 		/*NOTREACHED*/
779 		break;
780 
781 	case LW8_EVENT_NO_SCC_SHUTDOWN:
782 
783 		/*
784 		 * Let the user know why the domain is going down.
785 		 */
786 		cmn_err(CE_WARN, "%s", NO_SCC_EVENT_MSG);
787 		ddi_trigger_softintr(lw8_fast_shutdown_softint_id);
788 
789 		/*NOTREACHED*/
790 		break;
791 
792 	case LW8_EVENT_NEW_LOG_MSG:
793 
794 		/*
795 		 * Wake up the log retrieval thread.
796 		 */
797 		lw8_logger_wakeup();
798 
799 		break;
800 
801 	default:
802 		return (DDI_INTR_CLAIMED);
803 	}
804 
805 	return (DDI_INTR_CLAIMED);
806 }
807 
808 /*ARGSUSED*/
809 static int
810 lw8_open(dev_t *dev_p, int flag, int otyp, cred_t *cred_p)
811 {
812 	int error = 0;
813 	int instance = getminor(*dev_p);
814 	static fn_t f = "lw8_open";
815 
816 	if (instance != 0)
817 		return (ENXIO);
818 
819 	if ((error = drv_priv(cred_p)) != 0) {
820 		cmn_err(CE_WARN, "lw8:%s: inst %d drv_priv failed",
821 		    f, instance);
822 		return (error);
823 	}
824 	return (error);
825 }
826 
827 /*ARGSUSED*/
828 static int
829 lw8_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
830 {
831 	return (DDI_SUCCESS);
832 }
833 
834 static int
835 lw8_lomcmd(int cmd, intptr_t arg)
836 {
837 	sbbc_msg_t request, *reqp = &request;
838 	sbbc_msg_t response, *resp = &response;
839 	int rv = 0;
840 	lom_eventreq_t *eventreqp;
841 
842 	bzero((caddr_t)&request, sizeof (request));
843 	reqp->msg_type.type = LW8_MBOX;
844 	reqp->msg_type.sub_type = cmd;
845 	bzero((caddr_t)&response, sizeof (response));
846 	resp->msg_type.type = LW8_MBOX;
847 	resp->msg_type.sub_type = cmd;
848 
849 	switch (cmd) {
850 	case LW8_MBOX_GET_INFO:
851 		reqp->msg_len = 0;
852 		reqp->msg_buf = (caddr_t)NULL;
853 		resp->msg_len = sizeof (lom2_info_t);
854 		resp->msg_buf = (caddr_t)arg;
855 		break;
856 	case LW8_MBOX_SET_CTL:
857 		reqp->msg_len = sizeof (lom_ctl2_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_UPDATE_FW:
863 		reqp->msg_len = sizeof (lom_prog_t);
864 		reqp->msg_buf = (caddr_t)arg;
865 		resp->msg_len = 0;
866 		resp->msg_buf = (caddr_t)NULL;
867 		break;
868 	case LW8_MBOX_GET_LED:
869 		reqp->msg_len = sizeof (lw8_get_led_payload_t);
870 		reqp->msg_buf = (caddr_t)arg;
871 		resp->msg_len = sizeof (lw8_get_led_payload_t);
872 		resp->msg_buf = (caddr_t)arg;
873 		break;
874 	case LW8_MBOX_SET_LED:
875 		reqp->msg_len = sizeof (lw8_set_led_payload_t);
876 		reqp->msg_buf = (caddr_t)arg;
877 		resp->msg_len = 0;
878 		resp->msg_buf = (caddr_t)NULL;
879 		break;
880 	case LW8_MBOX_GET_EVENTS:
881 		/*
882 		 * cast as lom_eventreq_t to minimise data traffic
883 		 */
884 		eventreqp = (lom_eventreq_t *)arg;
885 		reqp->msg_len = sizeof (lom_eventreq_t);
886 		reqp->msg_buf = (caddr_t)arg;
887 		resp->msg_len = sizeof (lom_eventreq_t) +
888 		    (eventreqp->num * MAX_EVENT_STR);
889 		resp->msg_buf = (caddr_t)arg;
890 		break;
891 	case LW8_MBOX_GET_NEXT_MSG:
892 		reqp->msg_len = 0;
893 		reqp->msg_buf = (caddr_t)NULL;
894 		resp->msg_len = sizeof (lw8_logmsg_t);
895 		resp->msg_buf = (caddr_t)arg;
896 		break;
897 	default:
898 		return (EINVAL);
899 	}
900 
901 	rv = sbbc_mbox_request_response(reqp, resp,
902 	    LW8_DEFAULT_MAX_MBOX_WAIT_TIME);
903 
904 	if ((rv) || (resp->msg_status != SG_MBOX_STATUS_SUCCESS)) {
905 
906 		/* errors from sgsbbc */
907 		if (resp->msg_status > 0) {
908 			return (resp->msg_status);
909 		}
910 
911 		/* errors from SCAPP */
912 		switch (resp->msg_status) {
913 
914 		case SG_MBOX_STATUS_COMMAND_FAILURE:
915 			/* internal SCAPP error */
916 			return (EINTR);
917 
918 		case SG_MBOX_STATUS_HARDWARE_FAILURE:
919 			/* seprom read/write errors */
920 			return (EIO);
921 
922 		case SG_MBOX_STATUS_ILLEGAL_PARAMETER:
923 			/* illegal ioctl parameter */
924 			return (EINVAL);
925 
926 		case SG_MBOX_STATUS_BOARD_ACCESS_DENIED:
927 			/* board access denied */
928 			return (EACCES);
929 
930 		case SG_MBOX_STATUS_STALE_CONTENTS:
931 			/* stale contents */
932 			return (ESTALE);
933 
934 		case SG_MBOX_STATUS_STALE_OBJECT:
935 			/* stale handle */
936 			return (ENOENT);
937 
938 		case SG_MBOX_STATUS_NO_SEPROM_SPACE:
939 			/* seprom lacks space */
940 			return (ENOSPC);
941 
942 		case SG_MBOX_STATUS_NO_MEMORY:
943 			/* user prog. lacks space */
944 			return (ENOMEM);
945 
946 		case SG_MBOX_STATUS_NOT_SUPPORTED:
947 			/* unsupported operation */
948 			return (ENOTSUP);
949 
950 		default:
951 			return (EIO);
952 		}
953 	}
954 	return (0);
955 }
956 
957 /*
958  * set the requested led, and mark cache as empty
959  */
960 static int
961 lw8_setled(lom_set_led_t *set_ledp)
962 {
963 	int retval;
964 	int i, j;
965 	struct led_info *lip;
966 	lw8_set_led_payload_t lw8_set_led;
967 
968 	for (i = 0; i < MAX_FRUS; i++) {
969 		if (strncmp(set_ledp->location, fru_led_table[i].location,
970 		    MAX_LOCATION_LEN) != 0)
971 			continue;
972 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
973 			lip = &fru_led_table[i].led_info[j];
974 			if (lip->id == NULL)
975 				continue;
976 			if (strncmp(set_ledp->id, lip->id, MAX_ID_LEN) != 0)
977 				continue;
978 			lw8_set_led.value = set_ledp->status;
979 
980 			/*
981 			 * to minimise data transfer, the SC maintains
982 			 * just  3 values per fru - except for
983 			 * the chassis itself at the end which has
984 			 * MAX_LEDS_PER_FRU
985 			 */
986 			lw8_set_led.offset = (i * 3) + j;
987 			retval = lw8_lomcmd(LW8_MBOX_SET_LED,
988 			    (intptr_t)&lw8_set_led);
989 			if (retval != 0)
990 				return (retval);
991 			mutex_enter(&lw8_event_mutex);
992 			led_state_cached = B_FALSE;
993 			mutex_exit(&lw8_event_mutex);
994 			return (0);
995 		}
996 	}
997 	return (EINVAL);
998 }
999 
1000 /*
1001  * read led value from cache if possible, otherwise read from sc and
1002  * update the cache
1003  */
1004 static int
1005 lw8_getled(lom_get_led_t *get_ledp)
1006 {
1007 	int retval;
1008 	int i, j, k;
1009 	struct led_info *lip;
1010 	lw8_get_led_payload_t lw8_get_led;
1011 
1012 	for (i = 0; i < MAX_FRUS; i++) {
1013 		if (strncmp(get_ledp->location, fru_led_table[i].location,
1014 		    MAX_LOCATION_LEN) != 0)
1015 			continue;
1016 		if (get_ledp->id[0] == '\0') {
1017 			(void) strncpy(get_ledp->next_id,
1018 			    fru_led_table[i].led_info[0].id, MAX_ID_LEN);
1019 			return (0);
1020 		}
1021 		for (j = 0; j < MAX_LEDS_PER_FRU; j++) {
1022 			lip = &fru_led_table[i].led_info[j];
1023 			if (lip->id == NULL)
1024 				continue;
1025 			if (strncmp(get_ledp->id, lip->id, MAX_ID_LEN) != 0)
1026 				continue;
1027 			mutex_enter(&lw8_event_mutex);
1028 			if (!led_state_cached) {
1029 				mutex_exit(&lw8_event_mutex);
1030 				retval = lw8_lomcmd(LW8_MBOX_GET_LED,
1031 				    (intptr_t)&lw8_get_led);
1032 				if (retval != 0)
1033 					return (retval);
1034 				mutex_enter(&lw8_event_mutex);
1035 
1036 				/*
1037 				 * to minimise data transfer, the
1038 				 * lw8_get_led_payload_t structure just has 3
1039 				 * values per fru - except for the chassis
1040 				 * itself at the end which has MAX_LEDS_PER_FRU
1041 				 */
1042 				for (k = 0; k < (MAX_FRUS - 1) * 3; k++) {
1043 					fru_led_table[k / 3].led_info[k % 3].
1044 					    status = lw8_get_led.value[k];
1045 				}
1046 				for (k = 0; k < MAX_LEDS_PER_FRU; k++) {
1047 					fru_led_table[MAX_FRUS - 1].led_info[k].
1048 					    status = lw8_get_led.value[k +
1049 					    ((MAX_FRUS - 1) * 3)];
1050 				}
1051 				led_state_cached = B_TRUE;
1052 			}
1053 			get_ledp->status = lip->status;
1054 			mutex_exit(&lw8_event_mutex);
1055 			get_ledp->position = lip->position;
1056 			(void) strncpy(get_ledp->color, lip->color,
1057 			    MAX_COLOR_LEN);
1058 			if (j == MAX_LEDS_PER_FRU - 1) {
1059 				get_ledp->next_id[0] = '\0';
1060 				return (0);
1061 			}
1062 			(void) strncpy(get_ledp->next_id,
1063 			    fru_led_table[i].led_info[j + 1].id, MAX_ID_LEN);
1064 			return (0);
1065 		}
1066 	}
1067 	if (get_ledp->id[0] == '\0') {
1068 		get_ledp->next_id[0] = '\0';
1069 		return (0);
1070 	}
1071 	return (EINVAL);
1072 }
1073 
1074 /*ARGSUSED*/
1075 static int
1076 lw8_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
1077     int *rval_p)
1078 {
1079 	int instance = getminor(dev);
1080 	lom2_info_t lw8_info2;
1081 	lom_ctl_t lw8_ctl;
1082 	lom_ctl2_t lw8_ctl2;
1083 	lom_mprog_t lw8_mprog;
1084 	lom_fled_info_t lw8_fled_info;
1085 	lom_info_t lw8_info;
1086 	lom_aldata_t lw8_aldata;
1087 	lom_get_led_t lw8_get_led;
1088 	lom_set_led_t lw8_set_led;
1089 	lom_prog_t *lw8_progp;
1090 	lom_eventlog2_t *lw8_eventlogp;
1091 	lom_eventresp_t *lw8_eventresp;
1092 	int retval = 0;
1093 	int i, j;
1094 
1095 	if (instance != 0)
1096 		return (ENXIO);
1097 
1098 	switch (cmd) {
1099 	case LOMIOCWTMON:
1100 		mutex_enter(&lw8_event_mutex);
1101 		if (!lw8_event_pending) {
1102 			if (cv_wait_sig(&lw8_event_cv, &lw8_event_mutex) == 0) {
1103 				mutex_exit(&lw8_event_mutex);
1104 				retval = EINTR;
1105 				break;
1106 			}
1107 		}
1108 		lw8_event_pending = B_FALSE;
1109 		mutex_exit(&lw8_event_mutex);
1110 		break;
1111 	case LOMIOCMREAD:
1112 		bzero((caddr_t)&lw8_mprog, sizeof (lw8_mprog));
1113 		lw8_mprog.config = 4;
1114 		if (ddi_copyout((caddr_t)&lw8_mprog, (caddr_t)arg,
1115 		    sizeof (lw8_mprog), mode) != 0) {
1116 			retval = EFAULT;
1117 		}
1118 		break;
1119 	case LOMIOCCTL2:
1120 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl2,
1121 		    sizeof (lw8_ctl2), mode) != 0) {
1122 			retval = EFAULT;
1123 			break;
1124 		}
1125 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
1126 		break;
1127 	case LOMIOCPROG:
1128 		lw8_progp = kmem_alloc(sizeof (*lw8_progp), KM_SLEEP);
1129 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_progp,
1130 		    sizeof (*lw8_progp), mode) != 0) {
1131 			kmem_free(lw8_progp, sizeof (*lw8_progp));
1132 			retval = EFAULT;
1133 			break;
1134 		}
1135 		retval = lw8_lomcmd(LW8_MBOX_UPDATE_FW, (intptr_t)lw8_progp);
1136 		kmem_free(lw8_progp, sizeof (*lw8_progp));
1137 		break;
1138 	case LOMIOCINFO2:
1139 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1140 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1141 		if (retval != 0)
1142 			break;
1143 		if (ddi_copyout((caddr_t)&lw8_info2, (caddr_t)arg,
1144 		    sizeof (lw8_info2), mode) != 0) {
1145 			retval = EFAULT;
1146 		}
1147 		break;
1148 	case LOMIOCINFO:
1149 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1150 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1151 		if (retval != 0)
1152 			break;
1153 		bzero((caddr_t)&lw8_info, sizeof (lw8_info));
1154 		lw8_info.ser_char = lw8_info2.escape_chars[0];
1155 		lw8_info.fver = lw8_info2.fver;
1156 		lw8_info.fchksum = lw8_info2.fchksum;
1157 		lw8_info.prod_rev = lw8_info2.prod_rev;
1158 		(void) strncpy(lw8_info.prod_id, lw8_info2.prod_id, MAX_ID_LEN);
1159 		if (ddi_copyout((caddr_t)&lw8_info, (caddr_t)arg,
1160 		    sizeof (lw8_info), mode) != 0) {
1161 			retval = EFAULT;
1162 		}
1163 		break;
1164 	case LOMIOCFLEDSTATE:
1165 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
1166 		(void) strncpy(lw8_get_led.location, "chassis",
1167 		    MAX_LOCATION_LEN);
1168 		(void) strncpy(lw8_get_led.id, "fault", MAX_ID_LEN);
1169 		retval = lw8_getled(&lw8_get_led);
1170 		if (retval != 0)
1171 			break;
1172 		lw8_fled_info.on = lw8_get_led.status;
1173 		if (ddi_copyout((caddr_t)&lw8_fled_info, (caddr_t)arg,
1174 		    sizeof (lw8_fled_info), mode) != 0) {
1175 			retval = EFAULT;
1176 		}
1177 		break;
1178 	case LOMIOCALSTATE:
1179 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
1180 		    sizeof (lw8_aldata), mode) != 0) {
1181 			retval = EFAULT;
1182 			break;
1183 		}
1184 		bzero((caddr_t)&lw8_get_led, sizeof (lw8_get_led));
1185 		(void) strncpy(lw8_get_led.location, "chassis",
1186 		    MAX_LOCATION_LEN);
1187 		if (lw8_aldata.alarm_no == 3)
1188 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "system");
1189 		else
1190 			(void) snprintf(lw8_get_led.id, MAX_ID_LEN, "alarm%d",
1191 			    lw8_aldata.alarm_no);
1192 		retval = lw8_getled(&lw8_get_led);
1193 		if (retval != 0)
1194 			break;
1195 		lw8_aldata.state = lw8_get_led.status;
1196 		if (ddi_copyout((caddr_t)&lw8_aldata, (caddr_t)arg,
1197 		    sizeof (lw8_aldata), mode) != 0) {
1198 			retval = EFAULT;
1199 		}
1200 		break;
1201 	case LOMIOCGETLED:
1202 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_get_led,
1203 		    sizeof (lw8_get_led), mode) != 0) {
1204 			retval = EFAULT;
1205 			break;
1206 		}
1207 		retval = lw8_getled(&lw8_get_led);
1208 		if (retval != 0)
1209 			break;
1210 		if (ddi_copyout((caddr_t)&lw8_get_led, (caddr_t)arg,
1211 		    sizeof (lw8_get_led), mode) != 0) {
1212 			retval = EFAULT;
1213 		}
1214 		break;
1215 	case LOMIOCEVENTLOG2:
1216 		lw8_eventlogp = kmem_alloc(sizeof (*lw8_eventlogp), KM_SLEEP);
1217 		lw8_eventresp = kmem_zalloc(sizeof (*lw8_eventresp), KM_SLEEP);
1218 		if (ddi_copyin((caddr_t)arg, (caddr_t)lw8_eventlogp,
1219 		    sizeof (*lw8_eventlogp), mode) != 0) {
1220 			kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
1221 			kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
1222 			retval = EFAULT;
1223 			break;
1224 		}
1225 		lw8_eventresp->num = lw8_eventlogp->num;
1226 		lw8_eventresp->level = lw8_eventlogp->level;
1227 		retval = lw8_lomcmd(LW8_MBOX_GET_EVENTS,
1228 		    (intptr_t)lw8_eventresp);
1229 		if (retval == 0) {
1230 			lw8_eventlogp->num = lw8_eventresp->num;
1231 			for (i = 0; i < lw8_eventresp->num; i++) {
1232 				for (j = 0; j < MAX_EVENT_STR; j++) {
1233 					lw8_eventlogp->string[i][j] =
1234 					    lw8_eventresp->string[i][j];
1235 				}
1236 			}
1237 			if (ddi_copyout((caddr_t)lw8_eventlogp, (caddr_t)arg,
1238 			    sizeof (*lw8_eventlogp), mode) != 0) {
1239 				retval = EFAULT;
1240 			}
1241 		}
1242 		kmem_free(lw8_eventlogp, sizeof (*lw8_eventlogp));
1243 		kmem_free(lw8_eventresp, sizeof (*lw8_eventresp));
1244 		break;
1245 	case LOMIOCALCTL:
1246 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_aldata,
1247 		    sizeof (lw8_aldata), mode) != 0) {
1248 			retval = EFAULT;
1249 			break;
1250 		}
1251 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
1252 		(void) strncpy(lw8_set_led.location, "chassis",
1253 		    MAX_LOCATION_LEN);
1254 		if (lw8_aldata.alarm_no == 3)
1255 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "system");
1256 		else
1257 			(void) snprintf(lw8_set_led.id, MAX_ID_LEN, "alarm%d",
1258 			    lw8_aldata.alarm_no);
1259 		lw8_set_led.status = lw8_aldata.state;
1260 		retval = lw8_setled(&lw8_set_led);
1261 		break;
1262 	case LOMIOCSETLED:
1263 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_set_led,
1264 		    sizeof (lw8_set_led), mode) != 0) {
1265 			retval = EFAULT;
1266 			break;
1267 		}
1268 		retval = lw8_setled(&lw8_set_led);
1269 		break;
1270 	case LOMIOCCTL:
1271 		/*
1272 		 * for this ioctl, as well as setting the fault led in the
1273 		 * LOMIOCCTL case in lw8_lomcmd(), we also need to set the
1274 		 * escape character. To do this we must use LW8_MBOX_SET_CTL,
1275 		 * but this also needs the serial_event value which we have
1276 		 * to get via LW8_MBOX_GET_INFO
1277 		 */
1278 		if (ddi_copyin((caddr_t)arg, (caddr_t)&lw8_ctl,
1279 		    sizeof (lw8_ctl), mode) != 0) {
1280 			retval = EFAULT;
1281 			break;
1282 		}
1283 		bzero((caddr_t)&lw8_info2, sizeof (lw8_info2));
1284 		retval = lw8_lomcmd(LW8_MBOX_GET_INFO, (intptr_t)&lw8_info2);
1285 		if (retval != 0)
1286 			break;
1287 		bzero((caddr_t)&lw8_ctl2, sizeof (lw8_ctl2));
1288 		lw8_ctl2.escape_chars[0] = lw8_ctl.ser_char;
1289 		lw8_ctl2.serial_events = lw8_info2.serial_events;
1290 		retval = lw8_lomcmd(LW8_MBOX_SET_CTL, (intptr_t)&lw8_ctl2);
1291 		if (retval != 0)
1292 			break;
1293 
1294 		/*
1295 		 * if fault_led != 0, then set the led
1296 		 */
1297 		if (lw8_ctl.fault_led == 0)
1298 			break;
1299 		bzero((caddr_t)&lw8_set_led, sizeof (lw8_set_led));
1300 		(void) strncpy(lw8_set_led.location, "chassis",
1301 		    MAX_LOCATION_LEN);
1302 		(void) strncpy(lw8_set_led.id, "fault", MAX_ID_LEN);
1303 		lw8_set_led.status = lw8_ctl.fault_led - 1;
1304 		retval = lw8_setled(&lw8_set_led);
1305 		break;
1306 	default:
1307 		retval = ENOTSUP;
1308 		break;
1309 	}
1310 	return (retval);
1311 }
1312 
1313 /* ARGSUSED */
1314 static void
1315 lw8_logger(caddr_t arg)
1316 {
1317 	callb_cpr_t	cprinfo;
1318 	lw8_logmsg_t	*lw8_logmsgp;
1319 	boolean_t	more_waiting;
1320 	char		level;
1321 	int		retval;
1322 
1323 	CALLB_CPR_INIT(&cprinfo, &lw8_logger_lock, callb_generic_cpr,
1324 	    "lw8_logger");
1325 
1326 	lw8_logmsgp = kmem_zalloc(sizeof (*lw8_logmsgp), KM_SLEEP);
1327 	mutex_enter(&lw8_logger_lock);
1328 	for (;;) {
1329 
1330 		/*
1331 		 * Wait for someone to tell me to continue.
1332 		 */
1333 		while (lw8_logger_sig == LW8_LOGGER_WAIT) {
1334 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
1335 			cv_wait(&lw8_logger_sig_cv, &lw8_logger_lock);
1336 			CALLB_CPR_SAFE_END(&cprinfo, &lw8_logger_lock);
1337 		}
1338 
1339 		/* LW8_LOGGER_EXITNOW implies signal by _detach(). */
1340 		if (lw8_logger_sig == LW8_LOGGER_EXITNOW) {
1341 			lw8_logger_sig = LW8_LOGGER_WAIT;
1342 
1343 			kmem_free(lw8_logmsgp, sizeof (*lw8_logmsgp));
1344 
1345 			/* lw8_logger_lock is held at this point! */
1346 			CALLB_CPR_EXIT(&cprinfo);
1347 
1348 			thread_exit();
1349 			/* NOTREACHED */
1350 		}
1351 
1352 		ASSERT(lw8_logger_sig == LW8_LOGGER_PROCESSNOW);
1353 		lw8_logger_sig = LW8_LOGGER_WAIT;
1354 
1355 		mutex_exit(&lw8_logger_lock);
1356 
1357 		/* Do lw8_event logging */
1358 
1359 		/*
1360 		 * Get one message per iteration. We do not sleep if
1361 		 * there are more to process. This makes exit from the
1362 		 * routine much more reliable.
1363 		 */
1364 		more_waiting = B_FALSE;
1365 
1366 		retval = lw8_lomcmd(LW8_MBOX_GET_NEXT_MSG,
1367 		    (intptr_t)lw8_logmsgp);
1368 		if (retval == 0) {
1369 			if (lw8_logmsgp->msg_valid) {
1370 
1371 				switch (lw8_logmsgp->level) {
1372 				case 0:	/* LOG_EMERG */
1373 					level = SL_FATAL;
1374 					break;
1375 				case 1:	/* LOG_ALERT */
1376 					level = SL_FATAL;
1377 					break;
1378 				case 2:	/* LOG_CRIT */
1379 					level = SL_FATAL;
1380 					break;
1381 				case 3:	/* LOG_ERR */
1382 					level = SL_ERROR;
1383 					break;
1384 				case 4:	/* LOG_WARNING */
1385 					level = SL_WARN;
1386 					break;
1387 				case 5:	/* LOG_NOTICE */
1388 					level = SL_NOTE;
1389 					break;
1390 				case 6:	/* LOG_INFO */
1391 					level = SL_NOTE;
1392 					break;
1393 				case 7:	/* LOG_DEBUG */
1394 					level = SL_TRACE;
1395 					break;
1396 				default:	/* unknown */
1397 					level = SL_NOTE;
1398 					break;
1399 				}
1400 
1401 				/* Ensure NUL termination */
1402 				lw8_logmsgp->msg[
1403 				    sizeof (lw8_logmsgp->msg) - 1] = '\0';
1404 				(void) strlog(0, 0, 0, SL_CONSOLE | level,
1405 				    lw8_logmsgp->msg);
1406 			}
1407 
1408 			if (lw8_logmsgp->num_remaining > 0)
1409 				more_waiting = B_TRUE;
1410 		}
1411 
1412 		/*
1413 		 * Re-enter the lock to prepare for another iteration.
1414 		 * We must have the lock here to protect lw8_logger_sig.
1415 		 */
1416 		mutex_enter(&lw8_logger_lock);
1417 		if ((lw8_logger_sig == LW8_LOGGER_WAIT) && more_waiting)
1418 			/* We need to get more events */
1419 			lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1420 	}
1421 }
1422 
1423 static void
1424 lw8_logger_start(void)
1425 {
1426 	kthread_t *tp;
1427 
1428 	mutex_enter(&lw8_logger_lock);
1429 
1430 	if (lw8_logger_tid == 0) {
1431 		/* Force retrieval of any pending messages */
1432 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1433 
1434 		tp = thread_create(NULL, 0, lw8_logger, NULL, 0,
1435 		    &p0, TS_RUN, maxclsyspri);
1436 		lw8_logger_tid = tp->t_did;
1437 	}
1438 
1439 	mutex_exit(&lw8_logger_lock);
1440 }
1441 
1442 static void
1443 lw8_logger_destroy(void)
1444 {
1445 	kt_did_t tid;
1446 
1447 	mutex_enter(&lw8_logger_lock);
1448 	tid = lw8_logger_tid;
1449 	if (tid != 0) {
1450 		lw8_logger_sig = LW8_LOGGER_EXITNOW;
1451 		cv_signal(&lw8_logger_sig_cv);
1452 		lw8_logger_tid = 0;
1453 	}
1454 	mutex_exit(&lw8_logger_lock);
1455 
1456 	/*
1457 	 * Wait for lw8_logger() to finish.
1458 	 */
1459 	if (tid != 0)
1460 		thread_join(tid);
1461 }
1462 
1463 static void
1464 lw8_logger_wakeup(void)
1465 {
1466 	mutex_enter(&lw8_logger_lock);
1467 
1468 	if (lw8_logger_sig != LW8_LOGGER_EXITNOW)
1469 		lw8_logger_sig = LW8_LOGGER_PROCESSNOW;
1470 	cv_signal(&lw8_logger_sig_cv);
1471 
1472 	mutex_exit(&lw8_logger_lock);
1473 }
1474