xref: /titanic_54/usr/src/uts/common/io/bscv.c (revision 1c42de6d020629af774dd9e9fc81be3f3ed9398e)
1*1c42de6dSgd78059 /*
2*1c42de6dSgd78059  * CDDL HEADER START
3*1c42de6dSgd78059  *
4*1c42de6dSgd78059  * The contents of this file are subject to the terms of the
5*1c42de6dSgd78059  * Common Development and Distribution License (the "License").
6*1c42de6dSgd78059  * You may not use this file except in compliance with the License.
7*1c42de6dSgd78059  *
8*1c42de6dSgd78059  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1c42de6dSgd78059  * or http://www.opensolaris.org/os/licensing.
10*1c42de6dSgd78059  * See the License for the specific language governing permissions
11*1c42de6dSgd78059  * and limitations under the License.
12*1c42de6dSgd78059  *
13*1c42de6dSgd78059  * When distributing Covered Code, include this CDDL HEADER in each
14*1c42de6dSgd78059  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1c42de6dSgd78059  * If applicable, add the following below this CDDL HEADER, with the
16*1c42de6dSgd78059  * fields enclosed by brackets "[]" replaced with your own identifying
17*1c42de6dSgd78059  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1c42de6dSgd78059  *
19*1c42de6dSgd78059  * CDDL HEADER END
20*1c42de6dSgd78059  */
21*1c42de6dSgd78059 /*
22*1c42de6dSgd78059  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
23*1c42de6dSgd78059  * Use is subject to license terms.
24*1c42de6dSgd78059  */
25*1c42de6dSgd78059 
26*1c42de6dSgd78059 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*1c42de6dSgd78059 
28*1c42de6dSgd78059 /*
29*1c42de6dSgd78059  * bscv.c - multi-threaded lom driver for the Stiletto platform.
30*1c42de6dSgd78059  */
31*1c42de6dSgd78059 
32*1c42de6dSgd78059 /*
33*1c42de6dSgd78059  * Included files.
34*1c42de6dSgd78059  */
35*1c42de6dSgd78059 
36*1c42de6dSgd78059 #include <sys/note.h>
37*1c42de6dSgd78059 #include <sys/types.h>
38*1c42de6dSgd78059 #include <sys/param.h>
39*1c42de6dSgd78059 #include <sys/uio.h>
40*1c42de6dSgd78059 #include <sys/open.h>
41*1c42de6dSgd78059 #include <sys/cred.h>
42*1c42de6dSgd78059 #include <sys/stream.h>
43*1c42de6dSgd78059 #include <sys/systm.h>
44*1c42de6dSgd78059 #include <sys/conf.h>
45*1c42de6dSgd78059 #include <sys/cyclic.h>
46*1c42de6dSgd78059 #include <sys/reboot.h>
47*1c42de6dSgd78059 #include <sys/modctl.h>
48*1c42de6dSgd78059 #include <sys/mkdev.h>
49*1c42de6dSgd78059 #include <sys/errno.h>
50*1c42de6dSgd78059 #include <sys/debug.h>
51*1c42de6dSgd78059 #include <sys/kmem.h>
52*1c42de6dSgd78059 #include <sys/consdev.h>
53*1c42de6dSgd78059 #include <sys/file.h>
54*1c42de6dSgd78059 #include <sys/stat.h>
55*1c42de6dSgd78059 #include <sys/time.h>
56*1c42de6dSgd78059 #include <sys/disp.h>
57*1c42de6dSgd78059 #include <sys/ddi.h>
58*1c42de6dSgd78059 #include <sys/sunddi.h>
59*1c42de6dSgd78059 #include <sys/stream.h>
60*1c42de6dSgd78059 #include <sys/strlog.h>
61*1c42de6dSgd78059 #include <sys/log.h>
62*1c42de6dSgd78059 #include <sys/utsname.h>
63*1c42de6dSgd78059 #include <sys/callb.h>
64*1c42de6dSgd78059 #include <sys/sysevent.h>
65*1c42de6dSgd78059 #include <sys/nvpair.h>
66*1c42de6dSgd78059 #include <sys/sysevent/eventdefs.h>
67*1c42de6dSgd78059 #include <sys/sysevent/domain.h>
68*1c42de6dSgd78059 #include <sys/sysevent/env.h>
69*1c42de6dSgd78059 #include <sys/sysevent/dr.h>
70*1c42de6dSgd78059 
71*1c42de6dSgd78059 #include <sys/lom_io.h>
72*1c42de6dSgd78059 #include <sys/bscbus.h>
73*1c42de6dSgd78059 #include <sys/bscv_impl.h>
74*1c42de6dSgd78059 
75*1c42de6dSgd78059 /*
76*1c42de6dSgd78059  * Variables defined here and visible internally only
77*1c42de6dSgd78059  */
78*1c42de6dSgd78059 
79*1c42de6dSgd78059 static void *bscv_statep = NULL;
80*1c42de6dSgd78059 
81*1c42de6dSgd78059 /*
82*1c42de6dSgd78059  * Forward declarations
83*1c42de6dSgd78059  */
84*1c42de6dSgd78059 
85*1c42de6dSgd78059 static int bscv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
86*1c42de6dSgd78059 static int bscv_attach(dev_info_t *, ddi_attach_cmd_t);
87*1c42de6dSgd78059 static int bscv_detach(dev_info_t *, ddi_detach_cmd_t);
88*1c42de6dSgd78059 static int bscv_reset(dev_info_t *, ddi_reset_cmd_t);
89*1c42de6dSgd78059 static int bscv_map_regs(bscv_soft_state_t *);
90*1c42de6dSgd78059 static void bscv_unmap_regs(bscv_soft_state_t *);
91*1c42de6dSgd78059 static void bscv_map_chan_logical_physical(bscv_soft_state_t *);
92*1c42de6dSgd78059 
93*1c42de6dSgd78059 static int bscv_open(dev_t *, int, int, cred_t *);
94*1c42de6dSgd78059 static int bscv_close(dev_t, int, int, cred_t *);
95*1c42de6dSgd78059 static void bscv_full_stop(bscv_soft_state_t *);
96*1c42de6dSgd78059 
97*1c42de6dSgd78059 static void bscv_enter(bscv_soft_state_t *);
98*1c42de6dSgd78059 static void bscv_exit(bscv_soft_state_t *);
99*1c42de6dSgd78059 #ifdef DEBUG
100*1c42de6dSgd78059 static int bscv_held(bscv_soft_state_t *);
101*1c42de6dSgd78059 #endif /* DEBUG */
102*1c42de6dSgd78059 
103*1c42de6dSgd78059 static void bscv_put8(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
104*1c42de6dSgd78059 static void bscv_put16(bscv_soft_state_t *, int, bscv_addr_t, uint16_t);
105*1c42de6dSgd78059 static void bscv_put32(bscv_soft_state_t *, int, bscv_addr_t, uint32_t);
106*1c42de6dSgd78059 static uint8_t bscv_get8(bscv_soft_state_t *, int, bscv_addr_t);
107*1c42de6dSgd78059 static uint16_t bscv_get16(bscv_soft_state_t *, int, bscv_addr_t);
108*1c42de6dSgd78059 static uint32_t bscv_get32(bscv_soft_state_t *, int, bscv_addr_t);
109*1c42de6dSgd78059 static void bscv_setclear8(bscv_soft_state_t *, int,
110*1c42de6dSgd78059 	bscv_addr_t, uint8_t, uint8_t);
111*1c42de6dSgd78059 static void bscv_setclear8_volatile(bscv_soft_state_t *, int,
112*1c42de6dSgd78059 	bscv_addr_t, uint8_t, uint8_t);
113*1c42de6dSgd78059 static void bscv_rep_rw8(bscv_soft_state_t *, int,
114*1c42de6dSgd78059 	uint8_t *, bscv_addr_t, size_t, uint_t, boolean_t);
115*1c42de6dSgd78059 static uint8_t bscv_get8_cached(bscv_soft_state_t *, bscv_addr_t);
116*1c42de6dSgd78059 
117*1c42de6dSgd78059 static uint8_t bscv_get8_locked(bscv_soft_state_t *, int, bscv_addr_t, int *);
118*1c42de6dSgd78059 static void bscv_rep_get8_locked(bscv_soft_state_t *, int,
119*1c42de6dSgd78059 	uint8_t *, bscv_addr_t, size_t, uint_t, int *);
120*1c42de6dSgd78059 
121*1c42de6dSgd78059 static boolean_t bscv_faulty(bscv_soft_state_t *);
122*1c42de6dSgd78059 static void bscv_clear_fault(bscv_soft_state_t *);
123*1c42de6dSgd78059 static void bscv_set_fault(bscv_soft_state_t *);
124*1c42de6dSgd78059 static boolean_t bscv_session_error(bscv_soft_state_t *);
125*1c42de6dSgd78059 static int bscv_retcode(bscv_soft_state_t *);
126*1c42de6dSgd78059 static int bscv_should_retry(bscv_soft_state_t *);
127*1c42de6dSgd78059 static void bscv_locked_result(bscv_soft_state_t *, int *);
128*1c42de6dSgd78059 
129*1c42de6dSgd78059 static void bscv_put8_once(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
130*1c42de6dSgd78059 static uint8_t bscv_get8_once(bscv_soft_state_t *, int, bscv_addr_t);
131*1c42de6dSgd78059 static uint32_t bscv_probe(bscv_soft_state_t *, int, uint32_t *);
132*1c42de6dSgd78059 static void bscv_resync_comms(bscv_soft_state_t *, int);
133*1c42de6dSgd78059 
134*1c42de6dSgd78059 static boolean_t bscv_window_setup(bscv_soft_state_t *);
135*1c42de6dSgd78059 static int bscv_eerw(bscv_soft_state_t *, uint32_t, uint8_t *,
136*1c42de6dSgd78059     unsigned, boolean_t);
137*1c42de6dSgd78059 
138*1c42de6dSgd78059 static int bscv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
139*1c42de6dSgd78059 static int bscv_ioc_dogstate(bscv_soft_state_t *, intptr_t, int);
140*1c42de6dSgd78059 static int bscv_ioc_psustate(bscv_soft_state_t *, intptr_t, int);
141*1c42de6dSgd78059 static int bscv_ioc_fanstate(bscv_soft_state_t *, intptr_t, int);
142*1c42de6dSgd78059 static int bscv_ioc_fledstate(bscv_soft_state_t *, intptr_t, int);
143*1c42de6dSgd78059 static int bscv_ioc_ledstate(bscv_soft_state_t *, intptr_t, int);
144*1c42de6dSgd78059 static int bscv_ioc_info(bscv_soft_state_t *, intptr_t, int);
145*1c42de6dSgd78059 static int bscv_ioc_mread(bscv_soft_state_t *, intptr_t, int);
146*1c42de6dSgd78059 static int bscv_ioc_volts(bscv_soft_state_t *, intptr_t, int);
147*1c42de6dSgd78059 static int bscv_ioc_stats(bscv_soft_state_t *, intptr_t, int);
148*1c42de6dSgd78059 static int bscv_ioc_temp(bscv_soft_state_t *, intptr_t, int);
149*1c42de6dSgd78059 static int bscv_ioc_cons(bscv_soft_state_t *, intptr_t, int);
150*1c42de6dSgd78059 static int bscv_ioc_eventlog2(bscv_soft_state_t *, intptr_t, int);
151*1c42de6dSgd78059 static int bscv_ioc_info2(bscv_soft_state_t *, intptr_t, int);
152*1c42de6dSgd78059 static int bscv_ioc_test(bscv_soft_state_t *, intptr_t, int);
153*1c42de6dSgd78059 static int bscv_ioc_mprog2(bscv_soft_state_t *, intptr_t, int);
154*1c42de6dSgd78059 static int bscv_ioc_mread2(bscv_soft_state_t *, intptr_t, int);
155*1c42de6dSgd78059 
156*1c42de6dSgd78059 static void bscv_event_daemon(void *);
157*1c42de6dSgd78059 static void bscv_start_event_daemon(bscv_soft_state_t *);
158*1c42de6dSgd78059 static int bscv_stop_event_daemon(bscv_soft_state_t *);
159*1c42de6dSgd78059 static int bscv_pause_event_daemon(bscv_soft_state_t *);
160*1c42de6dSgd78059 static void bscv_resume_event_daemon(bscv_soft_state_t *);
161*1c42de6dSgd78059 static void bscv_event_process(bscv_soft_state_t *ssp, boolean_t);
162*1c42de6dSgd78059 static int bscv_event_validate(bscv_soft_state_t *, uint32_t, uint8_t);
163*1c42de6dSgd78059 static void bscv_event_process_one(bscv_soft_state_t *, lom_event_t *);
164*1c42de6dSgd78059 static void bscv_build_eventstring(bscv_soft_state_t *,
165*1c42de6dSgd78059     lom_event_t *, char *, char *);
166*1c42de6dSgd78059 static int bscv_level_of_event(lom_event_t *);
167*1c42de6dSgd78059 static void bscv_status(bscv_soft_state_t *, uint8_t, uint8_t);
168*1c42de6dSgd78059 char *bscv_get_label(char [][MAX_LOM2_NAME_STR], int, int);
169*1c42de6dSgd78059 static void bscv_generic_sysevent(bscv_soft_state_t *, char *, char *, char *,
170*1c42de6dSgd78059     char *, int32_t, char *);
171*1c42de6dSgd78059 static void bscv_sysevent(bscv_soft_state_t *, lom_event_t *);
172*1c42de6dSgd78059 
173*1c42de6dSgd78059 static int bscv_prog(bscv_soft_state_t *, intptr_t, int);
174*1c42de6dSgd78059 static int bscv_prog_image(bscv_soft_state_t *, boolean_t,
175*1c42de6dSgd78059     uint8_t *, int, uint32_t);
176*1c42de6dSgd78059 static int bscv_prog_receive_image(bscv_soft_state_t *, lom_prog_t *,
177*1c42de6dSgd78059     uint8_t *, int);
178*1c42de6dSgd78059 static void bscv_leave_programming_mode(bscv_soft_state_t *, boolean_t);
179*1c42de6dSgd78059 static int bscv_prog_stop_lom(bscv_soft_state_t *);
180*1c42de6dSgd78059 static int bscv_prog_start_lom(bscv_soft_state_t *);
181*1c42de6dSgd78059 
182*1c42de6dSgd78059 static int bscv_attach_common(bscv_soft_state_t *);
183*1c42de6dSgd78059 static int bscv_cleanup(bscv_soft_state_t *);
184*1c42de6dSgd78059 static void bscv_setup_capability(bscv_soft_state_t *);
185*1c42de6dSgd78059 static int bscv_probe_check(bscv_soft_state_t *);
186*1c42de6dSgd78059 static void bscv_setup_hostname(bscv_soft_state_t *);
187*1c42de6dSgd78059 static void bscv_read_hostname(bscv_soft_state_t *, char *);
188*1c42de6dSgd78059 static void bscv_write_hostname(bscv_soft_state_t *, char *, uint8_t);
189*1c42de6dSgd78059 static void bscv_setup_static_info(bscv_soft_state_t *);
190*1c42de6dSgd78059 static uint8_t bscv_read_env_name(bscv_soft_state_t *, uint8_t,
191*1c42de6dSgd78059     uint8_t, uint8_t, char [][MAX_LOM2_NAME_STR], int);
192*1c42de6dSgd78059 static void bscv_setup_events(bscv_soft_state_t *);
193*1c42de6dSgd78059 
194*1c42de6dSgd78059 static void bscv_trace(bscv_soft_state_t *, char, const char *,
195*1c42de6dSgd78059     const char *, ...);
196*1c42de6dSgd78059 
197*1c42de6dSgd78059 #ifdef __sparc
198*1c42de6dSgd78059 static void bscv_idi_init();
199*1c42de6dSgd78059 static void bscv_idi_fini();
200*1c42de6dSgd78059 static void bscv_idi_new_instance(dev_info_t *dip);
201*1c42de6dSgd78059 static void bscv_idi_clear_err();
202*1c42de6dSgd78059 void bscv_idi_set(struct bscv_idi_info info);
203*1c42de6dSgd78059 static boolean_t bscv_idi_err();
204*1c42de6dSgd78059 static boolean_t bscv_nodename_set(struct bscv_idi_info info);
205*1c42de6dSgd78059 static boolean_t bscv_sig_set(struct bscv_idi_info info);
206*1c42de6dSgd78059 static boolean_t bscv_wdog_pat(struct bscv_idi_info info);
207*1c42de6dSgd78059 static boolean_t bscv_wdog_cfg(struct bscv_idi_info info);
208*1c42de6dSgd78059 static void bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s);
209*1c42de6dSgd78059 #endif /* __sparc */
210*1c42de6dSgd78059 
211*1c42de6dSgd78059 static void bscv_setup_watchdog(bscv_soft_state_t *ssp);
212*1c42de6dSgd78059 static void bscv_write_wdog_cfg(bscv_soft_state_t *,
213*1c42de6dSgd78059     uint_t, boolean_t, uint8_t);
214*1c42de6dSgd78059 
215*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
216*1c42de6dSgd78059 static void bscv_inform_bsc(bscv_soft_state_t *, uint32_t);
217*1c42de6dSgd78059 static void bscv_watchdog_pat_request(void *);
218*1c42de6dSgd78059 static void bscv_watchdog_cfg_request(bscv_soft_state_t *, uint8_t);
219*1c42de6dSgd78059 static uint_t bscv_set_watchdog_timer(bscv_soft_state_t *, uint_t);
220*1c42de6dSgd78059 static void bscv_clear_watchdog_timer(bscv_soft_state_t *);
221*1c42de6dSgd78059 
222*1c42de6dSgd78059 static boolean_t bscv_panic_callback(void *, int);
223*1c42de6dSgd78059 static void bscv_watchdog_cyclic_add(bscv_soft_state_t *);
224*1c42de6dSgd78059 static void bscv_watchdog_cyclic_remove(bscv_soft_state_t *);
225*1c42de6dSgd78059 
226*1c42de6dSgd78059 extern kmutex_t	cpu_lock; 	/* needed for cyclics */
227*1c42de6dSgd78059 static uint8_t	wdog_reset_on_timeout = 1;
228*1c42de6dSgd78059 
229*1c42de6dSgd78059 #define	WDOG_ON			1
230*1c42de6dSgd78059 #define	WDOG_OFF		0
231*1c42de6dSgd78059 #define	CLK_WATCHDOG_DEFAULT	10		/* 10 seconds */
232*1c42de6dSgd78059 #define	WATCHDOG_PAT_INTERVAL	1000000000	/* 1 second */
233*1c42de6dSgd78059 
234*1c42de6dSgd78059 static int	bscv_watchdog_enable;
235*1c42de6dSgd78059 static int	bscv_watchdog_available;
236*1c42de6dSgd78059 static int	watchdog_activated;
237*1c42de6dSgd78059 static uint_t	bscv_watchdog_timeout_seconds;
238*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
239*1c42de6dSgd78059 
240*1c42de6dSgd78059 #ifdef __sparc
241*1c42de6dSgd78059 struct bscv_idi_callout bscv_idi_callout_table[] = {
242*1c42de6dSgd78059 	{BSCV_IDI_NODENAME,	&bscv_nodename_set	},
243*1c42de6dSgd78059 	{BSCV_IDI_SIG,		&bscv_sig_set		},
244*1c42de6dSgd78059 	{BSCV_IDI_WDOG_PAT,	&bscv_wdog_pat		},
245*1c42de6dSgd78059 	{BSCV_IDI_WDOG_CFG,	&bscv_wdog_cfg		},
246*1c42de6dSgd78059 	{BSCV_IDI_NULL,		NULL			}
247*1c42de6dSgd78059 };
248*1c42de6dSgd78059 
249*1c42de6dSgd78059 static struct bscv_idi_callout_mgr bscv_idi_mgr;
250*1c42de6dSgd78059 #endif /* __sparc */
251*1c42de6dSgd78059 
252*1c42de6dSgd78059 /*
253*1c42de6dSgd78059  * Local Definitions
254*1c42de6dSgd78059  */
255*1c42de6dSgd78059 #define	STATUS_READ_LIMIT	8   /* Read up to 8 status changes at a time */
256*1c42de6dSgd78059 #define	MYNAME			"bscv"
257*1c42de6dSgd78059 #define	BSCV_INST_TO_MINOR(i)	(i)
258*1c42de6dSgd78059 #define	BSCV_MINOR_TO_INST(m)	(m)
259*1c42de6dSgd78059 #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
260*1c42de6dSgd78059 
261*1c42de6dSgd78059 /*
262*1c42de6dSgd78059  * Strings for daemon event reporting
263*1c42de6dSgd78059  */
264*1c42de6dSgd78059 
265*1c42de6dSgd78059 static char *eventSubsysStrings[] =
266*1c42de6dSgd78059 {	"",				/* 00 */
267*1c42de6dSgd78059 	"Alarm ",			/* 01 */
268*1c42de6dSgd78059 	"temperature sensor ",		/* 02 */
269*1c42de6dSgd78059 	"overheat sensor ",		/* 03 */
270*1c42de6dSgd78059 	"Fan ",				/* 04 */
271*1c42de6dSgd78059 	"supply rail ",			/* 05 */
272*1c42de6dSgd78059 	"circuit breaker ",		/* 06 */
273*1c42de6dSgd78059 	"PSU ",				/* 07 */
274*1c42de6dSgd78059 	"user ",			/* 08 */
275*1c42de6dSgd78059 	"phonehome ",			/* 09; unutilized */
276*1c42de6dSgd78059 	"LOM ",				/* 0a */
277*1c42de6dSgd78059 	"host ",			/* 0b */
278*1c42de6dSgd78059 	"event log ",			/* 0c */
279*1c42de6dSgd78059 	"",				/* 0d; EVENT_SUBSYS_EXTRA unutilized */
280*1c42de6dSgd78059 	"LED ",				/* 0e */
281*1c42de6dSgd78059 };
282*1c42de6dSgd78059 
283*1c42de6dSgd78059 static char *eventTypeStrings[] =
284*1c42de6dSgd78059 {
285*1c42de6dSgd78059 	"[null event]",			/* 00 */
286*1c42de6dSgd78059 	"ON",				/* 01 */
287*1c42de6dSgd78059 	"OFF",				/* 02 */
288*1c42de6dSgd78059 	"state change",			/* 03 */
289*1c42de6dSgd78059 	"power on",			/* 04 */
290*1c42de6dSgd78059 	"power off",			/* 05 */
291*1c42de6dSgd78059 	"powered off unexpectedly",	/* 06 */
292*1c42de6dSgd78059 	"reset unexpectedly",		/* 07 */
293*1c42de6dSgd78059 	"booted",			/* 08 */
294*1c42de6dSgd78059 	"watchdog enabled",		/* 09 */
295*1c42de6dSgd78059 	"watchdog disabled",		/* 0a */
296*1c42de6dSgd78059 	"watchdog triggered",		/* 0b */
297*1c42de6dSgd78059 	"failed",			/* 0c */
298*1c42de6dSgd78059 	"recovered",			/* 0d */
299*1c42de6dSgd78059 	"reset",			/* 0e */
300*1c42de6dSgd78059 	"XIR reset",			/* 0f */
301*1c42de6dSgd78059 	"console selected",		/* 10 */
302*1c42de6dSgd78059 	"time reference",		/* 11 */
303*1c42de6dSgd78059 	"script failure",		/* 12 */
304*1c42de6dSgd78059 	"modem access failure",		/* 13 */
305*1c42de6dSgd78059 	"modem dialing failure",	/* 14 */
306*1c42de6dSgd78059 	"bad checksum",			/* 15 */
307*1c42de6dSgd78059 	"added",			/* 16 */
308*1c42de6dSgd78059 	"removed",			/* 17 */
309*1c42de6dSgd78059 	"changed",			/* 18 */
310*1c42de6dSgd78059 	"login",			/* 19 */
311*1c42de6dSgd78059 	"password changed",		/* 1a */
312*1c42de6dSgd78059 	"login failed",			/* 1b */
313*1c42de6dSgd78059 	"logout",			/* 1c */
314*1c42de6dSgd78059 	"flash download",		/* 1d */
315*1c42de6dSgd78059 	"data lost",			/* 1e */
316*1c42de6dSgd78059 	"device busy",			/* 1f */
317*1c42de6dSgd78059 	"fault led state",		/* 20 */
318*1c42de6dSgd78059 	"overheat",			/* 21 */
319*1c42de6dSgd78059 	"severe overheat",		/* 22 */
320*1c42de6dSgd78059 	"no overheat",			/* 23 */
321*1c42de6dSgd78059 	"SCC",				/* 24 */
322*1c42de6dSgd78059 	"device inaccessible",		/* 25 */
323*1c42de6dSgd78059 	"Hostname change",		/* 26 */
324*1c42de6dSgd78059 	"CPU signature timeout",	/* 27 */
325*1c42de6dSgd78059 	"Bootmode change",		/* 28 */
326*1c42de6dSgd78059 	"Watchdog change policy",	/* 29 */
327*1c42de6dSgd78059 	"Watchdog change timeout",	/* 2a */
328*1c42de6dSgd78059 };
329*1c42de6dSgd78059 
330*1c42de6dSgd78059 /*
331*1c42de6dSgd78059  * These store to mapping between the logical service, e.g. chan_prog for
332*1c42de6dSgd78059  * programming, and the actual Xbus channel which carries that traffic.
333*1c42de6dSgd78059  * Any services can be shared on the same channel apart from chan_wdogpat.
334*1c42de6dSgd78059  */
335*1c42de6dSgd78059 static int chan_general;	/* General Traffic */
336*1c42de6dSgd78059 static int chan_wdogpat;	/* Watchdog Patting */
337*1c42de6dSgd78059 static int chan_cpusig;		/* CPU signatures */
338*1c42de6dSgd78059 static int chan_eeprom;		/* EEPROM I/O */
339*1c42de6dSgd78059 static int chan_prog;		/* Programming */
340*1c42de6dSgd78059 
341*1c42de6dSgd78059 /*
342*1c42de6dSgd78059  * cb_ops structure defining the driver entry points
343*1c42de6dSgd78059  */
344*1c42de6dSgd78059 
345*1c42de6dSgd78059 static struct cb_ops bscv_cb_ops = {
346*1c42de6dSgd78059 	bscv_open,	/* open */
347*1c42de6dSgd78059 	bscv_close,	/* close */
348*1c42de6dSgd78059 	nodev,		/* strategy */
349*1c42de6dSgd78059 	nodev,		/* print */
350*1c42de6dSgd78059 	nodev,		/* dump */
351*1c42de6dSgd78059 	nodev,		/* read */
352*1c42de6dSgd78059 	nodev,		/* write */
353*1c42de6dSgd78059 	bscv_ioctl,	/* ioctl */
354*1c42de6dSgd78059 	nodev,		/* devmap */
355*1c42de6dSgd78059 	nodev,		/* mmap */
356*1c42de6dSgd78059 	nodev,		/* segmap */
357*1c42de6dSgd78059 	nochpoll,	/* poll */
358*1c42de6dSgd78059 	ddi_prop_op,	/* prop op */
359*1c42de6dSgd78059 	NULL,		/* ! STREAMS */
360*1c42de6dSgd78059 	D_NEW | D_MP	/* MT/MP Safe */
361*1c42de6dSgd78059 };
362*1c42de6dSgd78059 
363*1c42de6dSgd78059 /*
364*1c42de6dSgd78059  * dev_ops structure defining autoconfiguration driver autoconfiguration
365*1c42de6dSgd78059  * routines
366*1c42de6dSgd78059  */
367*1c42de6dSgd78059 
368*1c42de6dSgd78059 static struct dev_ops bscv_dev_ops = {
369*1c42de6dSgd78059 	DEVO_REV,		/* devo_rev */
370*1c42de6dSgd78059 	0,			/* devo_refcnt */
371*1c42de6dSgd78059 	bscv_getinfo,		/* devo_getinfo */
372*1c42de6dSgd78059 	nulldev,		/* devo_identify */
373*1c42de6dSgd78059 	nulldev,		/* devo_probe */
374*1c42de6dSgd78059 	bscv_attach,		/* devo_attach */
375*1c42de6dSgd78059 	bscv_detach,		/* devo_detach */
376*1c42de6dSgd78059 	bscv_reset,		/* devo_reset */
377*1c42de6dSgd78059 	&bscv_cb_ops,		/* devo_cb_ops */
378*1c42de6dSgd78059 	(struct bus_ops *)0	/* devo_bus_ops */
379*1c42de6dSgd78059 };
380*1c42de6dSgd78059 
381*1c42de6dSgd78059 /*
382*1c42de6dSgd78059  * module configuration section
383*1c42de6dSgd78059  */
384*1c42de6dSgd78059 
385*1c42de6dSgd78059 #ifdef DEBUG
386*1c42de6dSgd78059 #define	BSCV_VERSION_STRING "bscv driver - Debug v%I%"
387*1c42de6dSgd78059 #else /* DEBUG */
388*1c42de6dSgd78059 #define	BSCV_VERSION_STRING "bscv driver v%I%"
389*1c42de6dSgd78059 #endif /* DEBUG */
390*1c42de6dSgd78059 
391*1c42de6dSgd78059 static struct modldrv modldrv = {
392*1c42de6dSgd78059 	&mod_driverops,
393*1c42de6dSgd78059 	BSCV_VERSION_STRING,
394*1c42de6dSgd78059 	&bscv_dev_ops,
395*1c42de6dSgd78059 };
396*1c42de6dSgd78059 
397*1c42de6dSgd78059 static struct modlinkage modlinkage = {
398*1c42de6dSgd78059 	MODREV_1,
399*1c42de6dSgd78059 	&modldrv,
400*1c42de6dSgd78059 	NULL
401*1c42de6dSgd78059 };
402*1c42de6dSgd78059 
403*1c42de6dSgd78059 /*
404*1c42de6dSgd78059  * kernel accessible routines. These routines are necessarily global so the
405*1c42de6dSgd78059  * driver can be loaded, and unloaded successfully
406*1c42de6dSgd78059  */
407*1c42de6dSgd78059 
408*1c42de6dSgd78059 /*
409*1c42de6dSgd78059  * function	- _init
410*1c42de6dSgd78059  * description	- initializes the driver state structure and installs the
411*1c42de6dSgd78059  *		  driver module into the kernel
412*1c42de6dSgd78059  * inputs	- none
413*1c42de6dSgd78059  * outputs	- success or failure of module installation
414*1c42de6dSgd78059  */
415*1c42de6dSgd78059 
416*1c42de6dSgd78059 int
417*1c42de6dSgd78059 _init(void)
418*1c42de6dSgd78059 {
419*1c42de6dSgd78059 	register int e;
420*1c42de6dSgd78059 
421*1c42de6dSgd78059 	if ((e = ddi_soft_state_init(&bscv_statep,
422*1c42de6dSgd78059 	    sizeof (bscv_soft_state_t), 1)) != 0) {
423*1c42de6dSgd78059 		return (e);
424*1c42de6dSgd78059 	}
425*1c42de6dSgd78059 
426*1c42de6dSgd78059 	if ((e = mod_install(&modlinkage)) != 0) {
427*1c42de6dSgd78059 		ddi_soft_state_fini(&bscv_statep);
428*1c42de6dSgd78059 	}
429*1c42de6dSgd78059 
430*1c42de6dSgd78059 #ifdef __sparc
431*1c42de6dSgd78059 	if (e == 0) bscv_idi_init();
432*1c42de6dSgd78059 #endif /* __sparc */
433*1c42de6dSgd78059 	return (e);
434*1c42de6dSgd78059 }
435*1c42de6dSgd78059 
436*1c42de6dSgd78059 /*
437*1c42de6dSgd78059  * function	- _info
438*1c42de6dSgd78059  * description	- provide information about a kernel loaded module
439*1c42de6dSgd78059  * inputs	- module infomation
440*1c42de6dSgd78059  * outputs	- success or failure of information request
441*1c42de6dSgd78059  */
442*1c42de6dSgd78059 
443*1c42de6dSgd78059 int
444*1c42de6dSgd78059 _info(struct modinfo *modinfop)
445*1c42de6dSgd78059 {
446*1c42de6dSgd78059 	return (mod_info(&modlinkage, modinfop));
447*1c42de6dSgd78059 }
448*1c42de6dSgd78059 
449*1c42de6dSgd78059 /*
450*1c42de6dSgd78059  * function	- _fini
451*1c42de6dSgd78059  * description	- removes a module from the kernel and frees the driver soft
452*1c42de6dSgd78059  *		  state memory
453*1c42de6dSgd78059  * inputs	- none
454*1c42de6dSgd78059  * outputs	- success or failure of module removal
455*1c42de6dSgd78059  */
456*1c42de6dSgd78059 
457*1c42de6dSgd78059 int
458*1c42de6dSgd78059 _fini(void)
459*1c42de6dSgd78059 {
460*1c42de6dSgd78059 	register int e;
461*1c42de6dSgd78059 
462*1c42de6dSgd78059 	if ((e = mod_remove(&modlinkage)) != 0) {
463*1c42de6dSgd78059 		return (e);
464*1c42de6dSgd78059 	}
465*1c42de6dSgd78059 
466*1c42de6dSgd78059 #ifdef __sparc
467*1c42de6dSgd78059 	bscv_idi_fini();
468*1c42de6dSgd78059 #endif /* __sparc */
469*1c42de6dSgd78059 	ddi_soft_state_fini(&bscv_statep);
470*1c42de6dSgd78059 
471*1c42de6dSgd78059 	return (e);
472*1c42de6dSgd78059 }
473*1c42de6dSgd78059 
474*1c42de6dSgd78059 /*
475*1c42de6dSgd78059  * function	- bscv_getinfo
476*1c42de6dSgd78059  * description	- routine used to provide information on the driver
477*1c42de6dSgd78059  * inputs	- device information structure, command, command arg, storage
478*1c42de6dSgd78059  *		  area for the result
479*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
480*1c42de6dSgd78059  */
481*1c42de6dSgd78059 
482*1c42de6dSgd78059 /*ARGSUSED*/
483*1c42de6dSgd78059 static int
484*1c42de6dSgd78059 bscv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
485*1c42de6dSgd78059 {
486*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
487*1c42de6dSgd78059 	dev_t	dev = (dev_t)arg;
488*1c42de6dSgd78059 	int	instance;
489*1c42de6dSgd78059 	int	error;
490*1c42de6dSgd78059 
491*1c42de6dSgd78059 	instance = DEVICETOINSTANCE(dev);
492*1c42de6dSgd78059 
493*1c42de6dSgd78059 	switch (cmd) {
494*1c42de6dSgd78059 	case DDI_INFO_DEVT2INSTANCE:
495*1c42de6dSgd78059 		*result = (void *)(uintptr_t)instance;
496*1c42de6dSgd78059 		error = DDI_SUCCESS;
497*1c42de6dSgd78059 		break;
498*1c42de6dSgd78059 
499*1c42de6dSgd78059 	case DDI_INFO_DEVT2DEVINFO:
500*1c42de6dSgd78059 		ssp = ddi_get_soft_state(bscv_statep, instance);
501*1c42de6dSgd78059 		if (ssp == NULL)
502*1c42de6dSgd78059 			return (DDI_FAILURE);
503*1c42de6dSgd78059 		*result = (void *) ssp->dip;
504*1c42de6dSgd78059 		error = DDI_SUCCESS;
505*1c42de6dSgd78059 		break;
506*1c42de6dSgd78059 
507*1c42de6dSgd78059 	default:
508*1c42de6dSgd78059 		error = DDI_FAILURE;
509*1c42de6dSgd78059 		break;
510*1c42de6dSgd78059 	}
511*1c42de6dSgd78059 
512*1c42de6dSgd78059 	return (error);
513*1c42de6dSgd78059 }
514*1c42de6dSgd78059 
515*1c42de6dSgd78059 #ifdef __sparc
516*1c42de6dSgd78059 void
517*1c42de6dSgd78059 bscv_idi_init()
518*1c42de6dSgd78059 {
519*1c42de6dSgd78059 	bscv_idi_mgr.valid_inst = (uint32_t)~0;    /* No valid instances */
520*1c42de6dSgd78059 	bscv_idi_mgr.tbl = bscv_idi_callout_table;
521*1c42de6dSgd78059 	bscv_idi_mgr.errs = 0;
522*1c42de6dSgd78059 
523*1c42de6dSgd78059 	/*
524*1c42de6dSgd78059 	 * Now that all fields are initialized, set the magic flag.  This is
525*1c42de6dSgd78059 	 * a kind of integrity check for the data structure.
526*1c42de6dSgd78059 	 */
527*1c42de6dSgd78059 	bscv_idi_mgr.magic = BSCV_IDI_CALLOUT_MAGIC;
528*1c42de6dSgd78059 }
529*1c42de6dSgd78059 
530*1c42de6dSgd78059 static void
531*1c42de6dSgd78059 bscv_idi_clear_err()
532*1c42de6dSgd78059 {
533*1c42de6dSgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
534*1c42de6dSgd78059 
535*1c42de6dSgd78059 	bscv_idi_mgr.errs = 0;
536*1c42de6dSgd78059 }
537*1c42de6dSgd78059 
538*1c42de6dSgd78059 /*
539*1c42de6dSgd78059  * function	- bscv_idi_err
540*1c42de6dSgd78059  * description	- error messaging service which throttles the number of error
541*1c42de6dSgd78059  *		  messages to avoid overflowing storage
542*1c42de6dSgd78059  * inputs	- none
543*1c42de6dSgd78059  * returns	- boolean to indicate whether a message should be reported
544*1c42de6dSgd78059  * side-effects	- updates the error number counter
545*1c42de6dSgd78059  */
546*1c42de6dSgd78059 static boolean_t
547*1c42de6dSgd78059 bscv_idi_err()
548*1c42de6dSgd78059 {
549*1c42de6dSgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
550*1c42de6dSgd78059 
551*1c42de6dSgd78059 	bscv_idi_mgr.errs++;
552*1c42de6dSgd78059 
553*1c42de6dSgd78059 	if (bscv_idi_mgr.errs++ < BSCV_IDI_ERR_MSG_THRESHOLD)
554*1c42de6dSgd78059 		return (B_TRUE);
555*1c42de6dSgd78059 
556*1c42de6dSgd78059 	return (B_FALSE);
557*1c42de6dSgd78059 }
558*1c42de6dSgd78059 
559*1c42de6dSgd78059 void
560*1c42de6dSgd78059 bscv_idi_new_instance(dev_info_t *dip)
561*1c42de6dSgd78059 {
562*1c42de6dSgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
563*1c42de6dSgd78059 
564*1c42de6dSgd78059 	/*
565*1c42de6dSgd78059 	 * We don't care how many instances we have, or their value, so long
566*1c42de6dSgd78059 	 * as we have at least one valid value.  This is so service routines
567*1c42de6dSgd78059 	 * can get any required locks via a soft state pointer.
568*1c42de6dSgd78059 	 */
569*1c42de6dSgd78059 	if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
570*1c42de6dSgd78059 		bscv_idi_mgr.valid_inst = ddi_get_instance(dip);
571*1c42de6dSgd78059 	}
572*1c42de6dSgd78059 }
573*1c42de6dSgd78059 
574*1c42de6dSgd78059 void
575*1c42de6dSgd78059 bscv_idi_fini()
576*1c42de6dSgd78059 {
577*1c42de6dSgd78059 	bscv_idi_mgr.valid_inst = (uint32_t)~0;    /* No valid instances */
578*1c42de6dSgd78059 	bscv_idi_mgr.tbl = NULL;
579*1c42de6dSgd78059 }
580*1c42de6dSgd78059 #endif /* __sparc */
581*1c42de6dSgd78059 
582*1c42de6dSgd78059 /*
583*1c42de6dSgd78059  * function	- bscv_attach
584*1c42de6dSgd78059  * description	- this routine is responsible for setting aside memory for the
585*1c42de6dSgd78059  *		  driver data structures, initialising the mutexes and creating
586*1c42de6dSgd78059  *		  the device minor nodes. Additionally, this routine calls the
587*1c42de6dSgd78059  *		  the callback routine.
588*1c42de6dSgd78059  * inputs	- device information structure, DDI_ATTACH command
589*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
590*1c42de6dSgd78059  */
591*1c42de6dSgd78059 
592*1c42de6dSgd78059 int
593*1c42de6dSgd78059 bscv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
594*1c42de6dSgd78059 {
595*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
596*1c42de6dSgd78059 	int	instance;
597*1c42de6dSgd78059 
598*1c42de6dSgd78059 	switch (cmd) {
599*1c42de6dSgd78059 	case DDI_ATTACH:
600*1c42de6dSgd78059 
601*1c42de6dSgd78059 		instance = ddi_get_instance(dip);
602*1c42de6dSgd78059 
603*1c42de6dSgd78059 		if (ddi_soft_state_zalloc(bscv_statep, instance) !=
604*1c42de6dSgd78059 		    DDI_SUCCESS) {
605*1c42de6dSgd78059 			return (DDI_FAILURE);
606*1c42de6dSgd78059 		}
607*1c42de6dSgd78059 
608*1c42de6dSgd78059 
609*1c42de6dSgd78059 		ssp = ddi_get_soft_state(bscv_statep, instance);
610*1c42de6dSgd78059 
611*1c42de6dSgd78059 		ssp->progress = 0;
612*1c42de6dSgd78059 
613*1c42de6dSgd78059 		ssp->dip = dip;
614*1c42de6dSgd78059 		ssp->instance = instance;
615*1c42de6dSgd78059 		ssp->event_waiting = B_FALSE;
616*1c42de6dSgd78059 		ssp->status_change = B_FALSE;
617*1c42de6dSgd78059 		ssp->nodename_change = B_FALSE;
618*1c42de6dSgd78059 		ssp->cap0 = 0;
619*1c42de6dSgd78059 		ssp->cap1 = 0;
620*1c42de6dSgd78059 		ssp->cap2 = 0;
621*1c42de6dSgd78059 		ssp->prog_mode_only = B_FALSE;
622*1c42de6dSgd78059 		ssp->programming = B_FALSE;
623*1c42de6dSgd78059 		ssp->cssp_prog = B_FALSE;
624*1c42de6dSgd78059 		ssp->task_flags = 0;
625*1c42de6dSgd78059 		ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
626*1c42de6dSgd78059 			DDI_PROP_DONTPASS, "debug", 0);
627*1c42de6dSgd78059 		ssp->majornum = ddi_driver_major(dip);
628*1c42de6dSgd78059 		ssp->minornum = BSCV_INST_TO_MINOR(instance);
629*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
630*1c42de6dSgd78059 		ssp->last_nodename[0] = '\0';
631*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
632*1c42de6dSgd78059 
633*1c42de6dSgd78059 		/*
634*1c42de6dSgd78059 		 * initialise the mutexes
635*1c42de6dSgd78059 		 */
636*1c42de6dSgd78059 
637*1c42de6dSgd78059 		mutex_init(&ssp->cmd_mutex, NULL, MUTEX_DRIVER, NULL);
638*1c42de6dSgd78059 
639*1c42de6dSgd78059 		mutex_init(&ssp->task_mu, NULL, MUTEX_DRIVER, NULL);
640*1c42de6dSgd78059 		cv_init(&ssp->task_cv, NULL, CV_DRIVER, NULL);
641*1c42de6dSgd78059 		cv_init(&ssp->task_evnt_cv, NULL, CV_DRIVER, NULL);
642*1c42de6dSgd78059 		mutex_init(&ssp->prog_mu, NULL, MUTEX_DRIVER, NULL);
643*1c42de6dSgd78059 		ssp->progress |= BSCV_LOCKS;
644*1c42de6dSgd78059 
645*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_attach",
646*1c42de6dSgd78059 		    "bscv_attach: mutexes and condition vars initialised");
647*1c42de6dSgd78059 
648*1c42de6dSgd78059 		/* Map in physical communication channels */
649*1c42de6dSgd78059 
650*1c42de6dSgd78059 		if (bscv_map_regs(ssp) != DDI_SUCCESS) {
651*1c42de6dSgd78059 			(void) bscv_cleanup(ssp);
652*1c42de6dSgd78059 			return (DDI_FAILURE);
653*1c42de6dSgd78059 		}
654*1c42de6dSgd78059 		ssp->progress |= BSCV_MAPPED_REGS;
655*1c42de6dSgd78059 
656*1c42de6dSgd78059 		/* Associate logical channels to physical channels */
657*1c42de6dSgd78059 
658*1c42de6dSgd78059 		bscv_map_chan_logical_physical(ssp);
659*1c42de6dSgd78059 
660*1c42de6dSgd78059 		bscv_enter(ssp);
661*1c42de6dSgd78059 
662*1c42de6dSgd78059 		bscv_leave_programming_mode(ssp, B_FALSE);
663*1c42de6dSgd78059 
664*1c42de6dSgd78059 		if (bscv_attach_common(ssp) == DDI_FAILURE) {
665*1c42de6dSgd78059 			bscv_exit(ssp);
666*1c42de6dSgd78059 			(void) bscv_cleanup(ssp);
667*1c42de6dSgd78059 			return (DDI_FAILURE);
668*1c42de6dSgd78059 		}
669*1c42de6dSgd78059 
670*1c42de6dSgd78059 #ifdef __sparc
671*1c42de6dSgd78059 		/*
672*1c42de6dSgd78059 		 * At this point the inter-driver-interface is made available.
673*1c42de6dSgd78059 		 * The IDI uses the event thread service which
674*1c42de6dSgd78059 		 * bscv_attach_common() sets up.
675*1c42de6dSgd78059 		 */
676*1c42de6dSgd78059 		bscv_idi_new_instance(dip);
677*1c42de6dSgd78059 #endif /* __sparc */
678*1c42de6dSgd78059 
679*1c42de6dSgd78059 		bscv_exit(ssp);
680*1c42de6dSgd78059 
681*1c42de6dSgd78059 		/*
682*1c42de6dSgd78059 		 * now create the minor nodes
683*1c42de6dSgd78059 		 */
684*1c42de6dSgd78059 		if (ddi_create_minor_node(ssp->dip, "lom", S_IFCHR,
685*1c42de6dSgd78059 		    BSCV_INST_TO_MINOR(instance),
686*1c42de6dSgd78059 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
687*1c42de6dSgd78059 			(void) bscv_cleanup(ssp);
688*1c42de6dSgd78059 			return (DDI_FAILURE);
689*1c42de6dSgd78059 		}
690*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_attach",
691*1c42de6dSgd78059 		    "bscv_attach: device minor nodes created");
692*1c42de6dSgd78059 		ssp->progress |= BSCV_NODES;
693*1c42de6dSgd78059 
694*1c42de6dSgd78059 		if (!ssp->prog_mode_only)
695*1c42de6dSgd78059 			bscv_start_event_daemon(ssp);
696*1c42de6dSgd78059 
697*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
698*1c42de6dSgd78059 		bscv_watchdog_enable = 1;
699*1c42de6dSgd78059 		bscv_watchdog_available = 1;
700*1c42de6dSgd78059 		watchdog_activated = 0;
701*1c42de6dSgd78059 		bscv_watchdog_timeout_seconds = CLK_WATCHDOG_DEFAULT;
702*1c42de6dSgd78059 
703*1c42de6dSgd78059 		if (bscv_watchdog_enable && (boothowto & RB_DEBUG)) {
704*1c42de6dSgd78059 			bscv_watchdog_available = 0;
705*1c42de6dSgd78059 			cmn_err(CE_WARN, "bscv: kernel debugger "
706*1c42de6dSgd78059 				"detected: hardware watchdog disabled");
707*1c42de6dSgd78059 		}
708*1c42de6dSgd78059 
709*1c42de6dSgd78059 		/*
710*1c42de6dSgd78059 		 * Before we enable the watchdog - register the panic
711*1c42de6dSgd78059 		 * callback so that we get called to stop the watchdog
712*1c42de6dSgd78059 		 * in the case of a panic.
713*1c42de6dSgd78059 		 */
714*1c42de6dSgd78059 		ssp->callb_id = callb_add(bscv_panic_callback,
715*1c42de6dSgd78059 		    (void *)ssp, CB_CL_PANIC, "");
716*1c42de6dSgd78059 
717*1c42de6dSgd78059 		if (bscv_watchdog_available) {
718*1c42de6dSgd78059 			(void) bscv_set_watchdog_timer(ssp,
719*1c42de6dSgd78059 			    CLK_WATCHDOG_DEFAULT);
720*1c42de6dSgd78059 			bscv_enter(ssp);
721*1c42de6dSgd78059 			bscv_setup_watchdog(ssp);  /* starts cyclic callback */
722*1c42de6dSgd78059 			bscv_exit(ssp);
723*1c42de6dSgd78059 		}
724*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
725*1c42de6dSgd78059 		ddi_report_dev(dip);
726*1c42de6dSgd78059 		return (DDI_SUCCESS);
727*1c42de6dSgd78059 	default:
728*1c42de6dSgd78059 		return (DDI_FAILURE);
729*1c42de6dSgd78059 	}
730*1c42de6dSgd78059 }
731*1c42de6dSgd78059 
732*1c42de6dSgd78059 /*
733*1c42de6dSgd78059  * function	- bscv_detach
734*1c42de6dSgd78059  * description	- routine that prepares a module to be unloaded. It undoes all
735*1c42de6dSgd78059  *		  the work done by the bscv_attach)() routine. This is
736*1c42de6dSgd78059  *		  facilitated by the use of the progress indicator
737*1c42de6dSgd78059  * inputs	- device information structure, DDI_DETACH command
738*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
739*1c42de6dSgd78059  */
740*1c42de6dSgd78059 
741*1c42de6dSgd78059 /*ARGSUSED*/
742*1c42de6dSgd78059 static int
743*1c42de6dSgd78059 bscv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
744*1c42de6dSgd78059 {
745*1c42de6dSgd78059 	return (DDI_FAILURE);
746*1c42de6dSgd78059 }
747*1c42de6dSgd78059 
748*1c42de6dSgd78059 /*
749*1c42de6dSgd78059  * function	- bscv_reset
750*1c42de6dSgd78059  * description	- routine called when system is being stopped - used to disable
751*1c42de6dSgd78059  *		  the watchdog.
752*1c42de6dSgd78059  * inputs	- device information structure, DDI_RESET command
753*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
754*1c42de6dSgd78059  */
755*1c42de6dSgd78059 static int
756*1c42de6dSgd78059 bscv_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
757*1c42de6dSgd78059 {
758*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
759*1c42de6dSgd78059 	int	instance;
760*1c42de6dSgd78059 
761*1c42de6dSgd78059 	switch (cmd) {
762*1c42de6dSgd78059 	case DDI_RESET_FORCE:
763*1c42de6dSgd78059 
764*1c42de6dSgd78059 		instance = ddi_get_instance(dip);
765*1c42de6dSgd78059 		ssp = ddi_get_soft_state(bscv_statep, instance);
766*1c42de6dSgd78059 		if (ssp == NULL) {
767*1c42de6dSgd78059 			return (DDI_FAILURE);
768*1c42de6dSgd78059 		}
769*1c42de6dSgd78059 		bscv_full_stop(ssp);
770*1c42de6dSgd78059 		return (DDI_SUCCESS);
771*1c42de6dSgd78059 
772*1c42de6dSgd78059 	default:
773*1c42de6dSgd78059 		return (DDI_FAILURE);
774*1c42de6dSgd78059 	}
775*1c42de6dSgd78059 }
776*1c42de6dSgd78059 
777*1c42de6dSgd78059 /*
778*1c42de6dSgd78059  * cb_ops routines
779*1c42de6dSgd78059  */
780*1c42de6dSgd78059 
781*1c42de6dSgd78059 /*
782*1c42de6dSgd78059  * function	- bscv_open
783*1c42de6dSgd78059  * description	- routine to provide association between user fd and device
784*1c42de6dSgd78059  *		  minor number. This routine is necessarily simple since a
785*1c42de6dSgd78059  *		  read/write interface is not provided. Additionally, the
786*1c42de6dSgd78059  *		  driver does not enforce exclusive access (FEXCL) or
787*1c42de6dSgd78059  *		  non-blocking during an open (FNDELAY). Deferred attach is
788*1c42de6dSgd78059  *		  supported.
789*1c42de6dSgd78059  * inputs	- device number, flag specifying open type, device type,
790*1c42de6dSgd78059  *		  permissions
791*1c42de6dSgd78059  * outputs	- success or failure of operation
792*1c42de6dSgd78059  */
793*1c42de6dSgd78059 
794*1c42de6dSgd78059 /*ARGSUSED*/
795*1c42de6dSgd78059 static int
796*1c42de6dSgd78059 bscv_open(dev_t *devp, int flag, int otype, cred_t *cred)
797*1c42de6dSgd78059 {
798*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
799*1c42de6dSgd78059 	int instance;
800*1c42de6dSgd78059 
801*1c42de6dSgd78059 	instance = DEVICETOINSTANCE(*devp);
802*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
803*1c42de6dSgd78059 	if (ssp == NULL) {
804*1c42de6dSgd78059 		return (ENXIO);	/* not attached yet */
805*1c42de6dSgd78059 	}
806*1c42de6dSgd78059 	bscv_trace(ssp, 'O', "bscv_open", "instance 0x%x", instance);
807*1c42de6dSgd78059 
808*1c42de6dSgd78059 	if (otype != OTYP_CHR) {
809*1c42de6dSgd78059 		return (EINVAL);
810*1c42de6dSgd78059 	}
811*1c42de6dSgd78059 
812*1c42de6dSgd78059 	return (0);
813*1c42de6dSgd78059 }
814*1c42de6dSgd78059 
815*1c42de6dSgd78059 /*
816*1c42de6dSgd78059  * function	- bscv_close
817*1c42de6dSgd78059  * description	- routine to perform the final close on the device. As per the
818*1c42de6dSgd78059  *		  open routine, neither FEXCL or FNDELAY accesses are enforced
819*1c42de6dSgd78059  *		  by the driver.
820*1c42de6dSgd78059  * inputs	- device number,flag specifying open type, device type,
821*1c42de6dSgd78059  *		  permissions
822*1c42de6dSgd78059  * outputs	- success or failure of operation
823*1c42de6dSgd78059  */
824*1c42de6dSgd78059 
825*1c42de6dSgd78059 /*ARGSUSED1*/
826*1c42de6dSgd78059 static int
827*1c42de6dSgd78059 bscv_close(dev_t dev, int flag, int otype, cred_t *cred)
828*1c42de6dSgd78059 {
829*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
830*1c42de6dSgd78059 	int instance;
831*1c42de6dSgd78059 
832*1c42de6dSgd78059 	instance = DEVICETOINSTANCE(dev);
833*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
834*1c42de6dSgd78059 	if (ssp == NULL) {
835*1c42de6dSgd78059 		return (ENXIO);
836*1c42de6dSgd78059 	}
837*1c42de6dSgd78059 	bscv_trace(ssp, 'O', "bscv_close", "instance 0x%x", instance);
838*1c42de6dSgd78059 
839*1c42de6dSgd78059 	return (0);
840*1c42de6dSgd78059 }
841*1c42de6dSgd78059 
842*1c42de6dSgd78059 static int
843*1c42de6dSgd78059 bscv_map_regs(bscv_soft_state_t *ssp)
844*1c42de6dSgd78059 {
845*1c42de6dSgd78059 	int i;
846*1c42de6dSgd78059 	int retval;
847*1c42de6dSgd78059 	int *props;
848*1c42de6dSgd78059 	unsigned int nelements;
849*1c42de6dSgd78059 
850*1c42de6dSgd78059 	ASSERT(ssp);
851*1c42de6dSgd78059 
852*1c42de6dSgd78059 	ssp->nchannels = 0;
853*1c42de6dSgd78059 
854*1c42de6dSgd78059 	/*
855*1c42de6dSgd78059 	 * Work out how many channels are available by looking at the number
856*1c42de6dSgd78059 	 * of elements of the regs property array.
857*1c42de6dSgd78059 	 */
858*1c42de6dSgd78059 	retval = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ssp->dip,
859*1c42de6dSgd78059 		    DDI_PROP_DONTPASS, "reg", &props, &nelements);
860*1c42de6dSgd78059 
861*1c42de6dSgd78059 	/* We don't need props anymore.  Free memory if it was allocated */
862*1c42de6dSgd78059 	if (retval == DDI_PROP_SUCCESS)
863*1c42de6dSgd78059 		ddi_prop_free(props);
864*1c42de6dSgd78059 
865*1c42de6dSgd78059 	/* Check for sanity of nelements */
866*1c42de6dSgd78059 	if (retval != DDI_PROP_SUCCESS) {
867*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_map_regs", "lookup reg returned"
868*1c42de6dSgd78059 		    " 0x%x", retval);
869*1c42de6dSgd78059 		goto cleanup_exit;
870*1c42de6dSgd78059 	} else if (nelements % LOMBUS_REGSPEC_SIZE != 0) {
871*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d not"
872*1c42de6dSgd78059 		    " a multiple of %d", nelements, LOMBUS_REGSPEC_SIZE);
873*1c42de6dSgd78059 		goto cleanup_exit;
874*1c42de6dSgd78059 	} else if (nelements > BSCV_MAXCHANNELS * LOMBUS_REGSPEC_SIZE) {
875*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d too large"
876*1c42de6dSgd78059 		    ", probably a misconfiguration", nelements);
877*1c42de6dSgd78059 		goto cleanup_exit;
878*1c42de6dSgd78059 	} else if (nelements < BSCV_MINCHANNELS * LOMBUS_REGSPEC_SIZE) {
879*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_map_regs", "nelements %d too small"
880*1c42de6dSgd78059 		    ", need to have at least a general and a wdog channel",
881*1c42de6dSgd78059 		    nelements);
882*1c42de6dSgd78059 		goto cleanup_exit;
883*1c42de6dSgd78059 	}
884*1c42de6dSgd78059 
885*1c42de6dSgd78059 	ssp->nchannels = nelements / LOMBUS_REGSPEC_SIZE;
886*1c42de6dSgd78059 
887*1c42de6dSgd78059 	ssp->attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
888*1c42de6dSgd78059 	ssp->attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
889*1c42de6dSgd78059 	ssp->attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
890*1c42de6dSgd78059 
891*1c42de6dSgd78059 	for (i = 0; i < ssp->nchannels; i++) {
892*1c42de6dSgd78059 		retval = ddi_regs_map_setup(ssp->dip, i,
893*1c42de6dSgd78059 			    (caddr_t *)&ssp->channel[i].regs,
894*1c42de6dSgd78059 			    0, 0, &ssp->attr, &ssp->channel[i].handle);
895*1c42de6dSgd78059 		if (retval != DDI_SUCCESS) {
896*1c42de6dSgd78059 			bscv_trace(ssp, 'A', "bscv_map_regs", "map failure"
897*1c42de6dSgd78059 			    " 0x%x on space %d", retval, i);
898*1c42de6dSgd78059 
899*1c42de6dSgd78059 			/* Rewind all current mappings - avoiding failed one */
900*1c42de6dSgd78059 			i--;
901*1c42de6dSgd78059 			for (; i >= 0; i--) {
902*1c42de6dSgd78059 				ddi_regs_map_free(&ssp->channel[i].handle);
903*1c42de6dSgd78059 			}
904*1c42de6dSgd78059 
905*1c42de6dSgd78059 			goto cleanup_exit;
906*1c42de6dSgd78059 		}
907*1c42de6dSgd78059 	}
908*1c42de6dSgd78059 
909*1c42de6dSgd78059 	return (DDI_SUCCESS);
910*1c42de6dSgd78059 
911*1c42de6dSgd78059 cleanup_exit:
912*1c42de6dSgd78059 	/*
913*1c42de6dSgd78059 	 * It is important to set nchannels to 0 even if, say, only one of
914*1c42de6dSgd78059 	 * the two required handles was mapped.  If we cannot achieve our
915*1c42de6dSgd78059 	 * minimum config its not safe to do any IO; this keeps our failure
916*1c42de6dSgd78059 	 * mode handling simpler.
917*1c42de6dSgd78059 	 */
918*1c42de6dSgd78059 	ssp->nchannels = 0;
919*1c42de6dSgd78059 	return (DDI_FAILURE);
920*1c42de6dSgd78059 }
921*1c42de6dSgd78059 
922*1c42de6dSgd78059 static void
923*1c42de6dSgd78059 bscv_unmap_regs(bscv_soft_state_t *ssp)
924*1c42de6dSgd78059 {
925*1c42de6dSgd78059 	int i;
926*1c42de6dSgd78059 
927*1c42de6dSgd78059 	ASSERT(ssp);
928*1c42de6dSgd78059 
929*1c42de6dSgd78059 	for (i = 0; i < ssp->nchannels; i++) {
930*1c42de6dSgd78059 		ddi_regs_map_free(&ssp->channel[i].handle);
931*1c42de6dSgd78059 	}
932*1c42de6dSgd78059 }
933*1c42de6dSgd78059 
934*1c42de6dSgd78059 /*
935*1c42de6dSgd78059  * Map logical services onto physical XBus channels.
936*1c42de6dSgd78059  */
937*1c42de6dSgd78059 static void
938*1c42de6dSgd78059 bscv_map_chan_logical_physical(bscv_soft_state_t *ssp)
939*1c42de6dSgd78059 {
940*1c42de6dSgd78059 	ASSERT(ssp);
941*1c42de6dSgd78059 
942*1c42de6dSgd78059 	/*
943*1c42de6dSgd78059 	 * We can assert that there will always be at least two channels,
944*1c42de6dSgd78059 	 * to allow watchdog pats to be segregated from all other traffic.
945*1c42de6dSgd78059 	 */
946*1c42de6dSgd78059 	chan_general = 0;
947*1c42de6dSgd78059 	chan_wdogpat = 1;
948*1c42de6dSgd78059 
949*1c42de6dSgd78059 	/*
950*1c42de6dSgd78059 	 * By default move all other services onto the generic channel unless
951*1c42de6dSgd78059 	 * the hardware supports additional channels.
952*1c42de6dSgd78059 	 */
953*1c42de6dSgd78059 
954*1c42de6dSgd78059 	chan_cpusig = chan_eeprom = chan_prog = chan_general;
955*1c42de6dSgd78059 
956*1c42de6dSgd78059 	if (ssp->nchannels > 2)
957*1c42de6dSgd78059 		chan_cpusig = 2;
958*1c42de6dSgd78059 	if (ssp->nchannels > 3)
959*1c42de6dSgd78059 		chan_eeprom = 3;
960*1c42de6dSgd78059 	if (ssp->nchannels > 4)
961*1c42de6dSgd78059 		chan_prog = 4;
962*1c42de6dSgd78059 }
963*1c42de6dSgd78059 
964*1c42de6dSgd78059 
965*1c42de6dSgd78059 /*
966*1c42de6dSgd78059  * function	- bscv_full_stop
967*1c42de6dSgd78059  * description	- gracefully shut the lom down during panic or reboot.
968*1c42de6dSgd78059  *		  Disables the watchdog, setup up serial event reporting
969*1c42de6dSgd78059  *		  and stops the event daemon running.
970*1c42de6dSgd78059  * inputs	- soft state pointer
971*1c42de6dSgd78059  * outputs	- none
972*1c42de6dSgd78059  */
973*1c42de6dSgd78059 void
974*1c42de6dSgd78059 bscv_full_stop(bscv_soft_state_t *ssp)
975*1c42de6dSgd78059 {
976*1c42de6dSgd78059 	uint8_t bits2set = 0;
977*1c42de6dSgd78059 	uint8_t bits2clear = 0;
978*1c42de6dSgd78059 
979*1c42de6dSgd78059 	bscv_trace(ssp, 'W', "bscv_full_stop",
980*1c42de6dSgd78059 	    "turning off watchdog");
981*1c42de6dSgd78059 
982*1c42de6dSgd78059 	if (!ddi_in_panic()) {
983*1c42de6dSgd78059 		/* Stop the event daemon if we are not panicking. */
984*1c42de6dSgd78059 		(void) bscv_pause_event_daemon(ssp);
985*1c42de6dSgd78059 	}
986*1c42de6dSgd78059 
987*1c42de6dSgd78059 	bscv_enter(ssp);
988*1c42de6dSgd78059 
989*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
990*1c42de6dSgd78059 	if (ddi_in_panic()) {
991*1c42de6dSgd78059 	    bscv_inform_bsc(ssp, BSC_INFORM_PANIC);
992*1c42de6dSgd78059 	} else {
993*1c42de6dSgd78059 	    bscv_inform_bsc(ssp, BSC_INFORM_OFFLINE);
994*1c42de6dSgd78059 	}
995*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
996*1c42de6dSgd78059 
997*1c42de6dSgd78059 	/* set serial event reporting */
998*1c42de6dSgd78059 	switch (ssp->serial_reporting) {
999*1c42de6dSgd78059 	case LOM_SER_EVENTS_ON:
1000*1c42de6dSgd78059 	case LOM_SER_EVENTS_DEF:
1001*1c42de6dSgd78059 		/* Make sure serial event reporting is on */
1002*1c42de6dSgd78059 		bits2clear = EBUS_ALARM_NOEVENTS;
1003*1c42de6dSgd78059 		break;
1004*1c42de6dSgd78059 	case LOM_SER_EVENTS_OFF:
1005*1c42de6dSgd78059 		/* Make sure serial event reporting is on */
1006*1c42de6dSgd78059 		bits2set = EBUS_ALARM_NOEVENTS;
1007*1c42de6dSgd78059 		break;
1008*1c42de6dSgd78059 	default:
1009*1c42de6dSgd78059 		break;
1010*1c42de6dSgd78059 	}
1011*1c42de6dSgd78059 	bscv_setclear8_volatile(ssp, chan_general,
1012*1c42de6dSgd78059 		EBUS_IDX_ALARM, bits2set, bits2clear);
1013*1c42de6dSgd78059 
1014*1c42de6dSgd78059 	bscv_exit(ssp);
1015*1c42de6dSgd78059 }
1016*1c42de6dSgd78059 
1017*1c42de6dSgd78059 /*
1018*1c42de6dSgd78059  * LOM I/O routines.
1019*1c42de6dSgd78059  *
1020*1c42de6dSgd78059  * locking
1021*1c42de6dSgd78059  *
1022*1c42de6dSgd78059  * Two sets of routines are provided:
1023*1c42de6dSgd78059  *	normal - must be called after acquiring an appropriate lock.
1024*1c42de6dSgd78059  *	locked - perform all the locking required and return any error
1025*1c42de6dSgd78059  *		 code in the supplied 'res' argument. If there is no
1026*1c42de6dSgd78059  *		 error 'res' is not changed.
1027*1c42de6dSgd78059  * The locked routines are designed for use in ioctl commands where
1028*1c42de6dSgd78059  * only a single operation needs to be performed and the overhead of
1029*1c42de6dSgd78059  * locking and result checking adds significantly to code complexity.
1030*1c42de6dSgd78059  *
1031*1c42de6dSgd78059  * locking primitives
1032*1c42de6dSgd78059  *
1033*1c42de6dSgd78059  * bscv_enter() - acquires an I/O lock for the calling thread.
1034*1c42de6dSgd78059  * bscv_exit() - releases an I/O lock acquired by bscv_enter().
1035*1c42de6dSgd78059  * bscv_held() - used to assert ownership of an I/O lock.
1036*1c42de6dSgd78059  *
1037*1c42de6dSgd78059  * normal I/O routines
1038*1c42de6dSgd78059  *
1039*1c42de6dSgd78059  * Note bscv_{put|get}{16|32} routines are big-endian. This assumes that
1040*1c42de6dSgd78059  * the firmware works that way too.
1041*1c42de6dSgd78059  *
1042*1c42de6dSgd78059  * bscv_put8(), bscv_put16, bscv_put32 - write values to the LOM
1043*1c42de6dSgd78059  *		and handle any retries if necessary.
1044*1c42de6dSgd78059  *		16 and 32 bit values are big-endian.
1045*1c42de6dSgd78059  * bscv_get8(), bscv_get16, bscv_get32 - read values from the LOM
1046*1c42de6dSgd78059  *		and handle any retries if necessary.
1047*1c42de6dSgd78059  *		16 and 32 bit values are big-endian.
1048*1c42de6dSgd78059  * bscv_setclear8() - set or clear the specified bits in the register
1049*1c42de6dSgd78059  *		at the supplied address.
1050*1c42de6dSgd78059  * bscv_setclear8_volatile() - set or clear the specified bits in the
1051*1c42de6dSgd78059  *		register at the supplied address. If the lom reports
1052*1c42de6dSgd78059  *		that the registers has changed since the last read
1053*1c42de6dSgd78059  *		re-read and apply the set or clear to the new bits.
1054*1c42de6dSgd78059  * bscv_get8_cached() - Return a cached register value (addr < 0x80).
1055*1c42de6dSgd78059  *		Does not access the hardware. A read of the hardware
1056*1c42de6dSgd78059  *		automatically updates this cache.
1057*1c42de6dSgd78059  *
1058*1c42de6dSgd78059  * locked I/O routines
1059*1c42de6dSgd78059  *
1060*1c42de6dSgd78059  * bscv_get8_locked(), bscv_rep_get8_locked().
1061*1c42de6dSgd78059  *
1062*1c42de6dSgd78059  * Call the indicated function from above, but wrapping it with
1063*1c42de6dSgd78059  * bscv_enter()/bscv_exit().
1064*1c42de6dSgd78059  *
1065*1c42de6dSgd78059  *
1066*1c42de6dSgd78059  * Fault management
1067*1c42de6dSgd78059  *
1068*1c42de6dSgd78059  * LOM communications fault are grouped into three categories:
1069*1c42de6dSgd78059  * 1) Faulty - the LOM is not responding and no attempt to communicate
1070*1c42de6dSgd78059  *		with it should be made.
1071*1c42de6dSgd78059  * 2) Transient fault - something which might recover after a retry
1072*1c42de6dSgd78059  *		but which doesn't affect our ability to perform other
1073*1c42de6dSgd78059  *		commands.
1074*1c42de6dSgd78059  * 3) Command error - an inappropriate command was executed. A retry
1075*1c42de6dSgd78059  *		will not fix it but the command failed.
1076*1c42de6dSgd78059  *
1077*1c42de6dSgd78059  * The current implementation of the bscv driver is not very good at
1078*1c42de6dSgd78059  * noticing command errors due to the structure of the original code
1079*1c42de6dSgd78059  * that it is based on. It is possible to extend the driver to do this
1080*1c42de6dSgd78059  * and would probably involve having a concept of a "session error"
1081*1c42de6dSgd78059  * which is less severe than a fault but means that a sequence of
1082*1c42de6dSgd78059  * commands had some fault which cannot be recovered.
1083*1c42de6dSgd78059  *
1084*1c42de6dSgd78059  *
1085*1c42de6dSgd78059  * faults
1086*1c42de6dSgd78059  *
1087*1c42de6dSgd78059  * bscv_faulty() - returns B_TRUE if the LOM (communications) have been
1088*1c42de6dSgd78059  *		declared faulty.
1089*1c42de6dSgd78059  * bscv_clear_fault() - marks the LOM as not faulty.
1090*1c42de6dSgd78059  * bscv_set_fault() - marks the LOM as being faulty.
1091*1c42de6dSgd78059  *
1092*1c42de6dSgd78059  * bscv_clear_fault and bscv_set_fault should generally not be called
1093*1c42de6dSgd78059  * directly.
1094*1c42de6dSgd78059  *
1095*1c42de6dSgd78059  * command errors/transient faults
1096*1c42de6dSgd78059  *
1097*1c42de6dSgd78059  * bscv_retcode() - returns the actual error code of the last operation.
1098*1c42de6dSgd78059  * bscv_should_retry() - determines if last operation may suceed if
1099*1c42de6dSgd78059  *		retried.
1100*1c42de6dSgd78059  * bscv_locked_result() - Set the result of a locked register access.
1101*1c42de6dSgd78059  *
1102*1c42de6dSgd78059  * low level I/O primitives
1103*1c42de6dSgd78059  *
1104*1c42de6dSgd78059  * These are generally not called directly. These perform a single
1105*1c42de6dSgd78059  * access to the LOM device. They do not handle retries.
1106*1c42de6dSgd78059  *
1107*1c42de6dSgd78059  * bscv_put8_once()
1108*1c42de6dSgd78059  * bscv_get8_once()
1109*1c42de6dSgd78059  * bscv_probe() - perform a probe (NOP) operation to check out lom comms.
1110*1c42de6dSgd78059  * bscv_resync_comms() - resynchronise communications after a transient fault.
1111*1c42de6dSgd78059  */
1112*1c42de6dSgd78059 
1113*1c42de6dSgd78059 static void
1114*1c42de6dSgd78059 bscv_enter(bscv_soft_state_t *ssp)
1115*1c42de6dSgd78059 {
1116*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_enter", "");
1117*1c42de6dSgd78059 	mutex_enter(&ssp->cmd_mutex);
1118*1c42de6dSgd78059 	ssp->had_session_error = B_FALSE;
1119*1c42de6dSgd78059 }
1120*1c42de6dSgd78059 
1121*1c42de6dSgd78059 static void
1122*1c42de6dSgd78059 bscv_exit(bscv_soft_state_t *ssp)
1123*1c42de6dSgd78059 {
1124*1c42de6dSgd78059 	mutex_exit(&ssp->cmd_mutex);
1125*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_exit", "");
1126*1c42de6dSgd78059 }
1127*1c42de6dSgd78059 
1128*1c42de6dSgd78059 #ifdef DEBUG
1129*1c42de6dSgd78059 static int
1130*1c42de6dSgd78059 bscv_held(bscv_soft_state_t *ssp)
1131*1c42de6dSgd78059 {
1132*1c42de6dSgd78059 	return (mutex_owned(&ssp->cmd_mutex));
1133*1c42de6dSgd78059 }
1134*1c42de6dSgd78059 #endif /* DEBUG */
1135*1c42de6dSgd78059 
1136*1c42de6dSgd78059 static void
1137*1c42de6dSgd78059 bscv_put8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
1138*1c42de6dSgd78059 {
1139*1c42de6dSgd78059 	boolean_t needretry;
1140*1c42de6dSgd78059 	int num_failures;
1141*1c42de6dSgd78059 
1142*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1143*1c42de6dSgd78059 
1144*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
1145*1c42de6dSgd78059 		return;
1146*1c42de6dSgd78059 	}
1147*1c42de6dSgd78059 
1148*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_put8",
1149*1c42de6dSgd78059 	    "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
1150*1c42de6dSgd78059 
1151*1c42de6dSgd78059 	for (num_failures = 0;
1152*1c42de6dSgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
1153*1c42de6dSgd78059 	    num_failures++) {
1154*1c42de6dSgd78059 		bscv_put8_once(ssp, chan, addr, val);
1155*1c42de6dSgd78059 		needretry = bscv_should_retry(ssp);
1156*1c42de6dSgd78059 		if (!needretry) {
1157*1c42de6dSgd78059 			break;
1158*1c42de6dSgd78059 		}
1159*1c42de6dSgd78059 	}
1160*1c42de6dSgd78059 	if (ssp->command_error != 0) {
1161*1c42de6dSgd78059 		ssp->had_session_error = B_TRUE;
1162*1c42de6dSgd78059 	}
1163*1c42de6dSgd78059 
1164*1c42de6dSgd78059 	if (needretry) {
1165*1c42de6dSgd78059 		/* Failure - we ran out of retries */
1166*1c42de6dSgd78059 		cmn_err(CE_WARN, "bscv_put8: addr 0x%x.%02x retried "
1167*1c42de6dSgd78059 		    "write %d times, giving up",
1168*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1169*1c42de6dSgd78059 		bscv_set_fault(ssp);
1170*1c42de6dSgd78059 	} else if (num_failures > 0) {
1171*1c42de6dSgd78059 		bscv_trace(ssp, 'R', "bscv_put8",
1172*1c42de6dSgd78059 		    "addr 0x%x.%02x retried write %d times, succeeded",
1173*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1174*1c42de6dSgd78059 	}
1175*1c42de6dSgd78059 }
1176*1c42de6dSgd78059 
1177*1c42de6dSgd78059 static void
1178*1c42de6dSgd78059 bscv_put16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint16_t val)
1179*1c42de6dSgd78059 {
1180*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1181*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_put16",
1182*1c42de6dSgd78059 	    "addr 0x%x.%02x <= %04x", addr >> 8, addr & 0xff, val);
1183*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr, val >> 8);
1184*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr + 1, val & 0xff);
1185*1c42de6dSgd78059 }
1186*1c42de6dSgd78059 
1187*1c42de6dSgd78059 static void
1188*1c42de6dSgd78059 bscv_put32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint32_t val)
1189*1c42de6dSgd78059 {
1190*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1191*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_put32",
1192*1c42de6dSgd78059 	    "addr 0x%x.%02x <= %08x", addr >> 8, addr & 0xff, val);
1193*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr, (val >> 24) & 0xff);
1194*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr + 1, (val >> 16) & 0xff);
1195*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr + 2, (val >> 8) & 0xff);
1196*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr + 3, val & 0xff);
1197*1c42de6dSgd78059 }
1198*1c42de6dSgd78059 
1199*1c42de6dSgd78059 static uint8_t
1200*1c42de6dSgd78059 bscv_get8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
1201*1c42de6dSgd78059 {
1202*1c42de6dSgd78059 	uint8_t retval;
1203*1c42de6dSgd78059 	boolean_t needretry;
1204*1c42de6dSgd78059 	int num_failures;
1205*1c42de6dSgd78059 
1206*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1207*1c42de6dSgd78059 
1208*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
1209*1c42de6dSgd78059 		return (0);
1210*1c42de6dSgd78059 	}
1211*1c42de6dSgd78059 
1212*1c42de6dSgd78059 	for (num_failures = 0;
1213*1c42de6dSgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
1214*1c42de6dSgd78059 	    num_failures++) {
1215*1c42de6dSgd78059 		retval = bscv_get8_once(ssp, chan, addr);
1216*1c42de6dSgd78059 		needretry = bscv_should_retry(ssp);
1217*1c42de6dSgd78059 		if (!needretry) {
1218*1c42de6dSgd78059 			break;
1219*1c42de6dSgd78059 		}
1220*1c42de6dSgd78059 	}
1221*1c42de6dSgd78059 	if (ssp->command_error != 0) {
1222*1c42de6dSgd78059 		ssp->had_session_error = B_TRUE;
1223*1c42de6dSgd78059 	}
1224*1c42de6dSgd78059 
1225*1c42de6dSgd78059 	if (needretry) {
1226*1c42de6dSgd78059 		/* Failure */
1227*1c42de6dSgd78059 		cmn_err(CE_WARN, "bscv_get8: addr 0x%x.%02x retried "
1228*1c42de6dSgd78059 		    "read %d times, giving up",
1229*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1230*1c42de6dSgd78059 		bscv_set_fault(ssp);
1231*1c42de6dSgd78059 	} else if (num_failures > 0) {
1232*1c42de6dSgd78059 		bscv_trace(ssp, 'R', "bscv_get8",
1233*1c42de6dSgd78059 		    "addr 0x%x.%02x retried read %d times, succeeded",
1234*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1235*1c42de6dSgd78059 	}
1236*1c42de6dSgd78059 
1237*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_get8",
1238*1c42de6dSgd78059 	    "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
1239*1c42de6dSgd78059 	return (retval);
1240*1c42de6dSgd78059 }
1241*1c42de6dSgd78059 
1242*1c42de6dSgd78059 static uint16_t
1243*1c42de6dSgd78059 bscv_get16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
1244*1c42de6dSgd78059 {
1245*1c42de6dSgd78059 	uint16_t retval;
1246*1c42de6dSgd78059 
1247*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1248*1c42de6dSgd78059 
1249*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan, addr) << 8;
1250*1c42de6dSgd78059 	retval |= bscv_get8(ssp, chan, addr + 1);
1251*1c42de6dSgd78059 
1252*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_get16",
1253*1c42de6dSgd78059 	    "addr 0x%x.%02x => %04x", addr >> 8, addr & 0xff, retval);
1254*1c42de6dSgd78059 	return (retval);
1255*1c42de6dSgd78059 }
1256*1c42de6dSgd78059 
1257*1c42de6dSgd78059 static uint32_t
1258*1c42de6dSgd78059 bscv_get32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
1259*1c42de6dSgd78059 {
1260*1c42de6dSgd78059 	uint32_t retval;
1261*1c42de6dSgd78059 
1262*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1263*1c42de6dSgd78059 
1264*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan, addr) << 24;
1265*1c42de6dSgd78059 	retval |= bscv_get8(ssp, chan, addr + 1) << 16;
1266*1c42de6dSgd78059 	retval |= bscv_get8(ssp, chan, addr + 2) << 8;
1267*1c42de6dSgd78059 	retval |= bscv_get8(ssp, chan, addr + 3);
1268*1c42de6dSgd78059 
1269*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_get32",
1270*1c42de6dSgd78059 	    "addr 0x%x.%02x => %08x", addr >> 8, addr & 0xff, retval);
1271*1c42de6dSgd78059 	return (retval);
1272*1c42de6dSgd78059 }
1273*1c42de6dSgd78059 
1274*1c42de6dSgd78059 static void
1275*1c42de6dSgd78059 bscv_setclear8(bscv_soft_state_t *ssp, int chan,
1276*1c42de6dSgd78059     bscv_addr_t addr, uint8_t set, uint8_t clear)
1277*1c42de6dSgd78059 {
1278*1c42de6dSgd78059 	uint8_t val;
1279*1c42de6dSgd78059 
1280*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1281*1c42de6dSgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
1282*1c42de6dSgd78059 
1283*1c42de6dSgd78059 	val = ssp->lom_regs[addr] | set;
1284*1c42de6dSgd78059 	val &= ~clear;
1285*1c42de6dSgd78059 
1286*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_setclear8",
1287*1c42de6dSgd78059 	    "addr 0x%x.%02x, set %02x, clear %02x => %02x",
1288*1c42de6dSgd78059 	    addr >> 8, addr & 0xff,
1289*1c42de6dSgd78059 	    set, clear, val);
1290*1c42de6dSgd78059 
1291*1c42de6dSgd78059 	bscv_put8(ssp, chan, addr, val);
1292*1c42de6dSgd78059 }
1293*1c42de6dSgd78059 
1294*1c42de6dSgd78059 static void
1295*1c42de6dSgd78059 bscv_setclear8_volatile(bscv_soft_state_t *ssp, int chan,
1296*1c42de6dSgd78059     bscv_addr_t addr, uint8_t set, uint8_t clear)
1297*1c42de6dSgd78059 {
1298*1c42de6dSgd78059 	uint8_t val;
1299*1c42de6dSgd78059 	boolean_t needretry;
1300*1c42de6dSgd78059 	int num_failures;
1301*1c42de6dSgd78059 
1302*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1303*1c42de6dSgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
1304*1c42de6dSgd78059 
1305*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
1306*1c42de6dSgd78059 		return;
1307*1c42de6dSgd78059 	}
1308*1c42de6dSgd78059 
1309*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_setclear8_volatile",
1310*1c42de6dSgd78059 	    "addr 0x%x.%02x => set %02x clear %02x",
1311*1c42de6dSgd78059 	    addr >> 8, addr & 0xff, set, clear);
1312*1c42de6dSgd78059 
1313*1c42de6dSgd78059 	val = bscv_get8_cached(ssp, addr);
1314*1c42de6dSgd78059 	for (num_failures = 0;
1315*1c42de6dSgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
1316*1c42de6dSgd78059 	    num_failures++) {
1317*1c42de6dSgd78059 		val |= set;
1318*1c42de6dSgd78059 		val &= ~clear;
1319*1c42de6dSgd78059 		bscv_put8_once(ssp, chan, addr, val);
1320*1c42de6dSgd78059 		if (ssp->command_error == EBUS_ERROR_STALEDATA) {
1321*1c42de6dSgd78059 			/* Re-read the stale register from the lom */
1322*1c42de6dSgd78059 			val = bscv_get8_once(ssp, chan, addr);
1323*1c42de6dSgd78059 			needretry = 1;
1324*1c42de6dSgd78059 		} else {
1325*1c42de6dSgd78059 			needretry = bscv_should_retry(ssp);
1326*1c42de6dSgd78059 			if (!needretry) {
1327*1c42de6dSgd78059 				break;
1328*1c42de6dSgd78059 			}
1329*1c42de6dSgd78059 		}
1330*1c42de6dSgd78059 	}
1331*1c42de6dSgd78059 	if (ssp->command_error != 0) {
1332*1c42de6dSgd78059 		ssp->had_session_error = B_TRUE;
1333*1c42de6dSgd78059 	}
1334*1c42de6dSgd78059 
1335*1c42de6dSgd78059 	if (needretry) {
1336*1c42de6dSgd78059 		/* Failure */
1337*1c42de6dSgd78059 		cmn_err(CE_WARN, "bscv_setclear8_volatile: addr 0x%x.%02x "
1338*1c42de6dSgd78059 		    "retried write %d times, giving up",
1339*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1340*1c42de6dSgd78059 		if (ssp->command_error != EBUS_ERROR_STALEDATA) {
1341*1c42de6dSgd78059 			bscv_set_fault(ssp);
1342*1c42de6dSgd78059 		}
1343*1c42de6dSgd78059 	} else if (num_failures > 0) {
1344*1c42de6dSgd78059 		bscv_trace(ssp, 'R', "bscv_setclear8_volatile",
1345*1c42de6dSgd78059 		    "addr 0x%x.%02x retried write %d times, succeeded",
1346*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, num_failures);
1347*1c42de6dSgd78059 	}
1348*1c42de6dSgd78059 }
1349*1c42de6dSgd78059 
1350*1c42de6dSgd78059 static void
1351*1c42de6dSgd78059 bscv_rep_rw8(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
1352*1c42de6dSgd78059     bscv_addr_t dev_addr, size_t repcount, uint_t flags,
1353*1c42de6dSgd78059     boolean_t is_write)
1354*1c42de6dSgd78059 {
1355*1c42de6dSgd78059 	size_t inc;
1356*1c42de6dSgd78059 
1357*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1358*1c42de6dSgd78059 
1359*1c42de6dSgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1360*1c42de6dSgd78059 	for (; repcount--; dev_addr += inc) {
1361*1c42de6dSgd78059 		if (flags & DDI_DEV_AUTOINCR) {
1362*1c42de6dSgd78059 			if (is_write) {
1363*1c42de6dSgd78059 				bscv_put8(ssp, chan, dev_addr, *host_addr++);
1364*1c42de6dSgd78059 			} else {
1365*1c42de6dSgd78059 				*host_addr++ = bscv_get8(ssp, chan, dev_addr);
1366*1c42de6dSgd78059 			}
1367*1c42de6dSgd78059 		} else {
1368*1c42de6dSgd78059 			if (is_write) {
1369*1c42de6dSgd78059 				bscv_put8_once(ssp, chan,
1370*1c42de6dSgd78059 					dev_addr, *host_addr++);
1371*1c42de6dSgd78059 			} else {
1372*1c42de6dSgd78059 				*host_addr++ = bscv_get8_once(ssp, chan,
1373*1c42de6dSgd78059 								dev_addr);
1374*1c42de6dSgd78059 			}
1375*1c42de6dSgd78059 			/* We need this because _once routines don't do it */
1376*1c42de6dSgd78059 			if (ssp->command_error != 0) {
1377*1c42de6dSgd78059 				ssp->had_session_error = B_TRUE;
1378*1c42de6dSgd78059 			}
1379*1c42de6dSgd78059 		}
1380*1c42de6dSgd78059 		if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
1381*1c42de6dSgd78059 			/*
1382*1c42de6dSgd78059 			 * No retry here. If we were AUTOINCR then get/put
1383*1c42de6dSgd78059 			 * will have retried. For NO_AUTOINCR we cannot retry
1384*1c42de6dSgd78059 			 * because the data would be corrupted.
1385*1c42de6dSgd78059 			 */
1386*1c42de6dSgd78059 			break;
1387*1c42de6dSgd78059 		}
1388*1c42de6dSgd78059 	}
1389*1c42de6dSgd78059 }
1390*1c42de6dSgd78059 
1391*1c42de6dSgd78059 static uint8_t
1392*1c42de6dSgd78059 bscv_get8_cached(bscv_soft_state_t *ssp, bscv_addr_t addr)
1393*1c42de6dSgd78059 {
1394*1c42de6dSgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
1395*1c42de6dSgd78059 	/* Can be called with or without the lock held */
1396*1c42de6dSgd78059 
1397*1c42de6dSgd78059 	return (ssp->lom_regs[addr]);
1398*1c42de6dSgd78059 }
1399*1c42de6dSgd78059 
1400*1c42de6dSgd78059 static uint8_t
1401*1c42de6dSgd78059 bscv_get8_locked(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, int *res)
1402*1c42de6dSgd78059 {
1403*1c42de6dSgd78059 	uint8_t retval;
1404*1c42de6dSgd78059 
1405*1c42de6dSgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
1406*1c42de6dSgd78059 	bscv_enter(ssp);
1407*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan, addr);
1408*1c42de6dSgd78059 	bscv_locked_result(ssp, res);
1409*1c42de6dSgd78059 	bscv_exit(ssp);
1410*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_get8_locked",
1411*1c42de6dSgd78059 	    "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
1412*1c42de6dSgd78059 	return (retval);
1413*1c42de6dSgd78059 }
1414*1c42de6dSgd78059 
1415*1c42de6dSgd78059 static void
1416*1c42de6dSgd78059 bscv_rep_get8_locked(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
1417*1c42de6dSgd78059     bscv_addr_t dev_addr, size_t repcount, uint_t flags, int *res)
1418*1c42de6dSgd78059 {
1419*1c42de6dSgd78059 	bscv_enter(ssp);
1420*1c42de6dSgd78059 	bscv_rep_rw8(ssp, chan, host_addr, dev_addr, repcount,
1421*1c42de6dSgd78059 	    flags, B_FALSE /* read */);
1422*1c42de6dSgd78059 	bscv_locked_result(ssp, res);
1423*1c42de6dSgd78059 	bscv_exit(ssp);
1424*1c42de6dSgd78059 }
1425*1c42de6dSgd78059 
1426*1c42de6dSgd78059 static boolean_t
1427*1c42de6dSgd78059 bscv_faulty(bscv_soft_state_t *ssp)
1428*1c42de6dSgd78059 {
1429*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1430*1c42de6dSgd78059 	return (ssp->had_fault);
1431*1c42de6dSgd78059 }
1432*1c42de6dSgd78059 
1433*1c42de6dSgd78059 static void
1434*1c42de6dSgd78059 bscv_clear_fault(bscv_soft_state_t *ssp)
1435*1c42de6dSgd78059 {
1436*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1437*1c42de6dSgd78059 	bscv_trace(ssp, 'J', "bscv_clear_fault", "clearing fault flag");
1438*1c42de6dSgd78059 	ssp->had_fault = B_FALSE;
1439*1c42de6dSgd78059 	ssp->had_session_error = B_FALSE;
1440*1c42de6dSgd78059 }
1441*1c42de6dSgd78059 
1442*1c42de6dSgd78059 static void
1443*1c42de6dSgd78059 bscv_set_fault(bscv_soft_state_t *ssp)
1444*1c42de6dSgd78059 {
1445*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1446*1c42de6dSgd78059 	bscv_trace(ssp, 'J', "bscv_set_fault", "setting fault flag");
1447*1c42de6dSgd78059 	ssp->had_fault = B_TRUE;
1448*1c42de6dSgd78059 }
1449*1c42de6dSgd78059 
1450*1c42de6dSgd78059 static boolean_t
1451*1c42de6dSgd78059 bscv_session_error(bscv_soft_state_t *ssp)
1452*1c42de6dSgd78059 {
1453*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1454*1c42de6dSgd78059 	return (ssp->had_session_error);
1455*1c42de6dSgd78059 }
1456*1c42de6dSgd78059 
1457*1c42de6dSgd78059 static int
1458*1c42de6dSgd78059 bscv_retcode(bscv_soft_state_t *ssp)
1459*1c42de6dSgd78059 {
1460*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_retcode",
1461*1c42de6dSgd78059 	    "code 0x%x", ssp->command_error);
1462*1c42de6dSgd78059 	return (ssp->command_error);
1463*1c42de6dSgd78059 }
1464*1c42de6dSgd78059 
1465*1c42de6dSgd78059 static int
1466*1c42de6dSgd78059 bscv_should_retry(bscv_soft_state_t *ssp)
1467*1c42de6dSgd78059 {
1468*1c42de6dSgd78059 	if ((ssp->command_error == EBUS_ERROR_DEVICEFAIL) ||
1469*1c42de6dSgd78059 	    (ssp->command_error >= LOMBUS_ERR_BASE)) {
1470*1c42de6dSgd78059 		/* This command is due to an I/O fault - retry might fix */
1471*1c42de6dSgd78059 		return (1);
1472*1c42de6dSgd78059 	} else {
1473*1c42de6dSgd78059 		/*
1474*1c42de6dSgd78059 		 * The command itself was bad - there is no point in fixing
1475*1c42de6dSgd78059 		 * Note. Whatever happens we should know that if we were
1476*1c42de6dSgd78059 		 * doing EBUS_IDX_SELFTEST0..EBUS_IDX_SELFTEST7 and we
1477*1c42de6dSgd78059 		 * had 0x80 set then this is a test error not a retry
1478*1c42de6dSgd78059 		 * error.
1479*1c42de6dSgd78059 		 */
1480*1c42de6dSgd78059 		return (0);
1481*1c42de6dSgd78059 	}
1482*1c42de6dSgd78059 }
1483*1c42de6dSgd78059 
1484*1c42de6dSgd78059 static void
1485*1c42de6dSgd78059 bscv_locked_result(bscv_soft_state_t *ssp, int *res)
1486*1c42de6dSgd78059 {
1487*1c42de6dSgd78059 	if (bscv_faulty(ssp) || (bscv_retcode(ssp) != 0)) {
1488*1c42de6dSgd78059 		*res = EIO;
1489*1c42de6dSgd78059 	}
1490*1c42de6dSgd78059 }
1491*1c42de6dSgd78059 
1492*1c42de6dSgd78059 static void
1493*1c42de6dSgd78059 bscv_put8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
1494*1c42de6dSgd78059 {
1495*1c42de6dSgd78059 	uint32_t fault;
1496*1c42de6dSgd78059 
1497*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1498*1c42de6dSgd78059 
1499*1c42de6dSgd78059 	ssp->command_error = 0;
1500*1c42de6dSgd78059 
1501*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
1502*1c42de6dSgd78059 		/* Bail out things are not working */
1503*1c42de6dSgd78059 		return;
1504*1c42de6dSgd78059 	} else if (ssp->nchannels == 0) {
1505*1c42de6dSgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
1506*1c42de6dSgd78059 		bscv_trace(ssp, '@', "bscv_put8_once",
1507*1c42de6dSgd78059 		    "nchannels is 0x0 so cannot do IO");
1508*1c42de6dSgd78059 		return;
1509*1c42de6dSgd78059 	}
1510*1c42de6dSgd78059 
1511*1c42de6dSgd78059 	/* Clear any pending fault */
1512*1c42de6dSgd78059 	ddi_put32(ssp->channel[chan].handle,
1513*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
1514*1c42de6dSgd78059 
1515*1c42de6dSgd78059 	/* Do the access and get fault code - may take a long time */
1516*1c42de6dSgd78059 	ddi_put8(ssp->channel[chan].handle,
1517*1c42de6dSgd78059 		&ssp->channel[chan].regs[addr], val);
1518*1c42de6dSgd78059 	fault = ddi_get32(ssp->channel[chan].handle,
1519*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
1520*1c42de6dSgd78059 
1521*1c42de6dSgd78059 	ssp->command_error = fault;
1522*1c42de6dSgd78059 
1523*1c42de6dSgd78059 	if (fault == 0) {
1524*1c42de6dSgd78059 		/* Things were ok - update cache entry */
1525*1c42de6dSgd78059 		if (addr < BSC_ADDR_CACHE_LIMIT) {
1526*1c42de6dSgd78059 			/* Store cacheable entries */
1527*1c42de6dSgd78059 			ssp->lom_regs[addr] = val;
1528*1c42de6dSgd78059 		}
1529*1c42de6dSgd78059 	} else if (fault >= LOMBUS_ERR_BASE) {
1530*1c42de6dSgd78059 		/* lombus problem - do a resync session */
1531*1c42de6dSgd78059 		cmn_err(CE_WARN, "!bscv_put8_once: Had comms fault "
1532*1c42de6dSgd78059 		    "for address 0x%x.%02x - data 0x%x, fault 0x%x",
1533*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, val, fault);
1534*1c42de6dSgd78059 		/* Attempt to resync with the lom */
1535*1c42de6dSgd78059 		bscv_resync_comms(ssp, chan);
1536*1c42de6dSgd78059 		/*
1537*1c42de6dSgd78059 		 * Note: we do not set fault status here. That
1538*1c42de6dSgd78059 		 * is done if our caller decides to give up talking to
1539*1c42de6dSgd78059 		 * the lom. The observant might notice that this means
1540*1c42de6dSgd78059 		 * that if we mend things on the last attempt we still
1541*1c42de6dSgd78059 		 * get the fault set - we just live with that!
1542*1c42de6dSgd78059 		 */
1543*1c42de6dSgd78059 	}
1544*1c42de6dSgd78059 
1545*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_put8_once",
1546*1c42de6dSgd78059 	    "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
1547*1c42de6dSgd78059 }
1548*1c42de6dSgd78059 
1549*1c42de6dSgd78059 static uint8_t
1550*1c42de6dSgd78059 bscv_get8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
1551*1c42de6dSgd78059 {
1552*1c42de6dSgd78059 	uint8_t val;
1553*1c42de6dSgd78059 	uint32_t fault;
1554*1c42de6dSgd78059 
1555*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1556*1c42de6dSgd78059 
1557*1c42de6dSgd78059 	ssp->command_error = 0;
1558*1c42de6dSgd78059 
1559*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
1560*1c42de6dSgd78059 		/* Bail out things are not working */
1561*1c42de6dSgd78059 		return (0xff);
1562*1c42de6dSgd78059 	} else if (ssp->nchannels == 0) {
1563*1c42de6dSgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
1564*1c42de6dSgd78059 		bscv_trace(ssp, '@', "bscv_get8_once",
1565*1c42de6dSgd78059 		    "nchannels is 0x0 so cannot do IO");
1566*1c42de6dSgd78059 		return (0xff);
1567*1c42de6dSgd78059 	}
1568*1c42de6dSgd78059 
1569*1c42de6dSgd78059 	/* Clear any pending fault */
1570*1c42de6dSgd78059 	ddi_put32(ssp->channel[chan].handle,
1571*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
1572*1c42de6dSgd78059 
1573*1c42de6dSgd78059 	/* Do the access and get fault code - may take a long time */
1574*1c42de6dSgd78059 	val = ddi_get8(ssp->channel[chan].handle,
1575*1c42de6dSgd78059 			&ssp->channel[chan].regs[addr]);
1576*1c42de6dSgd78059 	fault = ddi_get32(ssp->channel[chan].handle,
1577*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
1578*1c42de6dSgd78059 	ssp->command_error = fault;
1579*1c42de6dSgd78059 
1580*1c42de6dSgd78059 	if (fault >= LOMBUS_ERR_BASE) {
1581*1c42de6dSgd78059 		/* lombus problem - do a resync session */
1582*1c42de6dSgd78059 		cmn_err(CE_WARN, "!bscv_get8_once: Had comms fault "
1583*1c42de6dSgd78059 		    "for address 0x%x.%02x - data 0x%x, fault 0x%x",
1584*1c42de6dSgd78059 		    addr >> 8, addr & 0xff, val, fault);
1585*1c42de6dSgd78059 		/* Attempt to resync with the lom */
1586*1c42de6dSgd78059 		bscv_resync_comms(ssp, chan);
1587*1c42de6dSgd78059 		/*
1588*1c42de6dSgd78059 		 * Note: we do not set fault status here. That
1589*1c42de6dSgd78059 		 * is done if our caller decides to give up talking to
1590*1c42de6dSgd78059 		 * the lom. The observant might notice that this means
1591*1c42de6dSgd78059 		 * that if we mend things on the last attempt we still
1592*1c42de6dSgd78059 		 * get the fault set - we just live with that!
1593*1c42de6dSgd78059 		 */
1594*1c42de6dSgd78059 	}
1595*1c42de6dSgd78059 	/*
1596*1c42de6dSgd78059 	 * FIXME - should report error if you get
1597*1c42de6dSgd78059 	 * EBUS_ERROR_DEVICEFAIL reported from the BSC. That gets
1598*1c42de6dSgd78059 	 * logged as a failure in bscv_should_retry and may contribute
1599*1c42de6dSgd78059 	 * to a permanent failure. Reference issues seen by Mitac.
1600*1c42de6dSgd78059 	 */
1601*1c42de6dSgd78059 
1602*1c42de6dSgd78059 	if (!bscv_faulty(ssp)) {
1603*1c42de6dSgd78059 		if (addr < BSC_ADDR_CACHE_LIMIT) {
1604*1c42de6dSgd78059 			/* Store cacheable entries */
1605*1c42de6dSgd78059 			ssp->lom_regs[addr] = val;
1606*1c42de6dSgd78059 		}
1607*1c42de6dSgd78059 	}
1608*1c42de6dSgd78059 
1609*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_get8_once",
1610*1c42de6dSgd78059 	    "addr 0x%x.%02x => 0x%02x", addr >> 8, addr & 0xff, val);
1611*1c42de6dSgd78059 	return (val);
1612*1c42de6dSgd78059 }
1613*1c42de6dSgd78059 
1614*1c42de6dSgd78059 static uint32_t
1615*1c42de6dSgd78059 bscv_probe(bscv_soft_state_t *ssp, int chan, uint32_t *fault)
1616*1c42de6dSgd78059 {
1617*1c42de6dSgd78059 	uint32_t async_reg;
1618*1c42de6dSgd78059 
1619*1c42de6dSgd78059 	if (ssp->nchannels == 0) {
1620*1c42de6dSgd78059 		/*
1621*1c42de6dSgd78059 		 * Failed to map handles, so cannot do any IO.  Set the
1622*1c42de6dSgd78059 		 * fault indicator and return a dummy value.
1623*1c42de6dSgd78059 		 */
1624*1c42de6dSgd78059 		bscv_trace(ssp, '@', "bscv_probe",
1625*1c42de6dSgd78059 		    "nchannels is 0x0 so cannot do any IO");
1626*1c42de6dSgd78059 		*fault = LOMBUS_ERR_REG_NUM;
1627*1c42de6dSgd78059 		return ((~(int8_t)0));
1628*1c42de6dSgd78059 	}
1629*1c42de6dSgd78059 
1630*1c42de6dSgd78059 	/* Clear faults */
1631*1c42de6dSgd78059 	ddi_put32(ssp->channel[chan].handle,
1632*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
1633*1c42de6dSgd78059 	/* Probe and Check faults */
1634*1c42de6dSgd78059 	*fault = ddi_get32(ssp->channel[chan].handle,
1635*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_PROBE_REG));
1636*1c42de6dSgd78059 	/* Read status */
1637*1c42de6dSgd78059 	async_reg = ddi_get32(ssp->channel[chan].handle,
1638*1c42de6dSgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_ASYNC_REG));
1639*1c42de6dSgd78059 
1640*1c42de6dSgd78059 	bscv_trace(ssp, '@', "bscv_probe",
1641*1c42de6dSgd78059 	    "async status 0x%x, fault 0x%x", async_reg, *fault);
1642*1c42de6dSgd78059 	return (async_reg);
1643*1c42de6dSgd78059 }
1644*1c42de6dSgd78059 
1645*1c42de6dSgd78059 static void
1646*1c42de6dSgd78059 bscv_resync_comms(bscv_soft_state_t *ssp, int chan)
1647*1c42de6dSgd78059 {
1648*1c42de6dSgd78059 	int try;
1649*1c42de6dSgd78059 	uint32_t command_error = ssp->command_error;
1650*1c42de6dSgd78059 	uint32_t fault = 0;
1651*1c42de6dSgd78059 
1652*1c42de6dSgd78059 	if (ssp->nchannels == 0) {
1653*1c42de6dSgd78059 		/*
1654*1c42de6dSgd78059 		 * Didn't manage to map handles so ddi_{get,put}* broken.
1655*1c42de6dSgd78059 		 * Therefore, there is no way to resync comms.
1656*1c42de6dSgd78059 		 */
1657*1c42de6dSgd78059 		bscv_trace(ssp, '@', "bscv_resync_comms",
1658*1c42de6dSgd78059 		    "nchannels is 0x0 so not possible to resync comms");
1659*1c42de6dSgd78059 		return;
1660*1c42de6dSgd78059 	}
1661*1c42de6dSgd78059 	if (command_error >= LOMBUS_ERR_BASE &&
1662*1c42de6dSgd78059 			command_error != LOMBUS_ERR_REG_NUM &&
1663*1c42de6dSgd78059 			command_error != LOMBUS_ERR_REG_SIZE &&
1664*1c42de6dSgd78059 			command_error != LOMBUS_ERR_TIMEOUT) {
1665*1c42de6dSgd78059 		/* Resync here to make sure that the lom is talking */
1666*1c42de6dSgd78059 		cmn_err(CE_WARN, "!bscv_resync_comms: "
1667*1c42de6dSgd78059 		    "Attempting comms resync after comms fault 0x%x",
1668*1c42de6dSgd78059 		    command_error);
1669*1c42de6dSgd78059 		for (try = 1; try <= 8; try++) {
1670*1c42de6dSgd78059 			/* Probe */
1671*1c42de6dSgd78059 			fault = ddi_get32(ssp->channel[chan].handle,
1672*1c42de6dSgd78059 			    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0,
1673*1c42de6dSgd78059 				LOMBUS_PROBE_REG));
1674*1c42de6dSgd78059 
1675*1c42de6dSgd78059 			if (fault == 0) {
1676*1c42de6dSgd78059 				break;
1677*1c42de6dSgd78059 			} else {
1678*1c42de6dSgd78059 				cmn_err(CE_WARN, "!bscv_resync_comms: "
1679*1c42de6dSgd78059 				    "comms resync (probing) - try 0x%x "
1680*1c42de6dSgd78059 				    "had fault 0x%x", try, fault);
1681*1c42de6dSgd78059 			}
1682*1c42de6dSgd78059 		}
1683*1c42de6dSgd78059 		if (fault != 0) {
1684*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_resync_comms: "
1685*1c42de6dSgd78059 			    "Failed to resync comms - giving up");
1686*1c42de6dSgd78059 			ssp->bad_resync++;
1687*1c42de6dSgd78059 		} else {
1688*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_resync_comms: "
1689*1c42de6dSgd78059 			    "resync comms after 0x%x tries", try);
1690*1c42de6dSgd78059 			ssp->bad_resync = 0;
1691*1c42de6dSgd78059 		}
1692*1c42de6dSgd78059 	}
1693*1c42de6dSgd78059 
1694*1c42de6dSgd78059 }
1695*1c42de6dSgd78059 
1696*1c42de6dSgd78059 
1697*1c42de6dSgd78059 /*
1698*1c42de6dSgd78059  * LOMLite configuration/event eeprom access routines
1699*1c42de6dSgd78059  *
1700*1c42de6dSgd78059  * bscv_window_setup() - Read/Sanity check the eeprom parameters.
1701*1c42de6dSgd78059  *		This must be called prior to calling bscv_eerw().
1702*1c42de6dSgd78059  * bscv_eerw() - Read/write data from/to the eeprom.
1703*1c42de6dSgd78059  */
1704*1c42de6dSgd78059 
1705*1c42de6dSgd78059 /*
1706*1c42de6dSgd78059  * function	- bscv_window_setup
1707*1c42de6dSgd78059  * description	- this routine reads the eeprom parameters and sanity
1708*1c42de6dSgd78059  *		  checks them to ensure that the lom is talking sense.
1709*1c42de6dSgd78059  * inputs	- soft state ptr
1710*1c42de6dSgd78059  * outputs	- B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
1711*1c42de6dSgd78059  */
1712*1c42de6dSgd78059 static boolean_t
1713*1c42de6dSgd78059 bscv_window_setup(bscv_soft_state_t *ssp)
1714*1c42de6dSgd78059 {
1715*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
1716*1c42de6dSgd78059 
1717*1c42de6dSgd78059 	if (ssp->eeinfo_valid) {
1718*1c42de6dSgd78059 		/* Already have good cached values */
1719*1c42de6dSgd78059 		return (ssp->eeinfo_valid);
1720*1c42de6dSgd78059 	}
1721*1c42de6dSgd78059 	ssp->eeprom_size =
1722*1c42de6dSgd78059 		bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) * 1024;
1723*1c42de6dSgd78059 	ssp->eventlog_start = bscv_get16(ssp, chan_general,
1724*1c42de6dSgd78059 				EBUS_IDX_LOG_START_HI);
1725*1c42de6dSgd78059 
1726*1c42de6dSgd78059 	/*
1727*1c42de6dSgd78059 	 * The log does not run to the end of the EEPROM because it is a
1728*1c42de6dSgd78059 	 * logical partition.  The last 8K partition is reserved for FRUID
1729*1c42de6dSgd78059 	 * usage.
1730*1c42de6dSgd78059 	 */
1731*1c42de6dSgd78059 	ssp->eventlog_size = EBUS_LOG_END - ssp->eventlog_start;
1732*1c42de6dSgd78059 
1733*1c42de6dSgd78059 	bscv_trace(ssp, 'I', "bscv_window_setup", "eeprom size 0x%x log_start"
1734*1c42de6dSgd78059 	    " 0x%x log_size 0x%x", ssp->eeprom_size, ssp->eventlog_start,
1735*1c42de6dSgd78059 	    ssp->eventlog_size);
1736*1c42de6dSgd78059 
1737*1c42de6dSgd78059 	if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
1738*1c42de6dSgd78059 		ssp->eeinfo_valid = B_FALSE;
1739*1c42de6dSgd78059 	} else if ((ssp->eeprom_size == 0) ||
1740*1c42de6dSgd78059 	    (ssp->eventlog_start >= ssp->eeprom_size)) {
1741*1c42de6dSgd78059 		/* Sanity check values */
1742*1c42de6dSgd78059 		cmn_err(CE_WARN,
1743*1c42de6dSgd78059 		    "!bscv_window_setup: read invalid eeprom parameters");
1744*1c42de6dSgd78059 		ssp->eeinfo_valid = B_FALSE;
1745*1c42de6dSgd78059 	} else {
1746*1c42de6dSgd78059 		ssp->eeinfo_valid = B_TRUE;
1747*1c42de6dSgd78059 	}
1748*1c42de6dSgd78059 
1749*1c42de6dSgd78059 	bscv_trace(ssp, 'I', "bscv_window_setup", "returning eeinfo_valid %s",
1750*1c42de6dSgd78059 	    ssp->eeinfo_valid ? "true" : "false");
1751*1c42de6dSgd78059 	return (ssp->eeinfo_valid);
1752*1c42de6dSgd78059 }
1753*1c42de6dSgd78059 
1754*1c42de6dSgd78059 /*
1755*1c42de6dSgd78059  * function	- bscv_eerw
1756*1c42de6dSgd78059  * description	- this routine reads/write data from/to the eeprom.
1757*1c42de6dSgd78059  *		  It takes care of setting the window on the eeprom correctly.
1758*1c42de6dSgd78059  * inputs	- soft state ptr, eeprom offset, data buffer, size, read/write
1759*1c42de6dSgd78059  * outputs	- B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
1760*1c42de6dSgd78059  */
1761*1c42de6dSgd78059 static int
1762*1c42de6dSgd78059 bscv_eerw(bscv_soft_state_t *ssp, uint32_t eeoffset, uint8_t *buf,
1763*1c42de6dSgd78059     unsigned size, boolean_t is_write)
1764*1c42de6dSgd78059 {
1765*1c42de6dSgd78059 	uint32_t blk_addr = eeoffset;
1766*1c42de6dSgd78059 	unsigned remaining = size;
1767*1c42de6dSgd78059 	uint8_t page_idx;
1768*1c42de6dSgd78059 	uint8_t this_page;
1769*1c42de6dSgd78059 	uint8_t blk_size;
1770*1c42de6dSgd78059 	int res = 0;
1771*1c42de6dSgd78059 
1772*1c42de6dSgd78059 	while (remaining > 0) {
1773*1c42de6dSgd78059 		page_idx = blk_addr & 0xff;
1774*1c42de6dSgd78059 		if ((page_idx + remaining) > 0x100) {
1775*1c42de6dSgd78059 			blk_size = 0x100 - page_idx;
1776*1c42de6dSgd78059 		} else {
1777*1c42de6dSgd78059 			blk_size = remaining;
1778*1c42de6dSgd78059 		}
1779*1c42de6dSgd78059 
1780*1c42de6dSgd78059 		/* Select correct eeprom page */
1781*1c42de6dSgd78059 		this_page = blk_addr >> 8;
1782*1c42de6dSgd78059 		bscv_put8(ssp, chan_eeprom, EBUS_IDX_EEPROM_PAGESEL, this_page);
1783*1c42de6dSgd78059 
1784*1c42de6dSgd78059 		bscv_trace(ssp, 'M', "lom_eerw",
1785*1c42de6dSgd78059 		    "%s data @0x%x.%02x, size 0x%x, 0x%x bytes remaining",
1786*1c42de6dSgd78059 		    is_write ? "writing" : "reading",
1787*1c42de6dSgd78059 		    this_page, page_idx, blk_size, remaining - blk_size);
1788*1c42de6dSgd78059 
1789*1c42de6dSgd78059 		bscv_rep_rw8(ssp, chan_eeprom,
1790*1c42de6dSgd78059 		    buf, BSCVA(EBUS_CMD_SPACE_EEPROM, page_idx),
1791*1c42de6dSgd78059 		    blk_size, DDI_DEV_AUTOINCR, is_write);
1792*1c42de6dSgd78059 
1793*1c42de6dSgd78059 		if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
1794*1c42de6dSgd78059 			res = EIO;
1795*1c42de6dSgd78059 			break;
1796*1c42de6dSgd78059 		}
1797*1c42de6dSgd78059 
1798*1c42de6dSgd78059 		remaining -= blk_size;
1799*1c42de6dSgd78059 		blk_addr += blk_size;
1800*1c42de6dSgd78059 		buf += blk_size;
1801*1c42de6dSgd78059 	}
1802*1c42de6dSgd78059 
1803*1c42de6dSgd78059 	return (res);
1804*1c42de6dSgd78059 }
1805*1c42de6dSgd78059 
1806*1c42de6dSgd78059 static boolean_t
1807*1c42de6dSgd78059 bscv_is_null_event(bscv_soft_state_t *ssp, lom_event_t *e)
1808*1c42de6dSgd78059 {
1809*1c42de6dSgd78059 	ASSERT(e != NULL);
1810*1c42de6dSgd78059 
1811*1c42de6dSgd78059 	if (EVENT_DECODE_SUBSYS(e->ev_subsys) == EVENT_SUBSYS_NONE &&
1812*1c42de6dSgd78059 	    e->ev_event == EVENT_NONE) {
1813*1c42de6dSgd78059 		/*
1814*1c42de6dSgd78059 		 * This marks a NULL event.
1815*1c42de6dSgd78059 		 */
1816*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_is_null_event",
1817*1c42de6dSgd78059 		    "EVENT_SUBSYS_NONE/EVENT_NONE null event");
1818*1c42de6dSgd78059 		return (B_TRUE);
1819*1c42de6dSgd78059 	} else if (e->ev_subsys == 0xff && e->ev_event == 0xff) {
1820*1c42de6dSgd78059 		/*
1821*1c42de6dSgd78059 		 * Under some circumstances, we've seen all 1s to represent
1822*1c42de6dSgd78059 		 * a manually cleared event log at the BSC prompt.  Only
1823*1c42de6dSgd78059 		 * a test/diagnosis environment is likely to show this.
1824*1c42de6dSgd78059 		 */
1825*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_is_null_event", "0xffff null event");
1826*1c42de6dSgd78059 		return (B_TRUE);
1827*1c42de6dSgd78059 	} else {
1828*1c42de6dSgd78059 		/*
1829*1c42de6dSgd78059 		 * Not a NULL event.
1830*1c42de6dSgd78059 		 */
1831*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_is_null_event", "returning False");
1832*1c42de6dSgd78059 		return (B_FALSE);
1833*1c42de6dSgd78059 	}
1834*1c42de6dSgd78059 }
1835*1c42de6dSgd78059 
1836*1c42de6dSgd78059 /*
1837*1c42de6dSgd78059  * *********************************************************************
1838*1c42de6dSgd78059  * IOCTL Processing
1839*1c42de6dSgd78059  * *********************************************************************
1840*1c42de6dSgd78059  */
1841*1c42de6dSgd78059 
1842*1c42de6dSgd78059 /*
1843*1c42de6dSgd78059  * function	- bscv_ioctl
1844*1c42de6dSgd78059  * description	- routine that acts as a high level manager for ioctls. It
1845*1c42de6dSgd78059  *		  calls the appropriate handler for ioctls on the alarm:mon and
1846*1c42de6dSgd78059  *		  alarm:ctl minor nodes respectively
1847*1c42de6dSgd78059  *
1848*1c42de6dSgd78059  *		  Unsupported ioctls (now deprecated)
1849*1c42de6dSgd78059  *			LOMIOCALCTL
1850*1c42de6dSgd78059  *			LOMIOCALSTATE
1851*1c42de6dSgd78059  *			LOMIOCCLEARLOG
1852*1c42de6dSgd78059  *			LOMIOCCTL
1853*1c42de6dSgd78059  *			LOMIOCCTL2
1854*1c42de6dSgd78059  *			LOMIOCDAEMON
1855*1c42de6dSgd78059  *			LOMIOCDMON
1856*1c42de6dSgd78059  *			LOMIOCDOGCTL, TSIOCDOGCTL
1857*1c42de6dSgd78059  *			LOMIOCDOGPAT, TSIOCDOGPAT
1858*1c42de6dSgd78059  *			LOMIOCDOGTIME, TSIOCDOGTIME
1859*1c42de6dSgd78059  *			LOMIOCEVENTLOG
1860*1c42de6dSgd78059  *			LOMIOCEVNT
1861*1c42de6dSgd78059  *			LOMIOCGETMASK
1862*1c42de6dSgd78059  *			LOMIOCMPROG
1863*1c42de6dSgd78059  *			LOMIOCNBMON, TSIOCNBMON
1864*1c42de6dSgd78059  *			LOMIOCSLEEP
1865*1c42de6dSgd78059  *			LOMIOCUNLOCK, TSIOCUNLOCK
1866*1c42de6dSgd78059  *			LOMIOCWTMON, TSIOCWTMON
1867*1c42de6dSgd78059  *
1868*1c42de6dSgd78059  *		  Supported ioctls
1869*1c42de6dSgd78059  *			LOMIOCDOGSTATE, TSIOCDOGSTATE
1870*1c42de6dSgd78059  *			LOMIOCPROG
1871*1c42de6dSgd78059  *			LOMIOCPSUSTATE
1872*1c42de6dSgd78059  *			LOMIOCFANSTATE
1873*1c42de6dSgd78059  *			LOMIOCFLEDSTATE
1874*1c42de6dSgd78059  *			LOMIOCINFO
1875*1c42de6dSgd78059  *			LOMIOCMREAD
1876*1c42de6dSgd78059  *			LOMIOCVOLTS
1877*1c42de6dSgd78059  *			LOMIOCSTATS
1878*1c42de6dSgd78059  *			LOMIOCTEMP
1879*1c42de6dSgd78059  *			LOMIOCCONS
1880*1c42de6dSgd78059  *			LOMIOCEVENTLOG2
1881*1c42de6dSgd78059  *			LOMIOCINFO2
1882*1c42de6dSgd78059  *			LOMIOCTEST
1883*1c42de6dSgd78059  *			LOMIOCMPROG2
1884*1c42de6dSgd78059  *			LOMIOCMREAD2
1885*1c42de6dSgd78059  *
1886*1c42de6dSgd78059  * inputs	- device number, command, user space arg, filemode, user
1887*1c42de6dSgd78059  *		  credentials, return value
1888*1c42de6dSgd78059  * outputs	- the return value propagated back by the lower level routines.
1889*1c42de6dSgd78059  */
1890*1c42de6dSgd78059 
1891*1c42de6dSgd78059 /*ARGSUSED*/
1892*1c42de6dSgd78059 static int
1893*1c42de6dSgd78059 bscv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
1894*1c42de6dSgd78059 {
1895*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
1896*1c42de6dSgd78059 	int instance;
1897*1c42de6dSgd78059 	int res = 0;
1898*1c42de6dSgd78059 
1899*1c42de6dSgd78059 	instance = DEVICETOINSTANCE(dev);
1900*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
1901*1c42de6dSgd78059 	if (ssp == NULL) {
1902*1c42de6dSgd78059 		return (ENXIO);
1903*1c42de6dSgd78059 	}
1904*1c42de6dSgd78059 
1905*1c42de6dSgd78059 	/*
1906*1c42de6dSgd78059 	 * The Combined Switch and Service Processor takes care of configuration
1907*1c42de6dSgd78059 	 * and control.  The CSSP tells the BSC chip about it; therefore the
1908*1c42de6dSgd78059 	 * bscv driver doesn't send such configuration and control to the BSC.
1909*1c42de6dSgd78059 	 * Additionally Watchdog configuration is no longer done from userland
1910*1c42de6dSgd78059 	 * lom.
1911*1c42de6dSgd78059 	 */
1912*1c42de6dSgd78059 	switch (cmd) {
1913*1c42de6dSgd78059 	case LOMIOCALCTL:
1914*1c42de6dSgd78059 	case LOMIOCALSTATE:
1915*1c42de6dSgd78059 	case LOMIOCCLEARLOG:
1916*1c42de6dSgd78059 	case LOMIOCCTL:
1917*1c42de6dSgd78059 	case LOMIOCCTL2:
1918*1c42de6dSgd78059 	case LOMIOCDAEMON:
1919*1c42de6dSgd78059 	case LOMIOCDMON:
1920*1c42de6dSgd78059 	case LOMIOCDOGCTL:
1921*1c42de6dSgd78059 	case LOMIOCDOGPAT:
1922*1c42de6dSgd78059 	case LOMIOCDOGTIME:
1923*1c42de6dSgd78059 	case LOMIOCEVENTLOG:
1924*1c42de6dSgd78059 	case LOMIOCEVNT:
1925*1c42de6dSgd78059 	case LOMIOCGETMASK:
1926*1c42de6dSgd78059 	case LOMIOCMPROG:
1927*1c42de6dSgd78059 	case LOMIOCNBMON:
1928*1c42de6dSgd78059 	case LOMIOCSLEEP:
1929*1c42de6dSgd78059 	case LOMIOCUNLOCK:
1930*1c42de6dSgd78059 	case LOMIOCWTMON:
1931*1c42de6dSgd78059 		return (ENOTSUP);
1932*1c42de6dSgd78059 	}
1933*1c42de6dSgd78059 
1934*1c42de6dSgd78059 	/*
1935*1c42de6dSgd78059 	 * set the default result.
1936*1c42de6dSgd78059 	 */
1937*1c42de6dSgd78059 
1938*1c42de6dSgd78059 	*rvalp = 0;
1939*1c42de6dSgd78059 
1940*1c42de6dSgd78059 	if (ssp->cssp_prog) {
1941*1c42de6dSgd78059 		return (ENXIO);
1942*1c42de6dSgd78059 	} else if ((ssp->prog_mode_only || ssp->programming) &&
1943*1c42de6dSgd78059 	    cmd != LOMIOCPROG) {
1944*1c42de6dSgd78059 		return (ENXIO);
1945*1c42de6dSgd78059 	}
1946*1c42de6dSgd78059 
1947*1c42de6dSgd78059 	/*
1948*1c42de6dSgd78059 	 * Check that the caller has appropriate access permissions
1949*1c42de6dSgd78059 	 * (FWRITE set in mode) for those ioctls which change lom
1950*1c42de6dSgd78059 	 * state
1951*1c42de6dSgd78059 	 */
1952*1c42de6dSgd78059 	if (!(mode & FWRITE)) {
1953*1c42de6dSgd78059 		switch (cmd) {
1954*1c42de6dSgd78059 		case LOMIOCMPROG2:
1955*1c42de6dSgd78059 		case LOMIOCMREAD2:
1956*1c42de6dSgd78059 		case LOMIOCPROG:
1957*1c42de6dSgd78059 		case LOMIOCTEST:
1958*1c42de6dSgd78059 			return (EACCES);
1959*1c42de6dSgd78059 			/* NOTREACHED */
1960*1c42de6dSgd78059 		default:
1961*1c42de6dSgd78059 			/* Does not require write access */
1962*1c42de6dSgd78059 			break;
1963*1c42de6dSgd78059 		}
1964*1c42de6dSgd78059 	}
1965*1c42de6dSgd78059 
1966*1c42de6dSgd78059 	switch (cmd) {
1967*1c42de6dSgd78059 
1968*1c42de6dSgd78059 	case LOMIOCDOGSTATE:
1969*1c42de6dSgd78059 		res = bscv_ioc_dogstate(ssp, arg, mode);
1970*1c42de6dSgd78059 		break;
1971*1c42de6dSgd78059 
1972*1c42de6dSgd78059 	case LOMIOCPROG:
1973*1c42de6dSgd78059 		res = bscv_prog(ssp, arg, mode);
1974*1c42de6dSgd78059 		break;
1975*1c42de6dSgd78059 
1976*1c42de6dSgd78059 	case LOMIOCPSUSTATE:
1977*1c42de6dSgd78059 		res = bscv_ioc_psustate(ssp, arg, mode);
1978*1c42de6dSgd78059 		break;
1979*1c42de6dSgd78059 
1980*1c42de6dSgd78059 	case LOMIOCFANSTATE:
1981*1c42de6dSgd78059 		res = bscv_ioc_fanstate(ssp, arg, mode);
1982*1c42de6dSgd78059 		break;
1983*1c42de6dSgd78059 
1984*1c42de6dSgd78059 	case LOMIOCFLEDSTATE:
1985*1c42de6dSgd78059 		res = bscv_ioc_fledstate(ssp, arg, mode);
1986*1c42de6dSgd78059 		break;
1987*1c42de6dSgd78059 
1988*1c42de6dSgd78059 	case LOMIOCLEDSTATE:
1989*1c42de6dSgd78059 		res = bscv_ioc_ledstate(ssp, arg, mode);
1990*1c42de6dSgd78059 		break;
1991*1c42de6dSgd78059 
1992*1c42de6dSgd78059 	case LOMIOCINFO:
1993*1c42de6dSgd78059 		res = bscv_ioc_info(ssp, arg, mode);
1994*1c42de6dSgd78059 		break;
1995*1c42de6dSgd78059 
1996*1c42de6dSgd78059 	case LOMIOCMREAD:
1997*1c42de6dSgd78059 		res = bscv_ioc_mread(ssp, arg, mode);
1998*1c42de6dSgd78059 		break;
1999*1c42de6dSgd78059 
2000*1c42de6dSgd78059 	case LOMIOCVOLTS:
2001*1c42de6dSgd78059 		res = bscv_ioc_volts(ssp, arg, mode);
2002*1c42de6dSgd78059 		break;
2003*1c42de6dSgd78059 
2004*1c42de6dSgd78059 	case LOMIOCSTATS:
2005*1c42de6dSgd78059 		res = bscv_ioc_stats(ssp, arg, mode);
2006*1c42de6dSgd78059 		break;
2007*1c42de6dSgd78059 
2008*1c42de6dSgd78059 	case LOMIOCTEMP:
2009*1c42de6dSgd78059 		res = bscv_ioc_temp(ssp, arg, mode);
2010*1c42de6dSgd78059 		break;
2011*1c42de6dSgd78059 
2012*1c42de6dSgd78059 	case LOMIOCCONS:
2013*1c42de6dSgd78059 		res = bscv_ioc_cons(ssp, arg, mode);
2014*1c42de6dSgd78059 		break;
2015*1c42de6dSgd78059 
2016*1c42de6dSgd78059 	case LOMIOCEVENTLOG2:
2017*1c42de6dSgd78059 		res = bscv_ioc_eventlog2(ssp, arg, mode);
2018*1c42de6dSgd78059 		break;
2019*1c42de6dSgd78059 
2020*1c42de6dSgd78059 	case LOMIOCINFO2:
2021*1c42de6dSgd78059 		res = bscv_ioc_info2(ssp, arg, mode);
2022*1c42de6dSgd78059 		break;
2023*1c42de6dSgd78059 
2024*1c42de6dSgd78059 	case LOMIOCTEST:
2025*1c42de6dSgd78059 		res = bscv_ioc_test(ssp, arg, mode);
2026*1c42de6dSgd78059 		break;
2027*1c42de6dSgd78059 
2028*1c42de6dSgd78059 	case LOMIOCMPROG2:
2029*1c42de6dSgd78059 		res = bscv_ioc_mprog2(ssp, arg, mode);
2030*1c42de6dSgd78059 		break;
2031*1c42de6dSgd78059 
2032*1c42de6dSgd78059 	case LOMIOCMREAD2:
2033*1c42de6dSgd78059 		res = bscv_ioc_mread2(ssp, arg, mode);
2034*1c42de6dSgd78059 		break;
2035*1c42de6dSgd78059 
2036*1c42de6dSgd78059 	default:
2037*1c42de6dSgd78059 		bscv_trace(ssp, 'I', "bscv_ioctl", "Invalid IOCTL 0x%x", cmd);
2038*1c42de6dSgd78059 		res = EINVAL;
2039*1c42de6dSgd78059 	}
2040*1c42de6dSgd78059 	return (res);
2041*1c42de6dSgd78059 }
2042*1c42de6dSgd78059 
2043*1c42de6dSgd78059 /*
2044*1c42de6dSgd78059  * LOMIOCDOGSTATE
2045*1c42de6dSgd78059  * TSIOCDOGSTATE - indicate whether the alarm watchdog and reset
2046*1c42de6dSgd78059  * circuitry is enabled or not.
2047*1c42de6dSgd78059  */
2048*1c42de6dSgd78059 static int
2049*1c42de6dSgd78059 bscv_ioc_dogstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2050*1c42de6dSgd78059 {
2051*1c42de6dSgd78059 	lom_dogstate_t dogstate;
2052*1c42de6dSgd78059 	uint8_t dogval;
2053*1c42de6dSgd78059 	int res = 0;
2054*1c42de6dSgd78059 
2055*1c42de6dSgd78059 	dogval = bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res);
2056*1c42de6dSgd78059 	dogstate.dog_enable = (dogval & EBUS_WDOG_ENABLE) ? 1 : 0;
2057*1c42de6dSgd78059 	dogstate.reset_enable = (dogval & EBUS_WDOG_RST) ? 1 : 0;
2058*1c42de6dSgd78059 	dogstate.dog_timeout = bscv_get8_locked(ssp, chan_general,
2059*1c42de6dSgd78059 		EBUS_IDX_WDOG_TIME, &res);
2060*1c42de6dSgd78059 
2061*1c42de6dSgd78059 	if ((res == 0) &&
2062*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&dogstate,
2063*1c42de6dSgd78059 		(caddr_t)arg, sizeof (dogstate), mode) < 0)) {
2064*1c42de6dSgd78059 		res = EFAULT;
2065*1c42de6dSgd78059 	}
2066*1c42de6dSgd78059 	return (res);
2067*1c42de6dSgd78059 }
2068*1c42de6dSgd78059 
2069*1c42de6dSgd78059 /*
2070*1c42de6dSgd78059  * LOMIOCPSUSTATE - returns full information for 4 PSUs. All this
2071*1c42de6dSgd78059  * information is available from two bytes of LOMlite RAM, but if
2072*1c42de6dSgd78059  * on the first read it is noticed that two or more of the PSUs are
2073*1c42de6dSgd78059  * not present only 1 byte will be read subsequently.
2074*1c42de6dSgd78059  */
2075*1c42de6dSgd78059 static int
2076*1c42de6dSgd78059 bscv_ioc_psustate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2077*1c42de6dSgd78059 {
2078*1c42de6dSgd78059 	lom_psudata_t psudata;
2079*1c42de6dSgd78059 	uint8_t psustat;
2080*1c42de6dSgd78059 	int i;
2081*1c42de6dSgd78059 	int res = 0;
2082*1c42de6dSgd78059 
2083*1c42de6dSgd78059 	for (i = 0; i < MAX_PSUS; i++) {
2084*1c42de6dSgd78059 		psustat = bscv_get8_locked(ssp, chan_general,
2085*1c42de6dSgd78059 			    EBUS_IDX_PSU1_STAT + i, &res);
2086*1c42de6dSgd78059 		psudata.fitted[i] = psustat & EBUS_PSU_PRESENT;
2087*1c42de6dSgd78059 		psudata.output[i] = psustat & EBUS_PSU_OUTPUT;
2088*1c42de6dSgd78059 		psudata.supplyb[i] = psustat & EBUS_PSU_INPUTB;
2089*1c42de6dSgd78059 		psudata.supplya[i] = psustat & EBUS_PSU_INPUTA;
2090*1c42de6dSgd78059 		psudata.standby[i] = psustat & EBUS_PSU_STANDBY;
2091*1c42de6dSgd78059 	}
2092*1c42de6dSgd78059 
2093*1c42de6dSgd78059 	if (ddi_copyout((caddr_t)&psudata, (caddr_t)arg, sizeof (psudata),
2094*1c42de6dSgd78059 	    mode) < 0) {
2095*1c42de6dSgd78059 		res = EFAULT;
2096*1c42de6dSgd78059 	}
2097*1c42de6dSgd78059 	return (res);
2098*1c42de6dSgd78059 }
2099*1c42de6dSgd78059 
2100*1c42de6dSgd78059 /*
2101*1c42de6dSgd78059  * LOMIOCFANSTATE - returns full information including speed for 4
2102*1c42de6dSgd78059  * fans and the minimum and maximum operating speeds for each fan as
2103*1c42de6dSgd78059  * stored in the READ ONLY EEPROM data. As this EEPROM data is set
2104*1c42de6dSgd78059  * at manufacture time, this data should only be read by the driver
2105*1c42de6dSgd78059  * once and stored locally.
2106*1c42de6dSgd78059  */
2107*1c42de6dSgd78059 static int
2108*1c42de6dSgd78059 bscv_ioc_fanstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2109*1c42de6dSgd78059 {
2110*1c42de6dSgd78059 	lom_fandata_t fandata;
2111*1c42de6dSgd78059 	int numfans;
2112*1c42de6dSgd78059 	int i;
2113*1c42de6dSgd78059 	int res = 0;
2114*1c42de6dSgd78059 
2115*1c42de6dSgd78059 	bzero(&fandata, sizeof (lom_fandata_t));
2116*1c42de6dSgd78059 	numfans = EBUS_CONFIG_NFAN_DEC(bscv_get8_locked(ssp,
2117*1c42de6dSgd78059 	    chan_general, EBUS_IDX_CONFIG, &res));
2118*1c42de6dSgd78059 	for (i = 0; (i < numfans) && (res == 0); i++) {
2119*1c42de6dSgd78059 		if (ssp->fanspeed[i] != LOM_FAN_NOT_PRESENT) {
2120*1c42de6dSgd78059 			fandata.fitted[i] = 1;
2121*1c42de6dSgd78059 			fandata.speed[i] = ssp->fanspeed[i];
2122*1c42de6dSgd78059 			fandata.minspeed[i] = bscv_get8_cached(ssp,
2123*1c42de6dSgd78059 			    EBUS_IDX_FAN1_LOW + i);
2124*1c42de6dSgd78059 		}
2125*1c42de6dSgd78059 	}
2126*1c42de6dSgd78059 
2127*1c42de6dSgd78059 	if ((res == 0) &&
2128*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&fandata, (caddr_t)arg, sizeof (fandata),
2129*1c42de6dSgd78059 	    mode) < 0)) {
2130*1c42de6dSgd78059 		res = EFAULT;
2131*1c42de6dSgd78059 	}
2132*1c42de6dSgd78059 	return (res);
2133*1c42de6dSgd78059 }
2134*1c42de6dSgd78059 
2135*1c42de6dSgd78059 /*
2136*1c42de6dSgd78059  * LOMIOCFLEDSTATE - returns the state of the fault LED
2137*1c42de6dSgd78059  */
2138*1c42de6dSgd78059 static int
2139*1c42de6dSgd78059 bscv_ioc_fledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2140*1c42de6dSgd78059 {
2141*1c42de6dSgd78059 	lom_fled_info_t fled_info;
2142*1c42de6dSgd78059 	uint8_t fledstate;
2143*1c42de6dSgd78059 	int res = 0;
2144*1c42de6dSgd78059 
2145*1c42de6dSgd78059 	fledstate = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res);
2146*1c42de6dSgd78059 
2147*1c42de6dSgd78059 	/* Decode of 0x0F is off and 0x00-0x07 is on. */
2148*1c42de6dSgd78059 	if (EBUS_ALARM_LED_DEC(fledstate) == 0x0F) {
2149*1c42de6dSgd78059 		fled_info.on = 0;
2150*1c42de6dSgd78059 	} else {
2151*1c42de6dSgd78059 		/* has +1 here - not 2 as in the info ioctl */
2152*1c42de6dSgd78059 		fled_info.on = EBUS_ALARM_LED_DEC(fledstate) + 1;
2153*1c42de6dSgd78059 	}
2154*1c42de6dSgd78059 	if ((res == 0) &&
2155*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&fled_info, (caddr_t)arg,
2156*1c42de6dSgd78059 		sizeof (fled_info), mode) < 0)) {
2157*1c42de6dSgd78059 		res = EFAULT;
2158*1c42de6dSgd78059 	}
2159*1c42de6dSgd78059 	return (res);
2160*1c42de6dSgd78059 }
2161*1c42de6dSgd78059 
2162*1c42de6dSgd78059 /*
2163*1c42de6dSgd78059  * LOMIOCLEDSTATE - returns the state of the requested LED
2164*1c42de6dSgd78059  */
2165*1c42de6dSgd78059 static int
2166*1c42de6dSgd78059 bscv_ioc_ledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2167*1c42de6dSgd78059 {
2168*1c42de6dSgd78059 	lom_led_state_t led_state;
2169*1c42de6dSgd78059 	int fw_led_state;
2170*1c42de6dSgd78059 	int res = 0;
2171*1c42de6dSgd78059 
2172*1c42de6dSgd78059 	/* copy in arguments supplied */
2173*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&led_state,
2174*1c42de6dSgd78059 	    sizeof (lom_led_state_t), mode) < 0) {
2175*1c42de6dSgd78059 		return (EFAULT);
2176*1c42de6dSgd78059 	}
2177*1c42de6dSgd78059 
2178*1c42de6dSgd78059 	/*
2179*1c42de6dSgd78059 	 * check if led index is -1, if so set it to max value for
2180*1c42de6dSgd78059 	 * this implementation.
2181*1c42de6dSgd78059 	 */
2182*1c42de6dSgd78059 	if (led_state.index == -1) {
2183*1c42de6dSgd78059 		led_state.index = MAX_LED_ID;
2184*1c42de6dSgd78059 	}
2185*1c42de6dSgd78059 
2186*1c42de6dSgd78059 	/* is the index in a valid range */
2187*1c42de6dSgd78059 	if ((led_state.index > MAX_LED_ID) || (led_state.index < 0)) {
2188*1c42de6dSgd78059 		led_state.state = LOM_LED_OUTOFRANGE;
2189*1c42de6dSgd78059 	} else {
2190*1c42de6dSgd78059 		/* read the relevant led info */
2191*1c42de6dSgd78059 		fw_led_state = bscv_get8_locked(ssp, chan_general,
2192*1c42de6dSgd78059 		    EBUS_IDX_LED1_STATUS + led_state.index, &res);
2193*1c42de6dSgd78059 
2194*1c42de6dSgd78059 		/* set the state values accordingly */
2195*1c42de6dSgd78059 		switch (fw_led_state) {
2196*1c42de6dSgd78059 		case LOM_LED_STATE_OFF:
2197*1c42de6dSgd78059 			led_state.state = LOM_LED_OFF;
2198*1c42de6dSgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
2199*1c42de6dSgd78059 			break;
2200*1c42de6dSgd78059 		case LOM_LED_STATE_ON_STEADY:
2201*1c42de6dSgd78059 			led_state.state = LOM_LED_ON;
2202*1c42de6dSgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
2203*1c42de6dSgd78059 			break;
2204*1c42de6dSgd78059 		case LOM_LED_STATE_ON_FLASHING:
2205*1c42de6dSgd78059 		case LOM_LED_STATE_ON_SLOWFLASH:
2206*1c42de6dSgd78059 			led_state.state = LOM_LED_BLINKING;
2207*1c42de6dSgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
2208*1c42de6dSgd78059 			break;
2209*1c42de6dSgd78059 		case LOM_LED_STATE_NOT_PRESENT:
2210*1c42de6dSgd78059 			led_state.state = LOM_LED_NOT_IMPLEMENTED;
2211*1c42de6dSgd78059 			led_state.colour = LOM_LED_COLOUR_NONE;
2212*1c42de6dSgd78059 			break;
2213*1c42de6dSgd78059 		case LOM_LED_STATE_INACCESSIBLE:
2214*1c42de6dSgd78059 		case LOM_LED_STATE_STANDBY:
2215*1c42de6dSgd78059 		default:
2216*1c42de6dSgd78059 			led_state.state = LOM_LED_ACCESS_ERROR;
2217*1c42de6dSgd78059 			led_state.colour = LOM_LED_COLOUR_NONE;
2218*1c42de6dSgd78059 			break;
2219*1c42de6dSgd78059 		}
2220*1c42de6dSgd78059 
2221*1c42de6dSgd78059 		/* set the label info */
2222*1c42de6dSgd78059 		(void) strcpy(led_state.label,
2223*1c42de6dSgd78059 		    ssp->led_names[led_state.index]);
2224*1c42de6dSgd78059 	}
2225*1c42de6dSgd78059 
2226*1c42de6dSgd78059 	/* copy out lom_state */
2227*1c42de6dSgd78059 	if ((res == 0) &&
2228*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&led_state, (caddr_t)arg,
2229*1c42de6dSgd78059 		sizeof (lom_led_state_t), mode) < 0)) {
2230*1c42de6dSgd78059 		res = EFAULT;
2231*1c42de6dSgd78059 	}
2232*1c42de6dSgd78059 	return (res);
2233*1c42de6dSgd78059 }
2234*1c42de6dSgd78059 
2235*1c42de6dSgd78059 /*
2236*1c42de6dSgd78059  * LOMIOCINFO - returns with a structure containing any information
2237*1c42de6dSgd78059  * stored on the LOMlite which a user should not need to access but
2238*1c42de6dSgd78059  * may be useful for diagnostic problems. The structure contains: the
2239*1c42de6dSgd78059  * serial escape character, alarm3 mode, version and checksum read from
2240*1c42de6dSgd78059  * RAM and the Product revision and ID read from EEPROM.
2241*1c42de6dSgd78059  */
2242*1c42de6dSgd78059 static int
2243*1c42de6dSgd78059 bscv_ioc_info(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2244*1c42de6dSgd78059 {
2245*1c42de6dSgd78059 	lom_info_t info;
2246*1c42de6dSgd78059 	int i;
2247*1c42de6dSgd78059 	uint16_t csum;
2248*1c42de6dSgd78059 	int res = 0;
2249*1c42de6dSgd78059 
2250*1c42de6dSgd78059 	info.ser_char = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ESCAPE,
2251*1c42de6dSgd78059 		&res);
2252*1c42de6dSgd78059 	info.a3mode = WATCHDOG;
2253*1c42de6dSgd78059 	info.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
2254*1c42de6dSgd78059 	csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
2255*1c42de6dSgd78059 		<< 8;
2256*1c42de6dSgd78059 	csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
2257*1c42de6dSgd78059 	info.fchksum = csum;
2258*1c42de6dSgd78059 	info.prod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
2259*1c42de6dSgd78059 		&res);
2260*1c42de6dSgd78059 	for (i = 0; i < sizeof (info.prod_id); i++) {
2261*1c42de6dSgd78059 		info.prod_id[i] = bscv_get8_locked(ssp,
2262*1c42de6dSgd78059 		    chan_general, EBUS_IDX_MODEL_ID1 + i, &res);
2263*1c42de6dSgd78059 	}
2264*1c42de6dSgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res) &
2265*1c42de6dSgd78059 	    EBUS_ALARM_NOEVENTS) {
2266*1c42de6dSgd78059 		info.events = OFF;
2267*1c42de6dSgd78059 	} else {
2268*1c42de6dSgd78059 		info.events = ON;
2269*1c42de6dSgd78059 	}
2270*1c42de6dSgd78059 
2271*1c42de6dSgd78059 	if ((res == 0) &&
2272*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&info, (caddr_t)arg, sizeof (info),
2273*1c42de6dSgd78059 	    mode) < 0)) {
2274*1c42de6dSgd78059 		res = EFAULT;
2275*1c42de6dSgd78059 	}
2276*1c42de6dSgd78059 	return (res);
2277*1c42de6dSgd78059 }
2278*1c42de6dSgd78059 
2279*1c42de6dSgd78059 /*
2280*1c42de6dSgd78059  * LOMIOCMREAD - used to query the LOMlite configuration parameters
2281*1c42de6dSgd78059  */
2282*1c42de6dSgd78059 static int
2283*1c42de6dSgd78059 bscv_ioc_mread(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2284*1c42de6dSgd78059 {
2285*1c42de6dSgd78059 	lom_mprog_t mprog;
2286*1c42de6dSgd78059 	int i;
2287*1c42de6dSgd78059 	int fanz;
2288*1c42de6dSgd78059 	int res = 0;
2289*1c42de6dSgd78059 
2290*1c42de6dSgd78059 	for (i = 0; i < sizeof (mprog.mod_id); i++) {
2291*1c42de6dSgd78059 		mprog.mod_id[i] = bscv_get8_locked(ssp, chan_general,
2292*1c42de6dSgd78059 		    EBUS_IDX_MODEL_ID1 + i, &res);
2293*1c42de6dSgd78059 	}
2294*1c42de6dSgd78059 	mprog.mod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
2295*1c42de6dSgd78059 		&res);
2296*1c42de6dSgd78059 	mprog.config = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG,
2297*1c42de6dSgd78059 		&res);
2298*1c42de6dSgd78059 
2299*1c42de6dSgd78059 	/* Read the fan calibration values */
2300*1c42de6dSgd78059 	fanz = sizeof (mprog.fanhz) / sizeof (mprog.fanhz[0]);
2301*1c42de6dSgd78059 	for (i = 0; i < fanz; i++) {
2302*1c42de6dSgd78059 		mprog.fanhz[i] = bscv_get8_cached(ssp,
2303*1c42de6dSgd78059 		    EBUS_IDX_FAN1_CAL + i);
2304*1c42de6dSgd78059 		mprog.fanmin[i] = bscv_get8_cached(ssp,
2305*1c42de6dSgd78059 		    EBUS_IDX_FAN1_LOW + i);
2306*1c42de6dSgd78059 	}
2307*1c42de6dSgd78059 
2308*1c42de6dSgd78059 	if ((res == 0) &&
2309*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&mprog, (caddr_t)arg, sizeof (mprog),
2310*1c42de6dSgd78059 	    mode) < 0)) {
2311*1c42de6dSgd78059 		res = EFAULT;
2312*1c42de6dSgd78059 	}
2313*1c42de6dSgd78059 	return (res);
2314*1c42de6dSgd78059 }
2315*1c42de6dSgd78059 
2316*1c42de6dSgd78059 /*
2317*1c42de6dSgd78059  * LOMIOCVOLTS
2318*1c42de6dSgd78059  */
2319*1c42de6dSgd78059 static int
2320*1c42de6dSgd78059 bscv_ioc_volts(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2321*1c42de6dSgd78059 {
2322*1c42de6dSgd78059 	int i;
2323*1c42de6dSgd78059 	uint16_t supply;
2324*1c42de6dSgd78059 	int res = 0;
2325*1c42de6dSgd78059 
2326*1c42de6dSgd78059 	supply = (bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_HI, &res)
2327*1c42de6dSgd78059 		<< 8) | bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_LO,
2328*1c42de6dSgd78059 		&res);
2329*1c42de6dSgd78059 
2330*1c42de6dSgd78059 	for (i = 0; i < ssp->volts.num; i++) {
2331*1c42de6dSgd78059 		ssp->volts.status[i] = (supply >> i) & 1;
2332*1c42de6dSgd78059 	}
2333*1c42de6dSgd78059 
2334*1c42de6dSgd78059 	if ((res == 0) &&
2335*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&ssp->volts, (caddr_t)arg,
2336*1c42de6dSgd78059 		sizeof (ssp->volts), mode) < 0)) {
2337*1c42de6dSgd78059 		res = EFAULT;
2338*1c42de6dSgd78059 	}
2339*1c42de6dSgd78059 	return (res);
2340*1c42de6dSgd78059 }
2341*1c42de6dSgd78059 
2342*1c42de6dSgd78059 /*
2343*1c42de6dSgd78059  * LOMIOCSTATS
2344*1c42de6dSgd78059  */
2345*1c42de6dSgd78059 static int
2346*1c42de6dSgd78059 bscv_ioc_stats(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2347*1c42de6dSgd78059 {
2348*1c42de6dSgd78059 	int i;
2349*1c42de6dSgd78059 	uint8_t status;
2350*1c42de6dSgd78059 	int res = 0;
2351*1c42de6dSgd78059 
2352*1c42de6dSgd78059 	status = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CBREAK_STATUS,
2353*1c42de6dSgd78059 		&res);
2354*1c42de6dSgd78059 	for (i = 0; i < ssp->sflags.num; i++) {
2355*1c42de6dSgd78059 		ssp->sflags.status[i] = (int)((status >> i) & 1);
2356*1c42de6dSgd78059 	}
2357*1c42de6dSgd78059 
2358*1c42de6dSgd78059 	if ((res == 0) &&
2359*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&ssp->sflags, (caddr_t)arg,
2360*1c42de6dSgd78059 		    sizeof (ssp->sflags), mode) < 0)) {
2361*1c42de6dSgd78059 		res = EFAULT;
2362*1c42de6dSgd78059 	}
2363*1c42de6dSgd78059 	return (res);
2364*1c42de6dSgd78059 }
2365*1c42de6dSgd78059 
2366*1c42de6dSgd78059 /*
2367*1c42de6dSgd78059  * LOMIOCTEMP
2368*1c42de6dSgd78059  */
2369*1c42de6dSgd78059 static int
2370*1c42de6dSgd78059 bscv_ioc_temp(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2371*1c42de6dSgd78059 {
2372*1c42de6dSgd78059 	int i;
2373*1c42de6dSgd78059 	int idx;
2374*1c42de6dSgd78059 	uint8_t status_ov;
2375*1c42de6dSgd78059 	lom_temp_t temps;
2376*1c42de6dSgd78059 	int res = 0;
2377*1c42de6dSgd78059 
2378*1c42de6dSgd78059 	bzero(&temps, sizeof (temps));
2379*1c42de6dSgd78059 	idx = 0;
2380*1c42de6dSgd78059 	for (i = 0; i < ssp->temps.num; i++) {
2381*1c42de6dSgd78059 		if (ssp->temps.temp[i] != LOM_TEMP_STATE_NOT_PRESENT) {
2382*1c42de6dSgd78059 			temps.temp[idx] = ssp->temps.temp[i];
2383*1c42de6dSgd78059 			bcopy(ssp->temps.name[i], temps.name[idx],
2384*1c42de6dSgd78059 			    sizeof (temps.name[idx]));
2385*1c42de6dSgd78059 			temps.warning[idx] = ssp->temps.warning[i];
2386*1c42de6dSgd78059 			temps.shutdown[idx] = ssp->temps.shutdown[i];
2387*1c42de6dSgd78059 			idx++;
2388*1c42de6dSgd78059 		}
2389*1c42de6dSgd78059 	}
2390*1c42de6dSgd78059 	temps.num = idx;
2391*1c42de6dSgd78059 
2392*1c42de6dSgd78059 	bcopy(ssp->temps.name_ov, temps.name_ov, sizeof (temps.name_ov));
2393*1c42de6dSgd78059 	temps.num_ov = ssp->temps.num_ov;
2394*1c42de6dSgd78059 	status_ov = bscv_get8_locked(ssp, chan_general, EBUS_IDX_OTEMP_STATUS,
2395*1c42de6dSgd78059 		&res);
2396*1c42de6dSgd78059 	for (i = 0; i < ssp->temps.num_ov; i++) {
2397*1c42de6dSgd78059 		ssp->temps.status_ov[i] = (status_ov >> i) & 1;
2398*1c42de6dSgd78059 	}
2399*1c42de6dSgd78059 
2400*1c42de6dSgd78059 	if ((res == 0) &&
2401*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&temps, (caddr_t)arg, sizeof (temps),
2402*1c42de6dSgd78059 	    mode) < 0)) {
2403*1c42de6dSgd78059 		res = EFAULT;
2404*1c42de6dSgd78059 	}
2405*1c42de6dSgd78059 	return (res);
2406*1c42de6dSgd78059 }
2407*1c42de6dSgd78059 
2408*1c42de6dSgd78059 /*
2409*1c42de6dSgd78059  * LOMIOCCONS
2410*1c42de6dSgd78059  */
2411*1c42de6dSgd78059 static int
2412*1c42de6dSgd78059 bscv_ioc_cons(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2413*1c42de6dSgd78059 {
2414*1c42de6dSgd78059 	lom_cbuf_t cbuf;
2415*1c42de6dSgd78059 	int datasize;
2416*1c42de6dSgd78059 	int res = 0;
2417*1c42de6dSgd78059 
2418*1c42de6dSgd78059 	bzero(&cbuf, sizeof (cbuf));
2419*1c42de6dSgd78059 	datasize = EBUS_IDX1_CONS_BUF_END - EBUS_IDX1_CONS_BUF_START + 1;
2420*1c42de6dSgd78059 	/* Ensure that we do not overfill cbuf and that it is NUL terminated */
2421*1c42de6dSgd78059 	if (datasize > (sizeof (cbuf) - 1)) {
2422*1c42de6dSgd78059 		datasize = sizeof (cbuf) - 1;
2423*1c42de6dSgd78059 	}
2424*1c42de6dSgd78059 	bscv_rep_get8_locked(ssp, chan_general, (uint8_t *)cbuf.lrbuf,
2425*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE1, (EBUS_IDX1_CONS_BUF_END - datasize + 1)),
2426*1c42de6dSgd78059 	    datasize, DDI_DEV_AUTOINCR, &res);
2427*1c42de6dSgd78059 	/* This is always within the array due to the checks above */
2428*1c42de6dSgd78059 	cbuf.lrbuf[datasize] = '\0';
2429*1c42de6dSgd78059 
2430*1c42de6dSgd78059 	if ((res == 0) &&
2431*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&cbuf, (caddr_t)arg, sizeof (cbuf),
2432*1c42de6dSgd78059 	    mode) < 0)) {
2433*1c42de6dSgd78059 		res = EFAULT;
2434*1c42de6dSgd78059 	}
2435*1c42de6dSgd78059 	return (res);
2436*1c42de6dSgd78059 }
2437*1c42de6dSgd78059 
2438*1c42de6dSgd78059 /*
2439*1c42de6dSgd78059  * LOMIOCEVENTLOG2
2440*1c42de6dSgd78059  */
2441*1c42de6dSgd78059 static int
2442*1c42de6dSgd78059 bscv_ioc_eventlog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2443*1c42de6dSgd78059 {
2444*1c42de6dSgd78059 	lom_eventlog2_t *eventlog2;
2445*1c42de6dSgd78059 	int events_recorded;
2446*1c42de6dSgd78059 	int level;
2447*1c42de6dSgd78059 	uint16_t next_offset;
2448*1c42de6dSgd78059 	lom_event_t event;
2449*1c42de6dSgd78059 	int res = 0;
2450*1c42de6dSgd78059 
2451*1c42de6dSgd78059 	eventlog2 = (lom_eventlog2_t *)kmem_zalloc(sizeof (*eventlog2),
2452*1c42de6dSgd78059 	    KM_SLEEP);
2453*1c42de6dSgd78059 
2454*1c42de6dSgd78059 	/*
2455*1c42de6dSgd78059 	 * First get number of events and level requested.
2456*1c42de6dSgd78059 	 */
2457*1c42de6dSgd78059 
2458*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)eventlog2,
2459*1c42de6dSgd78059 	    sizeof (lom_eventlog2_t), mode) < 0) {
2460*1c42de6dSgd78059 		kmem_free((void *)eventlog2, sizeof (*eventlog2));
2461*1c42de6dSgd78059 		return (EFAULT);
2462*1c42de6dSgd78059 	}
2463*1c42de6dSgd78059 
2464*1c42de6dSgd78059 	bscv_enter(ssp);
2465*1c42de6dSgd78059 
2466*1c42de6dSgd78059 	/*
2467*1c42de6dSgd78059 	 * OK we have full private access to the LOM now so loop
2468*1c42de6dSgd78059 	 * over the eventlog addr spaces until we get the required
2469*1c42de6dSgd78059 	 * number of events.
2470*1c42de6dSgd78059 	 */
2471*1c42de6dSgd78059 
2472*1c42de6dSgd78059 	if (!bscv_window_setup(ssp)) {
2473*1c42de6dSgd78059 		res = EIO;
2474*1c42de6dSgd78059 		bscv_exit(ssp);
2475*1c42de6dSgd78059 		kmem_free((void *)eventlog2, sizeof (*eventlog2));
2476*1c42de6dSgd78059 		return (res);
2477*1c42de6dSgd78059 	}
2478*1c42de6dSgd78059 
2479*1c42de6dSgd78059 	/*
2480*1c42de6dSgd78059 	 * Read count, next event ptr MSB,LSB. Note a read of count
2481*1c42de6dSgd78059 	 * is necessary to latch values for the next event ptr
2482*1c42de6dSgd78059 	 */
2483*1c42de6dSgd78059 	(void) bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
2484*1c42de6dSgd78059 	next_offset = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
2485*1c42de6dSgd78059 	bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "log_ptr_hi 0x%x",
2486*1c42de6dSgd78059 	    next_offset);
2487*1c42de6dSgd78059 
2488*1c42de6dSgd78059 	events_recorded = 0;
2489*1c42de6dSgd78059 
2490*1c42de6dSgd78059 	while (events_recorded < eventlog2->num) {
2491*1c42de6dSgd78059 		/*
2492*1c42de6dSgd78059 		 * Working backwards - read an event at a time.
2493*1c42de6dSgd78059 		 * next_offset is one event on from where we want to be!
2494*1c42de6dSgd78059 		 * Decrement next_offset and maybe wrap to the end of the
2495*1c42de6dSgd78059 		 * buffer.
2496*1c42de6dSgd78059 		 * Note the unsigned arithmetic, so check values first!
2497*1c42de6dSgd78059 		 */
2498*1c42de6dSgd78059 		if (next_offset <= ssp->eventlog_start) {
2499*1c42de6dSgd78059 			/* Wrap to the end of the buffer */
2500*1c42de6dSgd78059 			next_offset = ssp->eventlog_start + ssp->eventlog_size;
2501*1c42de6dSgd78059 			bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "wrapping"
2502*1c42de6dSgd78059 			    " around to end of buffer; next_offset 0x%x",
2503*1c42de6dSgd78059 			    next_offset);
2504*1c42de6dSgd78059 		}
2505*1c42de6dSgd78059 		next_offset -= sizeof (event);
2506*1c42de6dSgd78059 
2507*1c42de6dSgd78059 		if (bscv_eerw(ssp, next_offset, (uint8_t *)&event,
2508*1c42de6dSgd78059 		    sizeof (event), B_FALSE /* read */) != 0) {
2509*1c42de6dSgd78059 			/* Fault reading data - stop */
2510*1c42de6dSgd78059 			bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "read"
2511*1c42de6dSgd78059 			    " failure for offset 0x%x", next_offset);
2512*1c42de6dSgd78059 			res = EIO;
2513*1c42de6dSgd78059 			break;
2514*1c42de6dSgd78059 		}
2515*1c42de6dSgd78059 
2516*1c42de6dSgd78059 		if (bscv_is_null_event(ssp, &event)) {
2517*1c42de6dSgd78059 			/*
2518*1c42de6dSgd78059 			 * No more events in this log so give up.
2519*1c42de6dSgd78059 			 */
2520*1c42de6dSgd78059 			bscv_trace(ssp, 'I', "bscv_ioc_eventlog2", "no more"
2521*1c42de6dSgd78059 			    " events left at offset 0x%x", next_offset);
2522*1c42de6dSgd78059 			break;
2523*1c42de6dSgd78059 		}
2524*1c42de6dSgd78059 
2525*1c42de6dSgd78059 		/*
2526*1c42de6dSgd78059 		 * Are we interested in this event
2527*1c42de6dSgd78059 		 */
2528*1c42de6dSgd78059 
2529*1c42de6dSgd78059 		level = bscv_level_of_event(&event);
2530*1c42de6dSgd78059 		if (level <= eventlog2->level) {
2531*1c42de6dSgd78059 			/* Arggh why the funny byte ordering 3, 2, 0, 1 */
2532*1c42de6dSgd78059 			eventlog2->code[events_recorded] =
2533*1c42de6dSgd78059 				((unsigned)event.ev_event |
2534*1c42de6dSgd78059 				    ((unsigned)event.ev_subsys << 8) |
2535*1c42de6dSgd78059 				    ((unsigned)event.ev_resource << 16) |
2536*1c42de6dSgd78059 				    ((unsigned)event.ev_detail << 24));
2537*1c42de6dSgd78059 
2538*1c42de6dSgd78059 			eventlog2->time[events_recorded] =
2539*1c42de6dSgd78059 				((unsigned)event.ev_data[0] |
2540*1c42de6dSgd78059 				    ((unsigned)event.ev_data[1] << 8) |
2541*1c42de6dSgd78059 				    ((unsigned)event.ev_data[3] << 16) |
2542*1c42de6dSgd78059 				    ((unsigned)event.ev_data[2] << 24));
2543*1c42de6dSgd78059 
2544*1c42de6dSgd78059 			bscv_build_eventstring(ssp,
2545*1c42de6dSgd78059 			    &event, eventlog2->string[events_recorded],
2546*1c42de6dSgd78059 			    eventlog2->string[events_recorded] +
2547*1c42de6dSgd78059 			    sizeof (eventlog2->string[events_recorded]));
2548*1c42de6dSgd78059 			events_recorded++;
2549*1c42de6dSgd78059 		}
2550*1c42de6dSgd78059 	}
2551*1c42de6dSgd78059 
2552*1c42de6dSgd78059 	eventlog2->num = events_recorded;
2553*1c42de6dSgd78059 
2554*1c42de6dSgd78059 	bscv_exit(ssp);
2555*1c42de6dSgd78059 
2556*1c42de6dSgd78059 	if ((res == 0) &&
2557*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)eventlog2, (caddr_t)arg,
2558*1c42de6dSgd78059 		sizeof (lom_eventlog2_t), mode) < 0)) {
2559*1c42de6dSgd78059 		res = EFAULT;
2560*1c42de6dSgd78059 	}
2561*1c42de6dSgd78059 
2562*1c42de6dSgd78059 	kmem_free((void *)eventlog2, sizeof (lom_eventlog2_t));
2563*1c42de6dSgd78059 	return (res);
2564*1c42de6dSgd78059 }
2565*1c42de6dSgd78059 
2566*1c42de6dSgd78059 /*
2567*1c42de6dSgd78059  * LOMIOCINFO2
2568*1c42de6dSgd78059  */
2569*1c42de6dSgd78059 static int
2570*1c42de6dSgd78059 bscv_ioc_info2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2571*1c42de6dSgd78059 {
2572*1c42de6dSgd78059 	lom2_info_t info2;
2573*1c42de6dSgd78059 	int i;
2574*1c42de6dSgd78059 	uint16_t csum;
2575*1c42de6dSgd78059 	int res = 0;
2576*1c42de6dSgd78059 
2577*1c42de6dSgd78059 	bzero(&info2, sizeof (info2));
2578*1c42de6dSgd78059 
2579*1c42de6dSgd78059 	(void) strncpy(info2.escape_chars, ssp->escape_chars,
2580*1c42de6dSgd78059 	    sizeof (info2.escape_chars));
2581*1c42de6dSgd78059 	info2.serial_events = ssp->reporting_level | ssp->serial_reporting;
2582*1c42de6dSgd78059 	info2.a3mode = WATCHDOG;
2583*1c42de6dSgd78059 
2584*1c42de6dSgd78059 	info2.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
2585*1c42de6dSgd78059 	csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
2586*1c42de6dSgd78059 		<< 8;
2587*1c42de6dSgd78059 	csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
2588*1c42de6dSgd78059 	info2.fchksum = csum;
2589*1c42de6dSgd78059 	info2.prod_rev = bscv_get8_locked(ssp, chan_general,
2590*1c42de6dSgd78059 		EBUS_IDX_MODEL_REV, &res);
2591*1c42de6dSgd78059 	for (i = 0; i < sizeof (info2.prod_id); i++) {
2592*1c42de6dSgd78059 		info2.prod_id[i] = bscv_get8_locked(ssp, chan_general,
2593*1c42de6dSgd78059 		    EBUS_IDX_MODEL_ID1 + i, &res);
2594*1c42de6dSgd78059 	}
2595*1c42de6dSgd78059 	info2.serial_config = bscv_get8_locked(ssp, chan_general,
2596*1c42de6dSgd78059 		EBUS_IDX_SER_TIMEOUT, &res);
2597*1c42de6dSgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
2598*1c42de6dSgd78059 	    EBUS_CONFIG_MISC_SECURITY_ENABLED) {
2599*1c42de6dSgd78059 		info2.serial_config |= LOM_SER_SECURITY;
2600*1c42de6dSgd78059 	}
2601*1c42de6dSgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
2602*1c42de6dSgd78059 	    EBUS_CONFIG_MISC_AUTO_CONSOLE) {
2603*1c42de6dSgd78059 		info2.serial_config |= LOM_SER_RETURN;
2604*1c42de6dSgd78059 	}
2605*1c42de6dSgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res) &
2606*1c42de6dSgd78059 	    EBUS_WDOG_BREAK_DISABLE) {
2607*1c42de6dSgd78059 		info2.serial_config |= LOM_DISABLE_WDOG_BREAK;
2608*1c42de6dSgd78059 	}
2609*1c42de6dSgd78059 	info2.baud_rate = bscv_get8_locked(ssp, chan_general,
2610*1c42de6dSgd78059 		EBUS_IDX_SER_BAUD, &res);
2611*1c42de6dSgd78059 	info2.serial_hw_config =
2612*1c42de6dSgd78059 		    ((int)bscv_get8_locked(ssp, chan_general,
2613*1c42de6dSgd78059 			EBUS_IDX_SER_CHARMODE, &res) |
2614*1c42de6dSgd78059 		    ((int)bscv_get8_locked(ssp, chan_general,
2615*1c42de6dSgd78059 			EBUS_IDX_SER_FLOWCTL, &res) << 8) |
2616*1c42de6dSgd78059 		    ((int)bscv_get8_locked(ssp, chan_general,
2617*1c42de6dSgd78059 			EBUS_IDX_SER_MODEMTYPE, &res) << 16));
2618*1c42de6dSgd78059 
2619*1c42de6dSgd78059 	/*
2620*1c42de6dSgd78059 	 * There is no phone home support on the blade platform.  We hardcode
2621*1c42de6dSgd78059 	 * FALSE and NUL for config and script respectively.
2622*1c42de6dSgd78059 	 */
2623*1c42de6dSgd78059 	info2.phone_home_config = B_FALSE;
2624*1c42de6dSgd78059 	info2.phone_home_script[0] = '\0';
2625*1c42de6dSgd78059 
2626*1c42de6dSgd78059 	for (i = 0; i < ssp->num_fans; i++) {
2627*1c42de6dSgd78059 		(void) strcpy(info2.fan_names[i], ssp->fan_names[i]);
2628*1c42de6dSgd78059 	}
2629*1c42de6dSgd78059 
2630*1c42de6dSgd78059 	if ((res == 0) &&
2631*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&info2, (caddr_t)arg, sizeof (info2),
2632*1c42de6dSgd78059 	    mode) < 0)) {
2633*1c42de6dSgd78059 		res = EFAULT;
2634*1c42de6dSgd78059 	}
2635*1c42de6dSgd78059 	return (res);
2636*1c42de6dSgd78059 }
2637*1c42de6dSgd78059 
2638*1c42de6dSgd78059 /*
2639*1c42de6dSgd78059  * LOMIOCTEST
2640*1c42de6dSgd78059  */
2641*1c42de6dSgd78059 static int
2642*1c42de6dSgd78059 bscv_ioc_test(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2643*1c42de6dSgd78059 {
2644*1c42de6dSgd78059 	uint32_t test;
2645*1c42de6dSgd78059 	uint8_t testnum;
2646*1c42de6dSgd78059 	uint8_t testarg;
2647*1c42de6dSgd78059 	int res = 0;
2648*1c42de6dSgd78059 
2649*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&test, sizeof (test),
2650*1c42de6dSgd78059 	    mode) < 0) {
2651*1c42de6dSgd78059 		return (EFAULT);
2652*1c42de6dSgd78059 	}
2653*1c42de6dSgd78059 
2654*1c42de6dSgd78059 	/*
2655*1c42de6dSgd78059 	 * Extract num iterations.
2656*1c42de6dSgd78059 	 */
2657*1c42de6dSgd78059 
2658*1c42de6dSgd78059 	testarg = (test & 0xff00) >> 8;
2659*1c42de6dSgd78059 	testnum = test & 0xff;
2660*1c42de6dSgd78059 
2661*1c42de6dSgd78059 	bscv_trace(ssp, 'F', "bscv_ioc_test",
2662*1c42de6dSgd78059 	    "LOMIOCTEST data 0x%x (test 0x%x, arg 0x%x)",
2663*1c42de6dSgd78059 	    test, (EBUS_IDX_SELFTEST0 + testnum), testarg);
2664*1c42de6dSgd78059 
2665*1c42de6dSgd78059 	switch (testnum + EBUS_IDX_SELFTEST0) {
2666*1c42de6dSgd78059 	default:
2667*1c42de6dSgd78059 		/* Invalid test */
2668*1c42de6dSgd78059 		res = EINVAL;
2669*1c42de6dSgd78059 		break;
2670*1c42de6dSgd78059 
2671*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST0:	/* power on self-test result */
2672*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST1:	/* not used currently */
2673*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST2:	/* not used currently */
2674*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST3:	/* not used currently */
2675*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST4:	/* not used currently */
2676*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST5:	/* not used currently */
2677*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST6:	/* LED self-test */
2678*1c42de6dSgd78059 	case EBUS_IDX_SELFTEST7:	/* platform-specific tests */
2679*1c42de6dSgd78059 		/* Run the test */
2680*1c42de6dSgd78059 
2681*1c42de6dSgd78059 		/* Stop other things and then run the test */
2682*1c42de6dSgd78059 		bscv_enter(ssp);
2683*1c42de6dSgd78059 
2684*1c42de6dSgd78059 		/*
2685*1c42de6dSgd78059 		 * Then we simply write the argument to the relevant register
2686*1c42de6dSgd78059 		 * and wait for the return code.
2687*1c42de6dSgd78059 		 */
2688*1c42de6dSgd78059 		bscv_put8(ssp, chan_general,
2689*1c42de6dSgd78059 				EBUS_IDX_SELFTEST0 + testnum, testarg);
2690*1c42de6dSgd78059 		if (bscv_faulty(ssp)) {
2691*1c42de6dSgd78059 			res = EIO;
2692*1c42de6dSgd78059 		} else {
2693*1c42de6dSgd78059 			/* Get hold of the SunVTS error code */
2694*1c42de6dSgd78059 			test = bscv_retcode(ssp);
2695*1c42de6dSgd78059 		}
2696*1c42de6dSgd78059 
2697*1c42de6dSgd78059 		bscv_exit(ssp);
2698*1c42de6dSgd78059 		break;
2699*1c42de6dSgd78059 	}
2700*1c42de6dSgd78059 
2701*1c42de6dSgd78059 	bscv_trace(ssp, 'F', "bscv_ioc_test",
2702*1c42de6dSgd78059 	    "LOMIOCTEST status 0x%x, res 0x%x", test, res);
2703*1c42de6dSgd78059 	if ((res == 0) &&
2704*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&test, (caddr_t)arg, sizeof (test),
2705*1c42de6dSgd78059 	    mode) < 0)) {
2706*1c42de6dSgd78059 		res = EFAULT;
2707*1c42de6dSgd78059 	}
2708*1c42de6dSgd78059 	return (res);
2709*1c42de6dSgd78059 }
2710*1c42de6dSgd78059 
2711*1c42de6dSgd78059 /*
2712*1c42de6dSgd78059  * LOMIOCMPROG2
2713*1c42de6dSgd78059  */
2714*1c42de6dSgd78059 static int
2715*1c42de6dSgd78059 bscv_ioc_mprog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2716*1c42de6dSgd78059 {
2717*1c42de6dSgd78059 	lom2_mprog_t  mprog2;
2718*1c42de6dSgd78059 	uint32_t base_addr;
2719*1c42de6dSgd78059 	uint32_t data_size;
2720*1c42de6dSgd78059 	uint32_t eeprom_size;
2721*1c42de6dSgd78059 	int res = 0;
2722*1c42de6dSgd78059 
2723*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
2724*1c42de6dSgd78059 	    mode) < 0) {
2725*1c42de6dSgd78059 		return (EFAULT);
2726*1c42de6dSgd78059 	}
2727*1c42de6dSgd78059 
2728*1c42de6dSgd78059 	/*
2729*1c42de6dSgd78059 	 * Note that originally this was accessed as 255 byte pages
2730*1c42de6dSgd78059 	 * in address spaces 240-255. We have to emulate this behaviour.
2731*1c42de6dSgd78059 	 */
2732*1c42de6dSgd78059 	if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
2733*1c42de6dSgd78059 		return (EINVAL);
2734*1c42de6dSgd78059 	}
2735*1c42de6dSgd78059 
2736*1c42de6dSgd78059 	bscv_enter(ssp);
2737*1c42de6dSgd78059 
2738*1c42de6dSgd78059 	/* Calculate required data location */
2739*1c42de6dSgd78059 	data_size = 255;
2740*1c42de6dSgd78059 	base_addr = (mprog2.addr_space - 240) * data_size;
2741*1c42de6dSgd78059 
2742*1c42de6dSgd78059 	eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
2743*1c42de6dSgd78059 			1024;
2744*1c42de6dSgd78059 
2745*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2746*1c42de6dSgd78059 		bscv_exit(ssp);
2747*1c42de6dSgd78059 		return (EIO);
2748*1c42de6dSgd78059 	} else if ((base_addr + data_size) > eeprom_size) {
2749*1c42de6dSgd78059 		bscv_trace(ssp, 'M', "bscv_ioc_mprog2",
2750*1c42de6dSgd78059 		    "Request extends past end of eeprom");
2751*1c42de6dSgd78059 		bscv_exit(ssp);
2752*1c42de6dSgd78059 		return (ENXIO);
2753*1c42de6dSgd78059 	}
2754*1c42de6dSgd78059 
2755*1c42de6dSgd78059 	bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK1);
2756*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2757*1c42de6dSgd78059 		bscv_trace(ssp, 'M', "bscv_ioc_mprog2", "ML1 Write failed");
2758*1c42de6dSgd78059 		bscv_exit(ssp);
2759*1c42de6dSgd78059 		return (EIO);
2760*1c42de6dSgd78059 	}
2761*1c42de6dSgd78059 
2762*1c42de6dSgd78059 	bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK2);
2763*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2764*1c42de6dSgd78059 		bscv_trace(ssp, 'M', "bscv_ioc_mprog2", "ML2 Write failed");
2765*1c42de6dSgd78059 		bscv_exit(ssp);
2766*1c42de6dSgd78059 		return (EIO);
2767*1c42de6dSgd78059 	}
2768*1c42de6dSgd78059 
2769*1c42de6dSgd78059 	if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
2770*1c42de6dSgd78059 	    data_size, B_TRUE /* write */) != 0) {
2771*1c42de6dSgd78059 		res = EIO;
2772*1c42de6dSgd78059 	}
2773*1c42de6dSgd78059 
2774*1c42de6dSgd78059 	/* Read a probe key to release the lock. */
2775*1c42de6dSgd78059 	(void) bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
2776*1c42de6dSgd78059 
2777*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2778*1c42de6dSgd78059 		res = EIO;
2779*1c42de6dSgd78059 	}
2780*1c42de6dSgd78059 	bscv_exit(ssp);
2781*1c42de6dSgd78059 
2782*1c42de6dSgd78059 	return (res);
2783*1c42de6dSgd78059 }
2784*1c42de6dSgd78059 
2785*1c42de6dSgd78059 /*
2786*1c42de6dSgd78059  * LOMIOCMREAD2
2787*1c42de6dSgd78059  */
2788*1c42de6dSgd78059 static int
2789*1c42de6dSgd78059 bscv_ioc_mread2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
2790*1c42de6dSgd78059 {
2791*1c42de6dSgd78059 	lom2_mprog_t  mprog2;
2792*1c42de6dSgd78059 	uint32_t base_addr;
2793*1c42de6dSgd78059 	uint32_t data_size;
2794*1c42de6dSgd78059 	uint32_t eeprom_size;
2795*1c42de6dSgd78059 	int res = 0;
2796*1c42de6dSgd78059 
2797*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
2798*1c42de6dSgd78059 	    mode) < 0) {
2799*1c42de6dSgd78059 		return (EFAULT);
2800*1c42de6dSgd78059 	}
2801*1c42de6dSgd78059 
2802*1c42de6dSgd78059 	/*
2803*1c42de6dSgd78059 	 * Need to stop the queue and then just read
2804*1c42de6dSgd78059 	 * the bytes blind to the relevant addresses.
2805*1c42de6dSgd78059 	 * Note that originally this was accessed as 255 byte pages
2806*1c42de6dSgd78059 	 * in address spaces 240-255. We have to emulate this behaviour.
2807*1c42de6dSgd78059 	 */
2808*1c42de6dSgd78059 	if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
2809*1c42de6dSgd78059 		return (EINVAL);
2810*1c42de6dSgd78059 	}
2811*1c42de6dSgd78059 
2812*1c42de6dSgd78059 	bscv_enter(ssp);
2813*1c42de6dSgd78059 
2814*1c42de6dSgd78059 	/* Calculate required data location */
2815*1c42de6dSgd78059 	data_size = 255;
2816*1c42de6dSgd78059 	base_addr = (mprog2.addr_space - 240) * data_size;
2817*1c42de6dSgd78059 	eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
2818*1c42de6dSgd78059 			1024;
2819*1c42de6dSgd78059 
2820*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2821*1c42de6dSgd78059 		bscv_exit(ssp);
2822*1c42de6dSgd78059 		return (EIO);
2823*1c42de6dSgd78059 	} else if ((base_addr + data_size) > eeprom_size) {
2824*1c42de6dSgd78059 		bscv_trace(ssp, 'M', "bscv_ioc_mread2",
2825*1c42de6dSgd78059 		    "Request extends past end of eeprom");
2826*1c42de6dSgd78059 		bscv_exit(ssp);
2827*1c42de6dSgd78059 		return (ENXIO);
2828*1c42de6dSgd78059 	}
2829*1c42de6dSgd78059 
2830*1c42de6dSgd78059 	if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
2831*1c42de6dSgd78059 	    data_size, B_FALSE /* read */) != 0) {
2832*1c42de6dSgd78059 		res = EIO;
2833*1c42de6dSgd78059 	}
2834*1c42de6dSgd78059 
2835*1c42de6dSgd78059 	if (bscv_faulty(ssp)) {
2836*1c42de6dSgd78059 		res = EIO;
2837*1c42de6dSgd78059 	}
2838*1c42de6dSgd78059 	bscv_exit(ssp);
2839*1c42de6dSgd78059 
2840*1c42de6dSgd78059 	if ((res == 0) &&
2841*1c42de6dSgd78059 	    (ddi_copyout((caddr_t)&mprog2, (caddr_t)arg, sizeof (mprog2),
2842*1c42de6dSgd78059 	    mode) < 0)) {
2843*1c42de6dSgd78059 		res = EFAULT;
2844*1c42de6dSgd78059 	}
2845*1c42de6dSgd78059 	return (res);
2846*1c42de6dSgd78059 }
2847*1c42de6dSgd78059 
2848*1c42de6dSgd78059 static void
2849*1c42de6dSgd78059 bscv_get_state_changes(bscv_soft_state_t *ssp)
2850*1c42de6dSgd78059 {
2851*1c42de6dSgd78059 	int i = STATUS_READ_LIMIT;
2852*1c42de6dSgd78059 	uint8_t change;
2853*1c42de6dSgd78059 	uint8_t detail;
2854*1c42de6dSgd78059 
2855*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
2856*1c42de6dSgd78059 
2857*1c42de6dSgd78059 	while (i-- && !ssp->cssp_prog) {
2858*1c42de6dSgd78059 		/* Are there any changes to process? */
2859*1c42de6dSgd78059 		change = bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
2860*1c42de6dSgd78059 		change &= EBUS_STATE_MASK;
2861*1c42de6dSgd78059 		if (!change)
2862*1c42de6dSgd78059 			break;
2863*1c42de6dSgd78059 
2864*1c42de6dSgd78059 		/* Clarify the pending change */
2865*1c42de6dSgd78059 		detail = bscv_get8(ssp, chan_general, EBUS_IDX_EVENT_DETAIL);
2866*1c42de6dSgd78059 
2867*1c42de6dSgd78059 		bscv_status(ssp, change, detail);
2868*1c42de6dSgd78059 	}
2869*1c42de6dSgd78059 
2870*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_get_state_changes",
2871*1c42de6dSgd78059 	    "loop index %d ssp->cssp_prog 0x%x", i, ssp->cssp_prog);
2872*1c42de6dSgd78059 }
2873*1c42de6dSgd78059 
2874*1c42de6dSgd78059 /*
2875*1c42de6dSgd78059  * *********************************************************************
2876*1c42de6dSgd78059  * Event Processing
2877*1c42de6dSgd78059  * *********************************************************************
2878*1c42de6dSgd78059  */
2879*1c42de6dSgd78059 
2880*1c42de6dSgd78059 /*
2881*1c42de6dSgd78059  * function	- bscv_event_daemon
2882*1c42de6dSgd78059  * description	- Perform periodic lom tasks in a separate thread.
2883*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
2884*1c42de6dSgd78059  * outputs	- none.
2885*1c42de6dSgd78059  */
2886*1c42de6dSgd78059 static void
2887*1c42de6dSgd78059 bscv_event_daemon(void *arg)
2888*1c42de6dSgd78059 {
2889*1c42de6dSgd78059 	bscv_soft_state_t	*ssp = (void *)arg;
2890*1c42de6dSgd78059 	boolean_t do_events;
2891*1c42de6dSgd78059 	boolean_t do_status;
2892*1c42de6dSgd78059 	boolean_t do_nodename;
2893*1c42de6dSgd78059 	boolean_t do_watchdog;
2894*1c42de6dSgd78059 	uint32_t async_reg;
2895*1c42de6dSgd78059 	uint32_t fault;
2896*1c42de6dSgd78059 	clock_t poll_period = BSC_EVENT_POLL_NORMAL;
2897*1c42de6dSgd78059 	int fault_cnt = 0;
2898*1c42de6dSgd78059 
2899*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_event_daemon",
2900*1c42de6dSgd78059 	    "bscv_event_daemon: started");
2901*1c42de6dSgd78059 
2902*1c42de6dSgd78059 	/* Acquire task daemon lock. */
2903*1c42de6dSgd78059 	mutex_enter(&ssp->task_mu);
2904*1c42de6dSgd78059 
2905*1c42de6dSgd78059 	ssp->task_flags |= TASK_ALIVE_FLG;
2906*1c42de6dSgd78059 
2907*1c42de6dSgd78059 	for (;;) {
2908*1c42de6dSgd78059 		if ((ssp->task_flags & TASK_STOP_FLG) != 0) {
2909*1c42de6dSgd78059 			/* Stop request seen - terminate */
2910*1c42de6dSgd78059 			break;
2911*1c42de6dSgd78059 		}
2912*1c42de6dSgd78059 		if ((ssp->task_flags & TASK_PAUSE_FLG) == 0) {
2913*1c42de6dSgd78059 			/* Poll for events reported to the nexus */
2914*1c42de6dSgd78059 			mutex_exit(&ssp->task_mu);
2915*1c42de6dSgd78059 			/* Probe and Check faults */
2916*1c42de6dSgd78059 			bscv_enter(ssp);
2917*1c42de6dSgd78059 			async_reg = bscv_probe(ssp, chan_general, &fault);
2918*1c42de6dSgd78059 			bscv_trace(ssp, 'D', "bscv_event_daemon",
2919*1c42de6dSgd78059 			    "process event: async_reg 0x%x, fault 0x%x",
2920*1c42de6dSgd78059 			    async_reg, fault);
2921*1c42de6dSgd78059 
2922*1c42de6dSgd78059 			if (!fault) {
2923*1c42de6dSgd78059 				/* Treat non-fault conditions */
2924*1c42de6dSgd78059 
2925*1c42de6dSgd78059 				if (ssp->cssp_prog || ssp->prog_mode_only) {
2926*1c42de6dSgd78059 					/*
2927*1c42de6dSgd78059 					 * The BSC has become available again.
2928*1c42de6dSgd78059 					 */
2929*1c42de6dSgd78059 					fault_cnt = 0;
2930*1c42de6dSgd78059 					ssp->cssp_prog = B_FALSE;
2931*1c42de6dSgd78059 					ssp->prog_mode_only = B_FALSE;
2932*1c42de6dSgd78059 					(void) bscv_attach_common(ssp);
2933*1c42de6dSgd78059 				} else if (fault_cnt > 0) {
2934*1c42de6dSgd78059 					/* Previous fault has cleared */
2935*1c42de6dSgd78059 					bscv_clear_fault(ssp);
2936*1c42de6dSgd78059 					fault_cnt = 0;
2937*1c42de6dSgd78059 					cmn_err(CE_WARN,
2938*1c42de6dSgd78059 					    "!bscv_event_daemon previous fault "
2939*1c42de6dSgd78059 					    "cleared.");
2940*1c42de6dSgd78059 				} else if (bscv_faulty(ssp)) {
2941*1c42de6dSgd78059 					/* Previous fault has cleared */
2942*1c42de6dSgd78059 					bscv_clear_fault(ssp);
2943*1c42de6dSgd78059 					/* Sleep to avoid busy waiting */
2944*1c42de6dSgd78059 					ssp->event_sleep = B_TRUE;
2945*1c42de6dSgd78059 				}
2946*1c42de6dSgd78059 				poll_period = BSC_EVENT_POLL_NORMAL;
2947*1c42de6dSgd78059 
2948*1c42de6dSgd78059 				if (async_reg) {
2949*1c42de6dSgd78059 					ssp->status_change = B_TRUE;
2950*1c42de6dSgd78059 					ssp->event_waiting = B_TRUE;
2951*1c42de6dSgd78059 				}
2952*1c42de6dSgd78059 			} else if (ssp->cssp_prog) {
2953*1c42de6dSgd78059 				/*
2954*1c42de6dSgd78059 				 * Expect radio silence or error values
2955*1c42de6dSgd78059 				 * when the CSSP is upgrading the BSC firmware
2956*1c42de6dSgd78059 				 * so throw away any fault indication.
2957*1c42de6dSgd78059 				 */
2958*1c42de6dSgd78059 				fault = B_FALSE;
2959*1c42de6dSgd78059 			} else if (fault_cnt == BSC_PROBE_FAULT_LIMIT) {
2960*1c42de6dSgd78059 				/* Count previous faults and maybe fail */
2961*1c42de6dSgd78059 				/* Declare the lom broken */
2962*1c42de6dSgd78059 				bscv_set_fault(ssp);
2963*1c42de6dSgd78059 				poll_period = BSC_EVENT_POLL_FAULTY;
2964*1c42de6dSgd78059 				cmn_err(CE_WARN,
2965*1c42de6dSgd78059 				    "!bscv_event_daemon had faults probing "
2966*1c42de6dSgd78059 				    "lom - marking it as faulty.");
2967*1c42de6dSgd78059 				/*
2968*1c42de6dSgd78059 				 * Increment fault_cnt to ensure that
2969*1c42de6dSgd78059 				 * next time we do not report a message
2970*1c42de6dSgd78059 				 * i.e. we drop out of the bottom
2971*1c42de6dSgd78059 				 */
2972*1c42de6dSgd78059 				fault_cnt = BSC_PROBE_FAULT_LIMIT + 1;
2973*1c42de6dSgd78059 				ssp->event_sleep = B_TRUE;
2974*1c42de6dSgd78059 			} else if (fault_cnt < BSC_PROBE_FAULT_LIMIT) {
2975*1c42de6dSgd78059 				if (bscv_faulty(ssp)) {
2976*1c42de6dSgd78059 					poll_period = BSC_EVENT_POLL_FAULTY;
2977*1c42de6dSgd78059 					/*
2978*1c42de6dSgd78059 					 * No recovery messages in this case
2979*1c42de6dSgd78059 					 * because there was never a fault
2980*1c42de6dSgd78059 					 * message here.
2981*1c42de6dSgd78059 					 */
2982*1c42de6dSgd78059 					fault_cnt = 0;
2983*1c42de6dSgd78059 				} else {
2984*1c42de6dSgd78059 					/* Getting ready to explode */
2985*1c42de6dSgd78059 					fault_cnt++;
2986*1c42de6dSgd78059 					cmn_err(CE_WARN,
2987*1c42de6dSgd78059 					    "!bscv_event_daemon had fault 0x%x",
2988*1c42de6dSgd78059 					    fault);
2989*1c42de6dSgd78059 				}
2990*1c42de6dSgd78059 				ssp->event_sleep = B_TRUE;
2991*1c42de6dSgd78059 			}
2992*1c42de6dSgd78059 			bscv_exit(ssp);
2993*1c42de6dSgd78059 			mutex_enter(&ssp->task_mu);
2994*1c42de6dSgd78059 		}
2995*1c42de6dSgd78059 
2996*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
2997*1c42de6dSgd78059 		/*
2998*1c42de6dSgd78059 		 * we have no platmod hook on Solaris x86 to report
2999*1c42de6dSgd78059 		 * a change to the nodename so we keep a copy so
3000*1c42de6dSgd78059 		 * we can detect a change and request that the bsc
3001*1c42de6dSgd78059 		 * be updated when appropriate.
3002*1c42de6dSgd78059 		 */
3003*1c42de6dSgd78059 		if (strcmp(ssp->last_nodename, utsname.nodename) != 0) {
3004*1c42de6dSgd78059 
3005*1c42de6dSgd78059 			bscv_trace(ssp, 'X', "bscv_event_daemon",
3006*1c42de6dSgd78059 			    "utsname.nodename='%s' possible change detected",
3007*1c42de6dSgd78059 			    utsname.nodename);
3008*1c42de6dSgd78059 			ssp->nodename_change = B_TRUE;
3009*1c42de6dSgd78059 			(void) strncpy(ssp->last_nodename, utsname.nodename,
3010*1c42de6dSgd78059 				sizeof (ssp->last_nodename));
3011*1c42de6dSgd78059 			/* enforce null termination */
3012*1c42de6dSgd78059 			ssp->last_nodename[sizeof (ssp->last_nodename) - 1] =
3013*1c42de6dSgd78059 			    '\0';
3014*1c42de6dSgd78059 		}
3015*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
3016*1c42de6dSgd78059 
3017*1c42de6dSgd78059 		if (((ssp->task_flags & TASK_PAUSE_FLG) == 0) &&
3018*1c42de6dSgd78059 		    fault_cnt == 0 && ssp->cssp_prog == B_FALSE &&
3019*1c42de6dSgd78059 		    (ssp->event_waiting || ssp->status_change ||
3020*1c42de6dSgd78059 			ssp->nodename_change || ssp->watchdog_change)) {
3021*1c42de6dSgd78059 
3022*1c42de6dSgd78059 			do_events = ssp->event_waiting;
3023*1c42de6dSgd78059 			ssp->event_waiting = B_FALSE;
3024*1c42de6dSgd78059 			ssp->task_flags |= do_events ?
3025*1c42de6dSgd78059 				TASK_EVENT_PENDING_FLG : 0;
3026*1c42de6dSgd78059 			do_status = ssp->status_change;
3027*1c42de6dSgd78059 			ssp->status_change = B_FALSE;
3028*1c42de6dSgd78059 			do_nodename = ssp->nodename_change;
3029*1c42de6dSgd78059 			ssp->nodename_change = B_FALSE;
3030*1c42de6dSgd78059 			do_watchdog = ssp->watchdog_change;
3031*1c42de6dSgd78059 			if (ssp->watchdog_change) {
3032*1c42de6dSgd78059 				ssp->watchdog_change = B_FALSE;
3033*1c42de6dSgd78059 			}
3034*1c42de6dSgd78059 
3035*1c42de6dSgd78059 			mutex_exit(&ssp->task_mu);
3036*1c42de6dSgd78059 			/*
3037*1c42de6dSgd78059 			 * We must not hold task_mu whilst processing
3038*1c42de6dSgd78059 			 * events because this can lead to priority
3039*1c42de6dSgd78059 			 * inversion and hence our interrupts getting
3040*1c42de6dSgd78059 			 * locked out.
3041*1c42de6dSgd78059 			 */
3042*1c42de6dSgd78059 			bscv_enter(ssp);
3043*1c42de6dSgd78059 			if (do_events) {
3044*1c42de6dSgd78059 				bscv_event_process(ssp, do_events);
3045*1c42de6dSgd78059 			}
3046*1c42de6dSgd78059 			if (do_nodename) {
3047*1c42de6dSgd78059 				bscv_trace(ssp, 'D', "bscv_event_daemon",
3048*1c42de6dSgd78059 				    "do_nodename task");
3049*1c42de6dSgd78059 				bscv_setup_hostname(ssp);
3050*1c42de6dSgd78059 			}
3051*1c42de6dSgd78059 			if (do_watchdog) {
3052*1c42de6dSgd78059 				bscv_trace(ssp, 'D', "bscv_event_daemon",
3053*1c42de6dSgd78059 				    "do_watchdog task");
3054*1c42de6dSgd78059 				bscv_setup_watchdog(ssp);
3055*1c42de6dSgd78059 			}
3056*1c42de6dSgd78059 			/*
3057*1c42de6dSgd78059 			 * Pending status changes are dealt with last because
3058*1c42de6dSgd78059 			 * if we see that the BSC is about to be programmed,
3059*1c42de6dSgd78059 			 * then it will expect us to to quiescent in the
3060*1c42de6dSgd78059 			 * first second so it can cleanly tear down its comms
3061*1c42de6dSgd78059 			 * protocols; this takes ~100 ms.
3062*1c42de6dSgd78059 			 */
3063*1c42de6dSgd78059 			if (do_status) {
3064*1c42de6dSgd78059 				bscv_get_state_changes(ssp);
3065*1c42de6dSgd78059 			}
3066*1c42de6dSgd78059 			if (bscv_session_error(ssp)) {
3067*1c42de6dSgd78059 				/*
3068*1c42de6dSgd78059 				 * Had fault during event session. We always
3069*1c42de6dSgd78059 				 * sleep after one of these because there
3070*1c42de6dSgd78059 				 * may be a problem with the lom which stops
3071*1c42de6dSgd78059 				 * us doing useful work in the event daemon.
3072*1c42de6dSgd78059 				 * If we don't sleep then we may livelock.
3073*1c42de6dSgd78059 				 */
3074*1c42de6dSgd78059 				bscv_trace(ssp, 'D', "bscv_event_daemon",
3075*1c42de6dSgd78059 				    "had session error - sleeping");
3076*1c42de6dSgd78059 				ssp->event_sleep = B_TRUE;
3077*1c42de6dSgd78059 			}
3078*1c42de6dSgd78059 			bscv_exit(ssp);
3079*1c42de6dSgd78059 
3080*1c42de6dSgd78059 			mutex_enter(&ssp->task_mu);
3081*1c42de6dSgd78059 
3082*1c42de6dSgd78059 			if (ssp->task_flags & TASK_EVENT_PENDING_FLG) {
3083*1c42de6dSgd78059 				/*
3084*1c42de6dSgd78059 				 * We have read any events which were
3085*1c42de6dSgd78059 				 * pending. Let the consumer continue.
3086*1c42de6dSgd78059 				 * Ignore the race condition with new events
3087*1c42de6dSgd78059 				 * arriving - just let the consumer have
3088*1c42de6dSgd78059 				 * whatever was pending when they asked.
3089*1c42de6dSgd78059 				 */
3090*1c42de6dSgd78059 				ssp->event_active_count++;
3091*1c42de6dSgd78059 				ssp->task_flags &= ~(TASK_EVENT_PENDING_FLG |
3092*1c42de6dSgd78059 				    TASK_EVENT_CONSUMER_FLG);
3093*1c42de6dSgd78059 				cv_broadcast(&ssp->task_evnt_cv);
3094*1c42de6dSgd78059 			}
3095*1c42de6dSgd78059 		} else {
3096*1c42de6dSgd78059 			/* There was nothing to do - sleep */
3097*1c42de6dSgd78059 			ssp->event_sleep = B_TRUE;
3098*1c42de6dSgd78059 		}
3099*1c42de6dSgd78059 
3100*1c42de6dSgd78059 		if (ssp->event_sleep) {
3101*1c42de6dSgd78059 			ssp->task_flags |= TASK_SLEEPING_FLG;
3102*1c42de6dSgd78059 			/* Sleep until there is something to do */
3103*1c42de6dSgd78059 			(void) cv_timedwait(&ssp->task_cv,
3104*1c42de6dSgd78059 			    &ssp->task_mu,
3105*1c42de6dSgd78059 			    poll_period + ddi_get_lbolt());
3106*1c42de6dSgd78059 			ssp->task_flags &= ~TASK_SLEEPING_FLG;
3107*1c42de6dSgd78059 			ssp->event_sleep = B_FALSE;
3108*1c42de6dSgd78059 		}
3109*1c42de6dSgd78059 	}
3110*1c42de6dSgd78059 
3111*1c42de6dSgd78059 	if (ssp->task_flags & TASK_EVENT_CONSUMER_FLG) {
3112*1c42de6dSgd78059 		/*
3113*1c42de6dSgd78059 		 * We are going away so wake up any event consumer.
3114*1c42de6dSgd78059 		 * Pretend that any pending events have been processed.
3115*1c42de6dSgd78059 		 */
3116*1c42de6dSgd78059 		ssp->event_active_count += 2;
3117*1c42de6dSgd78059 		cv_broadcast(&ssp->task_evnt_cv);
3118*1c42de6dSgd78059 	}
3119*1c42de6dSgd78059 
3120*1c42de6dSgd78059 	ASSERT(!(ssp->task_flags & TASK_EVENT_PENDING_FLG));
3121*1c42de6dSgd78059 	ssp->task_flags &=
3122*1c42de6dSgd78059 		~(TASK_STOP_FLG | TASK_ALIVE_FLG | TASK_EVENT_CONSUMER_FLG);
3123*1c42de6dSgd78059 	mutex_exit(&ssp->task_mu);
3124*1c42de6dSgd78059 
3125*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_event_daemon",
3126*1c42de6dSgd78059 	    "exiting.");
3127*1c42de6dSgd78059 }
3128*1c42de6dSgd78059 
3129*1c42de6dSgd78059 /*
3130*1c42de6dSgd78059  * function	- bscv_start_event_daemon
3131*1c42de6dSgd78059  * description	- Create the event daemon thread.
3132*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
3133*1c42de6dSgd78059  * outputs	- none
3134*1c42de6dSgd78059  */
3135*1c42de6dSgd78059 static void
3136*1c42de6dSgd78059 bscv_start_event_daemon(bscv_soft_state_t *ssp)
3137*1c42de6dSgd78059 {
3138*1c42de6dSgd78059 	if (ssp->progress & BSCV_THREAD)
3139*1c42de6dSgd78059 		return;
3140*1c42de6dSgd78059 
3141*1c42de6dSgd78059 	/* Start the event thread after the queue has started */
3142*1c42de6dSgd78059 	(void) thread_create(NULL, 0, (void (*)())bscv_event_daemon, ssp,
3143*1c42de6dSgd78059 	    0, &p0, TS_RUN, minclsyspri);
3144*1c42de6dSgd78059 
3145*1c42de6dSgd78059 	ssp->progress |= BSCV_THREAD;
3146*1c42de6dSgd78059 }
3147*1c42de6dSgd78059 
3148*1c42de6dSgd78059 /*
3149*1c42de6dSgd78059  * function	- bscv_stop_event_daemon
3150*1c42de6dSgd78059  * description	- Attempt to stop the event daemon thread.
3151*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
3152*1c42de6dSgd78059  * outputs	- DDI_SUCCESS OR DDI_FAILURE
3153*1c42de6dSgd78059  */
3154*1c42de6dSgd78059 static int
3155*1c42de6dSgd78059 bscv_stop_event_daemon(bscv_soft_state_t *ssp)
3156*1c42de6dSgd78059 {
3157*1c42de6dSgd78059 	int try;
3158*1c42de6dSgd78059 	int res = DDI_SUCCESS;
3159*1c42de6dSgd78059 
3160*1c42de6dSgd78059 	mutex_enter(&ssp->task_mu);
3161*1c42de6dSgd78059 
3162*1c42de6dSgd78059 	/* Wait for task daemon to stop running. */
3163*1c42de6dSgd78059 	for (try = 0;
3164*1c42de6dSgd78059 		((ssp->task_flags & TASK_ALIVE_FLG) && try < 10);
3165*1c42de6dSgd78059 		try++) {
3166*1c42de6dSgd78059 		/* Signal that the task daemon should stop */
3167*1c42de6dSgd78059 		ssp->task_flags |= TASK_STOP_FLG;
3168*1c42de6dSgd78059 		cv_signal(&ssp->task_cv);
3169*1c42de6dSgd78059 		/* Release task daemon lock. */
3170*1c42de6dSgd78059 		mutex_exit(&ssp->task_mu);
3171*1c42de6dSgd78059 		/*
3172*1c42de6dSgd78059 		 * TODO - when the driver is modified to support
3173*1c42de6dSgd78059 		 * system suspend or if this routine gets called
3174*1c42de6dSgd78059 		 * during panic we should use drv_usecwait() rather
3175*1c42de6dSgd78059 		 * than delay in those circumstances.
3176*1c42de6dSgd78059 		 */
3177*1c42de6dSgd78059 		delay(drv_usectohz(1000000));
3178*1c42de6dSgd78059 		mutex_enter(&ssp->task_mu);
3179*1c42de6dSgd78059 	}
3180*1c42de6dSgd78059 
3181*1c42de6dSgd78059 	if (ssp->task_flags & TASK_ALIVE_FLG) {
3182*1c42de6dSgd78059 		res = DDI_FAILURE;
3183*1c42de6dSgd78059 	}
3184*1c42de6dSgd78059 	mutex_exit(&ssp->task_mu);
3185*1c42de6dSgd78059 
3186*1c42de6dSgd78059 	return (res);
3187*1c42de6dSgd78059 }
3188*1c42de6dSgd78059 
3189*1c42de6dSgd78059 /*
3190*1c42de6dSgd78059  * function	- bscv_pause_event_daemon
3191*1c42de6dSgd78059  * description	- Attempt to pause the event daemon thread.
3192*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
3193*1c42de6dSgd78059  * outputs	- DDI_SUCCESS OR DDI_FAILURE
3194*1c42de6dSgd78059  */
3195*1c42de6dSgd78059 static int
3196*1c42de6dSgd78059 bscv_pause_event_daemon(bscv_soft_state_t *ssp)
3197*1c42de6dSgd78059 {
3198*1c42de6dSgd78059 	int try;
3199*1c42de6dSgd78059 
3200*1c42de6dSgd78059 	if (!(ssp->progress & BSCV_THREAD)) {
3201*1c42de6dSgd78059 		/* Nothing to do */
3202*1c42de6dSgd78059 		return (BSCV_SUCCESS);
3203*1c42de6dSgd78059 	}
3204*1c42de6dSgd78059 
3205*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
3206*1c42de6dSgd78059 	    "Attempting to pause event daemon");
3207*1c42de6dSgd78059 
3208*1c42de6dSgd78059 	mutex_enter(&ssp->task_mu);
3209*1c42de6dSgd78059 	/* Signal that the task daemon should pause */
3210*1c42de6dSgd78059 	ssp->task_flags |= TASK_PAUSE_FLG;
3211*1c42de6dSgd78059 
3212*1c42de6dSgd78059 	/* Wait for task daemon to pause. */
3213*1c42de6dSgd78059 	for (try = 0;
3214*1c42de6dSgd78059 		(!(ssp->task_flags & TASK_SLEEPING_FLG) &&
3215*1c42de6dSgd78059 		    (ssp->task_flags & TASK_ALIVE_FLG) &&
3216*1c42de6dSgd78059 		    try < 10);
3217*1c42de6dSgd78059 		try++) {
3218*1c42de6dSgd78059 		/* Paranoia */
3219*1c42de6dSgd78059 		ssp->task_flags |= TASK_PAUSE_FLG;
3220*1c42de6dSgd78059 		cv_signal(&ssp->task_cv);
3221*1c42de6dSgd78059 		/* Release task daemon lock. */
3222*1c42de6dSgd78059 		mutex_exit(&ssp->task_mu);
3223*1c42de6dSgd78059 		delay(drv_usectohz(1000000));
3224*1c42de6dSgd78059 		mutex_enter(&ssp->task_mu);
3225*1c42de6dSgd78059 	}
3226*1c42de6dSgd78059 	if ((ssp->task_flags & TASK_SLEEPING_FLG) ||
3227*1c42de6dSgd78059 	    !(ssp->task_flags & TASK_ALIVE_FLG)) {
3228*1c42de6dSgd78059 		mutex_exit(&ssp->task_mu);
3229*1c42de6dSgd78059 		bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
3230*1c42de6dSgd78059 		    "Pause event daemon - success");
3231*1c42de6dSgd78059 		return (BSCV_SUCCESS);
3232*1c42de6dSgd78059 	}
3233*1c42de6dSgd78059 	mutex_exit(&ssp->task_mu);
3234*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
3235*1c42de6dSgd78059 	    "Pause event daemon - failed");
3236*1c42de6dSgd78059 	return (BSCV_FAILURE);
3237*1c42de6dSgd78059 }
3238*1c42de6dSgd78059 
3239*1c42de6dSgd78059 /*
3240*1c42de6dSgd78059  * function	- bscv_resume_event_daemon
3241*1c42de6dSgd78059  * description	- Resumethe event daemon thread.
3242*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
3243*1c42de6dSgd78059  * outputs	- None.
3244*1c42de6dSgd78059  */
3245*1c42de6dSgd78059 static void
3246*1c42de6dSgd78059 bscv_resume_event_daemon(bscv_soft_state_t *ssp)
3247*1c42de6dSgd78059 {
3248*1c42de6dSgd78059 	if (!(ssp->progress & BSCV_THREAD)) {
3249*1c42de6dSgd78059 		/* Nothing to do */
3250*1c42de6dSgd78059 		return;
3251*1c42de6dSgd78059 	}
3252*1c42de6dSgd78059 
3253*1c42de6dSgd78059 	mutex_enter(&ssp->task_mu);
3254*1c42de6dSgd78059 	/* Allow the task daemon to resume event processing */
3255*1c42de6dSgd78059 	ssp->task_flags &= ~TASK_PAUSE_FLG;
3256*1c42de6dSgd78059 	cv_signal(&ssp->task_cv);
3257*1c42de6dSgd78059 	mutex_exit(&ssp->task_mu);
3258*1c42de6dSgd78059 
3259*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_pause_event_daemon",
3260*1c42de6dSgd78059 	    "Event daemon resumed");
3261*1c42de6dSgd78059 }
3262*1c42de6dSgd78059 
3263*1c42de6dSgd78059 /*
3264*1c42de6dSgd78059  * function	- bscv_event_process
3265*1c42de6dSgd78059  * description	- process (report) events
3266*1c42de6dSgd78059  * inputs	- Soft state ptr, process event request
3267*1c42de6dSgd78059  * outputs	- none
3268*1c42de6dSgd78059  */
3269*1c42de6dSgd78059 static void
3270*1c42de6dSgd78059 bscv_event_process(bscv_soft_state_t *ssp, boolean_t do_events)
3271*1c42de6dSgd78059 {
3272*1c42de6dSgd78059 	uint32_t currptr;
3273*1c42de6dSgd78059 	unsigned int count;
3274*1c42de6dSgd78059 
3275*1c42de6dSgd78059 	/* Raw values read from the lom */
3276*1c42de6dSgd78059 	uint8_t evcount;
3277*1c42de6dSgd78059 	uint16_t logptr;
3278*1c42de6dSgd78059 
3279*1c42de6dSgd78059 	lom_event_t event;
3280*1c42de6dSgd78059 
3281*1c42de6dSgd78059 	if (do_events) {
3282*1c42de6dSgd78059 		/*
3283*1c42de6dSgd78059 		 * Read count, next event ptr MSB,LSB. Note a read of count
3284*1c42de6dSgd78059 		 * latches values for the next event ptr
3285*1c42de6dSgd78059 		 */
3286*1c42de6dSgd78059 		evcount = bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
3287*1c42de6dSgd78059 		logptr = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
3288*1c42de6dSgd78059 
3289*1c42de6dSgd78059 		/* Sanity check the values from the lom */
3290*1c42de6dSgd78059 		count = bscv_event_validate(ssp, logptr, evcount);
3291*1c42de6dSgd78059 
3292*1c42de6dSgd78059 		if (count == -1) {
3293*1c42de6dSgd78059 			/*
3294*1c42de6dSgd78059 			 * Nothing to do - or badly configured event log.
3295*1c42de6dSgd78059 			 * We really do not want to touch the lom in this
3296*1c42de6dSgd78059 			 * case because any data that we access may be bad!
3297*1c42de6dSgd78059 			 * This differs from zero because if we have zero
3298*1c42de6dSgd78059 			 * to read the lom probably things that unread is
3299*1c42de6dSgd78059 			 * non-zero and we want that to be set to zero!
3300*1c42de6dSgd78059 			 * Signal event fault to make the thread wait
3301*1c42de6dSgd78059 			 * before attempting to re-read the log.
3302*1c42de6dSgd78059 			 */
3303*1c42de6dSgd78059 			ssp->event_sleep = B_TRUE;
3304*1c42de6dSgd78059 
3305*1c42de6dSgd78059 			goto logdone;
3306*1c42de6dSgd78059 		}
3307*1c42de6dSgd78059 		if (ssp->event_fault_reported) {
3308*1c42de6dSgd78059 			/* Clear down any old status - things are fixed */
3309*1c42de6dSgd78059 			cmn_err(CE_NOTE, "Event pointer fault recovered.");
3310*1c42de6dSgd78059 			ssp->event_fault_reported = B_FALSE;
3311*1c42de6dSgd78059 		}
3312*1c42de6dSgd78059 
3313*1c42de6dSgd78059 		/* Compute the first entry that we need to read. */
3314*1c42de6dSgd78059 		currptr = logptr - ssp->eventlog_start;
3315*1c42de6dSgd78059 		currptr += ssp->eventlog_size;
3316*1c42de6dSgd78059 		currptr -= (count * sizeof (event));
3317*1c42de6dSgd78059 		currptr %= ssp->eventlog_size;
3318*1c42de6dSgd78059 		currptr += ssp->eventlog_start;
3319*1c42de6dSgd78059 
3320*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_event_process",
3321*1c42de6dSgd78059 		    "processing %d events from 0x%x in 0x%x:0x%x",
3322*1c42de6dSgd78059 		    count, currptr,
3323*1c42de6dSgd78059 		    ssp->eventlog_start,
3324*1c42de6dSgd78059 		    ssp->eventlog_start + ssp->eventlog_size);
3325*1c42de6dSgd78059 
3326*1c42de6dSgd78059 		for (; count > 0; count--) {
3327*1c42de6dSgd78059 			/* Ensure window is positioned correctly */
3328*1c42de6dSgd78059 			if (bscv_eerw(ssp, currptr, (uint8_t *)&event,
3329*1c42de6dSgd78059 			    sizeof (event), B_FALSE /* read */) != 0) {
3330*1c42de6dSgd78059 				/* Fault reading data - stop */
3331*1c42de6dSgd78059 				break;
3332*1c42de6dSgd78059 			}
3333*1c42de6dSgd78059 
3334*1c42de6dSgd78059 			bscv_event_process_one(ssp, &event);
3335*1c42de6dSgd78059 			bscv_sysevent(ssp, &event);
3336*1c42de6dSgd78059 
3337*1c42de6dSgd78059 			currptr += sizeof (event);
3338*1c42de6dSgd78059 			if (currptr >= ssp->eventlog_start +
3339*1c42de6dSgd78059 			    ssp->eventlog_size) {
3340*1c42de6dSgd78059 				currptr = ssp->eventlog_start;
3341*1c42de6dSgd78059 			}
3342*1c42de6dSgd78059 		}
3343*1c42de6dSgd78059 		/*
3344*1c42de6dSgd78059 		 * Clear event count - write the evcount value to remove that
3345*1c42de6dSgd78059 		 * many from the unread total.
3346*1c42de6dSgd78059 		 * Adjust the value to reflect how many we have left to
3347*1c42de6dSgd78059 		 * read just in case we had a failure reading events.
3348*1c42de6dSgd78059 		 */
3349*1c42de6dSgd78059 		if (count == 0) {
3350*1c42de6dSgd78059 			/*EMPTY*/
3351*1c42de6dSgd78059 			ASSERT(logptr == currptr);
3352*1c42de6dSgd78059 		} else if (count > evcount) {
3353*1c42de6dSgd78059 			evcount = 0;
3354*1c42de6dSgd78059 		} else {
3355*1c42de6dSgd78059 			evcount -= count;
3356*1c42de6dSgd78059 		}
3357*1c42de6dSgd78059 		bscv_put8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS, evcount);
3358*1c42de6dSgd78059 		    /* Remember where we were for next time */
3359*1c42de6dSgd78059 		ssp->oldeeptr = currptr;
3360*1c42de6dSgd78059 		ssp->oldeeptr_valid = B_TRUE;
3361*1c42de6dSgd78059 logdone:
3362*1c42de6dSgd78059 		;
3363*1c42de6dSgd78059 	}
3364*1c42de6dSgd78059 }
3365*1c42de6dSgd78059 
3366*1c42de6dSgd78059 /*
3367*1c42de6dSgd78059  * function	- bscv_event_validate
3368*1c42de6dSgd78059  * description	- validate the event data supplied by the lom and determine
3369*1c42de6dSgd78059  *		  how many (if any) events to read.
3370*1c42de6dSgd78059  *		  This function performs complex checks to ensure that
3371*1c42de6dSgd78059  *		  events are not lost due to lom resets or host resets.
3372*1c42de6dSgd78059  *		  A combination of lom reset and host reset (i.e. power fail)
3373*1c42de6dSgd78059  *		  may cause some events to not be reported.
3374*1c42de6dSgd78059  * inputs	- Soft state ptr, next event pointer, number of unread events.
3375*1c42de6dSgd78059  * outputs	- the number of events to read. -1 on error.
3376*1c42de6dSgd78059  *		  zero is a valid value because it forces the loms unread
3377*1c42de6dSgd78059  *		  count to be cleared.
3378*1c42de6dSgd78059  */
3379*1c42de6dSgd78059 static int
3380*1c42de6dSgd78059 bscv_event_validate(bscv_soft_state_t *ssp, uint32_t newptr, uint8_t unread)
3381*1c42de6dSgd78059 {
3382*1c42de6dSgd78059 	uint32_t oldptr;
3383*1c42de6dSgd78059 	unsigned int count;
3384*1c42de6dSgd78059 
3385*1c42de6dSgd78059 	if (!bscv_window_setup(ssp)) {
3386*1c42de6dSgd78059 		/* Problem with lom eeprom setup we cannot do anything */
3387*1c42de6dSgd78059 		return (-1);
3388*1c42de6dSgd78059 	}
3389*1c42de6dSgd78059 
3390*1c42de6dSgd78059 	/* Sanity check the event pointers */
3391*1c42de6dSgd78059 	if ((newptr < ssp->eventlog_start) ||
3392*1c42de6dSgd78059 	    (newptr >= (ssp->eventlog_start + ssp->eventlog_size))) {
3393*1c42de6dSgd78059 		if (!ssp->event_fault_reported) {
3394*1c42de6dSgd78059 			cmn_err(CE_WARN, "Event pointer out of range. "
3395*1c42de6dSgd78059 				"Cannot read events.");
3396*1c42de6dSgd78059 			ssp->event_fault_reported = B_TRUE;
3397*1c42de6dSgd78059 		}
3398*1c42de6dSgd78059 		return (-1);
3399*1c42de6dSgd78059 	}
3400*1c42de6dSgd78059 	oldptr = ssp->oldeeptr;
3401*1c42de6dSgd78059 	/* Now sanity check log pointer against count */
3402*1c42de6dSgd78059 	if (newptr < oldptr) {
3403*1c42de6dSgd78059 		/*
3404*1c42de6dSgd78059 		 * Must have wrapped add eventlog_size to get the
3405*1c42de6dSgd78059 		 * correct relative values - this makes the checks
3406*1c42de6dSgd78059 		 * below work!
3407*1c42de6dSgd78059 		 */
3408*1c42de6dSgd78059 		newptr += ssp->eventlog_size;
3409*1c42de6dSgd78059 	}
3410*1c42de6dSgd78059 	if (!ssp->oldeeptr_valid) {
3411*1c42de6dSgd78059 		/* We have just started up - we have to trust lom */
3412*1c42de6dSgd78059 		count = unread;
3413*1c42de6dSgd78059 	} else if ((unread == 0) && (newptr == oldptr)) {
3414*1c42de6dSgd78059 		/* Nothing to do - we were just polling */
3415*1c42de6dSgd78059 		return (-1);
3416*1c42de6dSgd78059 	} else if (oldptr + (unread * sizeof (lom_event_t)) == newptr) {
3417*1c42de6dSgd78059 		/* Ok - got as many events as we expected */
3418*1c42de6dSgd78059 		count = unread;
3419*1c42de6dSgd78059 	} else if (oldptr + (unread * sizeof (lom_event_t)) > newptr) {
3420*1c42de6dSgd78059 		/*
3421*1c42de6dSgd78059 		 * Errrm more messages than there should have been.
3422*1c42de6dSgd78059 		 * Possible causes:
3423*1c42de6dSgd78059 		 * 1.	the event log has filled - we have been
3424*1c42de6dSgd78059 		 *	away for a long time
3425*1c42de6dSgd78059 		 * 2.	software bug in lom or driver.
3426*1c42de6dSgd78059 		 * 3.	something that I haven't thought of!
3427*1c42de6dSgd78059 		 * Always warn about this we should really never
3428*1c42de6dSgd78059 		 * see it!
3429*1c42de6dSgd78059 		 */
3430*1c42de6dSgd78059 		count = (newptr - oldptr) / sizeof (lom_event_t);
3431*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_event_process",
3432*1c42de6dSgd78059 		    "bscv_event_process: lom reported "
3433*1c42de6dSgd78059 		    "more events (%d) than expected (%d).",
3434*1c42de6dSgd78059 		    unread, count);
3435*1c42de6dSgd78059 		cmn_err(CE_CONT, "only processing %d events", count);
3436*1c42de6dSgd78059 	} else {
3437*1c42de6dSgd78059 		/* Less messages - perhaps the lom has been reset */
3438*1c42de6dSgd78059 		count = (newptr - oldptr) / sizeof (lom_event_t);
3439*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_event_process",
3440*1c42de6dSgd78059 		    "lom reported less events (%d) than expected (%d)"
3441*1c42de6dSgd78059 		    " - the lom may have been reset",
3442*1c42de6dSgd78059 		    unread, count);
3443*1c42de6dSgd78059 	}
3444*1c42de6dSgd78059 	/* Whatever happens only read a maximum of 255 entries */
3445*1c42de6dSgd78059 	if ((count >= 0xff)) {
3446*1c42de6dSgd78059 		cmn_err(CE_WARN,
3447*1c42de6dSgd78059 		    "bscv_event_process: too many events (%d) to "
3448*1c42de6dSgd78059 		    "process - some may have been lost", count);
3449*1c42de6dSgd78059 		count = 0xff;
3450*1c42de6dSgd78059 	}
3451*1c42de6dSgd78059 	return (count);
3452*1c42de6dSgd78059 }
3453*1c42de6dSgd78059 
3454*1c42de6dSgd78059 /*
3455*1c42de6dSgd78059  * function	- bscv_event_process_one
3456*1c42de6dSgd78059  * description	- reports on state changes to the host.
3457*1c42de6dSgd78059  *
3458*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer.
3459*1c42de6dSgd78059  *
3460*1c42de6dSgd78059  * outputs	- none.
3461*1c42de6dSgd78059  */
3462*1c42de6dSgd78059 
3463*1c42de6dSgd78059 static void
3464*1c42de6dSgd78059 bscv_event_process_one(bscv_soft_state_t *ssp, lom_event_t *event)
3465*1c42de6dSgd78059 {
3466*1c42de6dSgd78059 	int level;
3467*1c42de6dSgd78059 	char eventstr[100];
3468*1c42de6dSgd78059 	int msg_type = 0;
3469*1c42de6dSgd78059 
3470*1c42de6dSgd78059 	if (bscv_is_null_event(ssp, event)) {
3471*1c42de6dSgd78059 		/* Cleared entry - do not report it */
3472*1c42de6dSgd78059 		return;
3473*1c42de6dSgd78059 	}
3474*1c42de6dSgd78059 
3475*1c42de6dSgd78059 	level = bscv_level_of_event(event);
3476*1c42de6dSgd78059 
3477*1c42de6dSgd78059 	switch (level) {
3478*1c42de6dSgd78059 	default:
3479*1c42de6dSgd78059 		msg_type = CE_NOTE;
3480*1c42de6dSgd78059 		break;
3481*1c42de6dSgd78059 
3482*1c42de6dSgd78059 	case EVENT_LEVEL_FATAL:
3483*1c42de6dSgd78059 	case EVENT_LEVEL_FAULT:
3484*1c42de6dSgd78059 		msg_type = CE_WARN;
3485*1c42de6dSgd78059 		break;
3486*1c42de6dSgd78059 	}
3487*1c42de6dSgd78059 
3488*1c42de6dSgd78059 	bscv_build_eventstring(ssp, event, eventstr, eventstr +
3489*1c42de6dSgd78059 	    sizeof (eventstr));
3490*1c42de6dSgd78059 
3491*1c42de6dSgd78059 	if (level <= ssp->reporting_level) {
3492*1c42de6dSgd78059 		/*
3493*1c42de6dSgd78059 		 * The message is important enough to be shown on the console
3494*1c42de6dSgd78059 		 * as well as the log.
3495*1c42de6dSgd78059 		 */
3496*1c42de6dSgd78059 		cmn_err(msg_type, "%s", eventstr);
3497*1c42de6dSgd78059 	} else {
3498*1c42de6dSgd78059 		/*
3499*1c42de6dSgd78059 		 * The message goes only to the log.
3500*1c42de6dSgd78059 		 */
3501*1c42de6dSgd78059 		cmn_err(msg_type, "!%s", eventstr);
3502*1c42de6dSgd78059 	}
3503*1c42de6dSgd78059 }
3504*1c42de6dSgd78059 
3505*1c42de6dSgd78059 /*
3506*1c42de6dSgd78059  * time formats
3507*1c42de6dSgd78059  *
3508*1c42de6dSgd78059  * The BSC represents times as seconds since epoch 1970.  Currently it gives
3509*1c42de6dSgd78059  * us 32 bits, unsigned.  In the future this might change to a 64-bit count,
3510*1c42de6dSgd78059  * to allow a greater range.
3511*1c42de6dSgd78059  *
3512*1c42de6dSgd78059  * Timestamp values below BSC_TIME_SANITY do not represent an absolute time,
3513*1c42de6dSgd78059  * but instead represent an offset from the last reset.  This must be
3514*1c42de6dSgd78059  * borne in mind by output routines.
3515*1c42de6dSgd78059  */
3516*1c42de6dSgd78059 
3517*1c42de6dSgd78059 typedef uint32_t bsctime_t;
3518*1c42de6dSgd78059 
3519*1c42de6dSgd78059 #define	BSC_TIME_SANITY		1000000000
3520*1c42de6dSgd78059 
3521*1c42de6dSgd78059 /*
3522*1c42de6dSgd78059  * render a formatted time for display
3523*1c42de6dSgd78059  */
3524*1c42de6dSgd78059 
3525*1c42de6dSgd78059 static size_t
3526*1c42de6dSgd78059 bscv_event_snprintgmttime(char *buf, size_t bufsz, todinfo_t t)
3527*1c42de6dSgd78059 {
3528*1c42de6dSgd78059 	int year;
3529*1c42de6dSgd78059 
3530*1c42de6dSgd78059 	/* tod_year is base 1900 so this code needs to adjust */
3531*1c42de6dSgd78059 	year = 1900 + t.tod_year;
3532*1c42de6dSgd78059 
3533*1c42de6dSgd78059 	return (snprintf(buf, bufsz, "%04d-%02d-%02d %02d:%02d:%02dZ",
3534*1c42de6dSgd78059 			year, t.tod_month, t.tod_day, t.tod_hour,
3535*1c42de6dSgd78059 			t.tod_min, t.tod_sec));
3536*1c42de6dSgd78059 }
3537*1c42de6dSgd78059 
3538*1c42de6dSgd78059 /*
3539*1c42de6dSgd78059  * function	- bscv_build_eventstring
3540*1c42de6dSgd78059  * description	- reports on state changes to the host.
3541*1c42de6dSgd78059  *
3542*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer.
3543*1c42de6dSgd78059  *
3544*1c42de6dSgd78059  * outputs	- none.
3545*1c42de6dSgd78059  */
3546*1c42de6dSgd78059 
3547*1c42de6dSgd78059 static void
3548*1c42de6dSgd78059 bscv_build_eventstring(bscv_soft_state_t *ssp, lom_event_t *event,
3549*1c42de6dSgd78059     char *buf, char *bufend)
3550*1c42de6dSgd78059 {
3551*1c42de6dSgd78059 	uint8_t subsystem;
3552*1c42de6dSgd78059 	uint8_t eventtype;
3553*1c42de6dSgd78059 	bsctime_t bsctm;
3554*1c42de6dSgd78059 
3555*1c42de6dSgd78059 	bscv_trace(ssp, 'S', "bscv_build_eventstring", "event %2x%2x%2x%2x",
3556*1c42de6dSgd78059 	    event->ev_subsys, event->ev_event,
3557*1c42de6dSgd78059 	    event->ev_resource, event->ev_detail);
3558*1c42de6dSgd78059 	bscv_trace(ssp, 'S', "bscv_build_eventstring", "time %2x%2x%2x%2x",
3559*1c42de6dSgd78059 	    event->ev_data[0], event->ev_data[1],
3560*1c42de6dSgd78059 	    event->ev_data[2], event->ev_data[3]);
3561*1c42de6dSgd78059 
3562*1c42de6dSgd78059 	/*
3563*1c42de6dSgd78059 	 * We accept bad subsystems and event type codes here.
3564*1c42de6dSgd78059 	 * The code decodes as much as possible and then produces
3565*1c42de6dSgd78059 	 * suitable output.
3566*1c42de6dSgd78059 	 */
3567*1c42de6dSgd78059 	subsystem = EVENT_DECODE_SUBSYS(event->ev_subsys);
3568*1c42de6dSgd78059 	eventtype = event->ev_event;
3569*1c42de6dSgd78059 
3570*1c42de6dSgd78059 	/* time */
3571*1c42de6dSgd78059 	bsctm = (((uint32_t)event->ev_data[0]) << 24) |
3572*1c42de6dSgd78059 			(((uint32_t)event->ev_data[1]) << 16) |
3573*1c42de6dSgd78059 			(((uint32_t)event->ev_data[2]) << 8) |
3574*1c42de6dSgd78059 			((uint32_t)event->ev_data[3]);
3575*1c42de6dSgd78059 	if (bsctm < BSC_TIME_SANITY) {
3576*1c42de6dSgd78059 		/* offset */
3577*1c42de6dSgd78059 		buf += snprintf(buf, bufend-buf, "+P%dd%02dh%02dm%02ds",
3578*1c42de6dSgd78059 			(int)(bsctm/86400), (int)(bsctm/3600%24),
3579*1c42de6dSgd78059 			(int)(bsctm/60%60), (int)(bsctm%60));
3580*1c42de6dSgd78059 	} else {
3581*1c42de6dSgd78059 		/* absolute time */
3582*1c42de6dSgd78059 		mutex_enter(&tod_lock);
3583*1c42de6dSgd78059 		buf += bscv_event_snprintgmttime(buf, bufend-buf,
3584*1c42de6dSgd78059 			    utc_to_tod(bsctm));
3585*1c42de6dSgd78059 		mutex_exit(&tod_lock);
3586*1c42de6dSgd78059 	}
3587*1c42de6dSgd78059 	buf += snprintf(buf, bufend-buf, " ");
3588*1c42de6dSgd78059 
3589*1c42de6dSgd78059 	/* subsysp */
3590*1c42de6dSgd78059 	if (subsystem <
3591*1c42de6dSgd78059 	    (sizeof (eventSubsysStrings)/sizeof (*eventSubsysStrings))) {
3592*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf, "%s",
3593*1c42de6dSgd78059 		    eventSubsysStrings[subsystem]);
3594*1c42de6dSgd78059 	} else {
3595*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf,
3596*1c42de6dSgd78059 		    "unknown subsystem %d ", subsystem);
3597*1c42de6dSgd78059 	}
3598*1c42de6dSgd78059 
3599*1c42de6dSgd78059 	/* resource */
3600*1c42de6dSgd78059 	switch (subsystem) {
3601*1c42de6dSgd78059 	case EVENT_SUBSYS_ALARM:
3602*1c42de6dSgd78059 	case EVENT_SUBSYS_TEMP:
3603*1c42de6dSgd78059 	case EVENT_SUBSYS_OVERTEMP:
3604*1c42de6dSgd78059 	case EVENT_SUBSYS_FAN:
3605*1c42de6dSgd78059 	case EVENT_SUBSYS_SUPPLY:
3606*1c42de6dSgd78059 	case EVENT_SUBSYS_BREAKER:
3607*1c42de6dSgd78059 	case EVENT_SUBSYS_PSU:
3608*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf, "%d ", event->ev_resource);
3609*1c42de6dSgd78059 		break;
3610*1c42de6dSgd78059 	case EVENT_SUBSYS_LED:
3611*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf, "%s ", bscv_get_label(
3612*1c42de6dSgd78059 		    ssp->led_names, MAX_LED_ID, event->ev_resource - 1));
3613*1c42de6dSgd78059 		break;
3614*1c42de6dSgd78059 	default:
3615*1c42de6dSgd78059 		break;
3616*1c42de6dSgd78059 	}
3617*1c42de6dSgd78059 
3618*1c42de6dSgd78059 	/* fatal */
3619*1c42de6dSgd78059 	if (event->ev_subsys & EVENT_MASK_FAULT) {
3620*1c42de6dSgd78059 		if (event->ev_subsys & EVENT_MASK_FATAL) {
3621*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, "FATAL FAULT: ");
3622*1c42de6dSgd78059 		} else {
3623*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, "FAULT: ");
3624*1c42de6dSgd78059 		}
3625*1c42de6dSgd78059 	}
3626*1c42de6dSgd78059 
3627*1c42de6dSgd78059 	/* eventp */
3628*1c42de6dSgd78059 	if (eventtype <
3629*1c42de6dSgd78059 	    (sizeof (eventTypeStrings)/sizeof (*eventTypeStrings))) {
3630*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf, "%s",
3631*1c42de6dSgd78059 		    eventTypeStrings[eventtype]);
3632*1c42de6dSgd78059 	} else {
3633*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf,
3634*1c42de6dSgd78059 		    "unknown event 0x%02x%02x%02x%02x",
3635*1c42de6dSgd78059 		    event->ev_subsys, event->ev_event,
3636*1c42de6dSgd78059 		    event->ev_resource, event->ev_detail);
3637*1c42de6dSgd78059 	}
3638*1c42de6dSgd78059 
3639*1c42de6dSgd78059 	/* detail */
3640*1c42de6dSgd78059 	switch (subsystem) {
3641*1c42de6dSgd78059 	case EVENT_SUBSYS_TEMP:
3642*1c42de6dSgd78059 		if ((eventtype != EVENT_RECOVERED) &&
3643*1c42de6dSgd78059 			eventtype != EVENT_DEVICE_INACCESSIBLE) {
3644*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " - %d degC",
3645*1c42de6dSgd78059 			    (int8_t)event->ev_detail);
3646*1c42de6dSgd78059 		}
3647*1c42de6dSgd78059 		break;
3648*1c42de6dSgd78059 	case EVENT_SUBSYS_FAN:
3649*1c42de6dSgd78059 		if (eventtype == EVENT_FAILED) {
3650*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf,
3651*1c42de6dSgd78059 			    " %d%%", event->ev_detail);
3652*1c42de6dSgd78059 		}
3653*1c42de6dSgd78059 		break;
3654*1c42de6dSgd78059 	case EVENT_SUBSYS_LOM:
3655*1c42de6dSgd78059 		switch (eventtype) {
3656*1c42de6dSgd78059 		case EVENT_FLASH_DOWNLOAD:
3657*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf,
3658*1c42de6dSgd78059 			    ": v%d.%d to v%d.%d",
3659*1c42de6dSgd78059 			    (event->ev_resource >> 4),
3660*1c42de6dSgd78059 			    (event->ev_resource & 0x0f),
3661*1c42de6dSgd78059 			    (event->ev_detail >> 4),
3662*1c42de6dSgd78059 			    (event->ev_detail & 0x0f));
3663*1c42de6dSgd78059 			break;
3664*1c42de6dSgd78059 		case EVENT_WATCHDOG_TRIGGER:
3665*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf,
3666*1c42de6dSgd78059 			    event->ev_detail ? "- soft" : " - hard");
3667*1c42de6dSgd78059 			break;
3668*1c42de6dSgd78059 		case EVENT_UNEXPECTED_RESET:
3669*1c42de6dSgd78059 			if (event->ev_detail &
3670*1c42de6dSgd78059 			    LOM_UNEXPECTEDRESET_MASK_BADTRAP) {
3671*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3672*1c42de6dSgd78059 				    " - unclaimed exception 0x%x",
3673*1c42de6dSgd78059 				    event->ev_detail &
3674*1c42de6dSgd78059 				    ~LOM_UNEXPECTEDRESET_MASK_BADTRAP);
3675*1c42de6dSgd78059 			}
3676*1c42de6dSgd78059 			break;
3677*1c42de6dSgd78059 		case EVENT_RESET:
3678*1c42de6dSgd78059 			switch (event->ev_detail) {
3679*1c42de6dSgd78059 			case LOM_RESET_DETAIL_BYUSER:
3680*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf, " by user");
3681*1c42de6dSgd78059 				break;
3682*1c42de6dSgd78059 			case LOM_RESET_DETAIL_REPROGRAMMING:
3683*1c42de6dSgd78059 			    buf += snprintf(buf, bufend - buf,
3684*1c42de6dSgd78059 				" after flash download");
3685*1c42de6dSgd78059 				break;
3686*1c42de6dSgd78059 			default:
3687*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3688*1c42de6dSgd78059 				    " - unknown reason");
3689*1c42de6dSgd78059 				break;
3690*1c42de6dSgd78059 			}
3691*1c42de6dSgd78059 			break;
3692*1c42de6dSgd78059 		default:
3693*1c42de6dSgd78059 			break;
3694*1c42de6dSgd78059 		}
3695*1c42de6dSgd78059 		break;
3696*1c42de6dSgd78059 	case EVENT_SUBSYS_LED:
3697*1c42de6dSgd78059 		switch (event->ev_detail) {
3698*1c42de6dSgd78059 		case LOM_LED_STATE_OFF:
3699*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": OFF");
3700*1c42de6dSgd78059 			break;
3701*1c42de6dSgd78059 		case LOM_LED_STATE_ON_STEADY:
3702*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": ON");
3703*1c42de6dSgd78059 			break;
3704*1c42de6dSgd78059 		case LOM_LED_STATE_ON_FLASHING:
3705*1c42de6dSgd78059 		case LOM_LED_STATE_ON_SLOWFLASH:
3706*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": BLINKING");
3707*1c42de6dSgd78059 			break;
3708*1c42de6dSgd78059 		case LOM_LED_STATE_INACCESSIBLE:
3709*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": inaccessible");
3710*1c42de6dSgd78059 			break;
3711*1c42de6dSgd78059 		case LOM_LED_STATE_STANDBY:
3712*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": standby");
3713*1c42de6dSgd78059 			break;
3714*1c42de6dSgd78059 		case LOM_LED_STATE_NOT_PRESENT:
3715*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": not present");
3716*1c42de6dSgd78059 			break;
3717*1c42de6dSgd78059 		default:
3718*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, ": 0x%x",
3719*1c42de6dSgd78059 			    event->ev_resource);
3720*1c42de6dSgd78059 			break;
3721*1c42de6dSgd78059 		}
3722*1c42de6dSgd78059 		break;
3723*1c42de6dSgd78059 	case EVENT_SUBSYS_USER:
3724*1c42de6dSgd78059 		switch (eventtype) {
3725*1c42de6dSgd78059 		case EVENT_USER_ADDED:
3726*1c42de6dSgd78059 		case EVENT_USER_REMOVED:
3727*1c42de6dSgd78059 		case EVENT_USER_PERMSCHANGED:
3728*1c42de6dSgd78059 		case EVENT_USER_LOGIN:
3729*1c42de6dSgd78059 		case EVENT_USER_PASSWORD_CHANGE:
3730*1c42de6dSgd78059 		case EVENT_USER_LOGINFAIL:
3731*1c42de6dSgd78059 		case EVENT_USER_LOGOUT:
3732*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " %d",
3733*1c42de6dSgd78059 			    event->ev_resource);
3734*1c42de6dSgd78059 		default:
3735*1c42de6dSgd78059 			break;
3736*1c42de6dSgd78059 		}
3737*1c42de6dSgd78059 		break;
3738*1c42de6dSgd78059 	case EVENT_SUBSYS_PSU:
3739*1c42de6dSgd78059 		if (event->ev_detail & LOM_PSU_NOACCESS) {
3740*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " - inaccessible");
3741*1c42de6dSgd78059 		} else if ((event->ev_detail & LOM_PSU_STATUS_MASK)
3742*1c42de6dSgd78059 		    == LOM_PSU_STATUS_MASK) {
3743*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " - OK");
3744*1c42de6dSgd78059 		} else {
3745*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " -");
3746*1c42de6dSgd78059 			/*
3747*1c42de6dSgd78059 			 * If both inputs are seen to have failed then simply
3748*1c42de6dSgd78059 			 * indicate that the PSU input has failed
3749*1c42de6dSgd78059 			 */
3750*1c42de6dSgd78059 			if (!(event->ev_detail &
3751*1c42de6dSgd78059 			    (LOM_PSU_INPUT_A_OK | LOM_PSU_INPUT_B_OK))) {
3752*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf, " Input");
3753*1c42de6dSgd78059 			} else {
3754*1c42de6dSgd78059 				/* At least one input is ok */
3755*1c42de6dSgd78059 				if (!(event->ev_detail & LOM_PSU_INPUT_A_OK)) {
3756*1c42de6dSgd78059 					buf += snprintf(buf, bufend - buf,
3757*1c42de6dSgd78059 					    " InA");
3758*1c42de6dSgd78059 				}
3759*1c42de6dSgd78059 				if (!(event->ev_detail & LOM_PSU_INPUT_B_OK)) {
3760*1c42de6dSgd78059 					buf += snprintf(buf, bufend - buf,
3761*1c42de6dSgd78059 					    " InB");
3762*1c42de6dSgd78059 				}
3763*1c42de6dSgd78059 				/*
3764*1c42de6dSgd78059 				 * Only flag an output error if an input is
3765*1c42de6dSgd78059 				 * still present
3766*1c42de6dSgd78059 				 */
3767*1c42de6dSgd78059 				if (!(event->ev_detail & LOM_PSU_OUTPUT_OK)) {
3768*1c42de6dSgd78059 					buf += snprintf(buf, bufend - buf,
3769*1c42de6dSgd78059 					    " Output");
3770*1c42de6dSgd78059 				}
3771*1c42de6dSgd78059 			}
3772*1c42de6dSgd78059 			buf += snprintf(buf, bufend - buf, " failed");
3773*1c42de6dSgd78059 		}
3774*1c42de6dSgd78059 		break;
3775*1c42de6dSgd78059 	case EVENT_SUBSYS_NONE:
3776*1c42de6dSgd78059 		if (eventtype == EVENT_FAULT_LED) {
3777*1c42de6dSgd78059 			switch (event->ev_detail) {
3778*1c42de6dSgd78059 			case 0:
3779*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf, " - ON");
3780*1c42de6dSgd78059 				break;
3781*1c42de6dSgd78059 			case 255:
3782*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf, " - OFF");
3783*1c42de6dSgd78059 				break;
3784*1c42de6dSgd78059 			default:
3785*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3786*1c42de6dSgd78059 				    " - %dHz", event->ev_detail);
3787*1c42de6dSgd78059 				break;
3788*1c42de6dSgd78059 			}
3789*1c42de6dSgd78059 		}
3790*1c42de6dSgd78059 		break;
3791*1c42de6dSgd78059 	case EVENT_SUBSYS_HOST:
3792*1c42de6dSgd78059 		if (eventtype == EVENT_BOOTMODE_CHANGE) {
3793*1c42de6dSgd78059 			switch (event->ev_detail &
3794*1c42de6dSgd78059 			    ~EBUS_BOOTMODE_FORCE_CONSOLE) {
3795*1c42de6dSgd78059 			case EBUS_BOOTMODE_FORCE_NOBOOT:
3796*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3797*1c42de6dSgd78059 				    " - no boot");
3798*1c42de6dSgd78059 				break;
3799*1c42de6dSgd78059 			case EBUS_BOOTMODE_RESET_DEFAULT:
3800*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3801*1c42de6dSgd78059 				    " - reset defaults");
3802*1c42de6dSgd78059 				break;
3803*1c42de6dSgd78059 			case EBUS_BOOTMODE_FULLDIAG:
3804*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3805*1c42de6dSgd78059 				    " - full diag");
3806*1c42de6dSgd78059 				break;
3807*1c42de6dSgd78059 			case EBUS_BOOTMODE_SKIPDIAG:
3808*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3809*1c42de6dSgd78059 				    " - skip diag");
3810*1c42de6dSgd78059 				break;
3811*1c42de6dSgd78059 			default:
3812*1c42de6dSgd78059 				break;
3813*1c42de6dSgd78059 			}
3814*1c42de6dSgd78059 		}
3815*1c42de6dSgd78059 		if (eventtype == EVENT_SCC_STATUS) {
3816*1c42de6dSgd78059 			switch (event->ev_detail) {
3817*1c42de6dSgd78059 			case 0:
3818*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3819*1c42de6dSgd78059 				    " - inserted");
3820*1c42de6dSgd78059 				break;
3821*1c42de6dSgd78059 			case 1:
3822*1c42de6dSgd78059 				buf += snprintf(buf, bufend - buf,
3823*1c42de6dSgd78059 				    " - removed");
3824*1c42de6dSgd78059 				break;
3825*1c42de6dSgd78059 			default:
3826*1c42de6dSgd78059 				break;
3827*1c42de6dSgd78059 			}
3828*1c42de6dSgd78059 		}
3829*1c42de6dSgd78059 		break;
3830*1c42de6dSgd78059 
3831*1c42de6dSgd78059 	default:
3832*1c42de6dSgd78059 		break;
3833*1c42de6dSgd78059 	}
3834*1c42de6dSgd78059 
3835*1c42de6dSgd78059 	/* shutd */
3836*1c42de6dSgd78059 	if (event->ev_subsys & EVENT_MASK_SHUTDOWN_REQD) {
3837*1c42de6dSgd78059 		buf += snprintf(buf, bufend - buf, " - shutdown req'd");
3838*1c42de6dSgd78059 	}
3839*1c42de6dSgd78059 
3840*1c42de6dSgd78059 	buf += snprintf(buf, bufend - buf, "\n");
3841*1c42de6dSgd78059 
3842*1c42de6dSgd78059 	if (buf >= bufend) {
3843*1c42de6dSgd78059 		/* Ensure newline at end of string */
3844*1c42de6dSgd78059 		bufend[-2] = '\n';
3845*1c42de6dSgd78059 		bufend[-1] = '\0';
3846*1c42de6dSgd78059 #ifdef DEBUG
3847*1c42de6dSgd78059 		cmn_err(CE_WARN, "!bscv_build_eventstring: buffer too small!");
3848*1c42de6dSgd78059 #endif /* DEBUG */
3849*1c42de6dSgd78059 	}
3850*1c42de6dSgd78059 }
3851*1c42de6dSgd78059 
3852*1c42de6dSgd78059 /*
3853*1c42de6dSgd78059  * function	- bscv_level_of_event
3854*1c42de6dSgd78059  * description	- This routine determines which level an event should be
3855*1c42de6dSgd78059  *		  reported at.
3856*1c42de6dSgd78059  * inputs	- lom event structure pointer
3857*1c42de6dSgd78059  * outputs	- event level.
3858*1c42de6dSgd78059  */
3859*1c42de6dSgd78059 static int
3860*1c42de6dSgd78059 bscv_level_of_event(lom_event_t *event)
3861*1c42de6dSgd78059 {
3862*1c42de6dSgd78059 	int level;
3863*1c42de6dSgd78059 	/*
3864*1c42de6dSgd78059 	 * This is the same criteria that the firmware uses except we
3865*1c42de6dSgd78059 	 * log the fault led on as being EVENT_LEVEL_FAULT
3866*1c42de6dSgd78059 	 */
3867*1c42de6dSgd78059 	if (EVENT_DECODE_SUBSYS(event->ev_subsys) == EVENT_SUBSYS_USER) {
3868*1c42de6dSgd78059 		level = EVENT_LEVEL_USER;
3869*1c42de6dSgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
3870*1c42de6dSgd78059 	    EVENT_SUBSYS_ALARM) && (event->ev_event == EVENT_STATE_ON)) {
3871*1c42de6dSgd78059 		level = EVENT_LEVEL_FAULT;
3872*1c42de6dSgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
3873*1c42de6dSgd78059 	    EVENT_SUBSYS_NONE) &&
3874*1c42de6dSgd78059 	    (event->ev_event == EVENT_FAULT_LED) &&
3875*1c42de6dSgd78059 	    (event->ev_detail != 0xff)) {
3876*1c42de6dSgd78059 		level = EVENT_LEVEL_FAULT;
3877*1c42de6dSgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
3878*1c42de6dSgd78059 	    EVENT_SUBSYS_LOM) && event->ev_event == EVENT_TIME_REFERENCE) {
3879*1c42de6dSgd78059 		level = EVENT_LEVEL_NOTICE;
3880*1c42de6dSgd78059 	} else if (event->ev_event == EVENT_RECOVERED) {
3881*1c42de6dSgd78059 		/*
3882*1c42de6dSgd78059 		 * All recovery messages need to be reported to the console
3883*1c42de6dSgd78059 		 * because during boot, the faults which occurred whilst
3884*1c42de6dSgd78059 		 * Solaris was not running are relayed to the console.  There
3885*1c42de6dSgd78059 		 * is a case whereby a fatal fault (eg. over temp) could
3886*1c42de6dSgd78059 		 * have occurred and then recovered.  The recovery condition
3887*1c42de6dSgd78059 		 * needs to be reported so the user doesn't think that the
3888*1c42de6dSgd78059 		 * failure (over temp) is still present.
3889*1c42de6dSgd78059 		 */
3890*1c42de6dSgd78059 		level = EVENT_LEVEL_FAULT;
3891*1c42de6dSgd78059 	} else if (EVENT_DECODE_FAULT(event->ev_subsys) == 0) {
3892*1c42de6dSgd78059 		/* None of FAULT, FATAL or SHUTDOWN REQD are set */
3893*1c42de6dSgd78059 		level = EVENT_LEVEL_NOTICE;
3894*1c42de6dSgd78059 	} else if (EVENT_DECODE_FAULT(event->ev_subsys) == EVENT_MASK_FAULT) {
3895*1c42de6dSgd78059 		/* Only FAULT set i.e not FATAL or SHUTDOWN REQD */
3896*1c42de6dSgd78059 		level = EVENT_LEVEL_FAULT;
3897*1c42de6dSgd78059 	} else {
3898*1c42de6dSgd78059 		level = EVENT_LEVEL_FATAL;
3899*1c42de6dSgd78059 	}
3900*1c42de6dSgd78059 
3901*1c42de6dSgd78059 	return (level);
3902*1c42de6dSgd78059 }
3903*1c42de6dSgd78059 
3904*1c42de6dSgd78059 /*
3905*1c42de6dSgd78059  * function	- bscv_status
3906*1c42de6dSgd78059  * description	- This routine is called when any change in the LOMlite2 status
3907*1c42de6dSgd78059  *		  is indicated by the status registers.
3908*1c42de6dSgd78059  *
3909*1c42de6dSgd78059  * inputs	- LOM soft state structure pointer
3910*1c42de6dSgd78059  *
3911*1c42de6dSgd78059  * outputs	- none.
3912*1c42de6dSgd78059  */
3913*1c42de6dSgd78059 static void
3914*1c42de6dSgd78059 bscv_status(bscv_soft_state_t *ssp, uint8_t state_chng, uint8_t dev_no)
3915*1c42de6dSgd78059 {
3916*1c42de6dSgd78059 	int8_t temp;
3917*1c42de6dSgd78059 	uint8_t fanspeed;
3918*1c42de6dSgd78059 
3919*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
3920*1c42de6dSgd78059 
3921*1c42de6dSgd78059 	bscv_trace(ssp, 'D', "bscv_status", "state_chng 0x%x dev_no 0x%x",
3922*1c42de6dSgd78059 	    state_chng, dev_no);
3923*1c42de6dSgd78059 
3924*1c42de6dSgd78059 	/*
3925*1c42de6dSgd78059 	 * The device that has changed is given by the state change
3926*1c42de6dSgd78059 	 * register and the event detail register so react
3927*1c42de6dSgd78059 	 * accordingly.
3928*1c42de6dSgd78059 	 */
3929*1c42de6dSgd78059 
3930*1c42de6dSgd78059 	if (state_chng == EBUS_STATE_NOTIFY) {
3931*1c42de6dSgd78059 		/*
3932*1c42de6dSgd78059 		 * The BSC is indicating a self state change
3933*1c42de6dSgd78059 		 */
3934*1c42de6dSgd78059 		if (dev_no == EBUS_DETAIL_FLASH) {
3935*1c42de6dSgd78059 			ssp->cssp_prog = B_TRUE;
3936*1c42de6dSgd78059 			bscv_trace(ssp, 'D', "bscv_status",
3937*1c42de6dSgd78059 			    "ssp->cssp_prog changed to 0x%x",
3938*1c42de6dSgd78059 			    ssp->cssp_prog);
3939*1c42de6dSgd78059 			/*
3940*1c42de6dSgd78059 			 * It takes the BSC at least 100 ms to
3941*1c42de6dSgd78059 			 * clear down the comms protocol.
3942*1c42de6dSgd78059 			 * We back-off from talking to the
3943*1c42de6dSgd78059 			 * BSC during this period.
3944*1c42de6dSgd78059 			 */
3945*1c42de6dSgd78059 			delay(BSC_EVENT_POLL_NORMAL);
3946*1c42de6dSgd78059 			bscv_trace(ssp, 'D', "bscv_status",
3947*1c42de6dSgd78059 			    "completed delay");
3948*1c42de6dSgd78059 		} else if (dev_no == EBUS_DETAIL_RESET) {
3949*1c42de6dSgd78059 			/*
3950*1c42de6dSgd78059 			 * The bsc has reset
3951*1c42de6dSgd78059 			 */
3952*1c42de6dSgd78059 			bscv_trace(ssp, 'D', "bscv_status",
3953*1c42de6dSgd78059 			    "BSC reset occured, re-synching");
3954*1c42de6dSgd78059 			(void) bscv_attach_common(ssp);
3955*1c42de6dSgd78059 			bscv_trace(ssp, 'D', "bscv_status",
3956*1c42de6dSgd78059 			    "completed attach_common");
3957*1c42de6dSgd78059 		}
3958*1c42de6dSgd78059 
3959*1c42de6dSgd78059 	}
3960*1c42de6dSgd78059 
3961*1c42de6dSgd78059 	if ((state_chng & EBUS_STATE_FAN) && ((dev_no - 1) < MAX_FANS)) {
3962*1c42de6dSgd78059 		fanspeed = bscv_get8(ssp, chan_general,
3963*1c42de6dSgd78059 					EBUS_IDX_FAN1_SPEED + dev_no - 1);
3964*1c42de6dSgd78059 		/*
3965*1c42de6dSgd78059 		 * Only remember fanspeeds which are real values or
3966*1c42de6dSgd78059 		 * NOT PRESENT values.
3967*1c42de6dSgd78059 		 */
3968*1c42de6dSgd78059 		if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
3969*1c42de6dSgd78059 		    (fanspeed == LOM_FAN_NOT_PRESENT)) {
3970*1c42de6dSgd78059 			ssp->fanspeed[dev_no - 1] = fanspeed;
3971*1c42de6dSgd78059 		}
3972*1c42de6dSgd78059 	}
3973*1c42de6dSgd78059 
3974*1c42de6dSgd78059 	if ((state_chng & EBUS_STATE_PSU) && ((dev_no - 1) < MAX_PSUS)) {
3975*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general,
3976*1c42de6dSgd78059 					EBUS_IDX_PSU1_STAT + dev_no - 1);
3977*1c42de6dSgd78059 	}
3978*1c42de6dSgd78059 
3979*1c42de6dSgd78059 	if (state_chng & EBUS_STATE_GP) {
3980*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_GPIP);
3981*1c42de6dSgd78059 	}
3982*1c42de6dSgd78059 
3983*1c42de6dSgd78059 	if (state_chng & EBUS_STATE_CB) {
3984*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_CBREAK_STATUS);
3985*1c42de6dSgd78059 	}
3986*1c42de6dSgd78059 
3987*1c42de6dSgd78059 	if ((state_chng & EBUS_STATE_TEMPERATURE) &&
3988*1c42de6dSgd78059 	    ((dev_no - 1) < MAX_TEMPS)) {
3989*1c42de6dSgd78059 		temp = bscv_get8(ssp, chan_general,
3990*1c42de6dSgd78059 					EBUS_IDX_TEMP1 + dev_no - 1);
3991*1c42de6dSgd78059 		/*
3992*1c42de6dSgd78059 		 * Only remember temperatures which are real values or
3993*1c42de6dSgd78059 		 * a NOT PRESENT value.
3994*1c42de6dSgd78059 		 */
3995*1c42de6dSgd78059 		if ((temp <= LOM_TEMP_MAX_VALUE) ||
3996*1c42de6dSgd78059 		    (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
3997*1c42de6dSgd78059 			ssp->temps.temp[dev_no - 1] = temp;
3998*1c42de6dSgd78059 		}
3999*1c42de6dSgd78059 	}
4000*1c42de6dSgd78059 
4001*1c42de6dSgd78059 	if (state_chng & EBUS_STATE_RAIL) {
4002*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_LO);
4003*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_HI);
4004*1c42de6dSgd78059 	}
4005*1c42de6dSgd78059 }
4006*1c42de6dSgd78059 
4007*1c42de6dSgd78059 char *
4008*1c42de6dSgd78059 bscv_get_label(char labels[][MAX_LOM2_NAME_STR], int limit, int index)
4009*1c42de6dSgd78059 {
4010*1c42de6dSgd78059 
4011*1c42de6dSgd78059 	if (labels == NULL)
4012*1c42de6dSgd78059 		return ("");
4013*1c42de6dSgd78059 
4014*1c42de6dSgd78059 	if (limit < 0 || index < 0 || index > limit)
4015*1c42de6dSgd78059 		return ("-");
4016*1c42de6dSgd78059 
4017*1c42de6dSgd78059 	return (labels[index]);
4018*1c42de6dSgd78059 }
4019*1c42de6dSgd78059 
4020*1c42de6dSgd78059 static void
4021*1c42de6dSgd78059 bscv_generic_sysevent(bscv_soft_state_t *ssp, char *class, char *subclass,
4022*1c42de6dSgd78059     char *fru_id, char *res_id, int32_t fru_state, char *msg)
4023*1c42de6dSgd78059 {
4024*1c42de6dSgd78059 	int rv;
4025*1c42de6dSgd78059 	nvlist_t *attr_list;
4026*1c42de6dSgd78059 
4027*1c42de6dSgd78059 	bscv_trace(ssp, 'E', "bscv_generic_sysevent", "%s/%s:(%s,%s,%d) %s",
4028*1c42de6dSgd78059 	    class, subclass, fru_id, res_id, fru_state, msg);
4029*1c42de6dSgd78059 
4030*1c42de6dSgd78059 
4031*1c42de6dSgd78059 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP)) {
4032*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4033*1c42de6dSgd78059 		    "nvlist alloc failure");
4034*1c42de6dSgd78059 		return;
4035*1c42de6dSgd78059 	}
4036*1c42de6dSgd78059 	if (nvlist_add_uint32(attr_list, ENV_VERSION, 1)) {
4037*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4038*1c42de6dSgd78059 		    "nvlist ENV_VERSION failure");
4039*1c42de6dSgd78059 		nvlist_free(attr_list);
4040*1c42de6dSgd78059 		return;
4041*1c42de6dSgd78059 	}
4042*1c42de6dSgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_ID, fru_id)) {
4043*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4044*1c42de6dSgd78059 		    "nvlist ENV_FRU_ID failure");
4045*1c42de6dSgd78059 		nvlist_free(attr_list);
4046*1c42de6dSgd78059 		return;
4047*1c42de6dSgd78059 	}
4048*1c42de6dSgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, res_id)) {
4049*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4050*1c42de6dSgd78059 		    "nvlist ENV_FRU_RESOURCE_ID failure");
4051*1c42de6dSgd78059 		nvlist_free(attr_list);
4052*1c42de6dSgd78059 		return;
4053*1c42de6dSgd78059 	}
4054*1c42de6dSgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR)) {
4055*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4056*1c42de6dSgd78059 		    "nvlist ENV_FRU_DEVICE failure");
4057*1c42de6dSgd78059 		nvlist_free(attr_list);
4058*1c42de6dSgd78059 		return;
4059*1c42de6dSgd78059 	}
4060*1c42de6dSgd78059 	if (nvlist_add_int32(attr_list, ENV_FRU_STATE, fru_state)) {
4061*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4062*1c42de6dSgd78059 		    "nvlist ENV_FRU_STATE failure");
4063*1c42de6dSgd78059 		nvlist_free(attr_list);
4064*1c42de6dSgd78059 		return;
4065*1c42de6dSgd78059 	}
4066*1c42de6dSgd78059 	if (nvlist_add_string(attr_list, ENV_MSG, msg)) {
4067*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent",
4068*1c42de6dSgd78059 		    "nvlist ENV_MSG failure");
4069*1c42de6dSgd78059 		nvlist_free(attr_list);
4070*1c42de6dSgd78059 		return;
4071*1c42de6dSgd78059 	}
4072*1c42de6dSgd78059 
4073*1c42de6dSgd78059 	rv = ddi_log_sysevent(ssp->dip, DDI_VENDOR_SUNW, class,
4074*1c42de6dSgd78059 	    subclass, attr_list, NULL, DDI_SLEEP);
4075*1c42de6dSgd78059 
4076*1c42de6dSgd78059 	if (rv == DDI_SUCCESS) {
4077*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_generic_sysevent", "sent sysevent");
4078*1c42de6dSgd78059 	} else {
4079*1c42de6dSgd78059 		cmn_err(CE_WARN, "!cannot deliver sysevent");
4080*1c42de6dSgd78059 	}
4081*1c42de6dSgd78059 
4082*1c42de6dSgd78059 	nvlist_free(attr_list);
4083*1c42de6dSgd78059 }
4084*1c42de6dSgd78059 
4085*1c42de6dSgd78059 /*
4086*1c42de6dSgd78059  * function	- bscv_sysevent
4087*1c42de6dSgd78059  * description	- send out a sysevent on the given change if needed
4088*1c42de6dSgd78059  * inputs	- soft state pointer, event to report
4089*1c42de6dSgd78059  * outputs	- none
4090*1c42de6dSgd78059  */
4091*1c42de6dSgd78059 
4092*1c42de6dSgd78059 static void
4093*1c42de6dSgd78059 bscv_sysevent(bscv_soft_state_t *ssp, lom_event_t *event)
4094*1c42de6dSgd78059 {
4095*1c42de6dSgd78059 	char *class = NULL;
4096*1c42de6dSgd78059 	char *subclass = NULL;
4097*1c42de6dSgd78059 	char *fru_id = "Blade";	/* The blade is only one FRU */
4098*1c42de6dSgd78059 	char *res_id;
4099*1c42de6dSgd78059 	int32_t fru_state = 0;
4100*1c42de6dSgd78059 
4101*1c42de6dSgd78059 	bscv_trace(ssp, 'E', "bscv_sysevent", "processing event");
4102*1c42de6dSgd78059 
4103*1c42de6dSgd78059 	ASSERT(event != NULL);
4104*1c42de6dSgd78059 
4105*1c42de6dSgd78059 	/* Map ev_subsys to sysevent class/sub-class */
4106*1c42de6dSgd78059 
4107*1c42de6dSgd78059 	switch (EVENT_DECODE_SUBSYS(event->ev_subsys)) {
4108*1c42de6dSgd78059 	    case EVENT_SUBSYS_NONE:
4109*1c42de6dSgd78059 		break;
4110*1c42de6dSgd78059 	    case EVENT_SUBSYS_ALARM:
4111*1c42de6dSgd78059 		break;
4112*1c42de6dSgd78059 	    case EVENT_SUBSYS_TEMP:
4113*1c42de6dSgd78059 		class = EC_ENV, subclass = ESC_ENV_TEMP;
4114*1c42de6dSgd78059 		res_id = bscv_get_label(ssp->temps.name, ssp->temps.num,
4115*1c42de6dSgd78059 		    event->ev_resource - 1);
4116*1c42de6dSgd78059 		switch (event->ev_event) {
4117*1c42de6dSgd78059 		    case EVENT_SEVERE_OVERHEAT:
4118*1c42de6dSgd78059 			fru_state = ENV_FAILED;
4119*1c42de6dSgd78059 			break;
4120*1c42de6dSgd78059 		    case EVENT_OVERHEAT:
4121*1c42de6dSgd78059 			fru_state = ENV_WARNING;
4122*1c42de6dSgd78059 			break;
4123*1c42de6dSgd78059 		    case EVENT_NO_OVERHEAT:
4124*1c42de6dSgd78059 			fru_state = ENV_OK;
4125*1c42de6dSgd78059 			break;
4126*1c42de6dSgd78059 		    default:
4127*1c42de6dSgd78059 			return;
4128*1c42de6dSgd78059 		}
4129*1c42de6dSgd78059 		break;
4130*1c42de6dSgd78059 	    case EVENT_SUBSYS_OVERTEMP:
4131*1c42de6dSgd78059 		break;
4132*1c42de6dSgd78059 	    case EVENT_SUBSYS_FAN:
4133*1c42de6dSgd78059 		class = EC_ENV, subclass = ESC_ENV_FAN;
4134*1c42de6dSgd78059 		res_id = bscv_get_label(ssp->fan_names, ssp->num_fans,
4135*1c42de6dSgd78059 		    event->ev_resource - 1);
4136*1c42de6dSgd78059 		switch (event->ev_event) {
4137*1c42de6dSgd78059 		    case EVENT_FAILED:
4138*1c42de6dSgd78059 			fru_state = ENV_FAILED;
4139*1c42de6dSgd78059 			break;
4140*1c42de6dSgd78059 		    case EVENT_RECOVERED:
4141*1c42de6dSgd78059 			fru_state = ENV_OK;
4142*1c42de6dSgd78059 			break;
4143*1c42de6dSgd78059 		    default:
4144*1c42de6dSgd78059 			return;
4145*1c42de6dSgd78059 		}
4146*1c42de6dSgd78059 		break;
4147*1c42de6dSgd78059 	    case EVENT_SUBSYS_SUPPLY:
4148*1c42de6dSgd78059 		class = EC_ENV, subclass = ESC_ENV_POWER;
4149*1c42de6dSgd78059 		res_id = bscv_get_label(ssp->sflags.name, ssp->sflags.num,
4150*1c42de6dSgd78059 		    event->ev_resource - 1);
4151*1c42de6dSgd78059 		switch (event->ev_event) {
4152*1c42de6dSgd78059 		    case EVENT_FAILED:
4153*1c42de6dSgd78059 			fru_state = ENV_FAILED;
4154*1c42de6dSgd78059 			break;
4155*1c42de6dSgd78059 		    case EVENT_RECOVERED:
4156*1c42de6dSgd78059 			fru_state = ENV_OK;
4157*1c42de6dSgd78059 			break;
4158*1c42de6dSgd78059 		    default:
4159*1c42de6dSgd78059 			return;
4160*1c42de6dSgd78059 		}
4161*1c42de6dSgd78059 		break;
4162*1c42de6dSgd78059 	    case EVENT_SUBSYS_BREAKER:
4163*1c42de6dSgd78059 		break;
4164*1c42de6dSgd78059 	    case EVENT_SUBSYS_PSU:
4165*1c42de6dSgd78059 		break;
4166*1c42de6dSgd78059 	    case EVENT_SUBSYS_USER:
4167*1c42de6dSgd78059 		break;
4168*1c42de6dSgd78059 	    case EVENT_SUBSYS_PHONEHOME:
4169*1c42de6dSgd78059 		break;
4170*1c42de6dSgd78059 	    case EVENT_SUBSYS_LOM:
4171*1c42de6dSgd78059 		break;
4172*1c42de6dSgd78059 	    case EVENT_SUBSYS_HOST:
4173*1c42de6dSgd78059 		break;
4174*1c42de6dSgd78059 	    case EVENT_SUBSYS_EVENTLOG:
4175*1c42de6dSgd78059 		break;
4176*1c42de6dSgd78059 	    case EVENT_SUBSYS_EXTRA:
4177*1c42de6dSgd78059 		break;
4178*1c42de6dSgd78059 	    case EVENT_SUBSYS_LED:
4179*1c42de6dSgd78059 		if (event->ev_event != EVENT_FAULT_LED &&
4180*1c42de6dSgd78059 		    event->ev_event != EVENT_STATE_CHANGE)
4181*1c42de6dSgd78059 			return;
4182*1c42de6dSgd78059 		/*
4183*1c42de6dSgd78059 		 * There are 3 LEDs : Power, Service, Ready-to-Remove on a
4184*1c42de6dSgd78059 		 * JBOS blade.  We'll never report the Power since Solaris
4185*1c42de6dSgd78059 		 * won't be running when it is _switched_ ON.  Ready-to-Remove
4186*1c42de6dSgd78059 		 * will only be lit when we're powered down which also means
4187*1c42de6dSgd78059 		 * Solaris won't be running. We don't want to report it
4188*1c42de6dSgd78059 		 * during system testing / Sun VTS exercising the LEDs.
4189*1c42de6dSgd78059 		 *
4190*1c42de6dSgd78059 		 * Therefore, we only report the Service Required LED.
4191*1c42de6dSgd78059 		 */
4192*1c42de6dSgd78059 		class = EC_ENV, subclass = ESC_ENV_LED;
4193*1c42de6dSgd78059 		res_id = bscv_get_label(ssp->led_names, MAX_LED_ID,
4194*1c42de6dSgd78059 		    event->ev_resource - 1);
4195*1c42de6dSgd78059 
4196*1c42de6dSgd78059 		switch (event->ev_detail) {
4197*1c42de6dSgd78059 		    case LOM_LED_STATE_ON_STEADY:
4198*1c42de6dSgd78059 			fru_state = ENV_LED_ON;
4199*1c42de6dSgd78059 			break;
4200*1c42de6dSgd78059 		    case LOM_LED_STATE_ON_FLASHING:
4201*1c42de6dSgd78059 		    case LOM_LED_STATE_ON_SLOWFLASH:
4202*1c42de6dSgd78059 			fru_state = ENV_LED_BLINKING;
4203*1c42de6dSgd78059 			break;
4204*1c42de6dSgd78059 		    case LOM_LED_STATE_OFF:
4205*1c42de6dSgd78059 			fru_state = ENV_LED_OFF;
4206*1c42de6dSgd78059 			break;
4207*1c42de6dSgd78059 		    case LOM_LED_STATE_INACCESSIBLE:
4208*1c42de6dSgd78059 			fru_state = ENV_LED_INACCESSIBLE;
4209*1c42de6dSgd78059 			break;
4210*1c42de6dSgd78059 		    case LOM_LED_STATE_STANDBY:
4211*1c42de6dSgd78059 			fru_state = ENV_LED_STANDBY;
4212*1c42de6dSgd78059 			break;
4213*1c42de6dSgd78059 		    case LOM_LED_STATE_NOT_PRESENT:
4214*1c42de6dSgd78059 			fru_state = ENV_LED_NOT_PRESENT;
4215*1c42de6dSgd78059 			break;
4216*1c42de6dSgd78059 		    default:
4217*1c42de6dSgd78059 			fru_state = ENV_LED_INACCESSIBLE;
4218*1c42de6dSgd78059 			break;
4219*1c42de6dSgd78059 		}
4220*1c42de6dSgd78059 		break;
4221*1c42de6dSgd78059 	    default :
4222*1c42de6dSgd78059 		break;
4223*1c42de6dSgd78059 	}
4224*1c42de6dSgd78059 
4225*1c42de6dSgd78059 	if (class == NULL || subclass == NULL) {
4226*1c42de6dSgd78059 		bscv_trace(ssp, 'E', "bscv_sysevent", "class/subclass NULL");
4227*1c42de6dSgd78059 		return;
4228*1c42de6dSgd78059 	}
4229*1c42de6dSgd78059 
4230*1c42de6dSgd78059 	bscv_generic_sysevent(ssp, class, subclass, fru_id, res_id, fru_state,
4231*1c42de6dSgd78059 	    ENV_RESERVED_ATTR);
4232*1c42de6dSgd78059 }
4233*1c42de6dSgd78059 
4234*1c42de6dSgd78059 /*
4235*1c42de6dSgd78059  * *********************************************************************
4236*1c42de6dSgd78059  * Firmware download (programming)
4237*1c42de6dSgd78059  * *********************************************************************
4238*1c42de6dSgd78059  */
4239*1c42de6dSgd78059 
4240*1c42de6dSgd78059 /*
4241*1c42de6dSgd78059  * function	- bscv_prog
4242*1c42de6dSgd78059  * description	- LOMlite2 flash programming code.
4243*1c42de6dSgd78059  *
4244*1c42de6dSgd78059  *		  bscv_prog_image - download a complete image to the lom.
4245*1c42de6dSgd78059  *		  bscv_prog_receive_image - receive data to build up a
4246*1c42de6dSgd78059  *			complete image.
4247*1c42de6dSgd78059  *		  bscv_prog_stop_lom - pause the event daemon and prepare
4248*1c42de6dSgd78059  *			lom for firmware upgrade.
4249*1c42de6dSgd78059  *		  bscv_prog_start_lom - reinit the driver/lom after upgrade
4250*1c42de6dSgd78059  *			and restart the event daemon
4251*1c42de6dSgd78059  *
4252*1c42de6dSgd78059  * inputs	- soft state pointer, arg ptr, ioctl mode
4253*1c42de6dSgd78059  * outputs	- status
4254*1c42de6dSgd78059  */
4255*1c42de6dSgd78059 
4256*1c42de6dSgd78059 static int
4257*1c42de6dSgd78059 bscv_prog(bscv_soft_state_t *ssp, intptr_t arg, int mode)
4258*1c42de6dSgd78059 {
4259*1c42de6dSgd78059 	lom_prog_t *prog;
4260*1c42de6dSgd78059 	int res = 0;
4261*1c42de6dSgd78059 
4262*1c42de6dSgd78059 	/*
4263*1c42de6dSgd78059 	 * We will get repeatedly called with bits of data first for
4264*1c42de6dSgd78059 	 * loader, then for main image.
4265*1c42de6dSgd78059 	 */
4266*1c42de6dSgd78059 	prog = (lom_prog_t *)kmem_alloc(sizeof (lom_prog_t), KM_SLEEP);
4267*1c42de6dSgd78059 
4268*1c42de6dSgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)prog, sizeof (*prog),
4269*1c42de6dSgd78059 	    mode) < 0) {
4270*1c42de6dSgd78059 		kmem_free((void *)prog, sizeof (*prog));
4271*1c42de6dSgd78059 		return (EFAULT);
4272*1c42de6dSgd78059 	}
4273*1c42de6dSgd78059 
4274*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_prog",
4275*1c42de6dSgd78059 	    "index 0x%x size 0x%x", prog->index, prog->size);
4276*1c42de6dSgd78059 
4277*1c42de6dSgd78059 	mutex_enter(&ssp->prog_mu);
4278*1c42de6dSgd78059 	if (prog->size == 0) {
4279*1c42de6dSgd78059 		if (prog->index == 2) {
4280*1c42de6dSgd78059 			/*
4281*1c42de6dSgd78059 			 * This is the initial request for the chip type so we
4282*1c42de6dSgd78059 			 * know what we are programming.
4283*1c42de6dSgd78059 			 * The type will have been read in at init so just
4284*1c42de6dSgd78059 			 * return it in data[0].
4285*1c42de6dSgd78059 			 */
4286*1c42de6dSgd78059 			prog->data[0] = bscv_get8_cached(ssp,
4287*1c42de6dSgd78059 			    EBUS_IDX_CPU_IDENT);
4288*1c42de6dSgd78059 
4289*1c42de6dSgd78059 			if (ddi_copyout((caddr_t)prog, (caddr_t)arg,
4290*1c42de6dSgd78059 			    sizeof (lom_prog_t), mode) < 0) {
4291*1c42de6dSgd78059 				res = EFAULT;
4292*1c42de6dSgd78059 			}
4293*1c42de6dSgd78059 		} else if (prog->index == 0) {
4294*1c42de6dSgd78059 			res = bscv_prog_stop_lom(ssp);
4295*1c42de6dSgd78059 		} else if (prog->index == 1) {
4296*1c42de6dSgd78059 			res = bscv_prog_start_lom(ssp);
4297*1c42de6dSgd78059 		} else {
4298*1c42de6dSgd78059 			res = EINVAL;
4299*1c42de6dSgd78059 		}
4300*1c42de6dSgd78059 	} else {
4301*1c42de6dSgd78059 		if (ssp->image == NULL) {
4302*1c42de6dSgd78059 			ssp->image = (uint8_t *)kmem_zalloc(
4303*1c42de6dSgd78059 				BSC_IMAGE_MAX_SIZE, KM_SLEEP);
4304*1c42de6dSgd78059 		}
4305*1c42de6dSgd78059 		res = bscv_prog_receive_image(ssp, prog,
4306*1c42de6dSgd78059 		    ssp->image, BSC_IMAGE_MAX_SIZE);
4307*1c42de6dSgd78059 	}
4308*1c42de6dSgd78059 	mutex_exit(&ssp->prog_mu);
4309*1c42de6dSgd78059 	kmem_free((void *)prog, sizeof (lom_prog_t));
4310*1c42de6dSgd78059 
4311*1c42de6dSgd78059 	return (res);
4312*1c42de6dSgd78059 }
4313*1c42de6dSgd78059 
4314*1c42de6dSgd78059 static int
4315*1c42de6dSgd78059 bscv_check_loader_config(bscv_soft_state_t *ssp, boolean_t is_image2)
4316*1c42de6dSgd78059 {
4317*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_check_loader_config",
4318*1c42de6dSgd78059 	    "loader_running %d, is_image2 %d",
4319*1c42de6dSgd78059 	    ssp->loader_running, is_image2);
4320*1c42de6dSgd78059 
4321*1c42de6dSgd78059 	/*
4322*1c42de6dSgd78059 	 * loader_running TRUE means that we have told the microcontroller to
4323*1c42de6dSgd78059 	 * JUMP into the loader code which has been downloaded into its RAM.
4324*1c42de6dSgd78059 	 * At this point its an error to try and download another loader.  We
4325*1c42de6dSgd78059 	 * should be downloading the actual image at this point.
4326*1c42de6dSgd78059 	 * Conversely, it is an error to download an image when the loader is
4327*1c42de6dSgd78059 	 * not already downloaded and the microcontroller hasn't JUMPed into it.
4328*1c42de6dSgd78059 	 * is_image2 TRUE means the image is being downloaded.
4329*1c42de6dSgd78059 	 * is_image2 FALSE means the loader is being downloaded.
4330*1c42de6dSgd78059 	 */
4331*1c42de6dSgd78059 	if (ssp->loader_running && !is_image2) {
4332*1c42de6dSgd78059 		cmn_err(CE_WARN, "Attempt to download loader image "
4333*1c42de6dSgd78059 		    "with loader image already active");
4334*1c42de6dSgd78059 		cmn_err(CE_CONT, "This maybe an attempt to restart a "
4335*1c42de6dSgd78059 		    "failed firmware download - ignoring download attempt");
4336*1c42de6dSgd78059 		return (B_FALSE);
4337*1c42de6dSgd78059 	} else if (!ssp->loader_running && is_image2) {
4338*1c42de6dSgd78059 		cmn_err(CE_WARN, "Attempt to download firmware image "
4339*1c42de6dSgd78059 		    "without loader image active");
4340*1c42de6dSgd78059 		return (B_FALSE);
4341*1c42de6dSgd78059 
4342*1c42de6dSgd78059 	}
4343*1c42de6dSgd78059 
4344*1c42de6dSgd78059 	return (B_TRUE);
4345*1c42de6dSgd78059 }
4346*1c42de6dSgd78059 
4347*1c42de6dSgd78059 static uint32_t
4348*1c42de6dSgd78059 bscv_get_pagesize(bscv_soft_state_t *ssp)
4349*1c42de6dSgd78059 {
4350*1c42de6dSgd78059 	uint32_t pagesize;
4351*1c42de6dSgd78059 
4352*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4353*1c42de6dSgd78059 
4354*1c42de6dSgd78059 	pagesize = bscv_get32(ssp, chan_prog,
4355*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PAGE0));
4356*1c42de6dSgd78059 
4357*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_get_pagesize", "pagesize 0x%x", pagesize);
4358*1c42de6dSgd78059 
4359*1c42de6dSgd78059 	return (pagesize);
4360*1c42de6dSgd78059 }
4361*1c42de6dSgd78059 
4362*1c42de6dSgd78059 /*
4363*1c42de6dSgd78059  * Sets the pagesize, returning the old value.
4364*1c42de6dSgd78059  */
4365*1c42de6dSgd78059 static uint32_t
4366*1c42de6dSgd78059 bscv_set_pagesize(bscv_soft_state_t *ssp, uint32_t pagesize)
4367*1c42de6dSgd78059 {
4368*1c42de6dSgd78059 	uint32_t old_pagesize;
4369*1c42de6dSgd78059 
4370*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4371*1c42de6dSgd78059 
4372*1c42de6dSgd78059 	old_pagesize = bscv_get_pagesize(ssp);
4373*1c42de6dSgd78059 
4374*1c42de6dSgd78059 	/*
4375*1c42de6dSgd78059 	 * The microcontroller remembers this value until until someone
4376*1c42de6dSgd78059 	 * changes it.
4377*1c42de6dSgd78059 	 */
4378*1c42de6dSgd78059 	bscv_put32(ssp, chan_prog,
4379*1c42de6dSgd78059 		BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0), pagesize);
4380*1c42de6dSgd78059 
4381*1c42de6dSgd78059 	return (old_pagesize);
4382*1c42de6dSgd78059 }
4383*1c42de6dSgd78059 
4384*1c42de6dSgd78059 static uint8_t
4385*1c42de6dSgd78059 bscv_enter_programming_mode(bscv_soft_state_t *ssp)
4386*1c42de6dSgd78059 {
4387*1c42de6dSgd78059 	uint8_t retval;
4388*1c42de6dSgd78059 
4389*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4390*1c42de6dSgd78059 
4391*1c42de6dSgd78059 	bscv_put8(ssp, chan_prog,
4392*1c42de6dSgd78059 		BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
4393*1c42de6dSgd78059 		EBUS_PROGRAM_PCR_PRGMODE_ON);
4394*1c42de6dSgd78059 
4395*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan_prog, BSCVA(EBUS_CMD_SPACE_PROGRAM,
4396*1c42de6dSgd78059 		    EBUS_PROGRAM_PCSR));
4397*1c42de6dSgd78059 
4398*1c42de6dSgd78059 	return (retval);
4399*1c42de6dSgd78059 }
4400*1c42de6dSgd78059 
4401*1c42de6dSgd78059 static void
4402*1c42de6dSgd78059 bscv_leave_programming_mode(bscv_soft_state_t *ssp, boolean_t with_jmp)
4403*1c42de6dSgd78059 {
4404*1c42de6dSgd78059 	uint8_t reg;
4405*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4406*1c42de6dSgd78059 
4407*1c42de6dSgd78059 	if (with_jmp) {
4408*1c42de6dSgd78059 		reg = EBUS_PROGRAM_PCR_PROGOFF_JUMPTOADDR;
4409*1c42de6dSgd78059 		bscv_trace(ssp, 'U', "bscv_leave_programming_mode",
4410*1c42de6dSgd78059 		    "jumptoaddr");
4411*1c42de6dSgd78059 	} else {
4412*1c42de6dSgd78059 		reg = EBUS_PROGRAM_PCR_PRGMODE_OFF;
4413*1c42de6dSgd78059 		bscv_trace(ssp, 'U', "bscv_leave_programming_mode",
4414*1c42de6dSgd78059 		    "prgmode_off");
4415*1c42de6dSgd78059 	}
4416*1c42de6dSgd78059 
4417*1c42de6dSgd78059 	bscv_put8(ssp, chan_prog,
4418*1c42de6dSgd78059 		BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR), reg);
4419*1c42de6dSgd78059 }
4420*1c42de6dSgd78059 
4421*1c42de6dSgd78059 
4422*1c42de6dSgd78059 static void
4423*1c42de6dSgd78059 bscv_set_jump_to_addr(bscv_soft_state_t *ssp, uint32_t loadaddr)
4424*1c42de6dSgd78059 {
4425*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4426*1c42de6dSgd78059 
4427*1c42de6dSgd78059 	bscv_put32(ssp, chan_prog,
4428*1c42de6dSgd78059 		BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0), loadaddr);
4429*1c42de6dSgd78059 
4430*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_set_jump_to_addr",
4431*1c42de6dSgd78059 	    "set jump to loadaddr 0x%x", loadaddr);
4432*1c42de6dSgd78059 }
4433*1c42de6dSgd78059 
4434*1c42de6dSgd78059 static uint8_t
4435*1c42de6dSgd78059 bscv_erase_once(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size)
4436*1c42de6dSgd78059 {
4437*1c42de6dSgd78059 	uint8_t retval;
4438*1c42de6dSgd78059 
4439*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4440*1c42de6dSgd78059 
4441*1c42de6dSgd78059 	/*
4442*1c42de6dSgd78059 	 * write PADR, PSIZ to define area to be erased
4443*1c42de6dSgd78059 	 * We do not send erase for zero size because the current
4444*1c42de6dSgd78059 	 * downloader gets this wrong
4445*1c42de6dSgd78059 	 */
4446*1c42de6dSgd78059 
4447*1c42de6dSgd78059 	/*
4448*1c42de6dSgd78059 	 * start at 0
4449*1c42de6dSgd78059 	 */
4450*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_erase_once", "sending erase command");
4451*1c42de6dSgd78059 
4452*1c42de6dSgd78059 	bscv_put32(ssp, chan_prog,
4453*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
4454*1c42de6dSgd78059 	    loadaddr);
4455*1c42de6dSgd78059 
4456*1c42de6dSgd78059 	/* set PSIZ to full size of image to be programmed */
4457*1c42de6dSgd78059 	bscv_put32(ssp, chan_prog,
4458*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0),
4459*1c42de6dSgd78059 	    image_size);
4460*1c42de6dSgd78059 
4461*1c42de6dSgd78059 	/* write ERASE to PCSR */
4462*1c42de6dSgd78059 	bscv_put8(ssp, chan_prog,
4463*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
4464*1c42de6dSgd78059 	    EBUS_PROGRAM_PCR_ERASE);
4465*1c42de6dSgd78059 
4466*1c42de6dSgd78059 	/* read PCSR to check status */
4467*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan_prog,
4468*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
4469*1c42de6dSgd78059 	return (retval);
4470*1c42de6dSgd78059 }
4471*1c42de6dSgd78059 
4472*1c42de6dSgd78059 static uint8_t
4473*1c42de6dSgd78059 bscv_do_erase(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
4474*1c42de6dSgd78059     boolean_t is_image2)
4475*1c42de6dSgd78059 {
4476*1c42de6dSgd78059 	int retryable = BSC_ERASE_RETRY_LIMIT;
4477*1c42de6dSgd78059 	uint8_t retval;
4478*1c42de6dSgd78059 
4479*1c42de6dSgd78059 	while (retryable--) {
4480*1c42de6dSgd78059 		retval = bscv_erase_once(ssp, loadaddr, image_size);
4481*1c42de6dSgd78059 		if (PSR_SUCCESS(retval))
4482*1c42de6dSgd78059 			break;
4483*1c42de6dSgd78059 		else
4484*1c42de6dSgd78059 			cmn_err(CE_WARN, "erase error 0x%x, attempt %d"
4485*1c42de6dSgd78059 			    ", base 0x%x, size 0x%x, %s image",
4486*1c42de6dSgd78059 			    retval, BSC_ERASE_RETRY_LIMIT - retryable,
4487*1c42de6dSgd78059 			    loadaddr, image_size,
4488*1c42de6dSgd78059 			    is_image2 ? "main" : "loader");
4489*1c42de6dSgd78059 	}
4490*1c42de6dSgd78059 
4491*1c42de6dSgd78059 	return (retval);
4492*1c42de6dSgd78059 }
4493*1c42de6dSgd78059 
4494*1c42de6dSgd78059 static uint8_t
4495*1c42de6dSgd78059 bscv_set_page(bscv_soft_state_t *ssp, uint32_t addr)
4496*1c42de6dSgd78059 {
4497*1c42de6dSgd78059 	uint32_t retval;
4498*1c42de6dSgd78059 	int retryable = BSC_PAGE_RETRY_LIMIT;
4499*1c42de6dSgd78059 
4500*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4501*1c42de6dSgd78059 
4502*1c42de6dSgd78059 	while (retryable--) {
4503*1c42de6dSgd78059 
4504*1c42de6dSgd78059 		/*
4505*1c42de6dSgd78059 		 * Write the page address and read it back for confirmation.
4506*1c42de6dSgd78059 		 */
4507*1c42de6dSgd78059 		bscv_put32(ssp, chan_prog,
4508*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
4509*1c42de6dSgd78059 		    addr);
4510*1c42de6dSgd78059 		retval = bscv_get32(ssp, chan_prog,
4511*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0));
4512*1c42de6dSgd78059 
4513*1c42de6dSgd78059 		if (retval == addr)
4514*1c42de6dSgd78059 			break;
4515*1c42de6dSgd78059 		else {
4516*1c42de6dSgd78059 			cmn_err(CE_WARN, "programmming error, attempt %d, "
4517*1c42de6dSgd78059 			    "set page 0x%x, read back 0x%x",
4518*1c42de6dSgd78059 			    BSC_PAGE_RETRY_LIMIT - retryable,
4519*1c42de6dSgd78059 			    addr, retval);
4520*1c42de6dSgd78059 		}
4521*1c42de6dSgd78059 	}
4522*1c42de6dSgd78059 	return ((addr == retval) ? EBUS_PROGRAM_PSR_SUCCESS :
4523*1c42de6dSgd78059 	    EBUS_PROGRAM_PSR_INVALID_OPERATION);
4524*1c42de6dSgd78059 }
4525*1c42de6dSgd78059 
4526*1c42de6dSgd78059 static uint8_t
4527*1c42de6dSgd78059 bscv_do_page_data_once(bscv_soft_state_t *ssp, uint32_t index,
4528*1c42de6dSgd78059     uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
4529*1c42de6dSgd78059     uint16_t *calcd_chksum)
4530*1c42de6dSgd78059 {
4531*1c42de6dSgd78059 	uint32_t size;
4532*1c42de6dSgd78059 	uint16_t chksum;
4533*1c42de6dSgd78059 	int i;
4534*1c42de6dSgd78059 	uint8_t retval;
4535*1c42de6dSgd78059 
4536*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4537*1c42de6dSgd78059 
4538*1c42de6dSgd78059 	bscv_trace(ssp, 'P', "bscv_do_page_data_once", "index 0x%x", index);
4539*1c42de6dSgd78059 
4540*1c42de6dSgd78059 	/* write PSIZ bytes to PDAT */
4541*1c42de6dSgd78059 	if (index + pagesize < image_size) {
4542*1c42de6dSgd78059 		bscv_rep_rw8(ssp, chan_prog, imagep + index,
4543*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
4544*1c42de6dSgd78059 		    pagesize, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
4545*1c42de6dSgd78059 		size = pagesize;
4546*1c42de6dSgd78059 	} else {
4547*1c42de6dSgd78059 		bscv_trace(ssp, 'P', "bscv_do_page_once",
4548*1c42de6dSgd78059 		    "Sending last block, last 0x%x bytes",
4549*1c42de6dSgd78059 		    (image_size % pagesize));
4550*1c42de6dSgd78059 		size = (image_size - index);
4551*1c42de6dSgd78059 		bscv_rep_rw8(ssp, chan_prog, imagep + index,
4552*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
4553*1c42de6dSgd78059 		    size, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
4554*1c42de6dSgd78059 		/* Now pad the rest of the page with zeros */
4555*1c42de6dSgd78059 		for (i = size; i < pagesize; i++) {
4556*1c42de6dSgd78059 			bscv_put8(ssp, chan_prog,
4557*1c42de6dSgd78059 			    BSCVA(EBUS_CMD_SPACE_PROGRAM,
4558*1c42de6dSgd78059 				EBUS_PROGRAM_DATA),
4559*1c42de6dSgd78059 			    0);
4560*1c42de6dSgd78059 		}
4561*1c42de6dSgd78059 	}
4562*1c42de6dSgd78059 
4563*1c42de6dSgd78059 	/* write the checksum to PCSM */
4564*1c42de6dSgd78059 	chksum = 0;
4565*1c42de6dSgd78059 	for (i = 0; i < size; i++) {
4566*1c42de6dSgd78059 		chksum = ((chksum << 3) | (chksum >> 13)) ^
4567*1c42de6dSgd78059 			*(imagep + index + i);
4568*1c42de6dSgd78059 	}
4569*1c42de6dSgd78059 	/* Cope with non-pagesize sized bufers */
4570*1c42de6dSgd78059 	for (; i < pagesize; i++) {
4571*1c42de6dSgd78059 		chksum = ((chksum << 3) | (chksum >> 13)) ^ 0;
4572*1c42de6dSgd78059 	}
4573*1c42de6dSgd78059 	bscv_put16(ssp, chan_prog,
4574*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSM0), chksum);
4575*1c42de6dSgd78059 
4576*1c42de6dSgd78059 	bscv_put8(ssp, chan_prog,
4577*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
4578*1c42de6dSgd78059 	    EBUS_PROGRAM_PCR_PROGRAM);
4579*1c42de6dSgd78059 
4580*1c42de6dSgd78059 	retval = bscv_get8(ssp, chan_prog,
4581*1c42de6dSgd78059 			BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
4582*1c42de6dSgd78059 
4583*1c42de6dSgd78059 	*calcd_chksum = chksum;
4584*1c42de6dSgd78059 	return (retval);
4585*1c42de6dSgd78059 }
4586*1c42de6dSgd78059 
4587*1c42de6dSgd78059 static uint8_t bscv_do_page(bscv_soft_state_t *ssp, uint32_t loadaddr,
4588*1c42de6dSgd78059     uint32_t index, uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
4589*1c42de6dSgd78059     boolean_t is_image2)
4590*1c42de6dSgd78059 {
4591*1c42de6dSgd78059 	int retryable = BSC_PAGE_RETRY_LIMIT;
4592*1c42de6dSgd78059 	uint8_t retval;
4593*1c42de6dSgd78059 	uint16_t checksum;
4594*1c42de6dSgd78059 
4595*1c42de6dSgd78059 	bscv_trace(ssp, 'P', "bscv_do_page", "index 0x%x", index);
4596*1c42de6dSgd78059 
4597*1c42de6dSgd78059 	while (retryable--) {
4598*1c42de6dSgd78059 		/*
4599*1c42de6dSgd78059 		 * Set the page address (with retries).  If this is not
4600*1c42de6dSgd78059 		 * successful, then there is no point carrying on and sending
4601*1c42de6dSgd78059 		 * the page's data since that could cause random memory
4602*1c42de6dSgd78059 		 * corruption in the microcontroller.
4603*1c42de6dSgd78059 		 */
4604*1c42de6dSgd78059 		retval = bscv_set_page(ssp, loadaddr + index);
4605*1c42de6dSgd78059 		if (!PSR_SUCCESS(retval)) {
4606*1c42de6dSgd78059 			cmn_err(CE_WARN, "programming error 0x%x, "
4607*1c42de6dSgd78059 			    "could not setup page address 0x%x, %s image",
4608*1c42de6dSgd78059 			    retval, loadaddr + index,
4609*1c42de6dSgd78059 			    is_image2 ? "main" : "loader");
4610*1c42de6dSgd78059 			break;
4611*1c42de6dSgd78059 		}
4612*1c42de6dSgd78059 
4613*1c42de6dSgd78059 		/*
4614*1c42de6dSgd78059 		 * Send down the data for the page
4615*1c42de6dSgd78059 		 */
4616*1c42de6dSgd78059 
4617*1c42de6dSgd78059 		bscv_trace(ssp, 'P', "bscv_do_page", "sending data for page");
4618*1c42de6dSgd78059 
4619*1c42de6dSgd78059 		retval = bscv_do_page_data_once(ssp, index, image_size,
4620*1c42de6dSgd78059 		    pagesize, imagep, &checksum);
4621*1c42de6dSgd78059 		if (PSR_SUCCESS(retval))
4622*1c42de6dSgd78059 			break;
4623*1c42de6dSgd78059 		else
4624*1c42de6dSgd78059 			cmn_err(CE_WARN, "programming error 0x%x,"
4625*1c42de6dSgd78059 			    " attempt %d, index 0x%x, checksum 0x%x, %s image",
4626*1c42de6dSgd78059 			    retval, BSC_PAGE_RETRY_LIMIT - retryable,
4627*1c42de6dSgd78059 			    index, checksum, is_image2 ? "main" : "loader");
4628*1c42de6dSgd78059 	}
4629*1c42de6dSgd78059 
4630*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_do_page", "Returning 0x%x for index 0x%x,"
4631*1c42de6dSgd78059 	    " checksum 0x%x, %s image", retval, index, checksum,
4632*1c42de6dSgd78059 	    is_image2 ? "main" : "loader");
4633*1c42de6dSgd78059 
4634*1c42de6dSgd78059 	return (retval);
4635*1c42de6dSgd78059 }
4636*1c42de6dSgd78059 
4637*1c42de6dSgd78059 static uint8_t
4638*1c42de6dSgd78059 bscv_do_pages(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
4639*1c42de6dSgd78059     uint32_t pagesize, uint8_t *imagep, boolean_t is_image2)
4640*1c42de6dSgd78059 {
4641*1c42de6dSgd78059 	uint8_t retval;
4642*1c42de6dSgd78059 	uint32_t index;
4643*1c42de6dSgd78059 
4644*1c42de6dSgd78059 	bscv_trace(ssp, 'P', "bscv_do_pages", "entered");
4645*1c42de6dSgd78059 
4646*1c42de6dSgd78059 	for (index = 0; index < image_size; index += pagesize) {
4647*1c42de6dSgd78059 		retval = bscv_do_page(ssp, loadaddr, index, image_size,
4648*1c42de6dSgd78059 			    pagesize, imagep, is_image2);
4649*1c42de6dSgd78059 		if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
4650*1c42de6dSgd78059 			bscv_trace(ssp, 'U', "bscv_do_pages",
4651*1c42de6dSgd78059 			    "Failed to program lom (status 0x%x)", retval);
4652*1c42de6dSgd78059 			break;
4653*1c42de6dSgd78059 		}
4654*1c42de6dSgd78059 	}
4655*1c42de6dSgd78059 
4656*1c42de6dSgd78059 	return (retval);
4657*1c42de6dSgd78059 }
4658*1c42de6dSgd78059 
4659*1c42de6dSgd78059 static int
4660*1c42de6dSgd78059 bscv_prog_image(bscv_soft_state_t *ssp, boolean_t is_image2,
4661*1c42de6dSgd78059     uint8_t *imagep, int image_size, uint32_t loadaddr)
4662*1c42de6dSgd78059 {
4663*1c42de6dSgd78059 	uint32_t pagesize;
4664*1c42de6dSgd78059 	int res = 0;
4665*1c42de6dSgd78059 	uint8_t retval;
4666*1c42de6dSgd78059 
4667*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_prog_image",
4668*1c42de6dSgd78059 	    "image 0x%x, imagep %p, size 0x%x",
4669*1c42de6dSgd78059 	    is_image2 ? 2 : 1, imagep, image_size);
4670*1c42de6dSgd78059 
4671*1c42de6dSgd78059 	if (!bscv_check_loader_config(ssp, is_image2))
4672*1c42de6dSgd78059 		/*
4673*1c42de6dSgd78059 		 * Return no error to allow userland to continue on with
4674*1c42de6dSgd78059 		 * downloading the image.
4675*1c42de6dSgd78059 		 */
4676*1c42de6dSgd78059 		return (0);
4677*1c42de6dSgd78059 
4678*1c42de6dSgd78059 	bscv_enter(ssp);
4679*1c42de6dSgd78059 
4680*1c42de6dSgd78059 	pagesize = bscv_get_pagesize(ssp);
4681*1c42de6dSgd78059 
4682*1c42de6dSgd78059 	retval = bscv_enter_programming_mode(ssp);
4683*1c42de6dSgd78059 	if (bscv_faulty(ssp) || !PSR_PROG(retval)) {
4684*1c42de6dSgd78059 		cmn_err(CE_WARN, "lom: Failed to enter program mode, error 0x%x"
4685*1c42de6dSgd78059 		    ", %s image", retval, is_image2 ? "main" : "loader");
4686*1c42de6dSgd78059 		res = EIO;
4687*1c42de6dSgd78059 		goto BSCV_PROG_IMAGE_END;
4688*1c42de6dSgd78059 	}
4689*1c42de6dSgd78059 	bscv_trace(ssp, 'U', "bscv_prog_image", "entered programming mode");
4690*1c42de6dSgd78059 
4691*1c42de6dSgd78059 	/*
4692*1c42de6dSgd78059 	 * Only issue an erase if we are downloading the image.  The loader
4693*1c42de6dSgd78059 	 * does not need this step.
4694*1c42de6dSgd78059 	 */
4695*1c42de6dSgd78059 	if (is_image2 && (image_size != 0)) {
4696*1c42de6dSgd78059 		retval = bscv_do_erase(ssp, loadaddr, image_size, is_image2);
4697*1c42de6dSgd78059 		if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
4698*1c42de6dSgd78059 			cmn_err(CE_WARN,
4699*1c42de6dSgd78059 			    "lom: Erase failed during programming, status 0x%x",
4700*1c42de6dSgd78059 			    retval);
4701*1c42de6dSgd78059 			res = EIO;
4702*1c42de6dSgd78059 			goto BSCV_PROG_IMAGE_END;
4703*1c42de6dSgd78059 		} else {
4704*1c42de6dSgd78059 			bscv_trace(ssp, 'U', "bscv_prog_image",
4705*1c42de6dSgd78059 			    "erase complete - programming...");
4706*1c42de6dSgd78059 
4707*1c42de6dSgd78059 		}
4708*1c42de6dSgd78059 	}
4709*1c42de6dSgd78059 
4710*1c42de6dSgd78059 	(void) bscv_set_pagesize(ssp, pagesize);
4711*1c42de6dSgd78059 
4712*1c42de6dSgd78059 	retval = bscv_do_pages(ssp, loadaddr, image_size, pagesize, imagep,
4713*1c42de6dSgd78059 		    is_image2);
4714*1c42de6dSgd78059 	if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
4715*1c42de6dSgd78059 		bscv_trace(ssp, 'U', "bscv_prog_image",
4716*1c42de6dSgd78059 		    "Failed to program lom (status 0x%x)", retval);
4717*1c42de6dSgd78059 		res = EIO;
4718*1c42de6dSgd78059 		goto BSCV_PROG_IMAGE_END;
4719*1c42de6dSgd78059 	}
4720*1c42de6dSgd78059 
4721*1c42de6dSgd78059 BSCV_PROG_IMAGE_END:
4722*1c42de6dSgd78059 	if (res == 0 && !is_image2) {
4723*1c42de6dSgd78059 		/*
4724*1c42de6dSgd78059 		 * We've downloaded the loader successfully.  Now make the
4725*1c42de6dSgd78059 		 * microcontroller jump to it.
4726*1c42de6dSgd78059 		 */
4727*1c42de6dSgd78059 		bscv_set_jump_to_addr(ssp, loadaddr);
4728*1c42de6dSgd78059 		ssp->loader_running = B_TRUE;
4729*1c42de6dSgd78059 		bscv_leave_programming_mode(ssp, B_TRUE);
4730*1c42de6dSgd78059 	} else {
4731*1c42de6dSgd78059 		/*
4732*1c42de6dSgd78059 		 * We've just downloaded either the loader which failed, or
4733*1c42de6dSgd78059 		 * the image (which may or may not have been successful).
4734*1c42de6dSgd78059 		 */
4735*1c42de6dSgd78059 		bscv_set_jump_to_addr(ssp, 0);
4736*1c42de6dSgd78059 
4737*1c42de6dSgd78059 		if (res != 0) {
4738*1c42de6dSgd78059 			bscv_trace(ssp, 'U', "bscv_prog_image",
4739*1c42de6dSgd78059 			    "got error 0x%x - leaving programming mode",
4740*1c42de6dSgd78059 			    res);
4741*1c42de6dSgd78059 			cmn_err(CE_WARN, "programming error 0x%x, %s image",
4742*1c42de6dSgd78059 			    res, is_image2 ? "main" : "loader");
4743*1c42de6dSgd78059 		} else {
4744*1c42de6dSgd78059 			bscv_trace(ssp, 'U', "bscv_prog_image",
4745*1c42de6dSgd78059 			    "programming complete - leaving programming mode");
4746*1c42de6dSgd78059 		}
4747*1c42de6dSgd78059 
4748*1c42de6dSgd78059 		bscv_leave_programming_mode(ssp, B_FALSE);
4749*1c42de6dSgd78059 		ssp->loader_running = B_FALSE;
4750*1c42de6dSgd78059 	}
4751*1c42de6dSgd78059 
4752*1c42de6dSgd78059 	bscv_exit(ssp);
4753*1c42de6dSgd78059 
4754*1c42de6dSgd78059 	return (res);
4755*1c42de6dSgd78059 }
4756*1c42de6dSgd78059 
4757*1c42de6dSgd78059 
4758*1c42de6dSgd78059 static int
4759*1c42de6dSgd78059 bscv_prog_receive_image(bscv_soft_state_t *ssp, lom_prog_t *prog,
4760*1c42de6dSgd78059     uint8_t *imagep, int max_size)
4761*1c42de6dSgd78059 {
4762*1c42de6dSgd78059 	int	res = 0;
4763*1c42de6dSgd78059 	uint_t	size;
4764*1c42de6dSgd78059 	int32_t loadaddr;
4765*1c42de6dSgd78059 	lom_prog_data_t *prog_data;
4766*1c42de6dSgd78059 
4767*1c42de6dSgd78059 	if ((prog->index & 0x7FFF) != ssp->prog_index) {
4768*1c42de6dSgd78059 		bscv_trace(ssp, 'U', "bscv_prog_receive_image",
4769*1c42de6dSgd78059 		    "Got wrong buffer 0x%x, expected 0x%x",
4770*1c42de6dSgd78059 		    prog->index & 0x7fff, ssp->prog_index);
4771*1c42de6dSgd78059 		return (EINVAL);
4772*1c42de6dSgd78059 	}
4773*1c42de6dSgd78059 
4774*1c42de6dSgd78059 	/*
4775*1c42de6dSgd78059 	 * We want to get the whole image and then do the download.
4776*1c42de6dSgd78059 	 * It is assumed the device is now in programming mode.
4777*1c42de6dSgd78059 	 */
4778*1c42de6dSgd78059 
4779*1c42de6dSgd78059 	if ((prog->index & 0x7fff) == 0) {
4780*1c42de6dSgd78059 		/* Starting a new image */
4781*1c42de6dSgd78059 		ssp->image_ptr = 0;
4782*1c42de6dSgd78059 	}
4783*1c42de6dSgd78059 
4784*1c42de6dSgd78059 	if ((ssp->image_ptr + prog->size) > max_size) {
4785*1c42de6dSgd78059 		cmn_err(CE_WARN,
4786*1c42de6dSgd78059 		    "lom image exceeded maximum size: got 0x%x, maximum 0x%x",
4787*1c42de6dSgd78059 		    (ssp->image_ptr + prog->size), max_size);
4788*1c42de6dSgd78059 		return (EFAULT);
4789*1c42de6dSgd78059 	}
4790*1c42de6dSgd78059 	bcopy(prog->data, &imagep[ssp->image_ptr], prog->size);
4791*1c42de6dSgd78059 	ssp->image_ptr += prog->size;
4792*1c42de6dSgd78059 
4793*1c42de6dSgd78059 	ssp->prog_index++;
4794*1c42de6dSgd78059 
4795*1c42de6dSgd78059 	if (prog->index & 0x8000) {
4796*1c42de6dSgd78059 		/*
4797*1c42de6dSgd78059 		 * OK we have the whole image so synch up and start download.
4798*1c42de6dSgd78059 		 */
4799*1c42de6dSgd78059 		prog_data = (lom_prog_data_t *)imagep;
4800*1c42de6dSgd78059 		if (prog_data->header.magic != PROG_MAGIC) {
4801*1c42de6dSgd78059 			/* Old style programming data */
4802*1c42de6dSgd78059 			/* Take care image may not fill all of structure */
4803*1c42de6dSgd78059 
4804*1c42de6dSgd78059 			/* sign extend loadaddr from 16  to 32 bits */
4805*1c42de6dSgd78059 			loadaddr = (int16_t)((uint16_t)((imagep[2] << 8) +
4806*1c42de6dSgd78059 			    imagep[3]));
4807*1c42de6dSgd78059 
4808*1c42de6dSgd78059 			size = (imagep[0] << 8) + imagep[1];
4809*1c42de6dSgd78059 			if (size != (ssp->image_ptr - 4)) {
4810*1c42de6dSgd78059 				cmn_err(CE_WARN, "Image size mismatch:"
4811*1c42de6dSgd78059 				    " expected 0x%x, got 0x%x",
4812*1c42de6dSgd78059 				    size, (ssp->image_ptr - 1));
4813*1c42de6dSgd78059 			}
4814*1c42de6dSgd78059 
4815*1c42de6dSgd78059 			res = bscv_prog_image(ssp,
4816*1c42de6dSgd78059 			    ssp->image2_processing,
4817*1c42de6dSgd78059 			    imagep + 4, ssp->image_ptr - 4, loadaddr);
4818*1c42de6dSgd78059 
4819*1c42de6dSgd78059 			/*
4820*1c42de6dSgd78059 			 * Done the loading so set the flag to say we are doing
4821*1c42de6dSgd78059 			 * the other image.
4822*1c42de6dSgd78059 			 */
4823*1c42de6dSgd78059 			ssp->image2_processing = !ssp->image2_processing;
4824*1c42de6dSgd78059 		} else if ((ssp->image_ptr < sizeof (*prog_data)) ||
4825*1c42de6dSgd78059 		    (prog_data->platform.bscv.size !=
4826*1c42de6dSgd78059 			(ssp->image_ptr - sizeof (*prog_data)))) {
4827*1c42de6dSgd78059 			/* Image too small for new style image */
4828*1c42de6dSgd78059 			cmn_err(CE_WARN, "image too small");
4829*1c42de6dSgd78059 			res = EINVAL;
4830*1c42de6dSgd78059 		} else {
4831*1c42de6dSgd78059 			/* New style programming image */
4832*1c42de6dSgd78059 			switch (prog_data->platmagic) {
4833*1c42de6dSgd78059 			case PROG_PLAT_BSCV_IMAGE:
4834*1c42de6dSgd78059 				res = bscv_prog_image(ssp, B_TRUE,
4835*1c42de6dSgd78059 				    imagep + sizeof (*prog_data),
4836*1c42de6dSgd78059 				    prog_data->platform.bscv.size,
4837*1c42de6dSgd78059 				    prog_data->platform.bscv.loadaddr);
4838*1c42de6dSgd78059 				ssp->image2_processing = B_FALSE;
4839*1c42de6dSgd78059 				break;
4840*1c42de6dSgd78059 			case PROG_PLAT_BSCV_LOADER:
4841*1c42de6dSgd78059 				res = bscv_prog_image(ssp, B_FALSE,
4842*1c42de6dSgd78059 				    imagep + sizeof (*prog_data),
4843*1c42de6dSgd78059 				    prog_data->platform.bscv.size,
4844*1c42de6dSgd78059 				    prog_data->platform.bscv.loadaddr);
4845*1c42de6dSgd78059 				ssp->image2_processing = B_TRUE;
4846*1c42de6dSgd78059 				break;
4847*1c42de6dSgd78059 			default:
4848*1c42de6dSgd78059 				cmn_err(CE_WARN, "unknown platmagic 0x%x",
4849*1c42de6dSgd78059 				    prog_data->platmagic);
4850*1c42de6dSgd78059 				res = EINVAL;
4851*1c42de6dSgd78059 				break;
4852*1c42de6dSgd78059 			}
4853*1c42de6dSgd78059 		}
4854*1c42de6dSgd78059 		ssp->prog_index = 0;
4855*1c42de6dSgd78059 		ssp->image_ptr = 0;
4856*1c42de6dSgd78059 	}
4857*1c42de6dSgd78059 	return (res);
4858*1c42de6dSgd78059 }
4859*1c42de6dSgd78059 
4860*1c42de6dSgd78059 static int
4861*1c42de6dSgd78059 bscv_prog_stop_lom(bscv_soft_state_t *ssp)
4862*1c42de6dSgd78059 {
4863*1c42de6dSgd78059 	if (ssp->programming) {
4864*1c42de6dSgd78059 		/*
4865*1c42de6dSgd78059 		 * Already programming - this may be a retry of a failed
4866*1c42de6dSgd78059 		 * programming attempt or just a software error!
4867*1c42de6dSgd78059 		 */
4868*1c42de6dSgd78059 		goto queue_stopped;
4869*1c42de6dSgd78059 	}
4870*1c42de6dSgd78059 
4871*1c42de6dSgd78059 	if (bscv_pause_event_daemon(ssp) == BSCV_FAILURE) {
4872*1c42de6dSgd78059 		bscv_trace(ssp, 'Q', "bscv_prog_stop_lom",
4873*1c42de6dSgd78059 		    "failed to pause event daemon thread");
4874*1c42de6dSgd78059 		return (EAGAIN);
4875*1c42de6dSgd78059 	}
4876*1c42de6dSgd78059 
4877*1c42de6dSgd78059 	bscv_enter(ssp);
4878*1c42de6dSgd78059 
4879*1c42de6dSgd78059 	ssp->programming = B_TRUE;
4880*1c42de6dSgd78059 
4881*1c42de6dSgd78059 	bscv_exit(ssp);
4882*1c42de6dSgd78059 
4883*1c42de6dSgd78059 queue_stopped:
4884*1c42de6dSgd78059 
4885*1c42de6dSgd78059 	ssp->prog_index = 0;
4886*1c42de6dSgd78059 	ssp->image2_processing = B_FALSE;
4887*1c42de6dSgd78059 
4888*1c42de6dSgd78059 	return (0);
4889*1c42de6dSgd78059 }
4890*1c42de6dSgd78059 
4891*1c42de6dSgd78059 static int
4892*1c42de6dSgd78059 bscv_prog_start_lom(bscv_soft_state_t *ssp)
4893*1c42de6dSgd78059 {
4894*1c42de6dSgd78059 	int res = 0;
4895*1c42de6dSgd78059 
4896*1c42de6dSgd78059 	if (!ssp->programming) {
4897*1c42de6dSgd78059 		/* Not programming so this is not a valid command */
4898*1c42de6dSgd78059 		return (EINVAL);
4899*1c42de6dSgd78059 	}
4900*1c42de6dSgd78059 
4901*1c42de6dSgd78059 	if (ssp->image != NULL) {
4902*1c42de6dSgd78059 		kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
4903*1c42de6dSgd78059 		ssp->image = NULL;
4904*1c42de6dSgd78059 	}
4905*1c42de6dSgd78059 
4906*1c42de6dSgd78059 	/*
4907*1c42de6dSgd78059 	 * OK we are out of reset now so:
4908*1c42de6dSgd78059 	 * Probe the firmware and set everything up.
4909*1c42de6dSgd78059 	 */
4910*1c42de6dSgd78059 
4911*1c42de6dSgd78059 	bscv_enter(ssp);
4912*1c42de6dSgd78059 
4913*1c42de6dSgd78059 	/* Explicit clear fault because things may have been mended now */
4914*1c42de6dSgd78059 	bscv_clear_fault(ssp);
4915*1c42de6dSgd78059 
4916*1c42de6dSgd78059 	if (ssp->loader_running) {
4917*1c42de6dSgd78059 		cmn_err(CE_WARN, "Firmware upgrade failed to exit loader - "
4918*1c42de6dSgd78059 		    "performing forced exit");
4919*1c42de6dSgd78059 		/* Must try to restart the lom here. */
4920*1c42de6dSgd78059 		/* Ensure prog mode entry to enable PRGMODE_OFF */
4921*1c42de6dSgd78059 		bscv_put8(ssp, chan_prog,
4922*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
4923*1c42de6dSgd78059 		    EBUS_PROGRAM_PCR_PRGMODE_ON);
4924*1c42de6dSgd78059 		bscv_put8(ssp, chan_prog,
4925*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
4926*1c42de6dSgd78059 		    EBUS_PROGRAM_PCR_PRGMODE_OFF);
4927*1c42de6dSgd78059 		ssp->loader_running = B_FALSE;
4928*1c42de6dSgd78059 		/* give the lom chance to recover */
4929*1c42de6dSgd78059 		delay(drv_usectohz(5000000));	/* 5 seconds */
4930*1c42de6dSgd78059 	}
4931*1c42de6dSgd78059 
4932*1c42de6dSgd78059 	ssp->prog_mode_only = B_FALSE;
4933*1c42de6dSgd78059 	ssp->programming = B_FALSE;
4934*1c42de6dSgd78059 
4935*1c42de6dSgd78059 	if (bscv_attach_common(ssp) == DDI_FAILURE) {
4936*1c42de6dSgd78059 		ssp->prog_mode_only = B_TRUE;
4937*1c42de6dSgd78059 		res = EIO;
4938*1c42de6dSgd78059 	}
4939*1c42de6dSgd78059 
4940*1c42de6dSgd78059 	bscv_exit(ssp);
4941*1c42de6dSgd78059 
4942*1c42de6dSgd78059 	if (!ssp->prog_mode_only) {
4943*1c42de6dSgd78059 		/*
4944*1c42de6dSgd78059 		 * Start the event thread after the queue has started
4945*1c42de6dSgd78059 		 *
4946*1c42de6dSgd78059 		 * Not sure if this is entirely correct because
4947*1c42de6dSgd78059 		 * the other code at the end of bscv_attach()
4948*1c42de6dSgd78059 		 * does not get run here.
4949*1c42de6dSgd78059 		 */
4950*1c42de6dSgd78059 		bscv_start_event_daemon(ssp);
4951*1c42de6dSgd78059 		bscv_resume_event_daemon(ssp);
4952*1c42de6dSgd78059 	}
4953*1c42de6dSgd78059 
4954*1c42de6dSgd78059 	return (res);
4955*1c42de6dSgd78059 }
4956*1c42de6dSgd78059 
4957*1c42de6dSgd78059 
4958*1c42de6dSgd78059 /*
4959*1c42de6dSgd78059  * *********************************************************************
4960*1c42de6dSgd78059  * Attach processing
4961*1c42de6dSgd78059  * *********************************************************************
4962*1c42de6dSgd78059  */
4963*1c42de6dSgd78059 
4964*1c42de6dSgd78059 /*
4965*1c42de6dSgd78059  * function	- bscv_attach_common
4966*1c42de6dSgd78059  * description	- this routine co-ordinates the initialisation of the
4967*1c42de6dSgd78059  *		  driver both at attach time and after firmware programming.
4968*1c42de6dSgd78059  * sequence	- bscv_setup_capability - read LOMlite2 capabilities
4969*1c42de6dSgd78059  *		  bscv_probe_check - test comms and setup register cache
4970*1c42de6dSgd78059  *		  bscv_setup_hostname - sync stored name in lom with nodename.
4971*1c42de6dSgd78059  *		  bscv_setup_static_info - read device names etc.
4972*1c42de6dSgd78059  *		  bscv_setup_events - start event daemon etc.
4973*1c42de6dSgd78059  *
4974*1c42de6dSgd78059  * inputs	- device information structure, DDI_ATTACH command
4975*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
4976*1c42de6dSgd78059  */
4977*1c42de6dSgd78059 
4978*1c42de6dSgd78059 static int
4979*1c42de6dSgd78059 bscv_attach_common(bscv_soft_state_t *ssp)
4980*1c42de6dSgd78059 {
4981*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
4982*1c42de6dSgd78059 
4983*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_attach_common:", "");
4984*1c42de6dSgd78059 
4985*1c42de6dSgd78059 	/*
4986*1c42de6dSgd78059 	 * Set the threshold for reporting messages to the console to
4987*1c42de6dSgd78059 	 * Warnings or higher.
4988*1c42de6dSgd78059 	 */
4989*1c42de6dSgd78059 	ssp->reporting_level = 2;
4990*1c42de6dSgd78059 
4991*1c42de6dSgd78059 	/*
4992*1c42de6dSgd78059 	 * When the system is not running the Operating System, make
4993*1c42de6dSgd78059 	 * the microcontroller print event messages straight onto the
4994*1c42de6dSgd78059 	 * console.
4995*1c42de6dSgd78059 	 */
4996*1c42de6dSgd78059 	ssp->serial_reporting = LOM_SER_EVENTS_DEF;
4997*1c42de6dSgd78059 
4998*1c42de6dSgd78059 	/* Setup capabilities */
4999*1c42de6dSgd78059 	bscv_setup_capability(ssp);
5000*1c42de6dSgd78059 
5001*1c42de6dSgd78059 	if (bscv_probe_check(ssp) == DDI_FAILURE) {
5002*1c42de6dSgd78059 		cmn_err(CE_WARN, "BSC chip not responding");
5003*1c42de6dSgd78059 		/*
5004*1c42de6dSgd78059 		 * We want lom -G to talk to this driver upon broken firmware
5005*1c42de6dSgd78059 		 * so we prematurely return success here.
5006*1c42de6dSgd78059 		 */
5007*1c42de6dSgd78059 		return (DDI_SUCCESS);
5008*1c42de6dSgd78059 	}
5009*1c42de6dSgd78059 
5010*1c42de6dSgd78059 	bscv_setup_hostname(ssp);
5011*1c42de6dSgd78059 	bscv_setup_static_info(ssp);
5012*1c42de6dSgd78059 	bscv_setup_events(ssp);
5013*1c42de6dSgd78059 
5014*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
5015*1c42de6dSgd78059 	bscv_inform_bsc(ssp, BSC_INFORM_ONLINE);
5016*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
5017*1c42de6dSgd78059 	/*
5018*1c42de6dSgd78059 	 * Watchdog configuration and CPU signatures are sent asynchronously
5019*1c42de6dSgd78059 	 * with respect to attach so only inform the BSC if we've already
5020*1c42de6dSgd78059 	 * sent the data in the past.
5021*1c42de6dSgd78059 	 */
5022*1c42de6dSgd78059 
5023*1c42de6dSgd78059 	if (ssp->progress & BSCV_WDOG_CFG)
5024*1c42de6dSgd78059 		bscv_setup_watchdog(ssp);
5025*1c42de6dSgd78059 
5026*1c42de6dSgd78059 #ifdef __sparc
5027*1c42de6dSgd78059 	if (ssp->progress & BSCV_SIG_SENT)
5028*1c42de6dSgd78059 		bscv_write_sig(ssp, ssp->last_sig);
5029*1c42de6dSgd78059 #endif /* __sparc */
5030*1c42de6dSgd78059 
5031*1c42de6dSgd78059 	return (DDI_SUCCESS);
5032*1c42de6dSgd78059 }
5033*1c42de6dSgd78059 
5034*1c42de6dSgd78059 /*
5035*1c42de6dSgd78059  * function	- bscv_cleanup
5036*1c42de6dSgd78059  * description	- routine that does the necessary tidying up if the attach
5037*1c42de6dSgd78059  *		  request fails or the driver is to be detached.
5038*1c42de6dSgd78059  *		  If the event thread has been started we may fail to
5039*1c42de6dSgd78059  *		  stop it (because it is busy) so we fail the cleanup
5040*1c42de6dSgd78059  *		  and hence the detach. All other calls to bscv_cleanup
5041*1c42de6dSgd78059  *		  are done before the event daemon is started.
5042*1c42de6dSgd78059  * inputs	- soft state structure address.
5043*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE.
5044*1c42de6dSgd78059  */
5045*1c42de6dSgd78059 
5046*1c42de6dSgd78059 static int
5047*1c42de6dSgd78059 bscv_cleanup(bscv_soft_state_t *ssp)
5048*1c42de6dSgd78059 {
5049*1c42de6dSgd78059 	int	instance;
5050*1c42de6dSgd78059 	uint8_t bits2set;
5051*1c42de6dSgd78059 	uint8_t bits2clear;
5052*1c42de6dSgd78059 
5053*1c42de6dSgd78059 	instance = ssp->instance;
5054*1c42de6dSgd78059 
5055*1c42de6dSgd78059 	if (ssp->progress & BSCV_LOCKS) {
5056*1c42de6dSgd78059 		bscv_enter(ssp);
5057*1c42de6dSgd78059 	}
5058*1c42de6dSgd78059 
5059*1c42de6dSgd78059 	if (ssp->progress & BSCV_THREAD) {
5060*1c42de6dSgd78059 		if (bscv_stop_event_daemon(ssp) == DDI_FAILURE) {
5061*1c42de6dSgd78059 			/* Fail the cleanup - may be able to cleanup later */
5062*1c42de6dSgd78059 			if (ssp->progress & BSCV_LOCKS) {
5063*1c42de6dSgd78059 				bscv_exit(ssp);
5064*1c42de6dSgd78059 			}
5065*1c42de6dSgd78059 			return (DDI_FAILURE);
5066*1c42de6dSgd78059 		}
5067*1c42de6dSgd78059 	}
5068*1c42de6dSgd78059 
5069*1c42de6dSgd78059 	if (ssp->progress & BSCV_NODES) {
5070*1c42de6dSgd78059 		ddi_remove_minor_node(ssp->dip, NULL);
5071*1c42de6dSgd78059 	}
5072*1c42de6dSgd78059 
5073*1c42de6dSgd78059 	if (ssp->progress & BSCV_MAPPED_REGS) {
5074*1c42de6dSgd78059 		/*
5075*1c42de6dSgd78059 		 * switch back on serial event reporting - cover all configs.
5076*1c42de6dSgd78059 		 */
5077*1c42de6dSgd78059 		bits2set = 0;
5078*1c42de6dSgd78059 		bits2clear = 0;
5079*1c42de6dSgd78059 		if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
5080*1c42de6dSgd78059 			bits2clear |= EBUS_ALARM_NOEVENTS;
5081*1c42de6dSgd78059 		} else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
5082*1c42de6dSgd78059 			bits2set |= EBUS_ALARM_NOEVENTS;
5083*1c42de6dSgd78059 		} else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
5084*1c42de6dSgd78059 			bits2clear |= EBUS_ALARM_NOEVENTS;
5085*1c42de6dSgd78059 		}
5086*1c42de6dSgd78059 		bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
5087*1c42de6dSgd78059 		    bits2set, bits2clear);
5088*1c42de6dSgd78059 
5089*1c42de6dSgd78059 		/*
5090*1c42de6dSgd78059 		 * disable the reset function if we have enabled
5091*1c42de6dSgd78059 		 * it. We don't want any nasty surprises like system
5092*1c42de6dSgd78059 		 * rebooting unexpectedly.  If we timeout on the busy
5093*1c42de6dSgd78059 		 * flag we just have to carry on.
5094*1c42de6dSgd78059 		 */
5095*1c42de6dSgd78059 
5096*1c42de6dSgd78059 		bscv_trace(ssp, 'W', "bscv_cleanup",
5097*1c42de6dSgd78059 		    "bscv_cleanup - disable wdog");
5098*1c42de6dSgd78059 		if (bscv_get8_cached(ssp, EBUS_IDX_WDOG_CTRL) &
5099*1c42de6dSgd78059 		    EBUS_WDOG_ENABLE) {
5100*1c42de6dSgd78059 			bscv_setclear8(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
5101*1c42de6dSgd78059 			    0, EBUS_WDOG_RST | EBUS_WDOG_ENABLE);
5102*1c42de6dSgd78059 		}
5103*1c42de6dSgd78059 	}
5104*1c42de6dSgd78059 
5105*1c42de6dSgd78059 	/*
5106*1c42de6dSgd78059 	 * unmap registers
5107*1c42de6dSgd78059 	 */
5108*1c42de6dSgd78059 
5109*1c42de6dSgd78059 	if (ssp->progress & BSCV_MAPPED_REGS) {
5110*1c42de6dSgd78059 		bscv_unmap_regs(ssp);
5111*1c42de6dSgd78059 	}
5112*1c42de6dSgd78059 
5113*1c42de6dSgd78059 	/*
5114*1c42de6dSgd78059 	 * release any memory allocated for mutexes and condition
5115*1c42de6dSgd78059 	 * variables before deallocating the structures containing them
5116*1c42de6dSgd78059 	 */
5117*1c42de6dSgd78059 
5118*1c42de6dSgd78059 	if (ssp->progress & BSCV_LOCKS) {
5119*1c42de6dSgd78059 		bscv_exit(ssp);
5120*1c42de6dSgd78059 		cv_destroy(&ssp->task_cv);
5121*1c42de6dSgd78059 		cv_destroy(&ssp->task_evnt_cv);
5122*1c42de6dSgd78059 		mutex_destroy(&ssp->task_mu);
5123*1c42de6dSgd78059 		mutex_destroy(&ssp->prog_mu);
5124*1c42de6dSgd78059 		mutex_destroy(&ssp->cmd_mutex);
5125*1c42de6dSgd78059 	}
5126*1c42de6dSgd78059 
5127*1c42de6dSgd78059 	if (ssp->image != NULL) {
5128*1c42de6dSgd78059 		kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
5129*1c42de6dSgd78059 	}
5130*1c42de6dSgd78059 
5131*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
5132*1c42de6dSgd78059 	mutex_enter(&cpu_lock);
5133*1c42de6dSgd78059 	bscv_watchdog_cyclic_remove(ssp);
5134*1c42de6dSgd78059 	mutex_exit(&cpu_lock);
5135*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
5136*1c42de6dSgd78059 	ddi_soft_state_free(bscv_statep, instance);
5137*1c42de6dSgd78059 
5138*1c42de6dSgd78059 	return (DDI_SUCCESS);
5139*1c42de6dSgd78059 }
5140*1c42de6dSgd78059 
5141*1c42de6dSgd78059 /*
5142*1c42de6dSgd78059  * function	- bscv_setup_capability
5143*1c42de6dSgd78059  * description	- probe the lom find what capabilities are present for
5144*1c42de6dSgd78059  *		  us to use.
5145*1c42de6dSgd78059  * inputs	- soft state ptr
5146*1c42de6dSgd78059  * outputs	- returns DDI_SUCCESS or DDI_FAILURE
5147*1c42de6dSgd78059  */
5148*1c42de6dSgd78059 static void bscv_setup_capability(bscv_soft_state_t *ssp)
5149*1c42de6dSgd78059 {
5150*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5151*1c42de6dSgd78059 
5152*1c42de6dSgd78059 	if (ssp->prog_mode_only) {
5153*1c42de6dSgd78059 		/* Turn off all capabilities */
5154*1c42de6dSgd78059 		ssp->cap0 = 0;
5155*1c42de6dSgd78059 		ssp->cap1 = 0;
5156*1c42de6dSgd78059 		ssp->cap2 = 0;
5157*1c42de6dSgd78059 		return;
5158*1c42de6dSgd78059 	}
5159*1c42de6dSgd78059 
5160*1c42de6dSgd78059 	ssp->cap0 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP0);
5161*1c42de6dSgd78059 	ssp->cap1 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP1);
5162*1c42de6dSgd78059 	ssp->cap2 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP2);
5163*1c42de6dSgd78059 	if (!bscv_faulty(ssp)) {
5164*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_setup_capability",
5165*1c42de6dSgd78059 		    "Capability flags cap0=0x%x cap1=0x%x, cap2=0x%x",
5166*1c42de6dSgd78059 		    ssp->cap0, ssp->cap1, ssp->cap2);
5167*1c42de6dSgd78059 	} else {
5168*1c42de6dSgd78059 		cmn_err(CE_WARN, "!Could not read capability flags");
5169*1c42de6dSgd78059 		ssp->cap0 = 0; ssp->cap1 = 0; ssp->cap2 = 0;
5170*1c42de6dSgd78059 	}
5171*1c42de6dSgd78059 }
5172*1c42de6dSgd78059 
5173*1c42de6dSgd78059 /*
5174*1c42de6dSgd78059  * function	- bscv_probe_check
5175*1c42de6dSgd78059  * description	- probe the lom to check for correct operation
5176*1c42de6dSgd78059  *		  has a side effect of setting up the cached registers and
5177*1c42de6dSgd78059  *		  updates ssp->prog_mode_only.
5178*1c42de6dSgd78059  * inputs	- soft state ptr
5179*1c42de6dSgd78059  * outputs	- returns DDI_SUCCESS or DDI_FAILURE
5180*1c42de6dSgd78059  */
5181*1c42de6dSgd78059 
5182*1c42de6dSgd78059 static int bscv_probe_check(bscv_soft_state_t *ssp)
5183*1c42de6dSgd78059 {
5184*1c42de6dSgd78059 	int i;
5185*1c42de6dSgd78059 	uint8_t probeval;
5186*1c42de6dSgd78059 
5187*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5188*1c42de6dSgd78059 
5189*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_probe_check", "");
5190*1c42de6dSgd78059 
5191*1c42de6dSgd78059 	if (!ssp->prog_mode_only) {
5192*1c42de6dSgd78059 		/*
5193*1c42de6dSgd78059 		 * Make sure probe location is OK so that we are
5194*1c42de6dSgd78059 		 * in sync.
5195*1c42de6dSgd78059 		 * We want to make sure that this is not faulty so we
5196*1c42de6dSgd78059 		 * do a bscv_clear_fault to clear any existing
5197*1c42de6dSgd78059 		 * fault records down.
5198*1c42de6dSgd78059 		 */
5199*1c42de6dSgd78059 		bscv_clear_fault(ssp);
5200*1c42de6dSgd78059 		probeval = bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
5201*1c42de6dSgd78059 		if (bscv_faulty(ssp)) {
5202*1c42de6dSgd78059 			ssp->prog_mode_only = B_TRUE;
5203*1c42de6dSgd78059 		} else if (probeval != 0xAA) {
5204*1c42de6dSgd78059 			bscv_trace(ssp, 'A', "bscv_probe_check",
5205*1c42de6dSgd78059 			    "LOMlite out of sync");
5206*1c42de6dSgd78059 
5207*1c42de6dSgd78059 			/*
5208*1c42de6dSgd78059 			 * It may be that the LOMlite was out of
5209*1c42de6dSgd78059 			 * sync so lets try the read again.
5210*1c42de6dSgd78059 			 */
5211*1c42de6dSgd78059 			probeval = bscv_get8(ssp, chan_general,
5212*1c42de6dSgd78059 							EBUS_IDX_PROBEAA);
5213*1c42de6dSgd78059 			if (bscv_faulty(ssp)) {
5214*1c42de6dSgd78059 				bscv_trace(ssp, 'A', "bscv_probe_check",
5215*1c42de6dSgd78059 				    "Init readAA1 failed");
5216*1c42de6dSgd78059 				ssp->prog_mode_only = B_TRUE;
5217*1c42de6dSgd78059 			} else if (probeval != 0xAA) {
5218*1c42de6dSgd78059 				/*
5219*1c42de6dSgd78059 				 * OK that is twice we are out so I
5220*1c42de6dSgd78059 				 * guess the LOMlite is in trouble
5221*1c42de6dSgd78059 				 */
5222*1c42de6dSgd78059 				bscv_trace(ssp, 'A', "bscv_probe_check",
5223*1c42de6dSgd78059 				    "Init readAA probe failed - got 0x%x",
5224*1c42de6dSgd78059 				    probeval);
5225*1c42de6dSgd78059 				ssp->prog_mode_only = B_TRUE;
5226*1c42de6dSgd78059 			}
5227*1c42de6dSgd78059 		}
5228*1c42de6dSgd78059 	}
5229*1c42de6dSgd78059 
5230*1c42de6dSgd78059 	/*
5231*1c42de6dSgd78059 	 * Read in all page zero lom registers.
5232*1c42de6dSgd78059 	 * Read state change 1st so we dont miss anything and clear it.
5233*1c42de6dSgd78059 	 * Note: we discard the values because we rely on bscv_get8 to
5234*1c42de6dSgd78059 	 * setup the cache of register values.
5235*1c42de6dSgd78059 	 */
5236*1c42de6dSgd78059 
5237*1c42de6dSgd78059 	if (!ssp->prog_mode_only) {
5238*1c42de6dSgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
5239*1c42de6dSgd78059 		if (bscv_faulty(ssp)) {
5240*1c42de6dSgd78059 			bscv_trace(ssp, 'A', "bscv_probe_check",
5241*1c42de6dSgd78059 			    "Read of state change register failed");
5242*1c42de6dSgd78059 			ssp->prog_mode_only = B_TRUE;
5243*1c42de6dSgd78059 		}
5244*1c42de6dSgd78059 	}
5245*1c42de6dSgd78059 
5246*1c42de6dSgd78059 	if (!ssp->prog_mode_only) {
5247*1c42de6dSgd78059 		for (i = 1; i < 0x80; i++) {
5248*1c42de6dSgd78059 			switch (i) {
5249*1c42de6dSgd78059 			case EBUS_IDX_STATE_CHNG:
5250*1c42de6dSgd78059 			case EBUS_IDX_CMD_RES:
5251*1c42de6dSgd78059 			case EBUS_IDX_HNAME_CHAR:
5252*1c42de6dSgd78059 				/*
5253*1c42de6dSgd78059 				 * Should not read these - they have side
5254*1c42de6dSgd78059 				 * effects.
5255*1c42de6dSgd78059 				 */
5256*1c42de6dSgd78059 				break;
5257*1c42de6dSgd78059 			default:
5258*1c42de6dSgd78059 				(void) bscv_get8(ssp, chan_general, i);
5259*1c42de6dSgd78059 				break;
5260*1c42de6dSgd78059 			}
5261*1c42de6dSgd78059 			if (bscv_faulty(ssp)) {
5262*1c42de6dSgd78059 				bscv_trace(ssp, 'A', "bscv_probe_check",
5263*1c42de6dSgd78059 				    "Initial read or register %2x failed", i);
5264*1c42de6dSgd78059 				ssp->prog_mode_only = B_TRUE;
5265*1c42de6dSgd78059 				/* Might as well give up now! */
5266*1c42de6dSgd78059 				break;
5267*1c42de6dSgd78059 			}
5268*1c42de6dSgd78059 		}
5269*1c42de6dSgd78059 	}
5270*1c42de6dSgd78059 
5271*1c42de6dSgd78059 	/*
5272*1c42de6dSgd78059 	 * Check the probe keys so we know the lom is OK
5273*1c42de6dSgd78059 	 */
5274*1c42de6dSgd78059 
5275*1c42de6dSgd78059 	if (!ssp->prog_mode_only) {
5276*1c42de6dSgd78059 		if ((bscv_get8_cached(ssp, EBUS_IDX_PROBE55) != 0x55) ||
5277*1c42de6dSgd78059 		    (bscv_get8_cached(ssp, EBUS_IDX_PROBEAA) != 0xAA)) {
5278*1c42de6dSgd78059 
5279*1c42de6dSgd78059 			bscv_trace(ssp, 'A', "bscv_probe_check",
5280*1c42de6dSgd78059 			    "LOMlite Probe failed");
5281*1c42de6dSgd78059 			for (i = 0; i < 0x8; i++) {
5282*1c42de6dSgd78059 				bscv_trace(ssp, 'A', "bscv_probe_check",
5283*1c42de6dSgd78059 				    "%2x %2x %2x %2x %2x %2x %2x %2x %2x "
5284*1c42de6dSgd78059 				    "%2x %2x %2x %2x %2x %2x %2x %2x %2x",
5285*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i),
5286*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 1),
5287*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 2),
5288*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 3),
5289*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 4),
5290*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 5),
5291*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 6),
5292*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 7),
5293*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 8),
5294*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 9),
5295*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 10),
5296*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 11),
5297*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 12),
5298*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 13),
5299*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 14),
5300*1c42de6dSgd78059 				    bscv_get8_cached(ssp, i + 15));
5301*1c42de6dSgd78059 			}
5302*1c42de6dSgd78059 			ssp->prog_mode_only = B_TRUE;
5303*1c42de6dSgd78059 		}
5304*1c42de6dSgd78059 	}
5305*1c42de6dSgd78059 
5306*1c42de6dSgd78059 	return ((ssp->prog_mode_only == B_FALSE) ? DDI_SUCCESS : DDI_FAILURE);
5307*1c42de6dSgd78059 }
5308*1c42de6dSgd78059 
5309*1c42de6dSgd78059 #ifdef __sparc
5310*1c42de6dSgd78059 /*
5311*1c42de6dSgd78059  * function	- bscv_idi_set
5312*1c42de6dSgd78059  * description	- bscv inter driver interface set function
5313*1c42de6dSgd78059  * inputs	- structure which defines type of service required and data
5314*1c42de6dSgd78059  * ouputs	- none
5315*1c42de6dSgd78059  *
5316*1c42de6dSgd78059  * This is the Entry Point function for the platmod driver. It works out which
5317*1c42de6dSgd78059  * X Bus channel ought to deliver the service requested.
5318*1c42de6dSgd78059  */
5319*1c42de6dSgd78059 void
5320*1c42de6dSgd78059 bscv_idi_set(struct bscv_idi_info info)
5321*1c42de6dSgd78059 {
5322*1c42de6dSgd78059 	struct bscv_idi_callout *tbl;
5323*1c42de6dSgd78059 	boolean_t retval;
5324*1c42de6dSgd78059 
5325*1c42de6dSgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
5326*1c42de6dSgd78059 
5327*1c42de6dSgd78059 	if (bscv_idi_mgr.tbl == NULL) {
5328*1c42de6dSgd78059 		if (bscv_idi_err())
5329*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_idi_set : cannot find "
5330*1c42de6dSgd78059 			    "bscv_callout_table");
5331*1c42de6dSgd78059 		return;
5332*1c42de6dSgd78059 	} else if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
5333*1c42de6dSgd78059 		if (bscv_idi_err())
5334*1c42de6dSgd78059 			/*
5335*1c42de6dSgd78059 			 * This error message can appear in the context of
5336*1c42de6dSgd78059 			 * another driver, say platmod or todblade.  We want
5337*1c42de6dSgd78059 			 * to clearly indicate the culprit driver so put in
5338*1c42de6dSgd78059 			 * the driver name.
5339*1c42de6dSgd78059 			 */
5340*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_idi_set : no valid "
5341*1c42de6dSgd78059 			    "driver instance of "
5342*1c42de6dSgd78059 			    MYNAME);
5343*1c42de6dSgd78059 		return;
5344*1c42de6dSgd78059 	}
5345*1c42de6dSgd78059 
5346*1c42de6dSgd78059 	tbl = bscv_idi_mgr.tbl;
5347*1c42de6dSgd78059 
5348*1c42de6dSgd78059 	while (tbl->type != BSCV_IDI_NULL) {
5349*1c42de6dSgd78059 		if (tbl->type == info.type) {
5350*1c42de6dSgd78059 			/*
5351*1c42de6dSgd78059 			 * We service the request with a valid instance number
5352*1c42de6dSgd78059 			 * for the driver.
5353*1c42de6dSgd78059 			 */
5354*1c42de6dSgd78059 			retval = ((tbl->fn) (info));
5355*1c42de6dSgd78059 
5356*1c42de6dSgd78059 			/*
5357*1c42de6dSgd78059 			 * If the request was serviced, clear any accumulated
5358*1c42de6dSgd78059 			 * error counters so future warnings will be reported if
5359*1c42de6dSgd78059 			 * seen.
5360*1c42de6dSgd78059 			 */
5361*1c42de6dSgd78059 			if (retval == B_TRUE)
5362*1c42de6dSgd78059 				bscv_idi_clear_err();
5363*1c42de6dSgd78059 			return;
5364*1c42de6dSgd78059 		} else {
5365*1c42de6dSgd78059 			tbl++;
5366*1c42de6dSgd78059 		}
5367*1c42de6dSgd78059 	}
5368*1c42de6dSgd78059 
5369*1c42de6dSgd78059 	if (bscv_idi_err())
5370*1c42de6dSgd78059 		cmn_err(CE_WARN, "!bscv_idi_set : cannot match info.type %d",
5371*1c42de6dSgd78059 		    info.type);
5372*1c42de6dSgd78059 }
5373*1c42de6dSgd78059 
5374*1c42de6dSgd78059 /*
5375*1c42de6dSgd78059  * function     - bscv_nodename_set
5376*1c42de6dSgd78059  * description  - notify the event thread that a nodename change has occurred.
5377*1c42de6dSgd78059  * inputs       - data from client driver
5378*1c42de6dSgd78059  * outputs	- none.
5379*1c42de6dSgd78059  * side-effects - the event thread will schedule an update to the lom firmware.
5380*1c42de6dSgd78059  */
5381*1c42de6dSgd78059 /*ARGSUSED*/
5382*1c42de6dSgd78059 static boolean_t
5383*1c42de6dSgd78059 bscv_nodename_set(struct bscv_idi_info info)
5384*1c42de6dSgd78059 {
5385*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
5386*1c42de6dSgd78059 
5387*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
5388*1c42de6dSgd78059 
5389*1c42de6dSgd78059 	if (ssp == NULL) {
5390*1c42de6dSgd78059 		if (bscv_idi_err())
5391*1c42de6dSgd78059 			cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
5392*1c42de6dSgd78059 		return (B_FALSE);
5393*1c42de6dSgd78059 	}
5394*1c42de6dSgd78059 
5395*1c42de6dSgd78059 	/* Get a lock on the SSP, notify our change, then exit */
5396*1c42de6dSgd78059 	mutex_enter(&ssp->task_mu);
5397*1c42de6dSgd78059 	ssp->nodename_change = B_TRUE;
5398*1c42de6dSgd78059 	cv_signal(&ssp->task_cv);
5399*1c42de6dSgd78059 	mutex_exit(&ssp->task_mu);
5400*1c42de6dSgd78059 
5401*1c42de6dSgd78059 	return (B_TRUE);
5402*1c42de6dSgd78059 }
5403*1c42de6dSgd78059 
5404*1c42de6dSgd78059 /*
5405*1c42de6dSgd78059  * function	- bscv_sig_set
5406*1c42de6dSgd78059  * description	- write a signature
5407*1c42de6dSgd78059  * inputs	- data from client driver
5408*1c42de6dSgd78059  * outputs	- none.
5409*1c42de6dSgd78059  */
5410*1c42de6dSgd78059 static boolean_t
5411*1c42de6dSgd78059 bscv_sig_set(struct bscv_idi_info info)
5412*1c42de6dSgd78059 {
5413*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
5414*1c42de6dSgd78059 	bscv_sig_t sig;
5415*1c42de6dSgd78059 
5416*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
5417*1c42de6dSgd78059 
5418*1c42de6dSgd78059 	if (ssp == NULL) {
5419*1c42de6dSgd78059 		if (bscv_idi_err())
5420*1c42de6dSgd78059 			cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
5421*1c42de6dSgd78059 		return (B_FALSE);
5422*1c42de6dSgd78059 	}
5423*1c42de6dSgd78059 
5424*1c42de6dSgd78059 	/* Service the request */
5425*1c42de6dSgd78059 	bcopy(info.data, &sig, sizeof (sig));
5426*1c42de6dSgd78059 	bscv_enter(ssp);
5427*1c42de6dSgd78059 	bscv_write_sig(ssp, sig);
5428*1c42de6dSgd78059 	bscv_exit(ssp);
5429*1c42de6dSgd78059 
5430*1c42de6dSgd78059 	return (B_TRUE);
5431*1c42de6dSgd78059 }
5432*1c42de6dSgd78059 #endif /* __sparc */
5433*1c42de6dSgd78059 
5434*1c42de6dSgd78059 static void
5435*1c42de6dSgd78059 bscv_wdog_do_pat(bscv_soft_state_t *ssp)
5436*1c42de6dSgd78059 {
5437*1c42de6dSgd78059 	uint8_t pat;
5438*1c42de6dSgd78059 
5439*1c42de6dSgd78059 	/*
5440*1c42de6dSgd78059 	 * The value of the dog pat is a sequence number which wraps around,
5441*1c42de6dSgd78059 	 * bounded by BSCV_WDOG_PAT_SEQ_MASK.
5442*1c42de6dSgd78059 	 */
5443*1c42de6dSgd78059 	pat = ssp->pat_seq++;
5444*1c42de6dSgd78059 	pat &= EBUS_WDOG_NB_PAT_SEQ_MASK;
5445*1c42de6dSgd78059 
5446*1c42de6dSgd78059 	/* Set top nibble to indicate a pat */
5447*1c42de6dSgd78059 	pat |= EBUS_WDOG_NB_PAT;
5448*1c42de6dSgd78059 
5449*1c42de6dSgd78059 	/*
5450*1c42de6dSgd78059 	 * Now pat the dog.  This exercises a special protocol in the
5451*1c42de6dSgd78059 	 * bus nexus that offers : non-blocking IO, and timely delivery,
5452*1c42de6dSgd78059 	 * callable from high-level interrupt context.  The requirement
5453*1c42de6dSgd78059 	 * on us is that the channel is not shared for any other use.
5454*1c42de6dSgd78059 	 * This means for chan_wdogpat, nothing may use channel[chan].regs
5455*1c42de6dSgd78059 	 * or channel.[chan].handle.
5456*1c42de6dSgd78059 	 */
5457*1c42de6dSgd78059 
5458*1c42de6dSgd78059 	ddi_put8(ssp->channel[chan_wdogpat].handle,
5459*1c42de6dSgd78059 	    ssp->channel[chan_wdogpat].regs, pat);
5460*1c42de6dSgd78059 
5461*1c42de6dSgd78059 	bscv_trace(ssp, 'W', "bscv_wdog_pat", "patted the dog with seq %d",
5462*1c42de6dSgd78059 	    pat);
5463*1c42de6dSgd78059 }
5464*1c42de6dSgd78059 
5465*1c42de6dSgd78059 #ifdef __sparc
5466*1c42de6dSgd78059 /*
5467*1c42de6dSgd78059  * function	- bscv_wdog_pat
5468*1c42de6dSgd78059  * description	- pat the watchdog
5469*1c42de6dSgd78059  * inputs	- data from client driver
5470*1c42de6dSgd78059  * outputs	- none.
5471*1c42de6dSgd78059  */
5472*1c42de6dSgd78059 /*ARGSUSED*/
5473*1c42de6dSgd78059 static boolean_t
5474*1c42de6dSgd78059 bscv_wdog_pat(struct bscv_idi_info info)
5475*1c42de6dSgd78059 {
5476*1c42de6dSgd78059 	/*
5477*1c42de6dSgd78059 	 * This function remembers if it has ever been called with the
5478*1c42de6dSgd78059 	 * configure option set.
5479*1c42de6dSgd78059 	 */
5480*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
5481*1c42de6dSgd78059 
5482*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
5483*1c42de6dSgd78059 
5484*1c42de6dSgd78059 	if (ssp == NULL) {
5485*1c42de6dSgd78059 		if (bscv_idi_err())
5486*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_wdog_pat: cannot get ssp");
5487*1c42de6dSgd78059 		return (B_FALSE);
5488*1c42de6dSgd78059 	} else if (ssp->nchannels == 0) {
5489*1c42de6dSgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
5490*1c42de6dSgd78059 		if (bscv_idi_err())
5491*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_wdog_pat: handle not mapped");
5492*1c42de6dSgd78059 		return (B_FALSE);
5493*1c42de6dSgd78059 	}
5494*1c42de6dSgd78059 
5495*1c42de6dSgd78059 	bscv_wdog_do_pat(ssp);
5496*1c42de6dSgd78059 	return (B_TRUE);
5497*1c42de6dSgd78059 }
5498*1c42de6dSgd78059 
5499*1c42de6dSgd78059 /*
5500*1c42de6dSgd78059  * function	- bscv_wdog_cfg
5501*1c42de6dSgd78059  * description	- configure the watchdog
5502*1c42de6dSgd78059  * inputs	- data from client driver
5503*1c42de6dSgd78059  * outputs	- none.
5504*1c42de6dSgd78059  */
5505*1c42de6dSgd78059 static boolean_t
5506*1c42de6dSgd78059 bscv_wdog_cfg(struct bscv_idi_info info)
5507*1c42de6dSgd78059 {
5508*1c42de6dSgd78059 	bscv_soft_state_t *ssp;
5509*1c42de6dSgd78059 
5510*1c42de6dSgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
5511*1c42de6dSgd78059 
5512*1c42de6dSgd78059 	if (ssp == NULL) {
5513*1c42de6dSgd78059 		if (bscv_idi_err())
5514*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_wdog_cfg: cannot get ssp");
5515*1c42de6dSgd78059 		return (B_FALSE);
5516*1c42de6dSgd78059 	} else if (ssp->nchannels == 0) {
5517*1c42de6dSgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
5518*1c42de6dSgd78059 		if (bscv_idi_err())
5519*1c42de6dSgd78059 			cmn_err(CE_WARN, "!bscv_wdog_cfg: handle not mapped");
5520*1c42de6dSgd78059 		return (B_FALSE);
5521*1c42de6dSgd78059 	}
5522*1c42de6dSgd78059 
5523*1c42de6dSgd78059 	if (sizeof (bscv_wdog_t) != info.size) {
5524*1c42de6dSgd78059 		bscv_trace(ssp, 'W', "bscv_wdog_set", "data passed in is size"
5525*1c42de6dSgd78059 		    " %d instead of %d", info.size,
5526*1c42de6dSgd78059 		    sizeof (bscv_wdog_t));
5527*1c42de6dSgd78059 		return (B_FALSE);
5528*1c42de6dSgd78059 	}
5529*1c42de6dSgd78059 
5530*1c42de6dSgd78059 	bscv_trace(ssp, 'W', "bscv_wdog_cfg", "enable_wdog %s, "
5531*1c42de6dSgd78059 	    "wdog_timeout_s %d, reset_system_on_timeout %s",
5532*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->enable_wdog ? "enabled" : "disabled",
5533*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->wdog_timeout_s,
5534*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->reset_system_on_timeout ? "yes" : "no");
5535*1c42de6dSgd78059 	bscv_write_wdog_cfg(ssp,
5536*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->wdog_timeout_s,
5537*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->enable_wdog,
5538*1c42de6dSgd78059 	    ((bscv_wdog_t *)info.data)->reset_system_on_timeout);
5539*1c42de6dSgd78059 	return (B_TRUE);
5540*1c42de6dSgd78059 }
5541*1c42de6dSgd78059 #endif /* __sparc */
5542*1c42de6dSgd78059 
5543*1c42de6dSgd78059 static void
5544*1c42de6dSgd78059 bscv_write_wdog_cfg(bscv_soft_state_t *ssp,
5545*1c42de6dSgd78059     uint_t wdog_timeout_s,
5546*1c42de6dSgd78059     boolean_t enable_wdog,
5547*1c42de6dSgd78059     uint8_t reset_system_on_timeout)
5548*1c42de6dSgd78059 {
5549*1c42de6dSgd78059 	uint8_t cfg = EBUS_WDOG_NB_CFG;
5550*1c42de6dSgd78059 
5551*1c42de6dSgd78059 	/*
5552*1c42de6dSgd78059 	 * Configure the timeout value (1 to 127 seconds).
5553*1c42de6dSgd78059 	 * Note that a policy is implemented at the bsc/ssp which bounds
5554*1c42de6dSgd78059 	 * the value further. The bounding here is to fit the timeout value
5555*1c42de6dSgd78059 	 * into the 7 bits the bsc uses.
5556*1c42de6dSgd78059 	 */
5557*1c42de6dSgd78059 	if (wdog_timeout_s < 1)
5558*1c42de6dSgd78059 		ssp->watchdog_timeout = 1;
5559*1c42de6dSgd78059 	else if (wdog_timeout_s > 127)
5560*1c42de6dSgd78059 		ssp->watchdog_timeout = 127;
5561*1c42de6dSgd78059 	else
5562*1c42de6dSgd78059 		ssp->watchdog_timeout = wdog_timeout_s;
5563*1c42de6dSgd78059 
5564*1c42de6dSgd78059 	/*
5565*1c42de6dSgd78059 	 * Configure the watchdog on or off.
5566*1c42de6dSgd78059 	 */
5567*1c42de6dSgd78059 	if (enable_wdog)
5568*1c42de6dSgd78059 		cfg |= EBUS_WDOG_NB_CFG_ENB;
5569*1c42de6dSgd78059 	else
5570*1c42de6dSgd78059 		cfg &= ~EBUS_WDOG_NB_CFG_ENB;
5571*1c42de6dSgd78059 
5572*1c42de6dSgd78059 	/*
5573*1c42de6dSgd78059 	 * Configure whether the microcontroller should reset the system when
5574*1c42de6dSgd78059 	 * the watchdog expires.
5575*1c42de6dSgd78059 	 */
5576*1c42de6dSgd78059 	ssp->watchdog_reset_on_timeout = reset_system_on_timeout;
5577*1c42de6dSgd78059 
5578*1c42de6dSgd78059 	ddi_put8(ssp->channel[chan_wdogpat].handle,
5579*1c42de6dSgd78059 	    ssp->channel[chan_wdogpat].regs, cfg);
5580*1c42de6dSgd78059 
5581*1c42de6dSgd78059 	/* have the event daemon set the timeout value and whether to reset */
5582*1c42de6dSgd78059 	ssp->watchdog_change = B_TRUE;
5583*1c42de6dSgd78059 
5584*1c42de6dSgd78059 	bscv_trace(ssp, 'W', "bscv_wdog_cfg",
5585*1c42de6dSgd78059 	    "configured the dog with cfg 0x%x", cfg);
5586*1c42de6dSgd78059 }
5587*1c42de6dSgd78059 
5588*1c42de6dSgd78059 /*
5589*1c42de6dSgd78059  * function	- bscv_setup_watchdog
5590*1c42de6dSgd78059  * description	- setup the  bsc watchdog
5591*1c42de6dSgd78059  * inputs	- soft state ptr
5592*1c42de6dSgd78059  * outputs	-
5593*1c42de6dSgd78059  */
5594*1c42de6dSgd78059 static void bscv_setup_watchdog(bscv_soft_state_t *ssp)
5595*1c42de6dSgd78059 {
5596*1c42de6dSgd78059 	uint8_t set = 0;
5597*1c42de6dSgd78059 	uint8_t clear = 0;
5598*1c42de6dSgd78059 #ifdef __sparc
5599*1c42de6dSgd78059 	extern int watchdog_activated;
5600*1c42de6dSgd78059 #endif /* __sparc */
5601*1c42de6dSgd78059 
5602*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5603*1c42de6dSgd78059 
5604*1c42de6dSgd78059 	/* Set the timeout */
5605*1c42de6dSgd78059 	bscv_put8(ssp, chan_general,
5606*1c42de6dSgd78059 		EBUS_IDX_WDOG_TIME, ssp->watchdog_timeout);
5607*1c42de6dSgd78059 
5608*1c42de6dSgd78059 	/* Set whether to reset the system on timeout */
5609*1c42de6dSgd78059 	if (ssp->watchdog_reset_on_timeout) {
5610*1c42de6dSgd78059 		set |= EBUS_WDOG_RST;
5611*1c42de6dSgd78059 	} else {
5612*1c42de6dSgd78059 		clear |= EBUS_WDOG_RST;
5613*1c42de6dSgd78059 	}
5614*1c42de6dSgd78059 
5615*1c42de6dSgd78059 	if (watchdog_activated) {
5616*1c42de6dSgd78059 		set |= EBUS_WDOG_ENABLE;
5617*1c42de6dSgd78059 	} else {
5618*1c42de6dSgd78059 		clear |= EBUS_WDOG_ENABLE;
5619*1c42de6dSgd78059 	}
5620*1c42de6dSgd78059 
5621*1c42de6dSgd78059 	/* Set other host defaults */
5622*1c42de6dSgd78059 	clear |= (EBUS_WDOG_BREAK_DISABLE | EBUS_WDOG_AL3_FANPSU
5623*1c42de6dSgd78059 	    | EBUS_WDOG_AL3_WDOG);
5624*1c42de6dSgd78059 
5625*1c42de6dSgd78059 	bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
5626*1c42de6dSgd78059 	    set, clear);
5627*1c42de6dSgd78059 
5628*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
5629*1c42de6dSgd78059 	/* start the cyclic based watchdog patter */
5630*1c42de6dSgd78059 	mutex_enter(&cpu_lock);
5631*1c42de6dSgd78059 	bscv_watchdog_cyclic_add(ssp);
5632*1c42de6dSgd78059 	mutex_exit(&cpu_lock);
5633*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
5634*1c42de6dSgd78059 	ssp->progress |= BSCV_WDOG_CFG;
5635*1c42de6dSgd78059 }
5636*1c42de6dSgd78059 
5637*1c42de6dSgd78059 
5638*1c42de6dSgd78059 /*
5639*1c42de6dSgd78059  * function	- bscv_setup_hostname
5640*1c42de6dSgd78059  * description	- setup the lom hostname if different from the nodename
5641*1c42de6dSgd78059  * inputs	- soft state ptr
5642*1c42de6dSgd78059  * outputs	- none
5643*1c42de6dSgd78059  */
5644*1c42de6dSgd78059 
5645*1c42de6dSgd78059 static void bscv_setup_hostname(bscv_soft_state_t *ssp)
5646*1c42de6dSgd78059 {
5647*1c42de6dSgd78059 	char	host_nodename[128];
5648*1c42de6dSgd78059 	char	lom_nodename[128];
5649*1c42de6dSgd78059 	size_t	hostlen;
5650*1c42de6dSgd78059 	size_t	nodelen;
5651*1c42de6dSgd78059 
5652*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5653*1c42de6dSgd78059 
5654*1c42de6dSgd78059 	/*
5655*1c42de6dSgd78059 	 * Check machine label is the same as the
5656*1c42de6dSgd78059 	 * system nodename.
5657*1c42de6dSgd78059 	 */
5658*1c42de6dSgd78059 	(void) strncpy(host_nodename, utsname.nodename,
5659*1c42de6dSgd78059 	    sizeof (host_nodename));
5660*1c42de6dSgd78059 
5661*1c42de6dSgd78059 	/* read in lom hostname */
5662*1c42de6dSgd78059 	bscv_read_hostname(ssp, lom_nodename);
5663*1c42de6dSgd78059 
5664*1c42de6dSgd78059 	/* Enforce null termination */
5665*1c42de6dSgd78059 	host_nodename[sizeof (host_nodename) - 1] = '\0';
5666*1c42de6dSgd78059 	lom_nodename[sizeof (lom_nodename) - 1] = '\0';
5667*1c42de6dSgd78059 
5668*1c42de6dSgd78059 	hostlen = (size_t)bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
5669*1c42de6dSgd78059 	nodelen = (size_t)strlen(host_nodename);
5670*1c42de6dSgd78059 	if ((nodelen > 0) &&
5671*1c42de6dSgd78059 	    ((hostlen != nodelen) || (strcmp((const char *)&lom_nodename,
5672*1c42de6dSgd78059 	    (const char *)&host_nodename)) ||
5673*1c42de6dSgd78059 	    (hostlen == 0))) {
5674*1c42de6dSgd78059 		bscv_trace(ssp, 'A', "bscv_setup_hostname",
5675*1c42de6dSgd78059 		    "nodename(%s,%d) != bsc label(%s,%d)",
5676*1c42de6dSgd78059 		    host_nodename, nodelen, lom_nodename, hostlen);
5677*1c42de6dSgd78059 
5678*1c42de6dSgd78059 		/* Write new label into LOM EEPROM */
5679*1c42de6dSgd78059 		bscv_write_hostname(ssp,
5680*1c42de6dSgd78059 		    host_nodename,
5681*1c42de6dSgd78059 		    (uint8_t)strlen(host_nodename));
5682*1c42de6dSgd78059 	}
5683*1c42de6dSgd78059 
5684*1c42de6dSgd78059 	ssp->progress |= BSCV_HOSTNAME_DONE;
5685*1c42de6dSgd78059 }
5686*1c42de6dSgd78059 
5687*1c42de6dSgd78059 /*
5688*1c42de6dSgd78059  * function	- bscv_read_hostname
5689*1c42de6dSgd78059  * description	- read the current hostname from the lom
5690*1c42de6dSgd78059  * inputs	- soft state pointer and buffer to store the hostname in.
5691*1c42de6dSgd78059  * outputs	- none
5692*1c42de6dSgd78059  */
5693*1c42de6dSgd78059 
5694*1c42de6dSgd78059 static void
5695*1c42de6dSgd78059 bscv_read_hostname(bscv_soft_state_t *ssp, char *lom_nodename)
5696*1c42de6dSgd78059 {
5697*1c42de6dSgd78059 	int num_failures;
5698*1c42de6dSgd78059 	boolean_t needretry;
5699*1c42de6dSgd78059 	int length;
5700*1c42de6dSgd78059 	int i;
5701*1c42de6dSgd78059 
5702*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5703*1c42de6dSgd78059 
5704*1c42de6dSgd78059 	/*
5705*1c42de6dSgd78059 	 * We have a special failure case here because a retry of a read
5706*1c42de6dSgd78059 	 * causes data to be lost. Thus we handle the retries ourselves
5707*1c42de6dSgd78059 	 * and are also responsible for detemining if the lom is faulty
5708*1c42de6dSgd78059 	 */
5709*1c42de6dSgd78059 	for (num_failures = 0;
5710*1c42de6dSgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
5711*1c42de6dSgd78059 	    num_failures++) {
5712*1c42de6dSgd78059 		bscv_clear_fault(ssp);
5713*1c42de6dSgd78059 		length = bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
5714*1c42de6dSgd78059 		if (bscv_faulty(ssp)) {
5715*1c42de6dSgd78059 			needretry = 1;
5716*1c42de6dSgd78059 		} else {
5717*1c42de6dSgd78059 			needretry = 0;
5718*1c42de6dSgd78059 			for (i = 0; i < length; i++) {
5719*1c42de6dSgd78059 				lom_nodename[i] = bscv_get8_once(ssp,
5720*1c42de6dSgd78059 				    chan_general, EBUS_IDX_HNAME_CHAR);
5721*1c42de6dSgd78059 				/* Retry on any error */
5722*1c42de6dSgd78059 				if (bscv_retcode(ssp) != 0) {
5723*1c42de6dSgd78059 					needretry = 1;
5724*1c42de6dSgd78059 					break;
5725*1c42de6dSgd78059 				}
5726*1c42de6dSgd78059 			}
5727*1c42de6dSgd78059 			/* null terminate for strcmp later */
5728*1c42de6dSgd78059 			lom_nodename[length] = '\0';
5729*1c42de6dSgd78059 		}
5730*1c42de6dSgd78059 		if (!needretry) {
5731*1c42de6dSgd78059 			break;
5732*1c42de6dSgd78059 		}
5733*1c42de6dSgd78059 		/* Force the nodename to be empty */
5734*1c42de6dSgd78059 		lom_nodename[0] = '\0';
5735*1c42de6dSgd78059 	}
5736*1c42de6dSgd78059 
5737*1c42de6dSgd78059 	if (needretry) {
5738*1c42de6dSgd78059 		/* Failure - we ran out of retries */
5739*1c42de6dSgd78059 		cmn_err(CE_WARN,
5740*1c42de6dSgd78059 		    "bscv_read_hostname: retried %d times, giving up",
5741*1c42de6dSgd78059 		    num_failures);
5742*1c42de6dSgd78059 		ssp->had_fault = B_TRUE;
5743*1c42de6dSgd78059 	} else if (num_failures > 0) {
5744*1c42de6dSgd78059 		bscv_trace(ssp, 'R', "bscv_read_hostname",
5745*1c42de6dSgd78059 		    "retried %d times, succeeded", num_failures);
5746*1c42de6dSgd78059 	}
5747*1c42de6dSgd78059 }
5748*1c42de6dSgd78059 
5749*1c42de6dSgd78059 /*
5750*1c42de6dSgd78059  * function	- bscv_write_hostname
5751*1c42de6dSgd78059  * description	- write a new hostname to the lom
5752*1c42de6dSgd78059  * inputs	- soft state pointer, pointer to new name, name length
5753*1c42de6dSgd78059  * outputs	- none
5754*1c42de6dSgd78059  */
5755*1c42de6dSgd78059 static void
5756*1c42de6dSgd78059 bscv_write_hostname(bscv_soft_state_t *ssp,
5757*1c42de6dSgd78059     char *host_nodename, uint8_t length)
5758*1c42de6dSgd78059 {
5759*1c42de6dSgd78059 	int num_failures;
5760*1c42de6dSgd78059 	boolean_t needretry;
5761*1c42de6dSgd78059 	int i;
5762*1c42de6dSgd78059 
5763*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5764*1c42de6dSgd78059 
5765*1c42de6dSgd78059 	/*
5766*1c42de6dSgd78059 	 * We have a special failure case here because a retry of a read
5767*1c42de6dSgd78059 	 * causes data to be lost. Thus we handle the retries ourselves
5768*1c42de6dSgd78059 	 * and are also responsible for detemining if the lom is faulty
5769*1c42de6dSgd78059 	 */
5770*1c42de6dSgd78059 	for (num_failures = 0;
5771*1c42de6dSgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
5772*1c42de6dSgd78059 	    num_failures++) {
5773*1c42de6dSgd78059 		bscv_clear_fault(ssp);
5774*1c42de6dSgd78059 		bscv_put8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH, length);
5775*1c42de6dSgd78059 		if (bscv_faulty(ssp)) {
5776*1c42de6dSgd78059 			needretry = 1;
5777*1c42de6dSgd78059 		} else {
5778*1c42de6dSgd78059 			needretry = 0;
5779*1c42de6dSgd78059 			for (i = 0; i < length; i++) {
5780*1c42de6dSgd78059 				bscv_put8_once(ssp, chan_general,
5781*1c42de6dSgd78059 					EBUS_IDX_HNAME_CHAR, host_nodename[i]);
5782*1c42de6dSgd78059 				/* Retry on any error */
5783*1c42de6dSgd78059 				if (bscv_retcode(ssp) != 0) {
5784*1c42de6dSgd78059 					needretry = 1;
5785*1c42de6dSgd78059 					break;
5786*1c42de6dSgd78059 				}
5787*1c42de6dSgd78059 			}
5788*1c42de6dSgd78059 		}
5789*1c42de6dSgd78059 		if (!needretry) {
5790*1c42de6dSgd78059 			break;
5791*1c42de6dSgd78059 		}
5792*1c42de6dSgd78059 	}
5793*1c42de6dSgd78059 
5794*1c42de6dSgd78059 	if (needretry) {
5795*1c42de6dSgd78059 		/* Failure - we ran out of retries */
5796*1c42de6dSgd78059 		cmn_err(CE_WARN,
5797*1c42de6dSgd78059 		    "bscv_write_hostname: retried %d times, giving up",
5798*1c42de6dSgd78059 		    num_failures);
5799*1c42de6dSgd78059 		ssp->had_fault = B_TRUE;
5800*1c42de6dSgd78059 	} else if (num_failures > 0) {
5801*1c42de6dSgd78059 		bscv_trace(ssp, 'R', "bscv_write_hostname",
5802*1c42de6dSgd78059 		    "retried %d times, succeeded", num_failures);
5803*1c42de6dSgd78059 	}
5804*1c42de6dSgd78059 }
5805*1c42de6dSgd78059 
5806*1c42de6dSgd78059 /*
5807*1c42de6dSgd78059  * function	- bscv_setup_static_info
5808*1c42de6dSgd78059  * description	- read in static information from the lom at attach time.
5809*1c42de6dSgd78059  * inputs	- soft state ptr
5810*1c42de6dSgd78059  * outputs	- none
5811*1c42de6dSgd78059  */
5812*1c42de6dSgd78059 
5813*1c42de6dSgd78059 static void
5814*1c42de6dSgd78059 bscv_setup_static_info(bscv_soft_state_t *ssp)
5815*1c42de6dSgd78059 {
5816*1c42de6dSgd78059 	uint8_t	addr_space_ptr;
5817*1c42de6dSgd78059 	uint16_t mask;
5818*1c42de6dSgd78059 	uint8_t fanspeed;
5819*1c42de6dSgd78059 	int oldtemps[MAX_TEMPS];
5820*1c42de6dSgd78059 	int8_t temp;
5821*1c42de6dSgd78059 	int i;
5822*1c42de6dSgd78059 
5823*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
5824*1c42de6dSgd78059 
5825*1c42de6dSgd78059 	/*
5826*1c42de6dSgd78059 	 * Finally read in some static info like device names,
5827*1c42de6dSgd78059 	 * shutdown enabled, etc before the queue starts.
5828*1c42de6dSgd78059 	 */
5829*1c42de6dSgd78059 
5830*1c42de6dSgd78059 	/*
5831*1c42de6dSgd78059 	 * To get the volts static info we need address space 2
5832*1c42de6dSgd78059 	 */
5833*1c42de6dSgd78059 	bzero(&ssp->volts, sizeof (lom_volts_t));
5834*1c42de6dSgd78059 	ssp->volts.num = EBUS_CONFIG2_NSUPPLY_DEC(
5835*1c42de6dSgd78059 				bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
5836*1c42de6dSgd78059 	if (ssp->volts.num > MAX_VOLTS) {
5837*1c42de6dSgd78059 		cmn_err(CE_WARN,
5838*1c42de6dSgd78059 		    "lom: firmware reported too many voltage lines. ");
5839*1c42de6dSgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
5840*1c42de6dSgd78059 		    ssp->volts.num, MAX_VOLTS);
5841*1c42de6dSgd78059 		ssp->volts.num = MAX_VOLTS;
5842*1c42de6dSgd78059 	}
5843*1c42de6dSgd78059 
5844*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_setup_static_info",
5845*1c42de6dSgd78059 	    "num volts %d", ssp->volts.num);
5846*1c42de6dSgd78059 	(void) bscv_read_env_name(ssp,
5847*1c42de6dSgd78059 	    EBUS_CMD_SPACE2,
5848*1c42de6dSgd78059 	    EBUS_IDX2_SUPPLY_NAME_START,
5849*1c42de6dSgd78059 	    EBUS_IDX2_SUPPLY_NAME_END,
5850*1c42de6dSgd78059 	    ssp->volts.name,
5851*1c42de6dSgd78059 	    ssp->volts.num);
5852*1c42de6dSgd78059 
5853*1c42de6dSgd78059 	mask = bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
5854*1c42de6dSgd78059 	    EBUS_IDX2_SUPPLY_FATAL_MASK1)) << 8;
5855*1c42de6dSgd78059 	mask |= bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
5856*1c42de6dSgd78059 	    EBUS_IDX2_SUPPLY_FATAL_MASK2));
5857*1c42de6dSgd78059 
5858*1c42de6dSgd78059 	for (i = 0; i < ssp->volts.num; i++) {
5859*1c42de6dSgd78059 		ssp->volts.shutdown_enabled[i] =
5860*1c42de6dSgd78059 			(((mask >> i) & 1) == 0) ? 0 : 1;
5861*1c42de6dSgd78059 	}
5862*1c42de6dSgd78059 
5863*1c42de6dSgd78059 	/*
5864*1c42de6dSgd78059 	 * Get the temperature static info and populate initial temperatures.
5865*1c42de6dSgd78059 	 * Do not destroy old temperature values if the new value is not
5866*1c42de6dSgd78059 	 * known i.e. if the device is inaccessible.
5867*1c42de6dSgd78059 	 */
5868*1c42de6dSgd78059 	bcopy(ssp->temps.temp, oldtemps, sizeof (oldtemps));
5869*1c42de6dSgd78059 
5870*1c42de6dSgd78059 	bzero(&ssp->temps, sizeof (lom_temp_t));
5871*1c42de6dSgd78059 	ssp->temps.num = EBUS_CONFIG2_NTEMP_DEC(
5872*1c42de6dSgd78059 				bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
5873*1c42de6dSgd78059 	if (ssp->temps.num > MAX_TEMPS) {
5874*1c42de6dSgd78059 		cmn_err(CE_WARN,
5875*1c42de6dSgd78059 		    "lom: firmware reported too many temperatures being "
5876*1c42de6dSgd78059 		    "monitored.");
5877*1c42de6dSgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
5878*1c42de6dSgd78059 		    ssp->temps.num, MAX_TEMPS);
5879*1c42de6dSgd78059 		ssp->temps.num = MAX_TEMPS;
5880*1c42de6dSgd78059 	}
5881*1c42de6dSgd78059 	ssp->temps.num_ov = EBUS_CONFIG3_NOTEMP_DEC(
5882*1c42de6dSgd78059 				bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG3));
5883*1c42de6dSgd78059 	if (ssp->temps.num_ov > MAX_TEMPS) {
5884*1c42de6dSgd78059 		cmn_err(CE_WARN,
5885*1c42de6dSgd78059 		    "lom: firmware reported too many over temperatures being "
5886*1c42de6dSgd78059 		    "monitored.");
5887*1c42de6dSgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
5888*1c42de6dSgd78059 		    ssp->temps.num_ov, MAX_TEMPS);
5889*1c42de6dSgd78059 		ssp->temps.num_ov = MAX_TEMPS;
5890*1c42de6dSgd78059 	}
5891*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_setup_static_info",
5892*1c42de6dSgd78059 	    "num temps %d, over temps %d",
5893*1c42de6dSgd78059 	    ssp->temps.num, ssp->temps.num_ov);
5894*1c42de6dSgd78059 
5895*1c42de6dSgd78059 	addr_space_ptr = bscv_read_env_name(ssp,
5896*1c42de6dSgd78059 	    EBUS_CMD_SPACE4,
5897*1c42de6dSgd78059 	    EBUS_IDX4_TEMP_NAME_START,
5898*1c42de6dSgd78059 	    EBUS_IDX4_TEMP_NAME_END,
5899*1c42de6dSgd78059 	    ssp->temps.name,
5900*1c42de6dSgd78059 	    ssp->temps.num);
5901*1c42de6dSgd78059 
5902*1c42de6dSgd78059 	for (i = 0; i < ssp->temps.num; i++) {
5903*1c42de6dSgd78059 		ssp->temps.warning[i] = (int8_t)bscv_get8(ssp, chan_general,
5904*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_WARN1 + i));
5905*1c42de6dSgd78059 
5906*1c42de6dSgd78059 		/*
5907*1c42de6dSgd78059 		 * If shutdown is not enabled then set it as zero so
5908*1c42de6dSgd78059 		 * it is not displayed by the utility.
5909*1c42de6dSgd78059 		 */
5910*1c42de6dSgd78059 		if ((bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE4,
5911*1c42de6dSgd78059 		    EBUS_IDX4_TEMP_FATAL_MASK)) >> i) & 0x01) {
5912*1c42de6dSgd78059 			ssp->temps.shutdown[i] = (int8_t)bscv_get8(ssp,
5913*1c42de6dSgd78059 			    chan_general,
5914*1c42de6dSgd78059 			    BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_SDOWN1 + i));
5915*1c42de6dSgd78059 		} else {
5916*1c42de6dSgd78059 			ssp->temps.shutdown[i] = 0;
5917*1c42de6dSgd78059 		}
5918*1c42de6dSgd78059 	}
5919*1c42de6dSgd78059 
5920*1c42de6dSgd78059 	for (i = 0; i < ssp->temps.num; i++) {
5921*1c42de6dSgd78059 		temp = bscv_get8(ssp, chan_general, EBUS_IDX_TEMP1 + i);
5922*1c42de6dSgd78059 		if ((temp <= LOM_TEMP_MAX_VALUE) ||
5923*1c42de6dSgd78059 		    (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
5924*1c42de6dSgd78059 			ssp->temps.temp[i] = temp;
5925*1c42de6dSgd78059 		} else {
5926*1c42de6dSgd78059 			/* New value is not known - use old value */
5927*1c42de6dSgd78059 			ssp->temps.temp[i] = oldtemps[i];
5928*1c42de6dSgd78059 		}
5929*1c42de6dSgd78059 	}
5930*1c42de6dSgd78059 
5931*1c42de6dSgd78059 	/*
5932*1c42de6dSgd78059 	 * Check for and skip a single 0xff character between the
5933*1c42de6dSgd78059 	 * temperature and over temperature names
5934*1c42de6dSgd78059 	 */
5935*1c42de6dSgd78059 	if (bscv_get8(ssp, chan_general,
5936*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE4, addr_space_ptr)) == 0xff) {
5937*1c42de6dSgd78059 		addr_space_ptr++;
5938*1c42de6dSgd78059 	}
5939*1c42de6dSgd78059 
5940*1c42de6dSgd78059 	(void) bscv_read_env_name(ssp,
5941*1c42de6dSgd78059 	    EBUS_CMD_SPACE4,
5942*1c42de6dSgd78059 	    addr_space_ptr,
5943*1c42de6dSgd78059 	    EBUS_IDX4_TEMP_NAME_END,
5944*1c42de6dSgd78059 	    ssp->temps.name_ov,
5945*1c42de6dSgd78059 	    ssp->temps.num_ov);
5946*1c42de6dSgd78059 
5947*1c42de6dSgd78059 	/*
5948*1c42de6dSgd78059 	 * To get the CB static info we need address space 3
5949*1c42de6dSgd78059 	 */
5950*1c42de6dSgd78059 	bzero(&ssp->sflags, sizeof (lom_sflags_t));
5951*1c42de6dSgd78059 	ssp->sflags.num = EBUS_CONFIG3_NBREAKERS_DEC(bscv_get8(ssp,
5952*1c42de6dSgd78059 	    chan_general, EBUS_IDX_CONFIG3));
5953*1c42de6dSgd78059 	if (ssp->sflags.num > MAX_STATS) {
5954*1c42de6dSgd78059 		cmn_err(CE_WARN,
5955*1c42de6dSgd78059 		    "lom: firmware reported too many status flags.");
5956*1c42de6dSgd78059 		cmn_err(CE_CONT,
5957*1c42de6dSgd78059 		    "Reported %d, maximum is %d",
5958*1c42de6dSgd78059 		    ssp->sflags.num, MAX_STATS);
5959*1c42de6dSgd78059 		ssp->sflags.num = MAX_STATS;
5960*1c42de6dSgd78059 	}
5961*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_setup_static_info",
5962*1c42de6dSgd78059 	    "num sflags %d", ssp->sflags.num);
5963*1c42de6dSgd78059 
5964*1c42de6dSgd78059 	(void) bscv_read_env_name(ssp,
5965*1c42de6dSgd78059 	    EBUS_CMD_SPACE3,
5966*1c42de6dSgd78059 	    EBUS_IDX3_BREAKER_NAME_START,
5967*1c42de6dSgd78059 	    EBUS_IDX3_BREAKER_NAME_END,
5968*1c42de6dSgd78059 	    ssp->sflags.name,
5969*1c42de6dSgd78059 	    ssp->sflags.num);
5970*1c42de6dSgd78059 
5971*1c42de6dSgd78059 
5972*1c42de6dSgd78059 	/*
5973*1c42de6dSgd78059 	 * To get the fan static info we need address space 5
5974*1c42de6dSgd78059 	 */
5975*1c42de6dSgd78059 	ssp->num_fans = EBUS_CONFIG_NFAN_DEC(
5976*1c42de6dSgd78059 				bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG));
5977*1c42de6dSgd78059 	if (ssp->num_fans > MAX_FANS) {
5978*1c42de6dSgd78059 		cmn_err(CE_WARN,
5979*1c42de6dSgd78059 		    "lom: firmware reported too many fans. ");
5980*1c42de6dSgd78059 		cmn_err(CE_CONT,
5981*1c42de6dSgd78059 		    "Reported %d, maximum is %d",
5982*1c42de6dSgd78059 		    ssp->num_fans, MAX_FANS);
5983*1c42de6dSgd78059 		ssp->num_fans = MAX_FANS;
5984*1c42de6dSgd78059 	}
5985*1c42de6dSgd78059 
5986*1c42de6dSgd78059 	for (i = 0; i < ssp->num_fans; i++) {
5987*1c42de6dSgd78059 		fanspeed = bscv_get8(ssp, chan_general,
5988*1c42de6dSgd78059 					EBUS_IDX_FAN1_SPEED + i);
5989*1c42de6dSgd78059 		if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
5990*1c42de6dSgd78059 		    (fanspeed == LOM_FAN_NOT_PRESENT)) {
5991*1c42de6dSgd78059 			/*
5992*1c42de6dSgd78059 			 * Do not destroy previous values unless the
5993*1c42de6dSgd78059 			 * value is definitive.
5994*1c42de6dSgd78059 			 */
5995*1c42de6dSgd78059 			ssp->fanspeed[i] = fanspeed;
5996*1c42de6dSgd78059 		}
5997*1c42de6dSgd78059 	}
5998*1c42de6dSgd78059 
5999*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_setup_static_info",
6000*1c42de6dSgd78059 	    "num fans %d", ssp->num_fans);
6001*1c42de6dSgd78059 
6002*1c42de6dSgd78059 	(void) bscv_read_env_name(ssp,
6003*1c42de6dSgd78059 	    EBUS_CMD_SPACE5,
6004*1c42de6dSgd78059 	    EBUS_IDX5_FAN_NAME_START,
6005*1c42de6dSgd78059 	    EBUS_IDX5_FAN_NAME_END,
6006*1c42de6dSgd78059 	    ssp->fan_names,
6007*1c42de6dSgd78059 	    ssp->num_fans);
6008*1c42de6dSgd78059 
6009*1c42de6dSgd78059 	/* Get led static information from address space 10 */
6010*1c42de6dSgd78059 
6011*1c42de6dSgd78059 	(void) bscv_read_env_name(ssp,
6012*1c42de6dSgd78059 	    EBUS_CMD_SPACE_LEDS,
6013*1c42de6dSgd78059 	    EBUS_IDX10_LED_NAME_START,
6014*1c42de6dSgd78059 	    EBUS_IDX10_LED_NAME_END,
6015*1c42de6dSgd78059 	    ssp->led_names,
6016*1c42de6dSgd78059 	    MAX_LED_ID);
6017*1c42de6dSgd78059 }
6018*1c42de6dSgd78059 
6019*1c42de6dSgd78059 /*
6020*1c42de6dSgd78059  * function	- bscv_read_env_name
6021*1c42de6dSgd78059  * description	- read in static environment names
6022*1c42de6dSgd78059  *		  warning changes address space and the caller relies
6023*1c42de6dSgd78059  *		  on this behaviour.
6024*1c42de6dSgd78059  * inputs	- soft state ptr, chosen address space,
6025*1c42de6dSgd78059  *		  start of name data, end of name data,
6026*1c42de6dSgd78059  *		  name storage, number of names.
6027*1c42de6dSgd78059  * outputs	- next address for reading name data.
6028*1c42de6dSgd78059  */
6029*1c42de6dSgd78059 
6030*1c42de6dSgd78059 static uint8_t
6031*1c42de6dSgd78059 bscv_read_env_name(bscv_soft_state_t *ssp,
6032*1c42de6dSgd78059     uint8_t addr_space,
6033*1c42de6dSgd78059     uint8_t addr_start,
6034*1c42de6dSgd78059     uint8_t addr_end,
6035*1c42de6dSgd78059     char namebuf[][MAX_LOM2_NAME_STR],
6036*1c42de6dSgd78059     int numnames)
6037*1c42de6dSgd78059 {
6038*1c42de6dSgd78059 	int i;
6039*1c42de6dSgd78059 	int nameidx;
6040*1c42de6dSgd78059 	int namemax;
6041*1c42de6dSgd78059 	unsigned int addr_space_ptr;
6042*1c42de6dSgd78059 	uint8_t this_char;
6043*1c42de6dSgd78059 
6044*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
6045*1c42de6dSgd78059 
6046*1c42de6dSgd78059 	bscv_trace(ssp, 'A', "bscv_read_env_name",
6047*1c42de6dSgd78059 	    "bscv_read_env_name, space %d, start 0x%x, end 0x%x, numnames %d",
6048*1c42de6dSgd78059 	    addr_space, addr_start, addr_end, numnames);
6049*1c42de6dSgd78059 
6050*1c42de6dSgd78059 	addr_space_ptr = addr_start;
6051*1c42de6dSgd78059 
6052*1c42de6dSgd78059 	for (i = 0; i < numnames; i++) {
6053*1c42de6dSgd78059 		nameidx = 0;
6054*1c42de6dSgd78059 		namemax = sizeof (namebuf[i]);
6055*1c42de6dSgd78059 		bzero(namebuf[i], namemax);
6056*1c42de6dSgd78059 
6057*1c42de6dSgd78059 		while (addr_space_ptr <= addr_end) {
6058*1c42de6dSgd78059 			/*
6059*1c42de6dSgd78059 			 * Read the current character.
6060*1c42de6dSgd78059 			 */
6061*1c42de6dSgd78059 			this_char = bscv_get8(ssp, chan_general,
6062*1c42de6dSgd78059 			    BSCVA(addr_space, addr_space_ptr));
6063*1c42de6dSgd78059 
6064*1c42de6dSgd78059 			if (this_char == 0xff) {
6065*1c42de6dSgd78059 				/*
6066*1c42de6dSgd78059 				 * Ran out of names - this must
6067*1c42de6dSgd78059 				 * be the end of the name.
6068*1c42de6dSgd78059 				 * This is really an error because
6069*1c42de6dSgd78059 				 * we have just seen either a non-NUL
6070*1c42de6dSgd78059 				 * terminated string or the number of
6071*1c42de6dSgd78059 				 * strings did not match what was
6072*1c42de6dSgd78059 				 * reported.
6073*1c42de6dSgd78059 				 */
6074*1c42de6dSgd78059 				break;
6075*1c42de6dSgd78059 			}
6076*1c42de6dSgd78059 			/*
6077*1c42de6dSgd78059 			 * We increment the buffer pointer now so that
6078*1c42de6dSgd78059 			 * it is ready for the next read
6079*1c42de6dSgd78059 			 */
6080*1c42de6dSgd78059 			addr_space_ptr++;
6081*1c42de6dSgd78059 
6082*1c42de6dSgd78059 			if (this_char == '\0') {
6083*1c42de6dSgd78059 				/* Found end of string - done */
6084*1c42de6dSgd78059 				break;
6085*1c42de6dSgd78059 			}
6086*1c42de6dSgd78059 			if (nameidx < (namemax - 1)) {
6087*1c42de6dSgd78059 				/*
6088*1c42de6dSgd78059 				 * Buffer not full - record character
6089*1c42de6dSgd78059 				 * NOTE we always leave room for the NUL
6090*1c42de6dSgd78059 				 * terminator.
6091*1c42de6dSgd78059 				 */
6092*1c42de6dSgd78059 				namebuf[i][nameidx++] = this_char;
6093*1c42de6dSgd78059 			}
6094*1c42de6dSgd78059 		}
6095*1c42de6dSgd78059 		/* Ensure null termination */
6096*1c42de6dSgd78059 		namebuf[i][nameidx] = '\0';
6097*1c42de6dSgd78059 	}
6098*1c42de6dSgd78059 	/* Clamp addr_space_ptr to 0xff because we return uint8_t */
6099*1c42de6dSgd78059 	if (addr_space_ptr > 0xff) {
6100*1c42de6dSgd78059 		addr_space_ptr = 0xff;
6101*1c42de6dSgd78059 	}
6102*1c42de6dSgd78059 	return (addr_space_ptr);
6103*1c42de6dSgd78059 }
6104*1c42de6dSgd78059 
6105*1c42de6dSgd78059 /*
6106*1c42de6dSgd78059  * function	- bscv_setup_events
6107*1c42de6dSgd78059  * description	- initialise the event reporting code
6108*1c42de6dSgd78059  * inputs	- soft state ptr
6109*1c42de6dSgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
6110*1c42de6dSgd78059  */
6111*1c42de6dSgd78059 
6112*1c42de6dSgd78059 static void
6113*1c42de6dSgd78059 bscv_setup_events(bscv_soft_state_t *ssp)
6114*1c42de6dSgd78059 {
6115*1c42de6dSgd78059 	uint8_t bits2set;
6116*1c42de6dSgd78059 	uint8_t bits2clear;
6117*1c42de6dSgd78059 
6118*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
6119*1c42de6dSgd78059 
6120*1c42de6dSgd78059 	/*
6121*1c42de6dSgd78059 	 * deal with event reporting - cover all cases
6122*1c42de6dSgd78059 	 */
6123*1c42de6dSgd78059 
6124*1c42de6dSgd78059 	bits2set = 0;
6125*1c42de6dSgd78059 	bits2clear = 0;
6126*1c42de6dSgd78059 	if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
6127*1c42de6dSgd78059 		bits2clear |= EBUS_ALARM_NOEVENTS;
6128*1c42de6dSgd78059 	} else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
6129*1c42de6dSgd78059 		bits2set |= EBUS_ALARM_NOEVENTS;
6130*1c42de6dSgd78059 	} else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
6131*1c42de6dSgd78059 		bits2set |= EBUS_ALARM_NOEVENTS;
6132*1c42de6dSgd78059 	}
6133*1c42de6dSgd78059 	bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
6134*1c42de6dSgd78059 		bits2set, bits2clear);
6135*1c42de6dSgd78059 }
6136*1c42de6dSgd78059 
6137*1c42de6dSgd78059 #ifdef __sparc
6138*1c42de6dSgd78059 /*
6139*1c42de6dSgd78059  * function	- bscv_write_sig
6140*1c42de6dSgd78059  * description	- write out a signature, taking care to deal with any strange
6141*1c42de6dSgd78059  *		    values for CPU ID
6142*1c42de6dSgd78059  * inputs	- soft state ptr, signature
6143*1c42de6dSgd78059  * outputs	- none
6144*1c42de6dSgd78059  */
6145*1c42de6dSgd78059 static void
6146*1c42de6dSgd78059 bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s)
6147*1c42de6dSgd78059 {
6148*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
6149*1c42de6dSgd78059 
6150*1c42de6dSgd78059 	/* Upload the signature */
6151*1c42de6dSgd78059 	bscv_put32(ssp, chan_cpusig,
6152*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB),
6153*1c42de6dSgd78059 	    s.sig_info.signature);
6154*1c42de6dSgd78059 
6155*1c42de6dSgd78059 	/*
6156*1c42de6dSgd78059 	 * We always write the CPU ID last because this tells the firmware
6157*1c42de6dSgd78059 	 * that the signature is fully uploaded and therefore to consume the
6158*1c42de6dSgd78059 	 * data.  This is required since the signature is > 1 byte in size
6159*1c42de6dSgd78059 	 * and we transmit data in single bytes.
6160*1c42de6dSgd78059 	 */
6161*1c42de6dSgd78059 	if (s.cpu == ~0) {
6162*1c42de6dSgd78059 		/* ~0 means the signature applies to any CPU. */
6163*1c42de6dSgd78059 		bscv_put8(ssp, chan_cpusig,
6164*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
6165*1c42de6dSgd78059 		    EBUS_ANY_CPU_ID);
6166*1c42de6dSgd78059 	} else {
6167*1c42de6dSgd78059 		if (s.cpu > 255) {
6168*1c42de6dSgd78059 			/*
6169*1c42de6dSgd78059 			 * The CPU ID supplied is unexpectedly large.  Lets
6170*1c42de6dSgd78059 			 * just use the bottom bits, in case other high order
6171*1c42de6dSgd78059 			 * bits are being used for special meaning.
6172*1c42de6dSgd78059 			 */
6173*1c42de6dSgd78059 			cmn_err(CE_WARN, "CPU Signature ID 0x%x > 255", s.cpu);
6174*1c42de6dSgd78059 			s.cpu %= 256;
6175*1c42de6dSgd78059 			cmn_err(CE_CONT, "using ID 0x%x instead ", s.cpu);
6176*1c42de6dSgd78059 		}
6177*1c42de6dSgd78059 		bscv_put8(ssp, chan_cpusig,
6178*1c42de6dSgd78059 		    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
6179*1c42de6dSgd78059 		    (uint8_t)s.cpu);
6180*1c42de6dSgd78059 	}
6181*1c42de6dSgd78059 
6182*1c42de6dSgd78059 	ssp->last_sig = s;
6183*1c42de6dSgd78059 	ssp->progress |= BSCV_SIG_SENT;
6184*1c42de6dSgd78059 }
6185*1c42de6dSgd78059 #endif /* __sparc */
6186*1c42de6dSgd78059 
6187*1c42de6dSgd78059 #if defined(__i386) || defined(__amd64)
6188*1c42de6dSgd78059 
6189*1c42de6dSgd78059 /*
6190*1c42de6dSgd78059  * function	- bscv_inform_bsc
6191*1c42de6dSgd78059  * description	- inform bsc of driver state for logging purposes
6192*1c42de6dSgd78059  * inputs	- driver soft state, state
6193*1c42de6dSgd78059  * outputs	- none
6194*1c42de6dSgd78059  *
6195*1c42de6dSgd78059  */
6196*1c42de6dSgd78059 static void
6197*1c42de6dSgd78059 bscv_inform_bsc(bscv_soft_state_t *ssp, uint32_t state)
6198*1c42de6dSgd78059 {
6199*1c42de6dSgd78059 	ASSERT(bscv_held(ssp));
6200*1c42de6dSgd78059 
6201*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_inform_bsc",
6202*1c42de6dSgd78059 	    "bscv_inform_bsc: state=%d", state);
6203*1c42de6dSgd78059 
6204*1c42de6dSgd78059 	bscv_put32(ssp, chan_general,
6205*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB), state);
6206*1c42de6dSgd78059 	bscv_put8(ssp, chan_cpusig,
6207*1c42de6dSgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID), EBUS_ANY_CPU_ID);
6208*1c42de6dSgd78059 }
6209*1c42de6dSgd78059 
6210*1c42de6dSgd78059 /*
6211*1c42de6dSgd78059  * function	- bscv_watchdog_pat_request
6212*1c42de6dSgd78059  * description	- request a heartbeat pat
6213*1c42de6dSgd78059  * inputs	- timeout value in seconds
6214*1c42de6dSgd78059  * outputs	- none
6215*1c42de6dSgd78059  */
6216*1c42de6dSgd78059 static void
6217*1c42de6dSgd78059 bscv_watchdog_pat_request(void *arg)
6218*1c42de6dSgd78059 {
6219*1c42de6dSgd78059 	bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
6220*1c42de6dSgd78059 
6221*1c42de6dSgd78059 	bscv_wdog_do_pat(ssp);
6222*1c42de6dSgd78059 }
6223*1c42de6dSgd78059 
6224*1c42de6dSgd78059 /*
6225*1c42de6dSgd78059  * function	- bscv_watchdog_cfg_request
6226*1c42de6dSgd78059  * description	- request configuration of the bsc hardware watchdog
6227*1c42de6dSgd78059  * inputs	- new state (0=disabled, 1=enabled)
6228*1c42de6dSgd78059  * outputs	- one if successful, zero if unsuccesful
6229*1c42de6dSgd78059  */
6230*1c42de6dSgd78059 static void
6231*1c42de6dSgd78059 bscv_watchdog_cfg_request(bscv_soft_state_t *ssp, uint8_t new_state)
6232*1c42de6dSgd78059 {
6233*1c42de6dSgd78059 	ASSERT(new_state == WDOG_ON || new_state == WDOG_OFF);
6234*1c42de6dSgd78059 
6235*1c42de6dSgd78059 	watchdog_activated = new_state;
6236*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_watchdog_cfg_request",
6237*1c42de6dSgd78059 	    "watchdog_activated=%d", watchdog_activated);
6238*1c42de6dSgd78059 	bscv_write_wdog_cfg(ssp,
6239*1c42de6dSgd78059 	    bscv_watchdog_timeout_seconds,
6240*1c42de6dSgd78059 	    new_state,
6241*1c42de6dSgd78059 	    wdog_reset_on_timeout);
6242*1c42de6dSgd78059 }
6243*1c42de6dSgd78059 
6244*1c42de6dSgd78059 /*
6245*1c42de6dSgd78059  * function	- bscv_set_watchdog_timer
6246*1c42de6dSgd78059  * description	- setup the heartbeat timeout value
6247*1c42de6dSgd78059  * inputs	- timeout value in seconds
6248*1c42de6dSgd78059  * outputs	- zero if the value was not changed
6249*1c42de6dSgd78059  *                otherwise the current value
6250*1c42de6dSgd78059  */
6251*1c42de6dSgd78059 static uint_t
6252*1c42de6dSgd78059 bscv_set_watchdog_timer(bscv_soft_state_t *ssp, uint_t timeoutval)
6253*1c42de6dSgd78059 {
6254*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_set_watchdog_timer:",
6255*1c42de6dSgd78059 	    "timeout=%d", timeoutval);
6256*1c42de6dSgd78059 
6257*1c42de6dSgd78059 	/*
6258*1c42de6dSgd78059 	 * We get started during bscv_attach only
6259*1c42de6dSgd78059 	 * if bscv_watchdog_enable is set.
6260*1c42de6dSgd78059 	 */
6261*1c42de6dSgd78059 	if (bscv_watchdog_available && (!watchdog_activated ||
6262*1c42de6dSgd78059 	    (watchdog_activated &&
6263*1c42de6dSgd78059 		(timeoutval != bscv_watchdog_timeout_seconds)))) {
6264*1c42de6dSgd78059 		bscv_watchdog_timeout_seconds = timeoutval;
6265*1c42de6dSgd78059 		bscv_watchdog_cfg_request(ssp, WDOG_ON);
6266*1c42de6dSgd78059 		return (bscv_watchdog_timeout_seconds);
6267*1c42de6dSgd78059 	}
6268*1c42de6dSgd78059 	return (0);
6269*1c42de6dSgd78059 }
6270*1c42de6dSgd78059 
6271*1c42de6dSgd78059 /*
6272*1c42de6dSgd78059  * function	- bscv_clear_watchdog_timer
6273*1c42de6dSgd78059  * description	- add the watchdog patter cyclic
6274*1c42de6dSgd78059  * inputs	- driver soft state
6275*1c42de6dSgd78059  * outputs	- value of watchdog timeout in seconds
6276*1c42de6dSgd78059  *
6277*1c42de6dSgd78059  * This function is a copy of the SPARC implementation
6278*1c42de6dSgd78059  * in the todblade clock driver.
6279*1c42de6dSgd78059  */
6280*1c42de6dSgd78059 static void
6281*1c42de6dSgd78059 bscv_clear_watchdog_timer(bscv_soft_state_t *ssp)
6282*1c42de6dSgd78059 {
6283*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_clear_watchdog_timer", "");
6284*1c42de6dSgd78059 
6285*1c42de6dSgd78059 	if (bscv_watchdog_available && watchdog_activated) {
6286*1c42de6dSgd78059 		bscv_watchdog_enable = 0;
6287*1c42de6dSgd78059 		bscv_watchdog_cfg_request(ssp, WDOG_OFF);
6288*1c42de6dSgd78059 	}
6289*1c42de6dSgd78059 }
6290*1c42de6dSgd78059 
6291*1c42de6dSgd78059 /*
6292*1c42de6dSgd78059  * function	- bscv_panic_callback
6293*1c42de6dSgd78059  * description	- called when we panic so we can disabled the watchdog
6294*1c42de6dSgd78059  * inputs	- driver soft state pointer
6295*1c42de6dSgd78059  * outputs	- DDI_SUCCESS
6296*1c42de6dSgd78059  */
6297*1c42de6dSgd78059 /*ARGSUSED1*/
6298*1c42de6dSgd78059 static boolean_t
6299*1c42de6dSgd78059 bscv_panic_callback(void *arg, int code)
6300*1c42de6dSgd78059 {
6301*1c42de6dSgd78059 	bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
6302*1c42de6dSgd78059 
6303*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_panic_callback",
6304*1c42de6dSgd78059 	    "disabling watchdog");
6305*1c42de6dSgd78059 
6306*1c42de6dSgd78059 	bscv_clear_watchdog_timer(ssp);
6307*1c42de6dSgd78059 	/*
6308*1c42de6dSgd78059 	 * We dont get interrupts during the panic callback. But bscbus
6309*1c42de6dSgd78059 	 * takes care of all this
6310*1c42de6dSgd78059 	 */
6311*1c42de6dSgd78059 	bscv_full_stop(ssp);
6312*1c42de6dSgd78059 	return (DDI_SUCCESS);
6313*1c42de6dSgd78059 }
6314*1c42de6dSgd78059 
6315*1c42de6dSgd78059 /*
6316*1c42de6dSgd78059  * function	- bscv_watchdog_cyclic_add
6317*1c42de6dSgd78059  * description	- add the watchdog patter cyclic
6318*1c42de6dSgd78059  * inputs	- driver soft state
6319*1c42de6dSgd78059  * outputs	- none
6320*1c42de6dSgd78059  */
6321*1c42de6dSgd78059 static void
6322*1c42de6dSgd78059 bscv_watchdog_cyclic_add(bscv_soft_state_t *ssp)
6323*1c42de6dSgd78059 {
6324*1c42de6dSgd78059 	cyc_handler_t hdlr;
6325*1c42de6dSgd78059 	cyc_time_t when;
6326*1c42de6dSgd78059 
6327*1c42de6dSgd78059 	ASSERT(MUTEX_HELD(&cpu_lock));	/* for cyclic_add */
6328*1c42de6dSgd78059 
6329*1c42de6dSgd78059 	if (ssp->cyclic_id != CYCLIC_NONE) {
6330*1c42de6dSgd78059 		return;
6331*1c42de6dSgd78059 	}
6332*1c42de6dSgd78059 
6333*1c42de6dSgd78059 	hdlr.cyh_level = CY_LOCK_LEVEL;
6334*1c42de6dSgd78059 	hdlr.cyh_func = (cyc_func_t)bscv_watchdog_pat_request;
6335*1c42de6dSgd78059 	hdlr.cyh_arg = (void *)ssp;
6336*1c42de6dSgd78059 
6337*1c42de6dSgd78059 	when.cyt_when = 0;
6338*1c42de6dSgd78059 	when.cyt_interval = WATCHDOG_PAT_INTERVAL;
6339*1c42de6dSgd78059 
6340*1c42de6dSgd78059 	ssp->cyclic_id = cyclic_add(&hdlr, &when);
6341*1c42de6dSgd78059 
6342*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_watchdog_cyclic_add:",
6343*1c42de6dSgd78059 	    "cyclic added");
6344*1c42de6dSgd78059 }
6345*1c42de6dSgd78059 
6346*1c42de6dSgd78059 /*
6347*1c42de6dSgd78059  * function	- bscv_watchdog_cyclic_remove
6348*1c42de6dSgd78059  * description	- remove the watchdog patter cyclic
6349*1c42de6dSgd78059  * inputs	- soft state ptr
6350*1c42de6dSgd78059  * outputs	- none
6351*1c42de6dSgd78059  */
6352*1c42de6dSgd78059 static void
6353*1c42de6dSgd78059 bscv_watchdog_cyclic_remove(bscv_soft_state_t *ssp)
6354*1c42de6dSgd78059 {
6355*1c42de6dSgd78059 	ASSERT(MUTEX_HELD(&cpu_lock));	/* for cyclic_remove */
6356*1c42de6dSgd78059 
6357*1c42de6dSgd78059 	if (ssp->cyclic_id == CYCLIC_NONE) {
6358*1c42de6dSgd78059 		return;
6359*1c42de6dSgd78059 	}
6360*1c42de6dSgd78059 
6361*1c42de6dSgd78059 	cyclic_remove(ssp->cyclic_id);
6362*1c42de6dSgd78059 	ssp->cyclic_id = CYCLIC_NONE;
6363*1c42de6dSgd78059 	bscv_trace(ssp, 'X', "bscv_watchdog_cyclic_remove:",
6364*1c42de6dSgd78059 	    "cyclic removed");
6365*1c42de6dSgd78059 }
6366*1c42de6dSgd78059 #endif /* __i386 || __amd64 */
6367*1c42de6dSgd78059 
6368*1c42de6dSgd78059 
6369*1c42de6dSgd78059 /*
6370*1c42de6dSgd78059  *  General utility routines ...
6371*1c42de6dSgd78059  */
6372*1c42de6dSgd78059 
6373*1c42de6dSgd78059 #ifdef DEBUG
6374*1c42de6dSgd78059 
6375*1c42de6dSgd78059 static void
6376*1c42de6dSgd78059 bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
6377*1c42de6dSgd78059 	const char *fmt, ...)
6378*1c42de6dSgd78059 {
6379*1c42de6dSgd78059 	char buf[256];
6380*1c42de6dSgd78059 	char *p;
6381*1c42de6dSgd78059 	va_list va;
6382*1c42de6dSgd78059 
6383*1c42de6dSgd78059 	if (ssp->debug & (1 << (code-'@'))) {
6384*1c42de6dSgd78059 		p = buf;
6385*1c42de6dSgd78059 		(void) snprintf(p, sizeof (buf) - (p - buf),
6386*1c42de6dSgd78059 			"%s/%s: ", MYNAME, caller);
6387*1c42de6dSgd78059 		p += strlen(p);
6388*1c42de6dSgd78059 
6389*1c42de6dSgd78059 		va_start(va, fmt);
6390*1c42de6dSgd78059 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
6391*1c42de6dSgd78059 		va_end(va);
6392*1c42de6dSgd78059 
6393*1c42de6dSgd78059 		buf[sizeof (buf) - 1] = '\0';
6394*1c42de6dSgd78059 		(void) strlog((short)ssp->majornum, (short)ssp->minornum, code,
6395*1c42de6dSgd78059 		    SL_TRACE, buf);
6396*1c42de6dSgd78059 	}
6397*1c42de6dSgd78059 }
6398*1c42de6dSgd78059 
6399*1c42de6dSgd78059 #else /* DEBUG */
6400*1c42de6dSgd78059 
6401*1c42de6dSgd78059 _NOTE(ARGSUSED(0))
6402*1c42de6dSgd78059 static void
6403*1c42de6dSgd78059 bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
6404*1c42de6dSgd78059 	const char *fmt, ...)
6405*1c42de6dSgd78059 {
6406*1c42de6dSgd78059 }
6407*1c42de6dSgd78059 
6408*1c42de6dSgd78059 #endif /* DEBUG */
6409