xref: /onnv-gate/usr/src/uts/common/io/bscv.c (revision 11066:cebb50cbe4f9)
14135Sgd78059 /*
24135Sgd78059  * CDDL HEADER START
34135Sgd78059  *
44135Sgd78059  * The contents of this file are subject to the terms of the
54135Sgd78059  * Common Development and Distribution License (the "License").
64135Sgd78059  * You may not use this file except in compliance with the License.
74135Sgd78059  *
84135Sgd78059  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94135Sgd78059  * or http://www.opensolaris.org/os/licensing.
104135Sgd78059  * See the License for the specific language governing permissions
114135Sgd78059  * and limitations under the License.
124135Sgd78059  *
134135Sgd78059  * When distributing Covered Code, include this CDDL HEADER in each
144135Sgd78059  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154135Sgd78059  * If applicable, add the following below this CDDL HEADER, with the
164135Sgd78059  * fields enclosed by brackets "[]" replaced with your own identifying
174135Sgd78059  * information: Portions Copyright [yyyy] [name of copyright owner]
184135Sgd78059  *
194135Sgd78059  * CDDL HEADER END
204135Sgd78059  */
214135Sgd78059 /*
229644Sandrew.rutz@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234135Sgd78059  * Use is subject to license terms.
244135Sgd78059  */
254135Sgd78059 
264135Sgd78059 
274135Sgd78059 /*
284135Sgd78059  * bscv.c - multi-threaded lom driver for the Stiletto platform.
294135Sgd78059  */
304135Sgd78059 
314135Sgd78059 /*
324135Sgd78059  * Included files.
334135Sgd78059  */
344135Sgd78059 
354135Sgd78059 #include <sys/note.h>
364135Sgd78059 #include <sys/types.h>
374135Sgd78059 #include <sys/param.h>
384135Sgd78059 #include <sys/uio.h>
394135Sgd78059 #include <sys/open.h>
404135Sgd78059 #include <sys/cred.h>
414135Sgd78059 #include <sys/stream.h>
424135Sgd78059 #include <sys/systm.h>
434135Sgd78059 #include <sys/conf.h>
444135Sgd78059 #include <sys/reboot.h>
454135Sgd78059 #include <sys/modctl.h>
464135Sgd78059 #include <sys/mkdev.h>
474135Sgd78059 #include <sys/errno.h>
484135Sgd78059 #include <sys/debug.h>
494135Sgd78059 #include <sys/kmem.h>
504135Sgd78059 #include <sys/consdev.h>
514135Sgd78059 #include <sys/file.h>
524135Sgd78059 #include <sys/stat.h>
534135Sgd78059 #include <sys/disp.h>
544135Sgd78059 #include <sys/ddi.h>
554135Sgd78059 #include <sys/sunddi.h>
564135Sgd78059 #include <sys/stream.h>
574135Sgd78059 #include <sys/strlog.h>
584135Sgd78059 #include <sys/log.h>
594135Sgd78059 #include <sys/utsname.h>
604135Sgd78059 #include <sys/callb.h>
614135Sgd78059 #include <sys/sysevent.h>
624135Sgd78059 #include <sys/nvpair.h>
634135Sgd78059 #include <sys/sysevent/eventdefs.h>
644135Sgd78059 #include <sys/sysevent/domain.h>
654135Sgd78059 #include <sys/sysevent/env.h>
664135Sgd78059 #include <sys/sysevent/dr.h>
674135Sgd78059 
684135Sgd78059 #include <sys/lom_io.h>
694135Sgd78059 #include <sys/bscbus.h>
704135Sgd78059 #include <sys/bscv_impl.h>
714135Sgd78059 
724135Sgd78059 /*
734135Sgd78059  * Variables defined here and visible internally only
744135Sgd78059  */
754135Sgd78059 
764135Sgd78059 static void *bscv_statep = NULL;
774135Sgd78059 
784135Sgd78059 /*
794135Sgd78059  * Forward declarations
804135Sgd78059  */
814135Sgd78059 
824135Sgd78059 static int bscv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
834135Sgd78059 static int bscv_attach(dev_info_t *, ddi_attach_cmd_t);
844135Sgd78059 static int bscv_detach(dev_info_t *, ddi_detach_cmd_t);
857656SSherry.Moore@Sun.COM static int bscv_quiesce(dev_info_t *);
864135Sgd78059 static int bscv_map_regs(bscv_soft_state_t *);
874135Sgd78059 static void bscv_unmap_regs(bscv_soft_state_t *);
884135Sgd78059 static void bscv_map_chan_logical_physical(bscv_soft_state_t *);
894135Sgd78059 
904135Sgd78059 static int bscv_open(dev_t *, int, int, cred_t *);
914135Sgd78059 static int bscv_close(dev_t, int, int, cred_t *);
924135Sgd78059 static void bscv_full_stop(bscv_soft_state_t *);
934135Sgd78059 
944135Sgd78059 static void bscv_enter(bscv_soft_state_t *);
959644Sandrew.rutz@sun.com static int bscv_tryenter(bscv_soft_state_t *ssp);
964135Sgd78059 static void bscv_exit(bscv_soft_state_t *);
974135Sgd78059 #ifdef DEBUG
984135Sgd78059 static int bscv_held(bscv_soft_state_t *);
994135Sgd78059 #endif /* DEBUG */
1004135Sgd78059 
1014135Sgd78059 static void bscv_put8(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
1024135Sgd78059 static void bscv_put16(bscv_soft_state_t *, int, bscv_addr_t, uint16_t);
1034135Sgd78059 static void bscv_put32(bscv_soft_state_t *, int, bscv_addr_t, uint32_t);
1044135Sgd78059 static uint8_t bscv_get8(bscv_soft_state_t *, int, bscv_addr_t);
1054135Sgd78059 static uint16_t bscv_get16(bscv_soft_state_t *, int, bscv_addr_t);
1064135Sgd78059 static uint32_t bscv_get32(bscv_soft_state_t *, int, bscv_addr_t);
1074135Sgd78059 static void bscv_setclear8(bscv_soft_state_t *, int,
1084135Sgd78059 	bscv_addr_t, uint8_t, uint8_t);
1094135Sgd78059 static void bscv_setclear8_volatile(bscv_soft_state_t *, int,
1104135Sgd78059 	bscv_addr_t, uint8_t, uint8_t);
1114135Sgd78059 static void bscv_rep_rw8(bscv_soft_state_t *, int,
1124135Sgd78059 	uint8_t *, bscv_addr_t, size_t, uint_t, boolean_t);
1134135Sgd78059 static uint8_t bscv_get8_cached(bscv_soft_state_t *, bscv_addr_t);
1144135Sgd78059 
1154135Sgd78059 static uint8_t bscv_get8_locked(bscv_soft_state_t *, int, bscv_addr_t, int *);
1164135Sgd78059 static void bscv_rep_get8_locked(bscv_soft_state_t *, int,
1174135Sgd78059 	uint8_t *, bscv_addr_t, size_t, uint_t, int *);
1184135Sgd78059 
1194135Sgd78059 static boolean_t bscv_faulty(bscv_soft_state_t *);
1204135Sgd78059 static void bscv_clear_fault(bscv_soft_state_t *);
1214135Sgd78059 static void bscv_set_fault(bscv_soft_state_t *);
1224135Sgd78059 static boolean_t bscv_session_error(bscv_soft_state_t *);
1234135Sgd78059 static int bscv_retcode(bscv_soft_state_t *);
1244135Sgd78059 static int bscv_should_retry(bscv_soft_state_t *);
1254135Sgd78059 static void bscv_locked_result(bscv_soft_state_t *, int *);
1264135Sgd78059 
1274135Sgd78059 static void bscv_put8_once(bscv_soft_state_t *, int, bscv_addr_t, uint8_t);
1284135Sgd78059 static uint8_t bscv_get8_once(bscv_soft_state_t *, int, bscv_addr_t);
1294135Sgd78059 static uint32_t bscv_probe(bscv_soft_state_t *, int, uint32_t *);
1304135Sgd78059 static void bscv_resync_comms(bscv_soft_state_t *, int);
1314135Sgd78059 
1324135Sgd78059 static boolean_t bscv_window_setup(bscv_soft_state_t *);
1334135Sgd78059 static int bscv_eerw(bscv_soft_state_t *, uint32_t, uint8_t *,
1344135Sgd78059     unsigned, boolean_t);
1354135Sgd78059 
1364135Sgd78059 static int bscv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
1374135Sgd78059 static int bscv_ioc_dogstate(bscv_soft_state_t *, intptr_t, int);
1384135Sgd78059 static int bscv_ioc_psustate(bscv_soft_state_t *, intptr_t, int);
1394135Sgd78059 static int bscv_ioc_fanstate(bscv_soft_state_t *, intptr_t, int);
1404135Sgd78059 static int bscv_ioc_fledstate(bscv_soft_state_t *, intptr_t, int);
1414135Sgd78059 static int bscv_ioc_ledstate(bscv_soft_state_t *, intptr_t, int);
1424135Sgd78059 static int bscv_ioc_info(bscv_soft_state_t *, intptr_t, int);
1434135Sgd78059 static int bscv_ioc_mread(bscv_soft_state_t *, intptr_t, int);
1444135Sgd78059 static int bscv_ioc_volts(bscv_soft_state_t *, intptr_t, int);
1454135Sgd78059 static int bscv_ioc_stats(bscv_soft_state_t *, intptr_t, int);
1464135Sgd78059 static int bscv_ioc_temp(bscv_soft_state_t *, intptr_t, int);
1474135Sgd78059 static int bscv_ioc_cons(bscv_soft_state_t *, intptr_t, int);
1484135Sgd78059 static int bscv_ioc_eventlog2(bscv_soft_state_t *, intptr_t, int);
1494135Sgd78059 static int bscv_ioc_info2(bscv_soft_state_t *, intptr_t, int);
1504135Sgd78059 static int bscv_ioc_test(bscv_soft_state_t *, intptr_t, int);
1514135Sgd78059 static int bscv_ioc_mprog2(bscv_soft_state_t *, intptr_t, int);
1524135Sgd78059 static int bscv_ioc_mread2(bscv_soft_state_t *, intptr_t, int);
1534135Sgd78059 
1544135Sgd78059 static void bscv_event_daemon(void *);
1554135Sgd78059 static void bscv_start_event_daemon(bscv_soft_state_t *);
1564135Sgd78059 static int bscv_stop_event_daemon(bscv_soft_state_t *);
1574135Sgd78059 static int bscv_pause_event_daemon(bscv_soft_state_t *);
1584135Sgd78059 static void bscv_resume_event_daemon(bscv_soft_state_t *);
1594135Sgd78059 static void bscv_event_process(bscv_soft_state_t *ssp, boolean_t);
1604135Sgd78059 static int bscv_event_validate(bscv_soft_state_t *, uint32_t, uint8_t);
1614135Sgd78059 static void bscv_event_process_one(bscv_soft_state_t *, lom_event_t *);
1624135Sgd78059 static void bscv_build_eventstring(bscv_soft_state_t *,
1634135Sgd78059     lom_event_t *, char *, char *);
1644135Sgd78059 static int bscv_level_of_event(lom_event_t *);
1654135Sgd78059 static void bscv_status(bscv_soft_state_t *, uint8_t, uint8_t);
1664135Sgd78059 char *bscv_get_label(char [][MAX_LOM2_NAME_STR], int, int);
1674135Sgd78059 static void bscv_generic_sysevent(bscv_soft_state_t *, char *, char *, char *,
1684135Sgd78059     char *, int32_t, char *);
1694135Sgd78059 static void bscv_sysevent(bscv_soft_state_t *, lom_event_t *);
1704135Sgd78059 
1714135Sgd78059 static int bscv_prog(bscv_soft_state_t *, intptr_t, int);
1724135Sgd78059 static int bscv_prog_image(bscv_soft_state_t *, boolean_t,
1734135Sgd78059     uint8_t *, int, uint32_t);
1744135Sgd78059 static int bscv_prog_receive_image(bscv_soft_state_t *, lom_prog_t *,
1754135Sgd78059     uint8_t *, int);
1764135Sgd78059 static void bscv_leave_programming_mode(bscv_soft_state_t *, boolean_t);
1774135Sgd78059 static int bscv_prog_stop_lom(bscv_soft_state_t *);
1784135Sgd78059 static int bscv_prog_start_lom(bscv_soft_state_t *);
1794135Sgd78059 
1804135Sgd78059 static int bscv_attach_common(bscv_soft_state_t *);
1814135Sgd78059 static int bscv_cleanup(bscv_soft_state_t *);
1824135Sgd78059 static void bscv_setup_capability(bscv_soft_state_t *);
1834135Sgd78059 static int bscv_probe_check(bscv_soft_state_t *);
1844135Sgd78059 static void bscv_setup_hostname(bscv_soft_state_t *);
1854135Sgd78059 static void bscv_read_hostname(bscv_soft_state_t *, char *);
1864135Sgd78059 static void bscv_write_hostname(bscv_soft_state_t *, char *, uint8_t);
1874135Sgd78059 static void bscv_setup_static_info(bscv_soft_state_t *);
1884135Sgd78059 static uint8_t bscv_read_env_name(bscv_soft_state_t *, uint8_t,
1894135Sgd78059     uint8_t, uint8_t, char [][MAX_LOM2_NAME_STR], int);
1904135Sgd78059 static void bscv_setup_events(bscv_soft_state_t *);
1914135Sgd78059 
1924135Sgd78059 static void bscv_trace(bscv_soft_state_t *, char, const char *,
1934135Sgd78059     const char *, ...);
1944135Sgd78059 
1954135Sgd78059 #ifdef __sparc
1964135Sgd78059 static void bscv_idi_init();
1974135Sgd78059 static void bscv_idi_fini();
1984135Sgd78059 static void bscv_idi_new_instance(dev_info_t *dip);
1994135Sgd78059 static void bscv_idi_clear_err();
2004135Sgd78059 void bscv_idi_set(struct bscv_idi_info info);
2014135Sgd78059 static boolean_t bscv_idi_err();
2024135Sgd78059 static boolean_t bscv_nodename_set(struct bscv_idi_info info);
2034135Sgd78059 static boolean_t bscv_sig_set(struct bscv_idi_info info);
2044135Sgd78059 static boolean_t bscv_wdog_pat(struct bscv_idi_info info);
2054135Sgd78059 static boolean_t bscv_wdog_cfg(struct bscv_idi_info info);
2064135Sgd78059 static void bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s);
2074135Sgd78059 #endif /* __sparc */
2084135Sgd78059 
2094135Sgd78059 static void bscv_setup_watchdog(bscv_soft_state_t *ssp);
2104135Sgd78059 static void bscv_write_wdog_cfg(bscv_soft_state_t *,
2114135Sgd78059     uint_t, boolean_t, uint8_t);
2124135Sgd78059 
2134135Sgd78059 #if defined(__i386) || defined(__amd64)
2144135Sgd78059 static void bscv_inform_bsc(bscv_soft_state_t *, uint32_t);
2154135Sgd78059 static void bscv_watchdog_pat_request(void *);
2164135Sgd78059 static void bscv_watchdog_cfg_request(bscv_soft_state_t *, uint8_t);
2174135Sgd78059 static uint_t bscv_set_watchdog_timer(bscv_soft_state_t *, uint_t);
2184135Sgd78059 static void bscv_clear_watchdog_timer(bscv_soft_state_t *);
2194135Sgd78059 
2204135Sgd78059 static boolean_t bscv_panic_callback(void *, int);
2214135Sgd78059 static void bscv_watchdog_cyclic_add(bscv_soft_state_t *);
2224135Sgd78059 static void bscv_watchdog_cyclic_remove(bscv_soft_state_t *);
2234135Sgd78059 
2244135Sgd78059 static uint8_t	wdog_reset_on_timeout = 1;
2254135Sgd78059 
2264135Sgd78059 #define	WDOG_ON			1
2274135Sgd78059 #define	WDOG_OFF		0
2284135Sgd78059 #define	CLK_WATCHDOG_DEFAULT	10		/* 10 seconds */
2294135Sgd78059 #define	WATCHDOG_PAT_INTERVAL	1000000000	/* 1 second */
2304135Sgd78059 
2314135Sgd78059 static int	bscv_watchdog_enable;
2324135Sgd78059 static int	bscv_watchdog_available;
2334135Sgd78059 static int	watchdog_activated;
2344135Sgd78059 static uint_t	bscv_watchdog_timeout_seconds;
2354135Sgd78059 #endif /* __i386 || __amd64 */
2364135Sgd78059 
2374135Sgd78059 #ifdef __sparc
2384135Sgd78059 struct bscv_idi_callout bscv_idi_callout_table[] = {
2394135Sgd78059 	{BSCV_IDI_NODENAME,	&bscv_nodename_set	},
2404135Sgd78059 	{BSCV_IDI_SIG,		&bscv_sig_set		},
2414135Sgd78059 	{BSCV_IDI_WDOG_PAT,	&bscv_wdog_pat		},
2424135Sgd78059 	{BSCV_IDI_WDOG_CFG,	&bscv_wdog_cfg		},
2434135Sgd78059 	{BSCV_IDI_NULL,		NULL			}
2444135Sgd78059 };
2454135Sgd78059 
2464135Sgd78059 static struct bscv_idi_callout_mgr bscv_idi_mgr;
2474135Sgd78059 #endif /* __sparc */
2484135Sgd78059 
2494135Sgd78059 /*
2504135Sgd78059  * Local Definitions
2514135Sgd78059  */
2524135Sgd78059 #define	STATUS_READ_LIMIT	8   /* Read up to 8 status changes at a time */
2534135Sgd78059 #define	MYNAME			"bscv"
2544135Sgd78059 #define	BSCV_INST_TO_MINOR(i)	(i)
2554135Sgd78059 #define	BSCV_MINOR_TO_INST(m)	(m)
2564135Sgd78059 
2574135Sgd78059 /*
2584135Sgd78059  * Strings for daemon event reporting
2594135Sgd78059  */
2604135Sgd78059 
2614135Sgd78059 static char *eventSubsysStrings[] =
2624135Sgd78059 {	"",				/* 00 */
2634135Sgd78059 	"Alarm ",			/* 01 */
2644135Sgd78059 	"temperature sensor ",		/* 02 */
2654135Sgd78059 	"overheat sensor ",		/* 03 */
2664135Sgd78059 	"Fan ",				/* 04 */
2674135Sgd78059 	"supply rail ",			/* 05 */
2684135Sgd78059 	"circuit breaker ",		/* 06 */
2694135Sgd78059 	"PSU ",				/* 07 */
2704135Sgd78059 	"user ",			/* 08 */
2714135Sgd78059 	"phonehome ",			/* 09; unutilized */
2724135Sgd78059 	"LOM ",				/* 0a */
2734135Sgd78059 	"host ",			/* 0b */
2744135Sgd78059 	"event log ",			/* 0c */
2754135Sgd78059 	"",				/* 0d; EVENT_SUBSYS_EXTRA unutilized */
2764135Sgd78059 	"LED ",				/* 0e */
2774135Sgd78059 };
2784135Sgd78059 
2794135Sgd78059 static char *eventTypeStrings[] =
2804135Sgd78059 {
2814135Sgd78059 	"[null event]",			/* 00 */
2824135Sgd78059 	"ON",				/* 01 */
2834135Sgd78059 	"OFF",				/* 02 */
2844135Sgd78059 	"state change",			/* 03 */
2854135Sgd78059 	"power on",			/* 04 */
2864135Sgd78059 	"power off",			/* 05 */
2874135Sgd78059 	"powered off unexpectedly",	/* 06 */
2884135Sgd78059 	"reset unexpectedly",		/* 07 */
2894135Sgd78059 	"booted",			/* 08 */
2904135Sgd78059 	"watchdog enabled",		/* 09 */
2914135Sgd78059 	"watchdog disabled",		/* 0a */
2924135Sgd78059 	"watchdog triggered",		/* 0b */
2934135Sgd78059 	"failed",			/* 0c */
2944135Sgd78059 	"recovered",			/* 0d */
2954135Sgd78059 	"reset",			/* 0e */
2964135Sgd78059 	"XIR reset",			/* 0f */
2974135Sgd78059 	"console selected",		/* 10 */
2984135Sgd78059 	"time reference",		/* 11 */
2994135Sgd78059 	"script failure",		/* 12 */
3004135Sgd78059 	"modem access failure",		/* 13 */
3014135Sgd78059 	"modem dialing failure",	/* 14 */
3024135Sgd78059 	"bad checksum",			/* 15 */
3034135Sgd78059 	"added",			/* 16 */
3044135Sgd78059 	"removed",			/* 17 */
3054135Sgd78059 	"changed",			/* 18 */
3064135Sgd78059 	"login",			/* 19 */
3074135Sgd78059 	"password changed",		/* 1a */
3084135Sgd78059 	"login failed",			/* 1b */
3094135Sgd78059 	"logout",			/* 1c */
3104135Sgd78059 	"flash download",		/* 1d */
3114135Sgd78059 	"data lost",			/* 1e */
3124135Sgd78059 	"device busy",			/* 1f */
3134135Sgd78059 	"fault led state",		/* 20 */
3144135Sgd78059 	"overheat",			/* 21 */
3154135Sgd78059 	"severe overheat",		/* 22 */
3164135Sgd78059 	"no overheat",			/* 23 */
3174135Sgd78059 	"SCC",				/* 24 */
3184135Sgd78059 	"device inaccessible",		/* 25 */
3194135Sgd78059 	"Hostname change",		/* 26 */
3204135Sgd78059 	"CPU signature timeout",	/* 27 */
3214135Sgd78059 	"Bootmode change",		/* 28 */
3224135Sgd78059 	"Watchdog change policy",	/* 29 */
3234135Sgd78059 	"Watchdog change timeout",	/* 2a */
3244135Sgd78059 };
3254135Sgd78059 
3264135Sgd78059 /*
3274135Sgd78059  * These store to mapping between the logical service, e.g. chan_prog for
3284135Sgd78059  * programming, and the actual Xbus channel which carries that traffic.
3294135Sgd78059  * Any services can be shared on the same channel apart from chan_wdogpat.
3304135Sgd78059  */
3314135Sgd78059 static int chan_general;	/* General Traffic */
3324135Sgd78059 static int chan_wdogpat;	/* Watchdog Patting */
3334135Sgd78059 static int chan_cpusig;		/* CPU signatures */
3344135Sgd78059 static int chan_eeprom;		/* EEPROM I/O */
3354135Sgd78059 static int chan_prog;		/* Programming */
3364135Sgd78059 
3374135Sgd78059 /*
3384135Sgd78059  * cb_ops structure defining the driver entry points
3394135Sgd78059  */
3404135Sgd78059 
3414135Sgd78059 static struct cb_ops bscv_cb_ops = {
3424135Sgd78059 	bscv_open,	/* open */
3434135Sgd78059 	bscv_close,	/* close */
3444135Sgd78059 	nodev,		/* strategy */
3454135Sgd78059 	nodev,		/* print */
3464135Sgd78059 	nodev,		/* dump */
3474135Sgd78059 	nodev,		/* read */
3484135Sgd78059 	nodev,		/* write */
3494135Sgd78059 	bscv_ioctl,	/* ioctl */
3504135Sgd78059 	nodev,		/* devmap */
3514135Sgd78059 	nodev,		/* mmap */
3524135Sgd78059 	nodev,		/* segmap */
3534135Sgd78059 	nochpoll,	/* poll */
3544135Sgd78059 	ddi_prop_op,	/* prop op */
3554135Sgd78059 	NULL,		/* ! STREAMS */
3564135Sgd78059 	D_NEW | D_MP	/* MT/MP Safe */
3574135Sgd78059 };
3584135Sgd78059 
3594135Sgd78059 /*
3604135Sgd78059  * dev_ops structure defining autoconfiguration driver autoconfiguration
3614135Sgd78059  * routines
3624135Sgd78059  */
3634135Sgd78059 
3644135Sgd78059 static struct dev_ops bscv_dev_ops = {
3654135Sgd78059 	DEVO_REV,		/* devo_rev */
3664135Sgd78059 	0,			/* devo_refcnt */
3674135Sgd78059 	bscv_getinfo,		/* devo_getinfo */
3684135Sgd78059 	nulldev,		/* devo_identify */
3694135Sgd78059 	nulldev,		/* devo_probe */
3704135Sgd78059 	bscv_attach,		/* devo_attach */
3714135Sgd78059 	bscv_detach,		/* devo_detach */
37210280SZachary.Kissel@Sun.COM 	nodev,			/* devo_reset */
3734135Sgd78059 	&bscv_cb_ops,		/* devo_cb_ops */
3747656SSherry.Moore@Sun.COM 	(struct bus_ops *)0,	/* devo_bus_ops */
3757656SSherry.Moore@Sun.COM 	NULL,			/* devo_power */
3767656SSherry.Moore@Sun.COM 	bscv_quiesce,		/* devo_quiesce */
3774135Sgd78059 };
3784135Sgd78059 
3794135Sgd78059 /*
3804135Sgd78059  * module configuration section
3814135Sgd78059  */
3824135Sgd78059 
3834135Sgd78059 #ifdef DEBUG
3847656SSherry.Moore@Sun.COM #define	BSCV_VERSION_STRING "bscv driver - Debug"
3854135Sgd78059 #else /* DEBUG */
3867656SSherry.Moore@Sun.COM #define	BSCV_VERSION_STRING "bscv driver"
3874135Sgd78059 #endif /* DEBUG */
3884135Sgd78059 
3894135Sgd78059 static struct modldrv modldrv = {
3904135Sgd78059 	&mod_driverops,
3914135Sgd78059 	BSCV_VERSION_STRING,
3924135Sgd78059 	&bscv_dev_ops,
3934135Sgd78059 };
3944135Sgd78059 
3954135Sgd78059 static struct modlinkage modlinkage = {
3964135Sgd78059 	MODREV_1,
3974135Sgd78059 	&modldrv,
3984135Sgd78059 	NULL
3994135Sgd78059 };
4004135Sgd78059 
4019644Sandrew.rutz@sun.com #ifdef DEBUG
4029644Sandrew.rutz@sun.com /* Tracing is enabled if value is non-zero. */
4039644Sandrew.rutz@sun.com static int bscv_trace_flag = 1;
4049644Sandrew.rutz@sun.com 
4059644Sandrew.rutz@sun.com #define	BSCV_TRACE   if (bscv_trace_flag != 0)	bscv_trace
4069644Sandrew.rutz@sun.com #else
4079644Sandrew.rutz@sun.com #define	BSCV_TRACE
4089644Sandrew.rutz@sun.com #endif
4099644Sandrew.rutz@sun.com 
4104135Sgd78059 /*
4114135Sgd78059  * kernel accessible routines. These routines are necessarily global so the
4124135Sgd78059  * driver can be loaded, and unloaded successfully
4134135Sgd78059  */
4144135Sgd78059 
4154135Sgd78059 /*
4164135Sgd78059  * function	- _init
4174135Sgd78059  * description	- initializes the driver state structure and installs the
4184135Sgd78059  *		  driver module into the kernel
4194135Sgd78059  * inputs	- none
4204135Sgd78059  * outputs	- success or failure of module installation
4214135Sgd78059  */
4224135Sgd78059 
4234135Sgd78059 int
_init(void)4244135Sgd78059 _init(void)
4254135Sgd78059 {
4264135Sgd78059 	register int e;
4274135Sgd78059 
4284135Sgd78059 	if ((e = ddi_soft_state_init(&bscv_statep,
4294135Sgd78059 	    sizeof (bscv_soft_state_t), 1)) != 0) {
4304135Sgd78059 		return (e);
4314135Sgd78059 	}
4324135Sgd78059 
4334135Sgd78059 	if ((e = mod_install(&modlinkage)) != 0) {
4344135Sgd78059 		ddi_soft_state_fini(&bscv_statep);
4354135Sgd78059 	}
4364135Sgd78059 
4374135Sgd78059 #ifdef __sparc
4384135Sgd78059 	if (e == 0) bscv_idi_init();
4394135Sgd78059 #endif /* __sparc */
4404135Sgd78059 	return (e);
4414135Sgd78059 }
4424135Sgd78059 
4434135Sgd78059 /*
4444135Sgd78059  * function	- _info
4454135Sgd78059  * description	- provide information about a kernel loaded module
4464135Sgd78059  * inputs	- module infomation
4474135Sgd78059  * outputs	- success or failure of information request
4484135Sgd78059  */
4494135Sgd78059 
4504135Sgd78059 int
_info(struct modinfo * modinfop)4514135Sgd78059 _info(struct modinfo *modinfop)
4524135Sgd78059 {
4534135Sgd78059 	return (mod_info(&modlinkage, modinfop));
4544135Sgd78059 }
4554135Sgd78059 
4564135Sgd78059 /*
4574135Sgd78059  * function	- _fini
4584135Sgd78059  * description	- removes a module from the kernel and frees the driver soft
4594135Sgd78059  *		  state memory
4604135Sgd78059  * inputs	- none
4614135Sgd78059  * outputs	- success or failure of module removal
4624135Sgd78059  */
4634135Sgd78059 
4644135Sgd78059 int
_fini(void)4654135Sgd78059 _fini(void)
4664135Sgd78059 {
4674135Sgd78059 	register int e;
4684135Sgd78059 
4694135Sgd78059 	if ((e = mod_remove(&modlinkage)) != 0) {
4704135Sgd78059 		return (e);
4714135Sgd78059 	}
4724135Sgd78059 
4734135Sgd78059 #ifdef __sparc
4744135Sgd78059 	bscv_idi_fini();
4754135Sgd78059 #endif /* __sparc */
4764135Sgd78059 	ddi_soft_state_fini(&bscv_statep);
4774135Sgd78059 
4784135Sgd78059 	return (e);
4794135Sgd78059 }
4804135Sgd78059 
4814135Sgd78059 /*
4824135Sgd78059  * function	- bscv_getinfo
4834135Sgd78059  * description	- routine used to provide information on the driver
4844135Sgd78059  * inputs	- device information structure, command, command arg, storage
4854135Sgd78059  *		  area for the result
4864135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
4874135Sgd78059  */
4884135Sgd78059 
4894135Sgd78059 /*ARGSUSED*/
4904135Sgd78059 static int
bscv_getinfo(dev_info_t * dip,ddi_info_cmd_t cmd,void * arg,void ** result)4914135Sgd78059 bscv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
4924135Sgd78059 {
4934135Sgd78059 	bscv_soft_state_t *ssp;
4944135Sgd78059 	dev_t	dev = (dev_t)arg;
4954135Sgd78059 	int	instance;
4964135Sgd78059 	int	error;
4974135Sgd78059 
4984135Sgd78059 	instance = DEVICETOINSTANCE(dev);
4994135Sgd78059 
5004135Sgd78059 	switch (cmd) {
5014135Sgd78059 	case DDI_INFO_DEVT2INSTANCE:
5024135Sgd78059 		*result = (void *)(uintptr_t)instance;
5034135Sgd78059 		error = DDI_SUCCESS;
5044135Sgd78059 		break;
5054135Sgd78059 
5064135Sgd78059 	case DDI_INFO_DEVT2DEVINFO:
5074135Sgd78059 		ssp = ddi_get_soft_state(bscv_statep, instance);
5084135Sgd78059 		if (ssp == NULL)
5094135Sgd78059 			return (DDI_FAILURE);
5104135Sgd78059 		*result = (void *) ssp->dip;
5114135Sgd78059 		error = DDI_SUCCESS;
5124135Sgd78059 		break;
5134135Sgd78059 
5144135Sgd78059 	default:
5154135Sgd78059 		error = DDI_FAILURE;
5164135Sgd78059 		break;
5174135Sgd78059 	}
5184135Sgd78059 
5194135Sgd78059 	return (error);
5204135Sgd78059 }
5214135Sgd78059 
5224135Sgd78059 #ifdef __sparc
5234135Sgd78059 void
bscv_idi_init()5244135Sgd78059 bscv_idi_init()
5254135Sgd78059 {
5264135Sgd78059 	bscv_idi_mgr.valid_inst = (uint32_t)~0;    /* No valid instances */
5274135Sgd78059 	bscv_idi_mgr.tbl = bscv_idi_callout_table;
5284135Sgd78059 	bscv_idi_mgr.errs = 0;
5294135Sgd78059 
5304135Sgd78059 	/*
5314135Sgd78059 	 * Now that all fields are initialized, set the magic flag.  This is
5324135Sgd78059 	 * a kind of integrity check for the data structure.
5334135Sgd78059 	 */
5344135Sgd78059 	bscv_idi_mgr.magic = BSCV_IDI_CALLOUT_MAGIC;
5354135Sgd78059 }
5364135Sgd78059 
5374135Sgd78059 static void
bscv_idi_clear_err()5384135Sgd78059 bscv_idi_clear_err()
5394135Sgd78059 {
5404135Sgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
5414135Sgd78059 
5424135Sgd78059 	bscv_idi_mgr.errs = 0;
5434135Sgd78059 }
5444135Sgd78059 
5454135Sgd78059 /*
5464135Sgd78059  * function	- bscv_idi_err
5474135Sgd78059  * description	- error messaging service which throttles the number of error
5484135Sgd78059  *		  messages to avoid overflowing storage
5494135Sgd78059  * inputs	- none
5504135Sgd78059  * returns	- boolean to indicate whether a message should be reported
5514135Sgd78059  * side-effects	- updates the error number counter
5524135Sgd78059  */
5534135Sgd78059 static boolean_t
bscv_idi_err()5544135Sgd78059 bscv_idi_err()
5554135Sgd78059 {
5564135Sgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
5574135Sgd78059 
5584135Sgd78059 	bscv_idi_mgr.errs++;
5594135Sgd78059 
5604135Sgd78059 	if (bscv_idi_mgr.errs++ < BSCV_IDI_ERR_MSG_THRESHOLD)
5614135Sgd78059 		return (B_TRUE);
5624135Sgd78059 
5634135Sgd78059 	return (B_FALSE);
5644135Sgd78059 }
5654135Sgd78059 
5664135Sgd78059 void
bscv_idi_new_instance(dev_info_t * dip)5674135Sgd78059 bscv_idi_new_instance(dev_info_t *dip)
5684135Sgd78059 {
5694135Sgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
5704135Sgd78059 
5714135Sgd78059 	/*
5724135Sgd78059 	 * We don't care how many instances we have, or their value, so long
5734135Sgd78059 	 * as we have at least one valid value.  This is so service routines
5744135Sgd78059 	 * can get any required locks via a soft state pointer.
5754135Sgd78059 	 */
5764135Sgd78059 	if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
5774135Sgd78059 		bscv_idi_mgr.valid_inst = ddi_get_instance(dip);
5784135Sgd78059 	}
5794135Sgd78059 }
5804135Sgd78059 
5814135Sgd78059 void
bscv_idi_fini()5824135Sgd78059 bscv_idi_fini()
5834135Sgd78059 {
5844135Sgd78059 	bscv_idi_mgr.valid_inst = (uint32_t)~0;    /* No valid instances */
5854135Sgd78059 	bscv_idi_mgr.tbl = NULL;
5864135Sgd78059 }
5874135Sgd78059 #endif /* __sparc */
5884135Sgd78059 
5894135Sgd78059 /*
5904135Sgd78059  * function	- bscv_attach
5914135Sgd78059  * description	- this routine is responsible for setting aside memory for the
5924135Sgd78059  *		  driver data structures, initialising the mutexes and creating
5934135Sgd78059  *		  the device minor nodes. Additionally, this routine calls the
5944135Sgd78059  *		  the callback routine.
5954135Sgd78059  * inputs	- device information structure, DDI_ATTACH command
5964135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
5974135Sgd78059  */
5984135Sgd78059 
5994135Sgd78059 int
bscv_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)6004135Sgd78059 bscv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
6014135Sgd78059 {
6024135Sgd78059 	bscv_soft_state_t *ssp;
6034135Sgd78059 	int	instance;
6044135Sgd78059 
6054135Sgd78059 	switch (cmd) {
6064135Sgd78059 	case DDI_ATTACH:
6074135Sgd78059 
6084135Sgd78059 		instance = ddi_get_instance(dip);
6094135Sgd78059 
6104135Sgd78059 		if (ddi_soft_state_zalloc(bscv_statep, instance) !=
6114135Sgd78059 		    DDI_SUCCESS) {
6124135Sgd78059 			return (DDI_FAILURE);
6134135Sgd78059 		}
6144135Sgd78059 
6154135Sgd78059 
6164135Sgd78059 		ssp = ddi_get_soft_state(bscv_statep, instance);
6174135Sgd78059 
6184135Sgd78059 		ssp->progress = 0;
6194135Sgd78059 
6204135Sgd78059 		ssp->dip = dip;
6214135Sgd78059 		ssp->instance = instance;
6224135Sgd78059 		ssp->event_waiting = B_FALSE;
6234135Sgd78059 		ssp->status_change = B_FALSE;
6244135Sgd78059 		ssp->nodename_change = B_FALSE;
6254135Sgd78059 		ssp->cap0 = 0;
6264135Sgd78059 		ssp->cap1 = 0;
6274135Sgd78059 		ssp->cap2 = 0;
6284135Sgd78059 		ssp->prog_mode_only = B_FALSE;
6294135Sgd78059 		ssp->programming = B_FALSE;
6304135Sgd78059 		ssp->cssp_prog = B_FALSE;
6314135Sgd78059 		ssp->task_flags = 0;
6324135Sgd78059 		ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
6335107Seota 		    DDI_PROP_DONTPASS, "debug", 0);
6344135Sgd78059 		ssp->majornum = ddi_driver_major(dip);
6354135Sgd78059 		ssp->minornum = BSCV_INST_TO_MINOR(instance);
6364135Sgd78059 #if defined(__i386) || defined(__amd64)
6374135Sgd78059 		ssp->last_nodename[0] = '\0';
6384135Sgd78059 #endif /* __i386 || __amd64 */
6394135Sgd78059 
6404135Sgd78059 		/*
6414135Sgd78059 		 * initialise the mutexes
6424135Sgd78059 		 */
6434135Sgd78059 
6444135Sgd78059 		mutex_init(&ssp->cmd_mutex, NULL, MUTEX_DRIVER, NULL);
6454135Sgd78059 
6464135Sgd78059 		mutex_init(&ssp->task_mu, NULL, MUTEX_DRIVER, NULL);
6474135Sgd78059 		cv_init(&ssp->task_cv, NULL, CV_DRIVER, NULL);
6484135Sgd78059 		cv_init(&ssp->task_evnt_cv, NULL, CV_DRIVER, NULL);
6494135Sgd78059 		mutex_init(&ssp->prog_mu, NULL, MUTEX_DRIVER, NULL);
6504135Sgd78059 		ssp->progress |= BSCV_LOCKS;
6514135Sgd78059 
6529644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_attach",
6534135Sgd78059 		    "bscv_attach: mutexes and condition vars initialised");
6544135Sgd78059 
6554135Sgd78059 		/* Map in physical communication channels */
6564135Sgd78059 
6574135Sgd78059 		if (bscv_map_regs(ssp) != DDI_SUCCESS) {
6584135Sgd78059 			(void) bscv_cleanup(ssp);
6594135Sgd78059 			return (DDI_FAILURE);
6604135Sgd78059 		}
6614135Sgd78059 		ssp->progress |= BSCV_MAPPED_REGS;
6624135Sgd78059 
6634135Sgd78059 		/* Associate logical channels to physical channels */
6644135Sgd78059 
6654135Sgd78059 		bscv_map_chan_logical_physical(ssp);
6664135Sgd78059 
6674135Sgd78059 		bscv_enter(ssp);
6684135Sgd78059 
6694135Sgd78059 		bscv_leave_programming_mode(ssp, B_FALSE);
6704135Sgd78059 
6714135Sgd78059 		if (bscv_attach_common(ssp) == DDI_FAILURE) {
6724135Sgd78059 			bscv_exit(ssp);
6734135Sgd78059 			(void) bscv_cleanup(ssp);
6744135Sgd78059 			return (DDI_FAILURE);
6754135Sgd78059 		}
6764135Sgd78059 
6774135Sgd78059 #ifdef __sparc
6784135Sgd78059 		/*
6794135Sgd78059 		 * At this point the inter-driver-interface is made available.
6804135Sgd78059 		 * The IDI uses the event thread service which
6814135Sgd78059 		 * bscv_attach_common() sets up.
6824135Sgd78059 		 */
6834135Sgd78059 		bscv_idi_new_instance(dip);
6844135Sgd78059 #endif /* __sparc */
6854135Sgd78059 
6864135Sgd78059 		bscv_exit(ssp);
6874135Sgd78059 
6884135Sgd78059 		/*
6894135Sgd78059 		 * now create the minor nodes
6904135Sgd78059 		 */
6914135Sgd78059 		if (ddi_create_minor_node(ssp->dip, "lom", S_IFCHR,
6924135Sgd78059 		    BSCV_INST_TO_MINOR(instance),
6934135Sgd78059 		    DDI_PSEUDO, 0) != DDI_SUCCESS) {
6944135Sgd78059 			(void) bscv_cleanup(ssp);
6954135Sgd78059 			return (DDI_FAILURE);
6964135Sgd78059 		}
6979644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_attach",
6984135Sgd78059 		    "bscv_attach: device minor nodes created");
6994135Sgd78059 		ssp->progress |= BSCV_NODES;
7004135Sgd78059 
7014135Sgd78059 		if (!ssp->prog_mode_only)
7024135Sgd78059 			bscv_start_event_daemon(ssp);
7034135Sgd78059 
7044135Sgd78059 #if defined(__i386) || defined(__amd64)
7054135Sgd78059 		bscv_watchdog_enable = 1;
7064135Sgd78059 		bscv_watchdog_available = 1;
7074135Sgd78059 		watchdog_activated = 0;
7084135Sgd78059 		bscv_watchdog_timeout_seconds = CLK_WATCHDOG_DEFAULT;
7094135Sgd78059 
7104135Sgd78059 		if (bscv_watchdog_enable && (boothowto & RB_DEBUG)) {
7114135Sgd78059 			bscv_watchdog_available = 0;
7124135Sgd78059 			cmn_err(CE_WARN, "bscv: kernel debugger "
7135107Seota 			    "detected: hardware watchdog disabled");
7144135Sgd78059 		}
7154135Sgd78059 
7164135Sgd78059 		/*
7174135Sgd78059 		 * Before we enable the watchdog - register the panic
7184135Sgd78059 		 * callback so that we get called to stop the watchdog
7194135Sgd78059 		 * in the case of a panic.
7204135Sgd78059 		 */
7214135Sgd78059 		ssp->callb_id = callb_add(bscv_panic_callback,
7224135Sgd78059 		    (void *)ssp, CB_CL_PANIC, "");
7234135Sgd78059 
7244135Sgd78059 		if (bscv_watchdog_available) {
7254135Sgd78059 			(void) bscv_set_watchdog_timer(ssp,
7264135Sgd78059 			    CLK_WATCHDOG_DEFAULT);
7274135Sgd78059 			bscv_enter(ssp);
7284135Sgd78059 			bscv_setup_watchdog(ssp);  /* starts cyclic callback */
7294135Sgd78059 			bscv_exit(ssp);
7304135Sgd78059 		}
7314135Sgd78059 #endif /* __i386 || __amd64 */
7324135Sgd78059 		ddi_report_dev(dip);
7334135Sgd78059 		return (DDI_SUCCESS);
7344135Sgd78059 	default:
7354135Sgd78059 		return (DDI_FAILURE);
7364135Sgd78059 	}
7374135Sgd78059 }
7384135Sgd78059 
7394135Sgd78059 /*
7404135Sgd78059  * function	- bscv_detach
7414135Sgd78059  * description	- routine that prepares a module to be unloaded. It undoes all
7424135Sgd78059  *		  the work done by the bscv_attach)() routine. This is
7434135Sgd78059  *		  facilitated by the use of the progress indicator
7444135Sgd78059  * inputs	- device information structure, DDI_DETACH command
7454135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
7464135Sgd78059  */
7474135Sgd78059 
7484135Sgd78059 /*ARGSUSED*/
7494135Sgd78059 static int
bscv_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)7504135Sgd78059 bscv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
7514135Sgd78059 {
7524135Sgd78059 	return (DDI_FAILURE);
7534135Sgd78059 }
7544135Sgd78059 
7554135Sgd78059 /*
7567656SSherry.Moore@Sun.COM  * quiesce(9E) entry point.
7577656SSherry.Moore@Sun.COM  *
7587656SSherry.Moore@Sun.COM  * This function is called when the system is single-threaded at high
7597656SSherry.Moore@Sun.COM  * PIL with preemption disabled. Therefore, this function must not be
7607656SSherry.Moore@Sun.COM  * blocked.
7617656SSherry.Moore@Sun.COM  *
7627656SSherry.Moore@Sun.COM  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
7637656SSherry.Moore@Sun.COM  * DDI_FAILURE indicates an error condition and should almost never happen.
7647656SSherry.Moore@Sun.COM  */
7657656SSherry.Moore@Sun.COM static int
bscv_quiesce(dev_info_t * dip)7667656SSherry.Moore@Sun.COM bscv_quiesce(dev_info_t *dip)
7677656SSherry.Moore@Sun.COM {
7687656SSherry.Moore@Sun.COM 	bscv_soft_state_t *ssp;
7697656SSherry.Moore@Sun.COM 	int	instance;
7707656SSherry.Moore@Sun.COM 
7717656SSherry.Moore@Sun.COM 
7727656SSherry.Moore@Sun.COM 	instance = ddi_get_instance(dip);
7737656SSherry.Moore@Sun.COM 	ssp = ddi_get_soft_state(bscv_statep, instance);
7747656SSherry.Moore@Sun.COM 	if (ssp == NULL) {
7757656SSherry.Moore@Sun.COM 		return (DDI_FAILURE);
7767656SSherry.Moore@Sun.COM 	}
7779644Sandrew.rutz@sun.com #ifdef DEBUG
7789644Sandrew.rutz@sun.com 	/* Disable tracing, as we are executing at High-Interrupt level */
7799644Sandrew.rutz@sun.com 	bscv_trace_flag = 0;
7809644Sandrew.rutz@sun.com #endif
7819644Sandrew.rutz@sun.com 	/* quiesce the device */
7827656SSherry.Moore@Sun.COM 	bscv_full_stop(ssp);
7839644Sandrew.rutz@sun.com 
7847656SSherry.Moore@Sun.COM 	return (DDI_SUCCESS);
7857656SSherry.Moore@Sun.COM }
7867656SSherry.Moore@Sun.COM 
7877656SSherry.Moore@Sun.COM /*
7884135Sgd78059  * cb_ops routines
7894135Sgd78059  */
7904135Sgd78059 
7914135Sgd78059 /*
7924135Sgd78059  * function	- bscv_open
7934135Sgd78059  * description	- routine to provide association between user fd and device
7944135Sgd78059  *		  minor number. This routine is necessarily simple since a
7954135Sgd78059  *		  read/write interface is not provided. Additionally, the
7964135Sgd78059  *		  driver does not enforce exclusive access (FEXCL) or
7974135Sgd78059  *		  non-blocking during an open (FNDELAY). Deferred attach is
7984135Sgd78059  *		  supported.
7994135Sgd78059  * inputs	- device number, flag specifying open type, device type,
8004135Sgd78059  *		  permissions
8014135Sgd78059  * outputs	- success or failure of operation
8024135Sgd78059  */
8034135Sgd78059 
8044135Sgd78059 /*ARGSUSED*/
8054135Sgd78059 static int
bscv_open(dev_t * devp,int flag,int otype,cred_t * cred)8064135Sgd78059 bscv_open(dev_t *devp, int flag, int otype, cred_t *cred)
8074135Sgd78059 {
8084135Sgd78059 	bscv_soft_state_t *ssp;
8094135Sgd78059 	int instance;
8104135Sgd78059 
8114135Sgd78059 	instance = DEVICETOINSTANCE(*devp);
8124135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
8134135Sgd78059 	if (ssp == NULL) {
8144135Sgd78059 		return (ENXIO);	/* not attached yet */
8154135Sgd78059 	}
8169644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'O', "bscv_open", "instance 0x%x", instance);
8174135Sgd78059 
8184135Sgd78059 	if (otype != OTYP_CHR) {
8194135Sgd78059 		return (EINVAL);
8204135Sgd78059 	}
8214135Sgd78059 
8224135Sgd78059 	return (0);
8234135Sgd78059 }
8244135Sgd78059 
8254135Sgd78059 /*
8264135Sgd78059  * function	- bscv_close
8274135Sgd78059  * description	- routine to perform the final close on the device. As per the
8284135Sgd78059  *		  open routine, neither FEXCL or FNDELAY accesses are enforced
8294135Sgd78059  *		  by the driver.
8304135Sgd78059  * inputs	- device number,flag specifying open type, device type,
8314135Sgd78059  *		  permissions
8324135Sgd78059  * outputs	- success or failure of operation
8334135Sgd78059  */
8344135Sgd78059 
8354135Sgd78059 /*ARGSUSED1*/
8364135Sgd78059 static int
bscv_close(dev_t dev,int flag,int otype,cred_t * cred)8374135Sgd78059 bscv_close(dev_t dev, int flag, int otype, cred_t *cred)
8384135Sgd78059 {
8394135Sgd78059 	bscv_soft_state_t *ssp;
8404135Sgd78059 	int instance;
8414135Sgd78059 
8424135Sgd78059 	instance = DEVICETOINSTANCE(dev);
8434135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
8444135Sgd78059 	if (ssp == NULL) {
8454135Sgd78059 		return (ENXIO);
8464135Sgd78059 	}
8479644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'O', "bscv_close", "instance 0x%x", instance);
8484135Sgd78059 
8494135Sgd78059 	return (0);
8504135Sgd78059 }
8514135Sgd78059 
8524135Sgd78059 static int
bscv_map_regs(bscv_soft_state_t * ssp)8534135Sgd78059 bscv_map_regs(bscv_soft_state_t *ssp)
8544135Sgd78059 {
8554135Sgd78059 	int i;
8564135Sgd78059 	int retval;
8574135Sgd78059 	int *props;
8584135Sgd78059 	unsigned int nelements;
8594135Sgd78059 
8604135Sgd78059 	ASSERT(ssp);
8614135Sgd78059 
8624135Sgd78059 	ssp->nchannels = 0;
8634135Sgd78059 
8644135Sgd78059 	/*
8654135Sgd78059 	 * Work out how many channels are available by looking at the number
8664135Sgd78059 	 * of elements of the regs property array.
8674135Sgd78059 	 */
8684135Sgd78059 	retval = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ssp->dip,
8695107Seota 	    DDI_PROP_DONTPASS, "reg", &props, &nelements);
8704135Sgd78059 
8714135Sgd78059 	/* We don't need props anymore.  Free memory if it was allocated */
8724135Sgd78059 	if (retval == DDI_PROP_SUCCESS)
8734135Sgd78059 		ddi_prop_free(props);
8744135Sgd78059 
8754135Sgd78059 	/* Check for sanity of nelements */
8764135Sgd78059 	if (retval != DDI_PROP_SUCCESS) {
8779644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_map_regs", "lookup reg returned"
8784135Sgd78059 		    " 0x%x", retval);
8794135Sgd78059 		goto cleanup_exit;
8804135Sgd78059 	} else if (nelements % LOMBUS_REGSPEC_SIZE != 0) {
8819644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d not"
8824135Sgd78059 		    " a multiple of %d", nelements, LOMBUS_REGSPEC_SIZE);
8834135Sgd78059 		goto cleanup_exit;
8844135Sgd78059 	} else if (nelements > BSCV_MAXCHANNELS * LOMBUS_REGSPEC_SIZE) {
8859644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d too large"
8864135Sgd78059 		    ", probably a misconfiguration", nelements);
8874135Sgd78059 		goto cleanup_exit;
8884135Sgd78059 	} else if (nelements < BSCV_MINCHANNELS * LOMBUS_REGSPEC_SIZE) {
8899644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d too small"
8904135Sgd78059 		    ", need to have at least a general and a wdog channel",
8914135Sgd78059 		    nelements);
8924135Sgd78059 		goto cleanup_exit;
8934135Sgd78059 	}
8944135Sgd78059 
8954135Sgd78059 	ssp->nchannels = nelements / LOMBUS_REGSPEC_SIZE;
8964135Sgd78059 
8974135Sgd78059 	ssp->attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
8984135Sgd78059 	ssp->attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
8994135Sgd78059 	ssp->attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
9004135Sgd78059 
9014135Sgd78059 	for (i = 0; i < ssp->nchannels; i++) {
9024135Sgd78059 		retval = ddi_regs_map_setup(ssp->dip, i,
9035107Seota 		    (caddr_t *)&ssp->channel[i].regs,
9045107Seota 		    0, 0, &ssp->attr, &ssp->channel[i].handle);
9054135Sgd78059 		if (retval != DDI_SUCCESS) {
9069644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'A', "bscv_map_regs", "map failure"
9074135Sgd78059 			    " 0x%x on space %d", retval, i);
9084135Sgd78059 
9094135Sgd78059 			/* Rewind all current mappings - avoiding failed one */
9104135Sgd78059 			i--;
9114135Sgd78059 			for (; i >= 0; i--) {
9124135Sgd78059 				ddi_regs_map_free(&ssp->channel[i].handle);
9134135Sgd78059 			}
9144135Sgd78059 
9154135Sgd78059 			goto cleanup_exit;
9164135Sgd78059 		}
9174135Sgd78059 	}
9184135Sgd78059 
9194135Sgd78059 	return (DDI_SUCCESS);
9204135Sgd78059 
9214135Sgd78059 cleanup_exit:
9224135Sgd78059 	/*
9234135Sgd78059 	 * It is important to set nchannels to 0 even if, say, only one of
9244135Sgd78059 	 * the two required handles was mapped.  If we cannot achieve our
9254135Sgd78059 	 * minimum config its not safe to do any IO; this keeps our failure
9264135Sgd78059 	 * mode handling simpler.
9274135Sgd78059 	 */
9284135Sgd78059 	ssp->nchannels = 0;
9294135Sgd78059 	return (DDI_FAILURE);
9304135Sgd78059 }
9314135Sgd78059 
9324135Sgd78059 static void
bscv_unmap_regs(bscv_soft_state_t * ssp)9334135Sgd78059 bscv_unmap_regs(bscv_soft_state_t *ssp)
9344135Sgd78059 {
9354135Sgd78059 	int i;
9364135Sgd78059 
9374135Sgd78059 	ASSERT(ssp);
9384135Sgd78059 
9394135Sgd78059 	for (i = 0; i < ssp->nchannels; i++) {
9404135Sgd78059 		ddi_regs_map_free(&ssp->channel[i].handle);
9414135Sgd78059 	}
9424135Sgd78059 }
9434135Sgd78059 
9444135Sgd78059 /*
9454135Sgd78059  * Map logical services onto physical XBus channels.
9464135Sgd78059  */
9474135Sgd78059 static void
bscv_map_chan_logical_physical(bscv_soft_state_t * ssp)9484135Sgd78059 bscv_map_chan_logical_physical(bscv_soft_state_t *ssp)
9494135Sgd78059 {
9504135Sgd78059 	ASSERT(ssp);
9514135Sgd78059 
9524135Sgd78059 	/*
9534135Sgd78059 	 * We can assert that there will always be at least two channels,
9544135Sgd78059 	 * to allow watchdog pats to be segregated from all other traffic.
9554135Sgd78059 	 */
9564135Sgd78059 	chan_general = 0;
9574135Sgd78059 	chan_wdogpat = 1;
9584135Sgd78059 
9594135Sgd78059 	/*
9604135Sgd78059 	 * By default move all other services onto the generic channel unless
9614135Sgd78059 	 * the hardware supports additional channels.
9624135Sgd78059 	 */
9634135Sgd78059 
9644135Sgd78059 	chan_cpusig = chan_eeprom = chan_prog = chan_general;
9654135Sgd78059 
9664135Sgd78059 	if (ssp->nchannels > 2)
9674135Sgd78059 		chan_cpusig = 2;
9684135Sgd78059 	if (ssp->nchannels > 3)
9694135Sgd78059 		chan_eeprom = 3;
9704135Sgd78059 	if (ssp->nchannels > 4)
9714135Sgd78059 		chan_prog = 4;
9724135Sgd78059 }
9734135Sgd78059 
9744135Sgd78059 
9754135Sgd78059 /*
9764135Sgd78059  * function	- bscv_full_stop
9774135Sgd78059  * description	- gracefully shut the lom down during panic or reboot.
9789644Sandrew.rutz@sun.com  *		  Disables the watchdog and sets up serial event reporting.
9794135Sgd78059  * inputs	- soft state pointer
9804135Sgd78059  * outputs	- none
9814135Sgd78059  */
9824135Sgd78059 void
bscv_full_stop(bscv_soft_state_t * ssp)9834135Sgd78059 bscv_full_stop(bscv_soft_state_t *ssp)
9844135Sgd78059 {
9854135Sgd78059 	uint8_t bits2set = 0;
9864135Sgd78059 	uint8_t bits2clear = 0;
9879644Sandrew.rutz@sun.com 	int obtained_lock;
9889644Sandrew.rutz@sun.com 
9899644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'W', "bscv_full_stop",
9904135Sgd78059 	    "turning off watchdog");
9914135Sgd78059 
9929644Sandrew.rutz@sun.com 	/*
9939644Sandrew.rutz@sun.com 	 * Obtain the softstate lock only if it is not already owned,
9949644Sandrew.rutz@sun.com 	 * as this function can be called from a High-level interrupt
9959644Sandrew.rutz@sun.com 	 * context.  As a result, our thread cannot sleep.
9969644Sandrew.rutz@sun.com 	 * At end of function, our thread releases the lock only if
9979644Sandrew.rutz@sun.com 	 * it acquired the lock.
9989644Sandrew.rutz@sun.com 	 */
9999644Sandrew.rutz@sun.com 	obtained_lock = (bscv_tryenter(ssp) != 0);
10004135Sgd78059 
10014135Sgd78059 #if defined(__i386) || defined(__amd64)
10024135Sgd78059 	if (ddi_in_panic()) {
10035107Seota 		bscv_inform_bsc(ssp, BSC_INFORM_PANIC);
10044135Sgd78059 	} else {
10055107Seota 		bscv_inform_bsc(ssp, BSC_INFORM_OFFLINE);
10064135Sgd78059 	}
10074135Sgd78059 #endif /* __i386 || __amd64 */
10084135Sgd78059 
10094135Sgd78059 	/* set serial event reporting */
10104135Sgd78059 	switch (ssp->serial_reporting) {
10114135Sgd78059 	case LOM_SER_EVENTS_ON:
10124135Sgd78059 	case LOM_SER_EVENTS_DEF:
10134135Sgd78059 		/* Make sure serial event reporting is on */
10144135Sgd78059 		bits2clear = EBUS_ALARM_NOEVENTS;
10154135Sgd78059 		break;
10164135Sgd78059 	case LOM_SER_EVENTS_OFF:
10174135Sgd78059 		/* Make sure serial event reporting is on */
10184135Sgd78059 		bits2set = EBUS_ALARM_NOEVENTS;
10194135Sgd78059 		break;
10204135Sgd78059 	default:
10214135Sgd78059 		break;
10224135Sgd78059 	}
10234135Sgd78059 	bscv_setclear8_volatile(ssp, chan_general,
10245107Seota 	    EBUS_IDX_ALARM, bits2set, bits2clear);
10254135Sgd78059 
10269644Sandrew.rutz@sun.com 	/* Do not free the lock if our thread did not obtain it. */
10279644Sandrew.rutz@sun.com 	if (obtained_lock != 0) {
10289644Sandrew.rutz@sun.com 		bscv_exit(ssp);
10299644Sandrew.rutz@sun.com 	}
10304135Sgd78059 }
10314135Sgd78059 
10324135Sgd78059 /*
10334135Sgd78059  * LOM I/O routines.
10344135Sgd78059  *
10354135Sgd78059  * locking
10364135Sgd78059  *
10374135Sgd78059  * Two sets of routines are provided:
10384135Sgd78059  *	normal - must be called after acquiring an appropriate lock.
10394135Sgd78059  *	locked - perform all the locking required and return any error
10404135Sgd78059  *		 code in the supplied 'res' argument. If there is no
10414135Sgd78059  *		 error 'res' is not changed.
10424135Sgd78059  * The locked routines are designed for use in ioctl commands where
10434135Sgd78059  * only a single operation needs to be performed and the overhead of
10444135Sgd78059  * locking and result checking adds significantly to code complexity.
10454135Sgd78059  *
10464135Sgd78059  * locking primitives
10474135Sgd78059  *
10489644Sandrew.rutz@sun.com  * bscv_enter()    - acquires an I/O lock for the calling thread.
10499644Sandrew.rutz@sun.com  * bscv_tryenter() - conditionally acquires an I/O lock for calling thread.
10509644Sandrew.rutz@sun.com  * bscv_exit()     - releases an I/O lock acquired by bscv_enter().
10519644Sandrew.rutz@sun.com  * bscv_held()     - used to assert ownership of an I/O lock.
10524135Sgd78059  *
10534135Sgd78059  * normal I/O routines
10544135Sgd78059  *
10554135Sgd78059  * Note bscv_{put|get}{16|32} routines are big-endian. This assumes that
10564135Sgd78059  * the firmware works that way too.
10574135Sgd78059  *
10584135Sgd78059  * bscv_put8(), bscv_put16, bscv_put32 - write values to the LOM
10594135Sgd78059  *		and handle any retries if necessary.
10604135Sgd78059  *		16 and 32 bit values are big-endian.
10614135Sgd78059  * bscv_get8(), bscv_get16, bscv_get32 - read values from the LOM
10624135Sgd78059  *		and handle any retries if necessary.
10634135Sgd78059  *		16 and 32 bit values are big-endian.
10644135Sgd78059  * bscv_setclear8() - set or clear the specified bits in the register
10654135Sgd78059  *		at the supplied address.
10664135Sgd78059  * bscv_setclear8_volatile() - set or clear the specified bits in the
10674135Sgd78059  *		register at the supplied address. If the lom reports
10684135Sgd78059  *		that the registers has changed since the last read
10694135Sgd78059  *		re-read and apply the set or clear to the new bits.
10704135Sgd78059  * bscv_get8_cached() - Return a cached register value (addr < 0x80).
10714135Sgd78059  *		Does not access the hardware. A read of the hardware
10724135Sgd78059  *		automatically updates this cache.
10734135Sgd78059  *
10744135Sgd78059  * locked I/O routines
10754135Sgd78059  *
10764135Sgd78059  * bscv_get8_locked(), bscv_rep_get8_locked().
10774135Sgd78059  *
10784135Sgd78059  * Call the indicated function from above, but wrapping it with
10794135Sgd78059  * bscv_enter()/bscv_exit().
10804135Sgd78059  *
10814135Sgd78059  *
10824135Sgd78059  * Fault management
10834135Sgd78059  *
10844135Sgd78059  * LOM communications fault are grouped into three categories:
10854135Sgd78059  * 1) Faulty - the LOM is not responding and no attempt to communicate
10864135Sgd78059  *		with it should be made.
10874135Sgd78059  * 2) Transient fault - something which might recover after a retry
10884135Sgd78059  *		but which doesn't affect our ability to perform other
10894135Sgd78059  *		commands.
10904135Sgd78059  * 3) Command error - an inappropriate command was executed. A retry
10914135Sgd78059  *		will not fix it but the command failed.
10924135Sgd78059  *
10934135Sgd78059  * The current implementation of the bscv driver is not very good at
10944135Sgd78059  * noticing command errors due to the structure of the original code
10954135Sgd78059  * that it is based on. It is possible to extend the driver to do this
10964135Sgd78059  * and would probably involve having a concept of a "session error"
10974135Sgd78059  * which is less severe than a fault but means that a sequence of
10984135Sgd78059  * commands had some fault which cannot be recovered.
10994135Sgd78059  *
11004135Sgd78059  *
11014135Sgd78059  * faults
11024135Sgd78059  *
11034135Sgd78059  * bscv_faulty() - returns B_TRUE if the LOM (communications) have been
11044135Sgd78059  *		declared faulty.
11054135Sgd78059  * bscv_clear_fault() - marks the LOM as not faulty.
11064135Sgd78059  * bscv_set_fault() - marks the LOM as being faulty.
11074135Sgd78059  *
11084135Sgd78059  * bscv_clear_fault and bscv_set_fault should generally not be called
11094135Sgd78059  * directly.
11104135Sgd78059  *
11114135Sgd78059  * command errors/transient faults
11124135Sgd78059  *
11134135Sgd78059  * bscv_retcode() - returns the actual error code of the last operation.
11144135Sgd78059  * bscv_should_retry() - determines if last operation may suceed if
11154135Sgd78059  *		retried.
11164135Sgd78059  * bscv_locked_result() - Set the result of a locked register access.
11174135Sgd78059  *
11184135Sgd78059  * low level I/O primitives
11194135Sgd78059  *
11204135Sgd78059  * These are generally not called directly. These perform a single
11214135Sgd78059  * access to the LOM device. They do not handle retries.
11224135Sgd78059  *
11234135Sgd78059  * bscv_put8_once()
11244135Sgd78059  * bscv_get8_once()
11254135Sgd78059  * bscv_probe() - perform a probe (NOP) operation to check out lom comms.
11264135Sgd78059  * bscv_resync_comms() - resynchronise communications after a transient fault.
11274135Sgd78059  */
11284135Sgd78059 
11294135Sgd78059 static void
bscv_enter(bscv_soft_state_t * ssp)11304135Sgd78059 bscv_enter(bscv_soft_state_t *ssp)
11314135Sgd78059 {
11329644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_enter", "");
11334135Sgd78059 	mutex_enter(&ssp->cmd_mutex);
11344135Sgd78059 	ssp->had_session_error = B_FALSE;
11354135Sgd78059 }
11364135Sgd78059 
11379644Sandrew.rutz@sun.com static int
bscv_tryenter(bscv_soft_state_t * ssp)11389644Sandrew.rutz@sun.com bscv_tryenter(bscv_soft_state_t *ssp)
11399644Sandrew.rutz@sun.com {
11409644Sandrew.rutz@sun.com 	int rv;
11419644Sandrew.rutz@sun.com 
11429644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_tryenter", "");
11439644Sandrew.rutz@sun.com 	if ((rv = mutex_tryenter(&ssp->cmd_mutex)) != 0) {
11449644Sandrew.rutz@sun.com 		ssp->had_session_error = B_FALSE;
11459644Sandrew.rutz@sun.com 	}
11469644Sandrew.rutz@sun.com 	return (rv);
11479644Sandrew.rutz@sun.com }
11489644Sandrew.rutz@sun.com 
11494135Sgd78059 static void
bscv_exit(bscv_soft_state_t * ssp)11504135Sgd78059 bscv_exit(bscv_soft_state_t *ssp)
11514135Sgd78059 {
11524135Sgd78059 	mutex_exit(&ssp->cmd_mutex);
11539644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_exit", "");
11544135Sgd78059 }
11554135Sgd78059 
11564135Sgd78059 #ifdef DEBUG
11574135Sgd78059 static int
bscv_held(bscv_soft_state_t * ssp)11584135Sgd78059 bscv_held(bscv_soft_state_t *ssp)
11594135Sgd78059 {
11604135Sgd78059 	return (mutex_owned(&ssp->cmd_mutex));
11614135Sgd78059 }
11624135Sgd78059 #endif /* DEBUG */
11634135Sgd78059 
11644135Sgd78059 static void
bscv_put8(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint8_t val)11654135Sgd78059 bscv_put8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
11664135Sgd78059 {
11674135Sgd78059 	boolean_t needretry;
11684135Sgd78059 	int num_failures;
11694135Sgd78059 
11704135Sgd78059 	ASSERT(bscv_held(ssp));
11714135Sgd78059 
11724135Sgd78059 	if (bscv_faulty(ssp)) {
11734135Sgd78059 		return;
11744135Sgd78059 	}
11754135Sgd78059 
11769644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_put8",
11774135Sgd78059 	    "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
11784135Sgd78059 
11794135Sgd78059 	for (num_failures = 0;
11804135Sgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
11814135Sgd78059 	    num_failures++) {
11824135Sgd78059 		bscv_put8_once(ssp, chan, addr, val);
11834135Sgd78059 		needretry = bscv_should_retry(ssp);
11844135Sgd78059 		if (!needretry) {
11854135Sgd78059 			break;
11864135Sgd78059 		}
11874135Sgd78059 	}
11884135Sgd78059 	if (ssp->command_error != 0) {
11894135Sgd78059 		ssp->had_session_error = B_TRUE;
11904135Sgd78059 	}
11914135Sgd78059 
11924135Sgd78059 	if (needretry) {
11934135Sgd78059 		/* Failure - we ran out of retries */
11944135Sgd78059 		cmn_err(CE_WARN, "bscv_put8: addr 0x%x.%02x retried "
11954135Sgd78059 		    "write %d times, giving up",
11964135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
11974135Sgd78059 		bscv_set_fault(ssp);
11984135Sgd78059 	} else if (num_failures > 0) {
11999644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'R', "bscv_put8",
12004135Sgd78059 		    "addr 0x%x.%02x retried write %d times, succeeded",
12014135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
12024135Sgd78059 	}
12034135Sgd78059 }
12044135Sgd78059 
12054135Sgd78059 static void
bscv_put16(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint16_t val)12064135Sgd78059 bscv_put16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint16_t val)
12074135Sgd78059 {
12084135Sgd78059 	ASSERT(bscv_held(ssp));
12099644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_put16",
12104135Sgd78059 	    "addr 0x%x.%02x <= %04x", addr >> 8, addr & 0xff, val);
12114135Sgd78059 	bscv_put8(ssp, chan, addr, val >> 8);
12124135Sgd78059 	bscv_put8(ssp, chan, addr + 1, val & 0xff);
12134135Sgd78059 }
12144135Sgd78059 
12154135Sgd78059 static void
bscv_put32(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint32_t val)12164135Sgd78059 bscv_put32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint32_t val)
12174135Sgd78059 {
12184135Sgd78059 	ASSERT(bscv_held(ssp));
12199644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_put32",
12204135Sgd78059 	    "addr 0x%x.%02x <= %08x", addr >> 8, addr & 0xff, val);
12214135Sgd78059 	bscv_put8(ssp, chan, addr, (val >> 24) & 0xff);
12224135Sgd78059 	bscv_put8(ssp, chan, addr + 1, (val >> 16) & 0xff);
12234135Sgd78059 	bscv_put8(ssp, chan, addr + 2, (val >> 8) & 0xff);
12244135Sgd78059 	bscv_put8(ssp, chan, addr + 3, val & 0xff);
12254135Sgd78059 }
12264135Sgd78059 
12274135Sgd78059 static uint8_t
bscv_get8(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr)12284135Sgd78059 bscv_get8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
12294135Sgd78059 {
12304135Sgd78059 	uint8_t retval;
12314135Sgd78059 	boolean_t needretry;
12324135Sgd78059 	int num_failures;
12334135Sgd78059 
12344135Sgd78059 	ASSERT(bscv_held(ssp));
12354135Sgd78059 
12364135Sgd78059 	if (bscv_faulty(ssp)) {
12374135Sgd78059 		return (0);
12384135Sgd78059 	}
12394135Sgd78059 
12404135Sgd78059 	for (num_failures = 0;
12414135Sgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
12424135Sgd78059 	    num_failures++) {
12434135Sgd78059 		retval = bscv_get8_once(ssp, chan, addr);
12444135Sgd78059 		needretry = bscv_should_retry(ssp);
12454135Sgd78059 		if (!needretry) {
12464135Sgd78059 			break;
12474135Sgd78059 		}
12484135Sgd78059 	}
12494135Sgd78059 	if (ssp->command_error != 0) {
12504135Sgd78059 		ssp->had_session_error = B_TRUE;
12514135Sgd78059 	}
12524135Sgd78059 
12534135Sgd78059 	if (needretry) {
12544135Sgd78059 		/* Failure */
12554135Sgd78059 		cmn_err(CE_WARN, "bscv_get8: addr 0x%x.%02x retried "
12564135Sgd78059 		    "read %d times, giving up",
12574135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
12584135Sgd78059 		bscv_set_fault(ssp);
12594135Sgd78059 	} else if (num_failures > 0) {
12609644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'R', "bscv_get8",
12614135Sgd78059 		    "addr 0x%x.%02x retried read %d times, succeeded",
12624135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
12634135Sgd78059 	}
12644135Sgd78059 
12659644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_get8",
12664135Sgd78059 	    "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
12674135Sgd78059 	return (retval);
12684135Sgd78059 }
12694135Sgd78059 
12704135Sgd78059 static uint16_t
bscv_get16(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr)12714135Sgd78059 bscv_get16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
12724135Sgd78059 {
12734135Sgd78059 	uint16_t retval;
12744135Sgd78059 
12754135Sgd78059 	ASSERT(bscv_held(ssp));
12764135Sgd78059 
12774135Sgd78059 	retval = bscv_get8(ssp, chan, addr) << 8;
12784135Sgd78059 	retval |= bscv_get8(ssp, chan, addr + 1);
12794135Sgd78059 
12809644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_get16",
12814135Sgd78059 	    "addr 0x%x.%02x => %04x", addr >> 8, addr & 0xff, retval);
12824135Sgd78059 	return (retval);
12834135Sgd78059 }
12844135Sgd78059 
12854135Sgd78059 static uint32_t
bscv_get32(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr)12864135Sgd78059 bscv_get32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
12874135Sgd78059 {
12884135Sgd78059 	uint32_t retval;
12894135Sgd78059 
12904135Sgd78059 	ASSERT(bscv_held(ssp));
12914135Sgd78059 
12924135Sgd78059 	retval = bscv_get8(ssp, chan, addr) << 24;
12934135Sgd78059 	retval |= bscv_get8(ssp, chan, addr + 1) << 16;
12944135Sgd78059 	retval |= bscv_get8(ssp, chan, addr + 2) << 8;
12954135Sgd78059 	retval |= bscv_get8(ssp, chan, addr + 3);
12964135Sgd78059 
12979644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_get32",
12984135Sgd78059 	    "addr 0x%x.%02x => %08x", addr >> 8, addr & 0xff, retval);
12994135Sgd78059 	return (retval);
13004135Sgd78059 }
13014135Sgd78059 
13024135Sgd78059 static void
bscv_setclear8(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint8_t set,uint8_t clear)13034135Sgd78059 bscv_setclear8(bscv_soft_state_t *ssp, int chan,
13044135Sgd78059     bscv_addr_t addr, uint8_t set, uint8_t clear)
13054135Sgd78059 {
13064135Sgd78059 	uint8_t val;
13074135Sgd78059 
13084135Sgd78059 	ASSERT(bscv_held(ssp));
13094135Sgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
13104135Sgd78059 
13114135Sgd78059 	val = ssp->lom_regs[addr] | set;
13124135Sgd78059 	val &= ~clear;
13134135Sgd78059 
13149644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_setclear8",
13154135Sgd78059 	    "addr 0x%x.%02x, set %02x, clear %02x => %02x",
13164135Sgd78059 	    addr >> 8, addr & 0xff,
13174135Sgd78059 	    set, clear, val);
13184135Sgd78059 
13194135Sgd78059 	bscv_put8(ssp, chan, addr, val);
13204135Sgd78059 }
13214135Sgd78059 
13224135Sgd78059 static void
bscv_setclear8_volatile(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint8_t set,uint8_t clear)13234135Sgd78059 bscv_setclear8_volatile(bscv_soft_state_t *ssp, int chan,
13244135Sgd78059     bscv_addr_t addr, uint8_t set, uint8_t clear)
13254135Sgd78059 {
13264135Sgd78059 	uint8_t val;
13274135Sgd78059 	boolean_t needretry;
13284135Sgd78059 	int num_failures;
13294135Sgd78059 
13304135Sgd78059 	ASSERT(bscv_held(ssp));
13314135Sgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
13324135Sgd78059 
13334135Sgd78059 	if (bscv_faulty(ssp)) {
13344135Sgd78059 		return;
13354135Sgd78059 	}
13364135Sgd78059 
13379644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_setclear8_volatile",
13384135Sgd78059 	    "addr 0x%x.%02x => set %02x clear %02x",
13394135Sgd78059 	    addr >> 8, addr & 0xff, set, clear);
13404135Sgd78059 
13414135Sgd78059 	val = bscv_get8_cached(ssp, addr);
13424135Sgd78059 	for (num_failures = 0;
13434135Sgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
13444135Sgd78059 	    num_failures++) {
13454135Sgd78059 		val |= set;
13464135Sgd78059 		val &= ~clear;
13474135Sgd78059 		bscv_put8_once(ssp, chan, addr, val);
13484135Sgd78059 		if (ssp->command_error == EBUS_ERROR_STALEDATA) {
13494135Sgd78059 			/* Re-read the stale register from the lom */
13504135Sgd78059 			val = bscv_get8_once(ssp, chan, addr);
13514135Sgd78059 			needretry = 1;
13524135Sgd78059 		} else {
13534135Sgd78059 			needretry = bscv_should_retry(ssp);
13544135Sgd78059 			if (!needretry) {
13554135Sgd78059 				break;
13564135Sgd78059 			}
13574135Sgd78059 		}
13584135Sgd78059 	}
13594135Sgd78059 	if (ssp->command_error != 0) {
13604135Sgd78059 		ssp->had_session_error = B_TRUE;
13614135Sgd78059 	}
13624135Sgd78059 
13634135Sgd78059 	if (needretry) {
13644135Sgd78059 		/* Failure */
13654135Sgd78059 		cmn_err(CE_WARN, "bscv_setclear8_volatile: addr 0x%x.%02x "
13664135Sgd78059 		    "retried write %d times, giving up",
13674135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
13684135Sgd78059 		if (ssp->command_error != EBUS_ERROR_STALEDATA) {
13694135Sgd78059 			bscv_set_fault(ssp);
13704135Sgd78059 		}
13714135Sgd78059 	} else if (num_failures > 0) {
13729644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'R', "bscv_setclear8_volatile",
13734135Sgd78059 		    "addr 0x%x.%02x retried write %d times, succeeded",
13744135Sgd78059 		    addr >> 8, addr & 0xff, num_failures);
13754135Sgd78059 	}
13764135Sgd78059 }
13774135Sgd78059 
13784135Sgd78059 static void
bscv_rep_rw8(bscv_soft_state_t * ssp,int chan,uint8_t * host_addr,bscv_addr_t dev_addr,size_t repcount,uint_t flags,boolean_t is_write)13794135Sgd78059 bscv_rep_rw8(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
13804135Sgd78059     bscv_addr_t dev_addr, size_t repcount, uint_t flags,
13814135Sgd78059     boolean_t is_write)
13824135Sgd78059 {
13834135Sgd78059 	size_t inc;
13844135Sgd78059 
13854135Sgd78059 	ASSERT(bscv_held(ssp));
13864135Sgd78059 
13874135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
13884135Sgd78059 	for (; repcount--; dev_addr += inc) {
13894135Sgd78059 		if (flags & DDI_DEV_AUTOINCR) {
13904135Sgd78059 			if (is_write) {
13914135Sgd78059 				bscv_put8(ssp, chan, dev_addr, *host_addr++);
13924135Sgd78059 			} else {
13934135Sgd78059 				*host_addr++ = bscv_get8(ssp, chan, dev_addr);
13944135Sgd78059 			}
13954135Sgd78059 		} else {
13964135Sgd78059 			if (is_write) {
13974135Sgd78059 				bscv_put8_once(ssp, chan,
13985107Seota 				    dev_addr, *host_addr++);
13994135Sgd78059 			} else {
14004135Sgd78059 				*host_addr++ = bscv_get8_once(ssp, chan,
14015107Seota 				    dev_addr);
14024135Sgd78059 			}
14034135Sgd78059 			/* We need this because _once routines don't do it */
14044135Sgd78059 			if (ssp->command_error != 0) {
14054135Sgd78059 				ssp->had_session_error = B_TRUE;
14064135Sgd78059 			}
14074135Sgd78059 		}
14084135Sgd78059 		if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
14094135Sgd78059 			/*
14104135Sgd78059 			 * No retry here. If we were AUTOINCR then get/put
14114135Sgd78059 			 * will have retried. For NO_AUTOINCR we cannot retry
14124135Sgd78059 			 * because the data would be corrupted.
14134135Sgd78059 			 */
14144135Sgd78059 			break;
14154135Sgd78059 		}
14164135Sgd78059 	}
14174135Sgd78059 }
14184135Sgd78059 
14194135Sgd78059 static uint8_t
bscv_get8_cached(bscv_soft_state_t * ssp,bscv_addr_t addr)14204135Sgd78059 bscv_get8_cached(bscv_soft_state_t *ssp, bscv_addr_t addr)
14214135Sgd78059 {
14224135Sgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
14234135Sgd78059 	/* Can be called with or without the lock held */
14244135Sgd78059 
14254135Sgd78059 	return (ssp->lom_regs[addr]);
14264135Sgd78059 }
14274135Sgd78059 
14284135Sgd78059 static uint8_t
bscv_get8_locked(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,int * res)14294135Sgd78059 bscv_get8_locked(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, int *res)
14304135Sgd78059 {
14314135Sgd78059 	uint8_t retval;
14324135Sgd78059 
14334135Sgd78059 	ASSERT(addr < BSC_ADDR_CACHE_LIMIT);
14344135Sgd78059 	bscv_enter(ssp);
14354135Sgd78059 	retval = bscv_get8(ssp, chan, addr);
14364135Sgd78059 	bscv_locked_result(ssp, res);
14374135Sgd78059 	bscv_exit(ssp);
14389644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_get8_locked",
14394135Sgd78059 	    "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval);
14404135Sgd78059 	return (retval);
14414135Sgd78059 }
14424135Sgd78059 
14434135Sgd78059 static void
bscv_rep_get8_locked(bscv_soft_state_t * ssp,int chan,uint8_t * host_addr,bscv_addr_t dev_addr,size_t repcount,uint_t flags,int * res)14444135Sgd78059 bscv_rep_get8_locked(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr,
14454135Sgd78059     bscv_addr_t dev_addr, size_t repcount, uint_t flags, int *res)
14464135Sgd78059 {
14474135Sgd78059 	bscv_enter(ssp);
14484135Sgd78059 	bscv_rep_rw8(ssp, chan, host_addr, dev_addr, repcount,
14494135Sgd78059 	    flags, B_FALSE /* read */);
14504135Sgd78059 	bscv_locked_result(ssp, res);
14514135Sgd78059 	bscv_exit(ssp);
14524135Sgd78059 }
14534135Sgd78059 
14544135Sgd78059 static boolean_t
bscv_faulty(bscv_soft_state_t * ssp)14554135Sgd78059 bscv_faulty(bscv_soft_state_t *ssp)
14564135Sgd78059 {
14574135Sgd78059 	ASSERT(bscv_held(ssp));
14584135Sgd78059 	return (ssp->had_fault);
14594135Sgd78059 }
14604135Sgd78059 
14614135Sgd78059 static void
bscv_clear_fault(bscv_soft_state_t * ssp)14624135Sgd78059 bscv_clear_fault(bscv_soft_state_t *ssp)
14634135Sgd78059 {
14644135Sgd78059 	ASSERT(bscv_held(ssp));
14659644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'J', "bscv_clear_fault", "clearing fault flag");
14664135Sgd78059 	ssp->had_fault = B_FALSE;
14674135Sgd78059 	ssp->had_session_error = B_FALSE;
14684135Sgd78059 }
14694135Sgd78059 
14704135Sgd78059 static void
bscv_set_fault(bscv_soft_state_t * ssp)14714135Sgd78059 bscv_set_fault(bscv_soft_state_t *ssp)
14724135Sgd78059 {
14734135Sgd78059 	ASSERT(bscv_held(ssp));
14749644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'J', "bscv_set_fault", "setting fault flag");
14754135Sgd78059 	ssp->had_fault = B_TRUE;
14764135Sgd78059 }
14774135Sgd78059 
14784135Sgd78059 static boolean_t
bscv_session_error(bscv_soft_state_t * ssp)14794135Sgd78059 bscv_session_error(bscv_soft_state_t *ssp)
14804135Sgd78059 {
14814135Sgd78059 	ASSERT(bscv_held(ssp));
14824135Sgd78059 	return (ssp->had_session_error);
14834135Sgd78059 }
14844135Sgd78059 
14854135Sgd78059 static int
bscv_retcode(bscv_soft_state_t * ssp)14864135Sgd78059 bscv_retcode(bscv_soft_state_t *ssp)
14874135Sgd78059 {
14889644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_retcode",
14894135Sgd78059 	    "code 0x%x", ssp->command_error);
14904135Sgd78059 	return (ssp->command_error);
14914135Sgd78059 }
14924135Sgd78059 
14934135Sgd78059 static int
bscv_should_retry(bscv_soft_state_t * ssp)14944135Sgd78059 bscv_should_retry(bscv_soft_state_t *ssp)
14954135Sgd78059 {
14964135Sgd78059 	if ((ssp->command_error == EBUS_ERROR_DEVICEFAIL) ||
14974135Sgd78059 	    (ssp->command_error >= LOMBUS_ERR_BASE)) {
14984135Sgd78059 		/* This command is due to an I/O fault - retry might fix */
14994135Sgd78059 		return (1);
15004135Sgd78059 	} else {
15014135Sgd78059 		/*
15024135Sgd78059 		 * The command itself was bad - there is no point in fixing
15034135Sgd78059 		 * Note. Whatever happens we should know that if we were
15044135Sgd78059 		 * doing EBUS_IDX_SELFTEST0..EBUS_IDX_SELFTEST7 and we
15054135Sgd78059 		 * had 0x80 set then this is a test error not a retry
15064135Sgd78059 		 * error.
15074135Sgd78059 		 */
15084135Sgd78059 		return (0);
15094135Sgd78059 	}
15104135Sgd78059 }
15114135Sgd78059 
15124135Sgd78059 static void
bscv_locked_result(bscv_soft_state_t * ssp,int * res)15134135Sgd78059 bscv_locked_result(bscv_soft_state_t *ssp, int *res)
15144135Sgd78059 {
15154135Sgd78059 	if (bscv_faulty(ssp) || (bscv_retcode(ssp) != 0)) {
15164135Sgd78059 		*res = EIO;
15174135Sgd78059 	}
15184135Sgd78059 }
15194135Sgd78059 
15204135Sgd78059 static void
bscv_put8_once(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr,uint8_t val)15214135Sgd78059 bscv_put8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val)
15224135Sgd78059 {
15234135Sgd78059 	uint32_t fault;
15244135Sgd78059 
15254135Sgd78059 	ASSERT(bscv_held(ssp));
15264135Sgd78059 
15274135Sgd78059 	ssp->command_error = 0;
15284135Sgd78059 
15294135Sgd78059 	if (bscv_faulty(ssp)) {
15304135Sgd78059 		/* Bail out things are not working */
15314135Sgd78059 		return;
15324135Sgd78059 	} else if (ssp->nchannels == 0) {
15334135Sgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
15349644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, '@', "bscv_put8_once",
15354135Sgd78059 		    "nchannels is 0x0 so cannot do IO");
15364135Sgd78059 		return;
15374135Sgd78059 	}
15384135Sgd78059 
15394135Sgd78059 	/* Clear any pending fault */
15404135Sgd78059 	ddi_put32(ssp->channel[chan].handle,
15414135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
15424135Sgd78059 
15434135Sgd78059 	/* Do the access and get fault code - may take a long time */
15444135Sgd78059 	ddi_put8(ssp->channel[chan].handle,
15455107Seota 	    &ssp->channel[chan].regs[addr], val);
15464135Sgd78059 	fault = ddi_get32(ssp->channel[chan].handle,
15474135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
15484135Sgd78059 
15494135Sgd78059 	ssp->command_error = fault;
15504135Sgd78059 
15514135Sgd78059 	if (fault == 0) {
15524135Sgd78059 		/* Things were ok - update cache entry */
15534135Sgd78059 		if (addr < BSC_ADDR_CACHE_LIMIT) {
15544135Sgd78059 			/* Store cacheable entries */
15554135Sgd78059 			ssp->lom_regs[addr] = val;
15564135Sgd78059 		}
15574135Sgd78059 	} else if (fault >= LOMBUS_ERR_BASE) {
15584135Sgd78059 		/* lombus problem - do a resync session */
15594135Sgd78059 		cmn_err(CE_WARN, "!bscv_put8_once: Had comms fault "
15604135Sgd78059 		    "for address 0x%x.%02x - data 0x%x, fault 0x%x",
15614135Sgd78059 		    addr >> 8, addr & 0xff, val, fault);
15624135Sgd78059 		/* Attempt to resync with the lom */
15634135Sgd78059 		bscv_resync_comms(ssp, chan);
15644135Sgd78059 		/*
15654135Sgd78059 		 * Note: we do not set fault status here. That
15664135Sgd78059 		 * is done if our caller decides to give up talking to
15674135Sgd78059 		 * the lom. The observant might notice that this means
15684135Sgd78059 		 * that if we mend things on the last attempt we still
15694135Sgd78059 		 * get the fault set - we just live with that!
15704135Sgd78059 		 */
15714135Sgd78059 	}
15724135Sgd78059 
15739644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_put8_once",
15744135Sgd78059 	    "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val);
15754135Sgd78059 }
15764135Sgd78059 
15774135Sgd78059 static uint8_t
bscv_get8_once(bscv_soft_state_t * ssp,int chan,bscv_addr_t addr)15784135Sgd78059 bscv_get8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr)
15794135Sgd78059 {
15804135Sgd78059 	uint8_t val;
15814135Sgd78059 	uint32_t fault;
15824135Sgd78059 
15834135Sgd78059 	ASSERT(bscv_held(ssp));
15844135Sgd78059 
15854135Sgd78059 	ssp->command_error = 0;
15864135Sgd78059 
15874135Sgd78059 	if (bscv_faulty(ssp)) {
15884135Sgd78059 		/* Bail out things are not working */
15894135Sgd78059 		return (0xff);
15904135Sgd78059 	} else if (ssp->nchannels == 0) {
15914135Sgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
15929644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, '@', "bscv_get8_once",
15934135Sgd78059 		    "nchannels is 0x0 so cannot do IO");
15944135Sgd78059 		return (0xff);
15954135Sgd78059 	}
15964135Sgd78059 
15974135Sgd78059 	/* Clear any pending fault */
15984135Sgd78059 	ddi_put32(ssp->channel[chan].handle,
15994135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
16004135Sgd78059 
16014135Sgd78059 	/* Do the access and get fault code - may take a long time */
16024135Sgd78059 	val = ddi_get8(ssp->channel[chan].handle,
16035107Seota 	    &ssp->channel[chan].regs[addr]);
16044135Sgd78059 	fault = ddi_get32(ssp->channel[chan].handle,
16054135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG));
16064135Sgd78059 	ssp->command_error = fault;
16074135Sgd78059 
16084135Sgd78059 	if (fault >= LOMBUS_ERR_BASE) {
16094135Sgd78059 		/* lombus problem - do a resync session */
16104135Sgd78059 		cmn_err(CE_WARN, "!bscv_get8_once: Had comms fault "
16114135Sgd78059 		    "for address 0x%x.%02x - data 0x%x, fault 0x%x",
16124135Sgd78059 		    addr >> 8, addr & 0xff, val, fault);
16134135Sgd78059 		/* Attempt to resync with the lom */
16144135Sgd78059 		bscv_resync_comms(ssp, chan);
16154135Sgd78059 		/*
16164135Sgd78059 		 * Note: we do not set fault status here. That
16174135Sgd78059 		 * is done if our caller decides to give up talking to
16184135Sgd78059 		 * the lom. The observant might notice that this means
16194135Sgd78059 		 * that if we mend things on the last attempt we still
16204135Sgd78059 		 * get the fault set - we just live with that!
16214135Sgd78059 		 */
16224135Sgd78059 	}
16234135Sgd78059 	/*
16244135Sgd78059 	 * FIXME - should report error if you get
16254135Sgd78059 	 * EBUS_ERROR_DEVICEFAIL reported from the BSC. That gets
16264135Sgd78059 	 * logged as a failure in bscv_should_retry and may contribute
16274135Sgd78059 	 * to a permanent failure. Reference issues seen by Mitac.
16284135Sgd78059 	 */
16294135Sgd78059 
16304135Sgd78059 	if (!bscv_faulty(ssp)) {
16314135Sgd78059 		if (addr < BSC_ADDR_CACHE_LIMIT) {
16324135Sgd78059 			/* Store cacheable entries */
16334135Sgd78059 			ssp->lom_regs[addr] = val;
16344135Sgd78059 		}
16354135Sgd78059 	}
16364135Sgd78059 
16379644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_get8_once",
16384135Sgd78059 	    "addr 0x%x.%02x => 0x%02x", addr >> 8, addr & 0xff, val);
16394135Sgd78059 	return (val);
16404135Sgd78059 }
16414135Sgd78059 
16424135Sgd78059 static uint32_t
bscv_probe(bscv_soft_state_t * ssp,int chan,uint32_t * fault)16434135Sgd78059 bscv_probe(bscv_soft_state_t *ssp, int chan, uint32_t *fault)
16444135Sgd78059 {
16454135Sgd78059 	uint32_t async_reg;
16464135Sgd78059 
16474135Sgd78059 	if (ssp->nchannels == 0) {
16484135Sgd78059 		/*
16494135Sgd78059 		 * Failed to map handles, so cannot do any IO.  Set the
16504135Sgd78059 		 * fault indicator and return a dummy value.
16514135Sgd78059 		 */
16529644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, '@', "bscv_probe",
16534135Sgd78059 		    "nchannels is 0x0 so cannot do any IO");
16544135Sgd78059 		*fault = LOMBUS_ERR_REG_NUM;
16554135Sgd78059 		return ((~(int8_t)0));
16564135Sgd78059 	}
16574135Sgd78059 
16584135Sgd78059 	/* Clear faults */
16594135Sgd78059 	ddi_put32(ssp->channel[chan].handle,
16604135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0);
16614135Sgd78059 	/* Probe and Check faults */
16624135Sgd78059 	*fault = ddi_get32(ssp->channel[chan].handle,
16634135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_PROBE_REG));
16644135Sgd78059 	/* Read status */
16654135Sgd78059 	async_reg = ddi_get32(ssp->channel[chan].handle,
16664135Sgd78059 	    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_ASYNC_REG));
16674135Sgd78059 
16689644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, '@', "bscv_probe",
16694135Sgd78059 	    "async status 0x%x, fault 0x%x", async_reg, *fault);
16704135Sgd78059 	return (async_reg);
16714135Sgd78059 }
16724135Sgd78059 
16734135Sgd78059 static void
bscv_resync_comms(bscv_soft_state_t * ssp,int chan)16744135Sgd78059 bscv_resync_comms(bscv_soft_state_t *ssp, int chan)
16754135Sgd78059 {
16764135Sgd78059 	int try;
16774135Sgd78059 	uint32_t command_error = ssp->command_error;
16784135Sgd78059 	uint32_t fault = 0;
16794135Sgd78059 
16804135Sgd78059 	if (ssp->nchannels == 0) {
16814135Sgd78059 		/*
16824135Sgd78059 		 * Didn't manage to map handles so ddi_{get,put}* broken.
16834135Sgd78059 		 * Therefore, there is no way to resync comms.
16844135Sgd78059 		 */
16859644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, '@', "bscv_resync_comms",
16864135Sgd78059 		    "nchannels is 0x0 so not possible to resync comms");
16874135Sgd78059 		return;
16884135Sgd78059 	}
16894135Sgd78059 	if (command_error >= LOMBUS_ERR_BASE &&
16905107Seota 	    command_error != LOMBUS_ERR_REG_NUM &&
16915107Seota 	    command_error != LOMBUS_ERR_REG_SIZE &&
16925107Seota 	    command_error != LOMBUS_ERR_TIMEOUT) {
16934135Sgd78059 		/* Resync here to make sure that the lom is talking */
16944135Sgd78059 		cmn_err(CE_WARN, "!bscv_resync_comms: "
16954135Sgd78059 		    "Attempting comms resync after comms fault 0x%x",
16964135Sgd78059 		    command_error);
16974135Sgd78059 		for (try = 1; try <= 8; try++) {
16984135Sgd78059 			/* Probe */
16994135Sgd78059 			fault = ddi_get32(ssp->channel[chan].handle,
17004135Sgd78059 			    (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0,
17015107Seota 			    LOMBUS_PROBE_REG));
17024135Sgd78059 
17034135Sgd78059 			if (fault == 0) {
17044135Sgd78059 				break;
17054135Sgd78059 			} else {
17064135Sgd78059 				cmn_err(CE_WARN, "!bscv_resync_comms: "
17074135Sgd78059 				    "comms resync (probing) - try 0x%x "
17084135Sgd78059 				    "had fault 0x%x", try, fault);
17094135Sgd78059 			}
17104135Sgd78059 		}
17114135Sgd78059 		if (fault != 0) {
17124135Sgd78059 			cmn_err(CE_WARN, "!bscv_resync_comms: "
17134135Sgd78059 			    "Failed to resync comms - giving up");
17144135Sgd78059 			ssp->bad_resync++;
17154135Sgd78059 		} else {
17164135Sgd78059 			cmn_err(CE_WARN, "!bscv_resync_comms: "
17174135Sgd78059 			    "resync comms after 0x%x tries", try);
17184135Sgd78059 			ssp->bad_resync = 0;
17194135Sgd78059 		}
17204135Sgd78059 	}
17214135Sgd78059 
17224135Sgd78059 }
17234135Sgd78059 
17244135Sgd78059 
17254135Sgd78059 /*
17264135Sgd78059  * LOMLite configuration/event eeprom access routines
17274135Sgd78059  *
17284135Sgd78059  * bscv_window_setup() - Read/Sanity check the eeprom parameters.
17294135Sgd78059  *		This must be called prior to calling bscv_eerw().
17304135Sgd78059  * bscv_eerw() - Read/write data from/to the eeprom.
17314135Sgd78059  */
17324135Sgd78059 
17334135Sgd78059 /*
17344135Sgd78059  * function	- bscv_window_setup
17354135Sgd78059  * description	- this routine reads the eeprom parameters and sanity
17364135Sgd78059  *		  checks them to ensure that the lom is talking sense.
17374135Sgd78059  * inputs	- soft state ptr
17384135Sgd78059  * outputs	- B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
17394135Sgd78059  */
17404135Sgd78059 static boolean_t
bscv_window_setup(bscv_soft_state_t * ssp)17414135Sgd78059 bscv_window_setup(bscv_soft_state_t *ssp)
17424135Sgd78059 {
17434135Sgd78059 	ASSERT(bscv_held(ssp));
17444135Sgd78059 
17454135Sgd78059 	if (ssp->eeinfo_valid) {
17464135Sgd78059 		/* Already have good cached values */
17474135Sgd78059 		return (ssp->eeinfo_valid);
17484135Sgd78059 	}
17494135Sgd78059 	ssp->eeprom_size =
17505107Seota 	    bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) * 1024;
17514135Sgd78059 	ssp->eventlog_start = bscv_get16(ssp, chan_general,
17525107Seota 	    EBUS_IDX_LOG_START_HI);
17534135Sgd78059 
17544135Sgd78059 	/*
17554135Sgd78059 	 * The log does not run to the end of the EEPROM because it is a
17564135Sgd78059 	 * logical partition.  The last 8K partition is reserved for FRUID
17574135Sgd78059 	 * usage.
17584135Sgd78059 	 */
17594135Sgd78059 	ssp->eventlog_size = EBUS_LOG_END - ssp->eventlog_start;
17604135Sgd78059 
17619644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'I', "bscv_window_setup", "eeprom size 0x%x log_start"
17624135Sgd78059 	    " 0x%x log_size 0x%x", ssp->eeprom_size, ssp->eventlog_start,
17634135Sgd78059 	    ssp->eventlog_size);
17644135Sgd78059 
17654135Sgd78059 	if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
17664135Sgd78059 		ssp->eeinfo_valid = B_FALSE;
17674135Sgd78059 	} else if ((ssp->eeprom_size == 0) ||
17684135Sgd78059 	    (ssp->eventlog_start >= ssp->eeprom_size)) {
17694135Sgd78059 		/* Sanity check values */
17704135Sgd78059 		cmn_err(CE_WARN,
17714135Sgd78059 		    "!bscv_window_setup: read invalid eeprom parameters");
17724135Sgd78059 		ssp->eeinfo_valid = B_FALSE;
17734135Sgd78059 	} else {
17744135Sgd78059 		ssp->eeinfo_valid = B_TRUE;
17754135Sgd78059 	}
17764135Sgd78059 
17779644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'I', "bscv_window_setup", "returning eeinfo_valid %s",
17784135Sgd78059 	    ssp->eeinfo_valid ? "true" : "false");
17794135Sgd78059 	return (ssp->eeinfo_valid);
17804135Sgd78059 }
17814135Sgd78059 
17824135Sgd78059 /*
17834135Sgd78059  * function	- bscv_eerw
17844135Sgd78059  * description	- this routine reads/write data from/to the eeprom.
17854135Sgd78059  *		  It takes care of setting the window on the eeprom correctly.
17864135Sgd78059  * inputs	- soft state ptr, eeprom offset, data buffer, size, read/write
17874135Sgd78059  * outputs	- B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK.
17884135Sgd78059  */
17894135Sgd78059 static int
bscv_eerw(bscv_soft_state_t * ssp,uint32_t eeoffset,uint8_t * buf,unsigned size,boolean_t is_write)17904135Sgd78059 bscv_eerw(bscv_soft_state_t *ssp, uint32_t eeoffset, uint8_t *buf,
17914135Sgd78059     unsigned size, boolean_t is_write)
17924135Sgd78059 {
17934135Sgd78059 	uint32_t blk_addr = eeoffset;
17944135Sgd78059 	unsigned remaining = size;
17954135Sgd78059 	uint8_t page_idx;
17964135Sgd78059 	uint8_t this_page;
17974135Sgd78059 	uint8_t blk_size;
17984135Sgd78059 	int res = 0;
17994135Sgd78059 
18004135Sgd78059 	while (remaining > 0) {
18014135Sgd78059 		page_idx = blk_addr & 0xff;
18024135Sgd78059 		if ((page_idx + remaining) > 0x100) {
18034135Sgd78059 			blk_size = 0x100 - page_idx;
18044135Sgd78059 		} else {
18054135Sgd78059 			blk_size = remaining;
18064135Sgd78059 		}
18074135Sgd78059 
18084135Sgd78059 		/* Select correct eeprom page */
18094135Sgd78059 		this_page = blk_addr >> 8;
18104135Sgd78059 		bscv_put8(ssp, chan_eeprom, EBUS_IDX_EEPROM_PAGESEL, this_page);
18114135Sgd78059 
18129644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'M', "lom_eerw",
18134135Sgd78059 		    "%s data @0x%x.%02x, size 0x%x, 0x%x bytes remaining",
18144135Sgd78059 		    is_write ? "writing" : "reading",
18154135Sgd78059 		    this_page, page_idx, blk_size, remaining - blk_size);
18164135Sgd78059 
18174135Sgd78059 		bscv_rep_rw8(ssp, chan_eeprom,
18184135Sgd78059 		    buf, BSCVA(EBUS_CMD_SPACE_EEPROM, page_idx),
18194135Sgd78059 		    blk_size, DDI_DEV_AUTOINCR, is_write);
18204135Sgd78059 
18214135Sgd78059 		if (bscv_faulty(ssp) || bscv_session_error(ssp)) {
18224135Sgd78059 			res = EIO;
18234135Sgd78059 			break;
18244135Sgd78059 		}
18254135Sgd78059 
18264135Sgd78059 		remaining -= blk_size;
18274135Sgd78059 		blk_addr += blk_size;
18284135Sgd78059 		buf += blk_size;
18294135Sgd78059 	}
18304135Sgd78059 
18314135Sgd78059 	return (res);
18324135Sgd78059 }
18334135Sgd78059 
18344135Sgd78059 static boolean_t
bscv_is_null_event(bscv_soft_state_t * ssp,lom_event_t * e)18354135Sgd78059 bscv_is_null_event(bscv_soft_state_t *ssp, lom_event_t *e)
18364135Sgd78059 {
18374135Sgd78059 	ASSERT(e != NULL);
18384135Sgd78059 
18394135Sgd78059 	if (EVENT_DECODE_SUBSYS(e->ev_subsys) == EVENT_SUBSYS_NONE &&
18404135Sgd78059 	    e->ev_event == EVENT_NONE) {
18414135Sgd78059 		/*
18424135Sgd78059 		 * This marks a NULL event.
18434135Sgd78059 		 */
18449644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_is_null_event",
18454135Sgd78059 		    "EVENT_SUBSYS_NONE/EVENT_NONE null event");
18464135Sgd78059 		return (B_TRUE);
18474135Sgd78059 	} else if (e->ev_subsys == 0xff && e->ev_event == 0xff) {
18484135Sgd78059 		/*
18494135Sgd78059 		 * Under some circumstances, we've seen all 1s to represent
18504135Sgd78059 		 * a manually cleared event log at the BSC prompt.  Only
18514135Sgd78059 		 * a test/diagnosis environment is likely to show this.
18524135Sgd78059 		 */
18539644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_is_null_event", "0xffff null event");
18544135Sgd78059 		return (B_TRUE);
18554135Sgd78059 	} else {
18564135Sgd78059 		/*
18574135Sgd78059 		 * Not a NULL event.
18584135Sgd78059 		 */
18599644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_is_null_event", "returning False");
18604135Sgd78059 		return (B_FALSE);
18614135Sgd78059 	}
18624135Sgd78059 }
18634135Sgd78059 
18644135Sgd78059 /*
18654135Sgd78059  * *********************************************************************
18664135Sgd78059  * IOCTL Processing
18674135Sgd78059  * *********************************************************************
18684135Sgd78059  */
18694135Sgd78059 
18704135Sgd78059 /*
18714135Sgd78059  * function	- bscv_ioctl
18724135Sgd78059  * description	- routine that acts as a high level manager for ioctls. It
18734135Sgd78059  *		  calls the appropriate handler for ioctls on the alarm:mon and
18744135Sgd78059  *		  alarm:ctl minor nodes respectively
18754135Sgd78059  *
18764135Sgd78059  *		  Unsupported ioctls (now deprecated)
18774135Sgd78059  *			LOMIOCALCTL
18784135Sgd78059  *			LOMIOCALSTATE
18794135Sgd78059  *			LOMIOCCLEARLOG
18804135Sgd78059  *			LOMIOCCTL
18814135Sgd78059  *			LOMIOCCTL2
18824135Sgd78059  *			LOMIOCDAEMON
18834135Sgd78059  *			LOMIOCDMON
18844135Sgd78059  *			LOMIOCDOGCTL, TSIOCDOGCTL
18854135Sgd78059  *			LOMIOCDOGPAT, TSIOCDOGPAT
18864135Sgd78059  *			LOMIOCDOGTIME, TSIOCDOGTIME
18874135Sgd78059  *			LOMIOCEVENTLOG
18884135Sgd78059  *			LOMIOCEVNT
18894135Sgd78059  *			LOMIOCGETMASK
18904135Sgd78059  *			LOMIOCMPROG
18914135Sgd78059  *			LOMIOCNBMON, TSIOCNBMON
18924135Sgd78059  *			LOMIOCSLEEP
18934135Sgd78059  *			LOMIOCUNLOCK, TSIOCUNLOCK
18944135Sgd78059  *			LOMIOCWTMON, TSIOCWTMON
18954135Sgd78059  *
18964135Sgd78059  *		  Supported ioctls
18974135Sgd78059  *			LOMIOCDOGSTATE, TSIOCDOGSTATE
18984135Sgd78059  *			LOMIOCPROG
18994135Sgd78059  *			LOMIOCPSUSTATE
19004135Sgd78059  *			LOMIOCFANSTATE
19014135Sgd78059  *			LOMIOCFLEDSTATE
19024135Sgd78059  *			LOMIOCINFO
19034135Sgd78059  *			LOMIOCMREAD
19044135Sgd78059  *			LOMIOCVOLTS
19054135Sgd78059  *			LOMIOCSTATS
19064135Sgd78059  *			LOMIOCTEMP
19074135Sgd78059  *			LOMIOCCONS
19084135Sgd78059  *			LOMIOCEVENTLOG2
19094135Sgd78059  *			LOMIOCINFO2
19104135Sgd78059  *			LOMIOCTEST
19114135Sgd78059  *			LOMIOCMPROG2
19124135Sgd78059  *			LOMIOCMREAD2
19134135Sgd78059  *
19144135Sgd78059  * inputs	- device number, command, user space arg, filemode, user
19154135Sgd78059  *		  credentials, return value
19164135Sgd78059  * outputs	- the return value propagated back by the lower level routines.
19174135Sgd78059  */
19184135Sgd78059 
19194135Sgd78059 /*ARGSUSED*/
19204135Sgd78059 static int
bscv_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred,int * rvalp)19214135Sgd78059 bscv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp)
19224135Sgd78059 {
19234135Sgd78059 	bscv_soft_state_t *ssp;
19244135Sgd78059 	int instance;
19254135Sgd78059 	int res = 0;
19264135Sgd78059 
19274135Sgd78059 	instance = DEVICETOINSTANCE(dev);
19284135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, instance);
19294135Sgd78059 	if (ssp == NULL) {
19304135Sgd78059 		return (ENXIO);
19314135Sgd78059 	}
19324135Sgd78059 
19334135Sgd78059 	/*
19344135Sgd78059 	 * The Combined Switch and Service Processor takes care of configuration
19354135Sgd78059 	 * and control.  The CSSP tells the BSC chip about it; therefore the
19364135Sgd78059 	 * bscv driver doesn't send such configuration and control to the BSC.
19374135Sgd78059 	 * Additionally Watchdog configuration is no longer done from userland
19384135Sgd78059 	 * lom.
19394135Sgd78059 	 */
19404135Sgd78059 	switch (cmd) {
19414135Sgd78059 	case LOMIOCALCTL:
19424135Sgd78059 	case LOMIOCALSTATE:
19434135Sgd78059 	case LOMIOCCLEARLOG:
19444135Sgd78059 	case LOMIOCCTL:
19454135Sgd78059 	case LOMIOCCTL2:
19464135Sgd78059 	case LOMIOCDAEMON:
19474135Sgd78059 	case LOMIOCDMON:
19484135Sgd78059 	case LOMIOCDOGCTL:
19494135Sgd78059 	case LOMIOCDOGPAT:
19504135Sgd78059 	case LOMIOCDOGTIME:
19514135Sgd78059 	case LOMIOCEVENTLOG:
19524135Sgd78059 	case LOMIOCEVNT:
19534135Sgd78059 	case LOMIOCGETMASK:
19544135Sgd78059 	case LOMIOCMPROG:
19554135Sgd78059 	case LOMIOCNBMON:
19564135Sgd78059 	case LOMIOCSLEEP:
19574135Sgd78059 	case LOMIOCUNLOCK:
19584135Sgd78059 	case LOMIOCWTMON:
19594135Sgd78059 		return (ENOTSUP);
19604135Sgd78059 	}
19614135Sgd78059 
19624135Sgd78059 	/*
19634135Sgd78059 	 * set the default result.
19644135Sgd78059 	 */
19654135Sgd78059 
19664135Sgd78059 	*rvalp = 0;
19674135Sgd78059 
19684135Sgd78059 	if (ssp->cssp_prog) {
19694135Sgd78059 		return (ENXIO);
19704135Sgd78059 	} else if ((ssp->prog_mode_only || ssp->programming) &&
19714135Sgd78059 	    cmd != LOMIOCPROG) {
19724135Sgd78059 		return (ENXIO);
19734135Sgd78059 	}
19744135Sgd78059 
19754135Sgd78059 	/*
19764135Sgd78059 	 * Check that the caller has appropriate access permissions
19774135Sgd78059 	 * (FWRITE set in mode) for those ioctls which change lom
19784135Sgd78059 	 * state
19794135Sgd78059 	 */
19804135Sgd78059 	if (!(mode & FWRITE)) {
19814135Sgd78059 		switch (cmd) {
19824135Sgd78059 		case LOMIOCMPROG2:
19834135Sgd78059 		case LOMIOCMREAD2:
19844135Sgd78059 		case LOMIOCPROG:
19854135Sgd78059 		case LOMIOCTEST:
19864135Sgd78059 			return (EACCES);
19874135Sgd78059 			/* NOTREACHED */
19884135Sgd78059 		default:
19894135Sgd78059 			/* Does not require write access */
19904135Sgd78059 			break;
19914135Sgd78059 		}
19924135Sgd78059 	}
19934135Sgd78059 
19944135Sgd78059 	switch (cmd) {
19954135Sgd78059 
19964135Sgd78059 	case LOMIOCDOGSTATE:
19974135Sgd78059 		res = bscv_ioc_dogstate(ssp, arg, mode);
19984135Sgd78059 		break;
19994135Sgd78059 
20004135Sgd78059 	case LOMIOCPROG:
20014135Sgd78059 		res = bscv_prog(ssp, arg, mode);
20024135Sgd78059 		break;
20034135Sgd78059 
20044135Sgd78059 	case LOMIOCPSUSTATE:
20054135Sgd78059 		res = bscv_ioc_psustate(ssp, arg, mode);
20064135Sgd78059 		break;
20074135Sgd78059 
20084135Sgd78059 	case LOMIOCFANSTATE:
20094135Sgd78059 		res = bscv_ioc_fanstate(ssp, arg, mode);
20104135Sgd78059 		break;
20114135Sgd78059 
20124135Sgd78059 	case LOMIOCFLEDSTATE:
20134135Sgd78059 		res = bscv_ioc_fledstate(ssp, arg, mode);
20144135Sgd78059 		break;
20154135Sgd78059 
20164135Sgd78059 	case LOMIOCLEDSTATE:
20174135Sgd78059 		res = bscv_ioc_ledstate(ssp, arg, mode);
20184135Sgd78059 		break;
20194135Sgd78059 
20204135Sgd78059 	case LOMIOCINFO:
20214135Sgd78059 		res = bscv_ioc_info(ssp, arg, mode);
20224135Sgd78059 		break;
20234135Sgd78059 
20244135Sgd78059 	case LOMIOCMREAD:
20254135Sgd78059 		res = bscv_ioc_mread(ssp, arg, mode);
20264135Sgd78059 		break;
20274135Sgd78059 
20284135Sgd78059 	case LOMIOCVOLTS:
20294135Sgd78059 		res = bscv_ioc_volts(ssp, arg, mode);
20304135Sgd78059 		break;
20314135Sgd78059 
20324135Sgd78059 	case LOMIOCSTATS:
20334135Sgd78059 		res = bscv_ioc_stats(ssp, arg, mode);
20344135Sgd78059 		break;
20354135Sgd78059 
20364135Sgd78059 	case LOMIOCTEMP:
20374135Sgd78059 		res = bscv_ioc_temp(ssp, arg, mode);
20384135Sgd78059 		break;
20394135Sgd78059 
20404135Sgd78059 	case LOMIOCCONS:
20414135Sgd78059 		res = bscv_ioc_cons(ssp, arg, mode);
20424135Sgd78059 		break;
20434135Sgd78059 
20444135Sgd78059 	case LOMIOCEVENTLOG2:
20454135Sgd78059 		res = bscv_ioc_eventlog2(ssp, arg, mode);
20464135Sgd78059 		break;
20474135Sgd78059 
20484135Sgd78059 	case LOMIOCINFO2:
20494135Sgd78059 		res = bscv_ioc_info2(ssp, arg, mode);
20504135Sgd78059 		break;
20514135Sgd78059 
20524135Sgd78059 	case LOMIOCTEST:
20534135Sgd78059 		res = bscv_ioc_test(ssp, arg, mode);
20544135Sgd78059 		break;
20554135Sgd78059 
20564135Sgd78059 	case LOMIOCMPROG2:
20574135Sgd78059 		res = bscv_ioc_mprog2(ssp, arg, mode);
20584135Sgd78059 		break;
20594135Sgd78059 
20604135Sgd78059 	case LOMIOCMREAD2:
20614135Sgd78059 		res = bscv_ioc_mread2(ssp, arg, mode);
20624135Sgd78059 		break;
20634135Sgd78059 
20644135Sgd78059 	default:
20659644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'I', "bscv_ioctl", "Invalid IOCTL 0x%x", cmd);
20664135Sgd78059 		res = EINVAL;
20674135Sgd78059 	}
20684135Sgd78059 	return (res);
20694135Sgd78059 }
20704135Sgd78059 
20714135Sgd78059 /*
20724135Sgd78059  * LOMIOCDOGSTATE
20734135Sgd78059  * TSIOCDOGSTATE - indicate whether the alarm watchdog and reset
20744135Sgd78059  * circuitry is enabled or not.
20754135Sgd78059  */
20764135Sgd78059 static int
bscv_ioc_dogstate(bscv_soft_state_t * ssp,intptr_t arg,int mode)20774135Sgd78059 bscv_ioc_dogstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
20784135Sgd78059 {
20794135Sgd78059 	lom_dogstate_t dogstate;
20804135Sgd78059 	uint8_t dogval;
20814135Sgd78059 	int res = 0;
20824135Sgd78059 
20834135Sgd78059 	dogval = bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res);
20844135Sgd78059 	dogstate.dog_enable = (dogval & EBUS_WDOG_ENABLE) ? 1 : 0;
20854135Sgd78059 	dogstate.reset_enable = (dogval & EBUS_WDOG_RST) ? 1 : 0;
20864135Sgd78059 	dogstate.dog_timeout = bscv_get8_locked(ssp, chan_general,
20875107Seota 	    EBUS_IDX_WDOG_TIME, &res);
20884135Sgd78059 
20894135Sgd78059 	if ((res == 0) &&
20904135Sgd78059 	    (ddi_copyout((caddr_t)&dogstate,
20915107Seota 	    (caddr_t)arg, sizeof (dogstate), mode) < 0)) {
20924135Sgd78059 		res = EFAULT;
20934135Sgd78059 	}
20944135Sgd78059 	return (res);
20954135Sgd78059 }
20964135Sgd78059 
20974135Sgd78059 /*
20984135Sgd78059  * LOMIOCPSUSTATE - returns full information for 4 PSUs. All this
20994135Sgd78059  * information is available from two bytes of LOMlite RAM, but if
21004135Sgd78059  * on the first read it is noticed that two or more of the PSUs are
21014135Sgd78059  * not present only 1 byte will be read subsequently.
21024135Sgd78059  */
21034135Sgd78059 static int
bscv_ioc_psustate(bscv_soft_state_t * ssp,intptr_t arg,int mode)21044135Sgd78059 bscv_ioc_psustate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
21054135Sgd78059 {
21064135Sgd78059 	lom_psudata_t psudata;
21074135Sgd78059 	uint8_t psustat;
21084135Sgd78059 	int i;
21094135Sgd78059 	int res = 0;
21104135Sgd78059 
21114135Sgd78059 	for (i = 0; i < MAX_PSUS; i++) {
21124135Sgd78059 		psustat = bscv_get8_locked(ssp, chan_general,
21135107Seota 		    EBUS_IDX_PSU1_STAT + i, &res);
21144135Sgd78059 		psudata.fitted[i] = psustat & EBUS_PSU_PRESENT;
21154135Sgd78059 		psudata.output[i] = psustat & EBUS_PSU_OUTPUT;
21164135Sgd78059 		psudata.supplyb[i] = psustat & EBUS_PSU_INPUTB;
21174135Sgd78059 		psudata.supplya[i] = psustat & EBUS_PSU_INPUTA;
21184135Sgd78059 		psudata.standby[i] = psustat & EBUS_PSU_STANDBY;
21194135Sgd78059 	}
21204135Sgd78059 
21214135Sgd78059 	if (ddi_copyout((caddr_t)&psudata, (caddr_t)arg, sizeof (psudata),
21224135Sgd78059 	    mode) < 0) {
21234135Sgd78059 		res = EFAULT;
21244135Sgd78059 	}
21254135Sgd78059 	return (res);
21264135Sgd78059 }
21274135Sgd78059 
21284135Sgd78059 /*
21294135Sgd78059  * LOMIOCFANSTATE - returns full information including speed for 4
21304135Sgd78059  * fans and the minimum and maximum operating speeds for each fan as
21314135Sgd78059  * stored in the READ ONLY EEPROM data. As this EEPROM data is set
21324135Sgd78059  * at manufacture time, this data should only be read by the driver
21334135Sgd78059  * once and stored locally.
21344135Sgd78059  */
21354135Sgd78059 static int
bscv_ioc_fanstate(bscv_soft_state_t * ssp,intptr_t arg,int mode)21364135Sgd78059 bscv_ioc_fanstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
21374135Sgd78059 {
21384135Sgd78059 	lom_fandata_t fandata;
21394135Sgd78059 	int numfans;
21404135Sgd78059 	int i;
21414135Sgd78059 	int res = 0;
21424135Sgd78059 
21434135Sgd78059 	bzero(&fandata, sizeof (lom_fandata_t));
21444135Sgd78059 	numfans = EBUS_CONFIG_NFAN_DEC(bscv_get8_locked(ssp,
21454135Sgd78059 	    chan_general, EBUS_IDX_CONFIG, &res));
21464135Sgd78059 	for (i = 0; (i < numfans) && (res == 0); i++) {
21474135Sgd78059 		if (ssp->fanspeed[i] != LOM_FAN_NOT_PRESENT) {
21484135Sgd78059 			fandata.fitted[i] = 1;
21494135Sgd78059 			fandata.speed[i] = ssp->fanspeed[i];
21504135Sgd78059 			fandata.minspeed[i] = bscv_get8_cached(ssp,
21514135Sgd78059 			    EBUS_IDX_FAN1_LOW + i);
21524135Sgd78059 		}
21534135Sgd78059 	}
21544135Sgd78059 
21554135Sgd78059 	if ((res == 0) &&
21564135Sgd78059 	    (ddi_copyout((caddr_t)&fandata, (caddr_t)arg, sizeof (fandata),
21574135Sgd78059 	    mode) < 0)) {
21584135Sgd78059 		res = EFAULT;
21594135Sgd78059 	}
21604135Sgd78059 	return (res);
21614135Sgd78059 }
21624135Sgd78059 
21634135Sgd78059 /*
21644135Sgd78059  * LOMIOCFLEDSTATE - returns the state of the fault LED
21654135Sgd78059  */
21664135Sgd78059 static int
bscv_ioc_fledstate(bscv_soft_state_t * ssp,intptr_t arg,int mode)21674135Sgd78059 bscv_ioc_fledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
21684135Sgd78059 {
21694135Sgd78059 	lom_fled_info_t fled_info;
21704135Sgd78059 	uint8_t fledstate;
21714135Sgd78059 	int res = 0;
21724135Sgd78059 
21734135Sgd78059 	fledstate = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res);
21744135Sgd78059 
21754135Sgd78059 	/* Decode of 0x0F is off and 0x00-0x07 is on. */
21764135Sgd78059 	if (EBUS_ALARM_LED_DEC(fledstate) == 0x0F) {
21774135Sgd78059 		fled_info.on = 0;
21784135Sgd78059 	} else {
21794135Sgd78059 		/* has +1 here - not 2 as in the info ioctl */
21804135Sgd78059 		fled_info.on = EBUS_ALARM_LED_DEC(fledstate) + 1;
21814135Sgd78059 	}
21824135Sgd78059 	if ((res == 0) &&
21834135Sgd78059 	    (ddi_copyout((caddr_t)&fled_info, (caddr_t)arg,
21845107Seota 	    sizeof (fled_info), mode) < 0)) {
21854135Sgd78059 		res = EFAULT;
21864135Sgd78059 	}
21874135Sgd78059 	return (res);
21884135Sgd78059 }
21894135Sgd78059 
21904135Sgd78059 /*
21914135Sgd78059  * LOMIOCLEDSTATE - returns the state of the requested LED
21924135Sgd78059  */
21934135Sgd78059 static int
bscv_ioc_ledstate(bscv_soft_state_t * ssp,intptr_t arg,int mode)21944135Sgd78059 bscv_ioc_ledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode)
21954135Sgd78059 {
21964135Sgd78059 	lom_led_state_t led_state;
21974135Sgd78059 	int fw_led_state;
21984135Sgd78059 	int res = 0;
21994135Sgd78059 
22004135Sgd78059 	/* copy in arguments supplied */
22014135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&led_state,
22024135Sgd78059 	    sizeof (lom_led_state_t), mode) < 0) {
22034135Sgd78059 		return (EFAULT);
22044135Sgd78059 	}
22054135Sgd78059 
22064135Sgd78059 	/*
22074135Sgd78059 	 * check if led index is -1, if so set it to max value for
22084135Sgd78059 	 * this implementation.
22094135Sgd78059 	 */
22104135Sgd78059 	if (led_state.index == -1) {
22114135Sgd78059 		led_state.index = MAX_LED_ID;
22124135Sgd78059 	}
22134135Sgd78059 
22144135Sgd78059 	/* is the index in a valid range */
22154135Sgd78059 	if ((led_state.index > MAX_LED_ID) || (led_state.index < 0)) {
22164135Sgd78059 		led_state.state = LOM_LED_OUTOFRANGE;
22174135Sgd78059 	} else {
22184135Sgd78059 		/* read the relevant led info */
22194135Sgd78059 		fw_led_state = bscv_get8_locked(ssp, chan_general,
22204135Sgd78059 		    EBUS_IDX_LED1_STATUS + led_state.index, &res);
22214135Sgd78059 
22224135Sgd78059 		/* set the state values accordingly */
22234135Sgd78059 		switch (fw_led_state) {
22244135Sgd78059 		case LOM_LED_STATE_OFF:
22254135Sgd78059 			led_state.state = LOM_LED_OFF;
22264135Sgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
22274135Sgd78059 			break;
22284135Sgd78059 		case LOM_LED_STATE_ON_STEADY:
22294135Sgd78059 			led_state.state = LOM_LED_ON;
22304135Sgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
22314135Sgd78059 			break;
22324135Sgd78059 		case LOM_LED_STATE_ON_FLASHING:
22334135Sgd78059 		case LOM_LED_STATE_ON_SLOWFLASH:
22344135Sgd78059 			led_state.state = LOM_LED_BLINKING;
22354135Sgd78059 			led_state.colour = LOM_LED_COLOUR_ANY;
22364135Sgd78059 			break;
22374135Sgd78059 		case LOM_LED_STATE_NOT_PRESENT:
22384135Sgd78059 			led_state.state = LOM_LED_NOT_IMPLEMENTED;
22394135Sgd78059 			led_state.colour = LOM_LED_COLOUR_NONE;
22404135Sgd78059 			break;
22414135Sgd78059 		case LOM_LED_STATE_INACCESSIBLE:
22424135Sgd78059 		case LOM_LED_STATE_STANDBY:
22434135Sgd78059 		default:
22444135Sgd78059 			led_state.state = LOM_LED_ACCESS_ERROR;
22454135Sgd78059 			led_state.colour = LOM_LED_COLOUR_NONE;
22464135Sgd78059 			break;
22474135Sgd78059 		}
22484135Sgd78059 
22494135Sgd78059 		/* set the label info */
22504135Sgd78059 		(void) strcpy(led_state.label,
22514135Sgd78059 		    ssp->led_names[led_state.index]);
22524135Sgd78059 	}
22534135Sgd78059 
22544135Sgd78059 	/* copy out lom_state */
22554135Sgd78059 	if ((res == 0) &&
22564135Sgd78059 	    (ddi_copyout((caddr_t)&led_state, (caddr_t)arg,
22575107Seota 	    sizeof (lom_led_state_t), mode) < 0)) {
22584135Sgd78059 		res = EFAULT;
22594135Sgd78059 	}
22604135Sgd78059 	return (res);
22614135Sgd78059 }
22624135Sgd78059 
22634135Sgd78059 /*
22644135Sgd78059  * LOMIOCINFO - returns with a structure containing any information
22654135Sgd78059  * stored on the LOMlite which a user should not need to access but
22664135Sgd78059  * may be useful for diagnostic problems. The structure contains: the
22674135Sgd78059  * serial escape character, alarm3 mode, version and checksum read from
22684135Sgd78059  * RAM and the Product revision and ID read from EEPROM.
22694135Sgd78059  */
22704135Sgd78059 static int
bscv_ioc_info(bscv_soft_state_t * ssp,intptr_t arg,int mode)22714135Sgd78059 bscv_ioc_info(bscv_soft_state_t *ssp, intptr_t arg, int mode)
22724135Sgd78059 {
22734135Sgd78059 	lom_info_t info;
22744135Sgd78059 	int i;
22754135Sgd78059 	uint16_t csum;
22764135Sgd78059 	int res = 0;
22774135Sgd78059 
22784135Sgd78059 	info.ser_char = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ESCAPE,
22795107Seota 	    &res);
22804135Sgd78059 	info.a3mode = WATCHDOG;
22814135Sgd78059 	info.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
22824135Sgd78059 	csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
22835107Seota 	    << 8;
22844135Sgd78059 	csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
22854135Sgd78059 	info.fchksum = csum;
22864135Sgd78059 	info.prod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
22875107Seota 	    &res);
22884135Sgd78059 	for (i = 0; i < sizeof (info.prod_id); i++) {
22894135Sgd78059 		info.prod_id[i] = bscv_get8_locked(ssp,
22904135Sgd78059 		    chan_general, EBUS_IDX_MODEL_ID1 + i, &res);
22914135Sgd78059 	}
22924135Sgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res) &
22934135Sgd78059 	    EBUS_ALARM_NOEVENTS) {
22944135Sgd78059 		info.events = OFF;
22954135Sgd78059 	} else {
22964135Sgd78059 		info.events = ON;
22974135Sgd78059 	}
22984135Sgd78059 
22994135Sgd78059 	if ((res == 0) &&
23004135Sgd78059 	    (ddi_copyout((caddr_t)&info, (caddr_t)arg, sizeof (info),
23014135Sgd78059 	    mode) < 0)) {
23024135Sgd78059 		res = EFAULT;
23034135Sgd78059 	}
23044135Sgd78059 	return (res);
23054135Sgd78059 }
23064135Sgd78059 
23074135Sgd78059 /*
23084135Sgd78059  * LOMIOCMREAD - used to query the LOMlite configuration parameters
23094135Sgd78059  */
23104135Sgd78059 static int
bscv_ioc_mread(bscv_soft_state_t * ssp,intptr_t arg,int mode)23114135Sgd78059 bscv_ioc_mread(bscv_soft_state_t *ssp, intptr_t arg, int mode)
23124135Sgd78059 {
23134135Sgd78059 	lom_mprog_t mprog;
23144135Sgd78059 	int i;
23154135Sgd78059 	int fanz;
23164135Sgd78059 	int res = 0;
23174135Sgd78059 
23184135Sgd78059 	for (i = 0; i < sizeof (mprog.mod_id); i++) {
23194135Sgd78059 		mprog.mod_id[i] = bscv_get8_locked(ssp, chan_general,
23204135Sgd78059 		    EBUS_IDX_MODEL_ID1 + i, &res);
23214135Sgd78059 	}
23224135Sgd78059 	mprog.mod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV,
23235107Seota 	    &res);
23244135Sgd78059 	mprog.config = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG,
23255107Seota 	    &res);
23264135Sgd78059 
23274135Sgd78059 	/* Read the fan calibration values */
23284135Sgd78059 	fanz = sizeof (mprog.fanhz) / sizeof (mprog.fanhz[0]);
23294135Sgd78059 	for (i = 0; i < fanz; i++) {
23304135Sgd78059 		mprog.fanhz[i] = bscv_get8_cached(ssp,
23314135Sgd78059 		    EBUS_IDX_FAN1_CAL + i);
23324135Sgd78059 		mprog.fanmin[i] = bscv_get8_cached(ssp,
23334135Sgd78059 		    EBUS_IDX_FAN1_LOW + i);
23344135Sgd78059 	}
23354135Sgd78059 
23364135Sgd78059 	if ((res == 0) &&
23374135Sgd78059 	    (ddi_copyout((caddr_t)&mprog, (caddr_t)arg, sizeof (mprog),
23384135Sgd78059 	    mode) < 0)) {
23394135Sgd78059 		res = EFAULT;
23404135Sgd78059 	}
23414135Sgd78059 	return (res);
23424135Sgd78059 }
23434135Sgd78059 
23444135Sgd78059 /*
23454135Sgd78059  * LOMIOCVOLTS
23464135Sgd78059  */
23474135Sgd78059 static int
bscv_ioc_volts(bscv_soft_state_t * ssp,intptr_t arg,int mode)23484135Sgd78059 bscv_ioc_volts(bscv_soft_state_t *ssp, intptr_t arg, int mode)
23494135Sgd78059 {
23504135Sgd78059 	int i;
23514135Sgd78059 	uint16_t supply;
23524135Sgd78059 	int res = 0;
23534135Sgd78059 
23544135Sgd78059 	supply = (bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_HI, &res)
23555107Seota 	    << 8) | bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_LO,
23565107Seota 	    &res);
23574135Sgd78059 
23584135Sgd78059 	for (i = 0; i < ssp->volts.num; i++) {
23594135Sgd78059 		ssp->volts.status[i] = (supply >> i) & 1;
23604135Sgd78059 	}
23614135Sgd78059 
23624135Sgd78059 	if ((res == 0) &&
23634135Sgd78059 	    (ddi_copyout((caddr_t)&ssp->volts, (caddr_t)arg,
23645107Seota 	    sizeof (ssp->volts), mode) < 0)) {
23654135Sgd78059 		res = EFAULT;
23664135Sgd78059 	}
23674135Sgd78059 	return (res);
23684135Sgd78059 }
23694135Sgd78059 
23704135Sgd78059 /*
23714135Sgd78059  * LOMIOCSTATS
23724135Sgd78059  */
23734135Sgd78059 static int
bscv_ioc_stats(bscv_soft_state_t * ssp,intptr_t arg,int mode)23744135Sgd78059 bscv_ioc_stats(bscv_soft_state_t *ssp, intptr_t arg, int mode)
23754135Sgd78059 {
23764135Sgd78059 	int i;
23774135Sgd78059 	uint8_t status;
23784135Sgd78059 	int res = 0;
23794135Sgd78059 
23804135Sgd78059 	status = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CBREAK_STATUS,
23815107Seota 	    &res);
23824135Sgd78059 	for (i = 0; i < ssp->sflags.num; i++) {
23834135Sgd78059 		ssp->sflags.status[i] = (int)((status >> i) & 1);
23844135Sgd78059 	}
23854135Sgd78059 
23864135Sgd78059 	if ((res == 0) &&
23874135Sgd78059 	    (ddi_copyout((caddr_t)&ssp->sflags, (caddr_t)arg,
23885107Seota 	    sizeof (ssp->sflags), mode) < 0)) {
23894135Sgd78059 		res = EFAULT;
23904135Sgd78059 	}
23914135Sgd78059 	return (res);
23924135Sgd78059 }
23934135Sgd78059 
23944135Sgd78059 /*
23954135Sgd78059  * LOMIOCTEMP
23964135Sgd78059  */
23974135Sgd78059 static int
bscv_ioc_temp(bscv_soft_state_t * ssp,intptr_t arg,int mode)23984135Sgd78059 bscv_ioc_temp(bscv_soft_state_t *ssp, intptr_t arg, int mode)
23994135Sgd78059 {
24004135Sgd78059 	int i;
24014135Sgd78059 	int idx;
24024135Sgd78059 	uint8_t status_ov;
24034135Sgd78059 	lom_temp_t temps;
24044135Sgd78059 	int res = 0;
24054135Sgd78059 
24064135Sgd78059 	bzero(&temps, sizeof (temps));
24074135Sgd78059 	idx = 0;
24084135Sgd78059 	for (i = 0; i < ssp->temps.num; i++) {
24094135Sgd78059 		if (ssp->temps.temp[i] != LOM_TEMP_STATE_NOT_PRESENT) {
24104135Sgd78059 			temps.temp[idx] = ssp->temps.temp[i];
24114135Sgd78059 			bcopy(ssp->temps.name[i], temps.name[idx],
24124135Sgd78059 			    sizeof (temps.name[idx]));
24134135Sgd78059 			temps.warning[idx] = ssp->temps.warning[i];
24144135Sgd78059 			temps.shutdown[idx] = ssp->temps.shutdown[i];
24154135Sgd78059 			idx++;
24164135Sgd78059 		}
24174135Sgd78059 	}
24184135Sgd78059 	temps.num = idx;
24194135Sgd78059 
24204135Sgd78059 	bcopy(ssp->temps.name_ov, temps.name_ov, sizeof (temps.name_ov));
24214135Sgd78059 	temps.num_ov = ssp->temps.num_ov;
24224135Sgd78059 	status_ov = bscv_get8_locked(ssp, chan_general, EBUS_IDX_OTEMP_STATUS,
24235107Seota 	    &res);
24244135Sgd78059 	for (i = 0; i < ssp->temps.num_ov; i++) {
24254135Sgd78059 		ssp->temps.status_ov[i] = (status_ov >> i) & 1;
24264135Sgd78059 	}
24274135Sgd78059 
24284135Sgd78059 	if ((res == 0) &&
24294135Sgd78059 	    (ddi_copyout((caddr_t)&temps, (caddr_t)arg, sizeof (temps),
24304135Sgd78059 	    mode) < 0)) {
24314135Sgd78059 		res = EFAULT;
24324135Sgd78059 	}
24334135Sgd78059 	return (res);
24344135Sgd78059 }
24354135Sgd78059 
24364135Sgd78059 /*
24374135Sgd78059  * LOMIOCCONS
24384135Sgd78059  */
24394135Sgd78059 static int
bscv_ioc_cons(bscv_soft_state_t * ssp,intptr_t arg,int mode)24404135Sgd78059 bscv_ioc_cons(bscv_soft_state_t *ssp, intptr_t arg, int mode)
24414135Sgd78059 {
24424135Sgd78059 	lom_cbuf_t cbuf;
24434135Sgd78059 	int datasize;
24444135Sgd78059 	int res = 0;
24454135Sgd78059 
24464135Sgd78059 	bzero(&cbuf, sizeof (cbuf));
24474135Sgd78059 	datasize = EBUS_IDX1_CONS_BUF_END - EBUS_IDX1_CONS_BUF_START + 1;
24484135Sgd78059 	/* Ensure that we do not overfill cbuf and that it is NUL terminated */
24494135Sgd78059 	if (datasize > (sizeof (cbuf) - 1)) {
24504135Sgd78059 		datasize = sizeof (cbuf) - 1;
24514135Sgd78059 	}
24524135Sgd78059 	bscv_rep_get8_locked(ssp, chan_general, (uint8_t *)cbuf.lrbuf,
24534135Sgd78059 	    BSCVA(EBUS_CMD_SPACE1, (EBUS_IDX1_CONS_BUF_END - datasize + 1)),
24544135Sgd78059 	    datasize, DDI_DEV_AUTOINCR, &res);
24554135Sgd78059 	/* This is always within the array due to the checks above */
24564135Sgd78059 	cbuf.lrbuf[datasize] = '\0';
24574135Sgd78059 
24584135Sgd78059 	if ((res == 0) &&
24594135Sgd78059 	    (ddi_copyout((caddr_t)&cbuf, (caddr_t)arg, sizeof (cbuf),
24604135Sgd78059 	    mode) < 0)) {
24614135Sgd78059 		res = EFAULT;
24624135Sgd78059 	}
24634135Sgd78059 	return (res);
24644135Sgd78059 }
24654135Sgd78059 
24664135Sgd78059 /*
24674135Sgd78059  * LOMIOCEVENTLOG2
24684135Sgd78059  */
24694135Sgd78059 static int
bscv_ioc_eventlog2(bscv_soft_state_t * ssp,intptr_t arg,int mode)24704135Sgd78059 bscv_ioc_eventlog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
24714135Sgd78059 {
24724135Sgd78059 	lom_eventlog2_t *eventlog2;
24734135Sgd78059 	int events_recorded;
24744135Sgd78059 	int level;
24754135Sgd78059 	uint16_t next_offset;
24764135Sgd78059 	lom_event_t event;
24774135Sgd78059 	int res = 0;
24784135Sgd78059 
24794135Sgd78059 	eventlog2 = (lom_eventlog2_t *)kmem_zalloc(sizeof (*eventlog2),
24804135Sgd78059 	    KM_SLEEP);
24814135Sgd78059 
24824135Sgd78059 	/*
24834135Sgd78059 	 * First get number of events and level requested.
24844135Sgd78059 	 */
24854135Sgd78059 
24864135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)eventlog2,
24874135Sgd78059 	    sizeof (lom_eventlog2_t), mode) < 0) {
24884135Sgd78059 		kmem_free((void *)eventlog2, sizeof (*eventlog2));
24894135Sgd78059 		return (EFAULT);
24904135Sgd78059 	}
24914135Sgd78059 
24924135Sgd78059 	bscv_enter(ssp);
24934135Sgd78059 
24944135Sgd78059 	/*
24954135Sgd78059 	 * OK we have full private access to the LOM now so loop
24964135Sgd78059 	 * over the eventlog addr spaces until we get the required
24974135Sgd78059 	 * number of events.
24984135Sgd78059 	 */
24994135Sgd78059 
25004135Sgd78059 	if (!bscv_window_setup(ssp)) {
25014135Sgd78059 		res = EIO;
25024135Sgd78059 		bscv_exit(ssp);
25034135Sgd78059 		kmem_free((void *)eventlog2, sizeof (*eventlog2));
25044135Sgd78059 		return (res);
25054135Sgd78059 	}
25064135Sgd78059 
25074135Sgd78059 	/*
25084135Sgd78059 	 * Read count, next event ptr MSB,LSB. Note a read of count
25094135Sgd78059 	 * is necessary to latch values for the next event ptr
25104135Sgd78059 	 */
25114135Sgd78059 	(void) bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
25124135Sgd78059 	next_offset = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
25139644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "log_ptr_hi 0x%x",
25144135Sgd78059 	    next_offset);
25154135Sgd78059 
25164135Sgd78059 	events_recorded = 0;
25174135Sgd78059 
25184135Sgd78059 	while (events_recorded < eventlog2->num) {
25194135Sgd78059 		/*
25204135Sgd78059 		 * Working backwards - read an event at a time.
25214135Sgd78059 		 * next_offset is one event on from where we want to be!
25224135Sgd78059 		 * Decrement next_offset and maybe wrap to the end of the
25234135Sgd78059 		 * buffer.
25244135Sgd78059 		 * Note the unsigned arithmetic, so check values first!
25254135Sgd78059 		 */
25264135Sgd78059 		if (next_offset <= ssp->eventlog_start) {
25274135Sgd78059 			/* Wrap to the end of the buffer */
25284135Sgd78059 			next_offset = ssp->eventlog_start + ssp->eventlog_size;
25299644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "wrapping"
25304135Sgd78059 			    " around to end of buffer; next_offset 0x%x",
25314135Sgd78059 			    next_offset);
25324135Sgd78059 		}
25334135Sgd78059 		next_offset -= sizeof (event);
25344135Sgd78059 
25354135Sgd78059 		if (bscv_eerw(ssp, next_offset, (uint8_t *)&event,
25364135Sgd78059 		    sizeof (event), B_FALSE /* read */) != 0) {
25374135Sgd78059 			/* Fault reading data - stop */
25389644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "read"
25394135Sgd78059 			    " failure for offset 0x%x", next_offset);
25404135Sgd78059 			res = EIO;
25414135Sgd78059 			break;
25424135Sgd78059 		}
25434135Sgd78059 
25444135Sgd78059 		if (bscv_is_null_event(ssp, &event)) {
25454135Sgd78059 			/*
25464135Sgd78059 			 * No more events in this log so give up.
25474135Sgd78059 			 */
25489644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "no more"
25494135Sgd78059 			    " events left at offset 0x%x", next_offset);
25504135Sgd78059 			break;
25514135Sgd78059 		}
25524135Sgd78059 
25534135Sgd78059 		/*
25544135Sgd78059 		 * Are we interested in this event
25554135Sgd78059 		 */
25564135Sgd78059 
25574135Sgd78059 		level = bscv_level_of_event(&event);
25584135Sgd78059 		if (level <= eventlog2->level) {
25594135Sgd78059 			/* Arggh why the funny byte ordering 3, 2, 0, 1 */
25604135Sgd78059 			eventlog2->code[events_recorded] =
25615107Seota 			    ((unsigned)event.ev_event |
25625107Seota 			    ((unsigned)event.ev_subsys << 8) |
25635107Seota 			    ((unsigned)event.ev_resource << 16) |
25645107Seota 			    ((unsigned)event.ev_detail << 24));
25654135Sgd78059 
25664135Sgd78059 			eventlog2->time[events_recorded] =
25675107Seota 			    ((unsigned)event.ev_data[0] |
25685107Seota 			    ((unsigned)event.ev_data[1] << 8) |
25695107Seota 			    ((unsigned)event.ev_data[3] << 16) |
25705107Seota 			    ((unsigned)event.ev_data[2] << 24));
25714135Sgd78059 
25724135Sgd78059 			bscv_build_eventstring(ssp,
25734135Sgd78059 			    &event, eventlog2->string[events_recorded],
25744135Sgd78059 			    eventlog2->string[events_recorded] +
25754135Sgd78059 			    sizeof (eventlog2->string[events_recorded]));
25764135Sgd78059 			events_recorded++;
25774135Sgd78059 		}
25784135Sgd78059 	}
25794135Sgd78059 
25804135Sgd78059 	eventlog2->num = events_recorded;
25814135Sgd78059 
25824135Sgd78059 	bscv_exit(ssp);
25834135Sgd78059 
25844135Sgd78059 	if ((res == 0) &&
25854135Sgd78059 	    (ddi_copyout((caddr_t)eventlog2, (caddr_t)arg,
25865107Seota 	    sizeof (lom_eventlog2_t), mode) < 0)) {
25874135Sgd78059 		res = EFAULT;
25884135Sgd78059 	}
25894135Sgd78059 
25904135Sgd78059 	kmem_free((void *)eventlog2, sizeof (lom_eventlog2_t));
25914135Sgd78059 	return (res);
25924135Sgd78059 }
25934135Sgd78059 
25944135Sgd78059 /*
25954135Sgd78059  * LOMIOCINFO2
25964135Sgd78059  */
25974135Sgd78059 static int
bscv_ioc_info2(bscv_soft_state_t * ssp,intptr_t arg,int mode)25984135Sgd78059 bscv_ioc_info2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
25994135Sgd78059 {
26004135Sgd78059 	lom2_info_t info2;
26014135Sgd78059 	int i;
26024135Sgd78059 	uint16_t csum;
26034135Sgd78059 	int res = 0;
26044135Sgd78059 
26054135Sgd78059 	bzero(&info2, sizeof (info2));
26064135Sgd78059 
26074135Sgd78059 	(void) strncpy(info2.escape_chars, ssp->escape_chars,
26084135Sgd78059 	    sizeof (info2.escape_chars));
26094135Sgd78059 	info2.serial_events = ssp->reporting_level | ssp->serial_reporting;
26104135Sgd78059 	info2.a3mode = WATCHDOG;
26114135Sgd78059 
26124135Sgd78059 	info2.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res);
26134135Sgd78059 	csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res)
26145107Seota 	    << 8;
26154135Sgd78059 	csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res);
26164135Sgd78059 	info2.fchksum = csum;
26174135Sgd78059 	info2.prod_rev = bscv_get8_locked(ssp, chan_general,
26185107Seota 	    EBUS_IDX_MODEL_REV, &res);
26194135Sgd78059 	for (i = 0; i < sizeof (info2.prod_id); i++) {
26204135Sgd78059 		info2.prod_id[i] = bscv_get8_locked(ssp, chan_general,
26214135Sgd78059 		    EBUS_IDX_MODEL_ID1 + i, &res);
26224135Sgd78059 	}
26234135Sgd78059 	info2.serial_config = bscv_get8_locked(ssp, chan_general,
26245107Seota 	    EBUS_IDX_SER_TIMEOUT, &res);
26254135Sgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
26264135Sgd78059 	    EBUS_CONFIG_MISC_SECURITY_ENABLED) {
26274135Sgd78059 		info2.serial_config |= LOM_SER_SECURITY;
26284135Sgd78059 	}
26294135Sgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) &
26304135Sgd78059 	    EBUS_CONFIG_MISC_AUTO_CONSOLE) {
26314135Sgd78059 		info2.serial_config |= LOM_SER_RETURN;
26324135Sgd78059 	}
26334135Sgd78059 	if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res) &
26344135Sgd78059 	    EBUS_WDOG_BREAK_DISABLE) {
26354135Sgd78059 		info2.serial_config |= LOM_DISABLE_WDOG_BREAK;
26364135Sgd78059 	}
26374135Sgd78059 	info2.baud_rate = bscv_get8_locked(ssp, chan_general,
26385107Seota 	    EBUS_IDX_SER_BAUD, &res);
26394135Sgd78059 	info2.serial_hw_config =
26405107Seota 	    ((int)bscv_get8_locked(ssp, chan_general,
26415107Seota 	    EBUS_IDX_SER_CHARMODE, &res) |
26425107Seota 	    ((int)bscv_get8_locked(ssp, chan_general,
26435107Seota 	    EBUS_IDX_SER_FLOWCTL, &res) << 8) |
26445107Seota 	    ((int)bscv_get8_locked(ssp, chan_general,
26455107Seota 	    EBUS_IDX_SER_MODEMTYPE, &res) << 16));
26464135Sgd78059 
26474135Sgd78059 	/*
26484135Sgd78059 	 * There is no phone home support on the blade platform.  We hardcode
26494135Sgd78059 	 * FALSE and NUL for config and script respectively.
26504135Sgd78059 	 */
26514135Sgd78059 	info2.phone_home_config = B_FALSE;
26524135Sgd78059 	info2.phone_home_script[0] = '\0';
26534135Sgd78059 
26544135Sgd78059 	for (i = 0; i < ssp->num_fans; i++) {
26554135Sgd78059 		(void) strcpy(info2.fan_names[i], ssp->fan_names[i]);
26564135Sgd78059 	}
26574135Sgd78059 
26584135Sgd78059 	if ((res == 0) &&
26594135Sgd78059 	    (ddi_copyout((caddr_t)&info2, (caddr_t)arg, sizeof (info2),
26604135Sgd78059 	    mode) < 0)) {
26614135Sgd78059 		res = EFAULT;
26624135Sgd78059 	}
26634135Sgd78059 	return (res);
26644135Sgd78059 }
26654135Sgd78059 
26664135Sgd78059 /*
26674135Sgd78059  * LOMIOCTEST
26684135Sgd78059  */
26694135Sgd78059 static int
bscv_ioc_test(bscv_soft_state_t * ssp,intptr_t arg,int mode)26704135Sgd78059 bscv_ioc_test(bscv_soft_state_t *ssp, intptr_t arg, int mode)
26714135Sgd78059 {
26724135Sgd78059 	uint32_t test;
26734135Sgd78059 	uint8_t testnum;
26744135Sgd78059 	uint8_t testarg;
26754135Sgd78059 	int res = 0;
26764135Sgd78059 
26774135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&test, sizeof (test),
26784135Sgd78059 	    mode) < 0) {
26794135Sgd78059 		return (EFAULT);
26804135Sgd78059 	}
26814135Sgd78059 
26824135Sgd78059 	/*
26834135Sgd78059 	 * Extract num iterations.
26844135Sgd78059 	 */
26854135Sgd78059 
26864135Sgd78059 	testarg = (test & 0xff00) >> 8;
26874135Sgd78059 	testnum = test & 0xff;
26884135Sgd78059 
26899644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'F', "bscv_ioc_test",
26904135Sgd78059 	    "LOMIOCTEST data 0x%x (test 0x%x, arg 0x%x)",
26914135Sgd78059 	    test, (EBUS_IDX_SELFTEST0 + testnum), testarg);
26924135Sgd78059 
26934135Sgd78059 	switch (testnum + EBUS_IDX_SELFTEST0) {
26944135Sgd78059 	default:
26954135Sgd78059 		/* Invalid test */
26964135Sgd78059 		res = EINVAL;
26974135Sgd78059 		break;
26984135Sgd78059 
26994135Sgd78059 	case EBUS_IDX_SELFTEST0:	/* power on self-test result */
27004135Sgd78059 	case EBUS_IDX_SELFTEST1:	/* not used currently */
27014135Sgd78059 	case EBUS_IDX_SELFTEST2:	/* not used currently */
27024135Sgd78059 	case EBUS_IDX_SELFTEST3:	/* not used currently */
27034135Sgd78059 	case EBUS_IDX_SELFTEST4:	/* not used currently */
27044135Sgd78059 	case EBUS_IDX_SELFTEST5:	/* not used currently */
27054135Sgd78059 	case EBUS_IDX_SELFTEST6:	/* LED self-test */
27064135Sgd78059 	case EBUS_IDX_SELFTEST7:	/* platform-specific tests */
27074135Sgd78059 		/* Run the test */
27084135Sgd78059 
27094135Sgd78059 		/* Stop other things and then run the test */
27104135Sgd78059 		bscv_enter(ssp);
27114135Sgd78059 
27124135Sgd78059 		/*
27134135Sgd78059 		 * Then we simply write the argument to the relevant register
27144135Sgd78059 		 * and wait for the return code.
27154135Sgd78059 		 */
27164135Sgd78059 		bscv_put8(ssp, chan_general,
27175107Seota 		    EBUS_IDX_SELFTEST0 + testnum, testarg);
27184135Sgd78059 		if (bscv_faulty(ssp)) {
27194135Sgd78059 			res = EIO;
27204135Sgd78059 		} else {
27214135Sgd78059 			/* Get hold of the SunVTS error code */
27224135Sgd78059 			test = bscv_retcode(ssp);
27234135Sgd78059 		}
27244135Sgd78059 
27254135Sgd78059 		bscv_exit(ssp);
27264135Sgd78059 		break;
27274135Sgd78059 	}
27284135Sgd78059 
27299644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'F', "bscv_ioc_test",
27304135Sgd78059 	    "LOMIOCTEST status 0x%x, res 0x%x", test, res);
27314135Sgd78059 	if ((res == 0) &&
27324135Sgd78059 	    (ddi_copyout((caddr_t)&test, (caddr_t)arg, sizeof (test),
27334135Sgd78059 	    mode) < 0)) {
27344135Sgd78059 		res = EFAULT;
27354135Sgd78059 	}
27364135Sgd78059 	return (res);
27374135Sgd78059 }
27384135Sgd78059 
27394135Sgd78059 /*
27404135Sgd78059  * LOMIOCMPROG2
27414135Sgd78059  */
27424135Sgd78059 static int
bscv_ioc_mprog2(bscv_soft_state_t * ssp,intptr_t arg,int mode)27434135Sgd78059 bscv_ioc_mprog2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
27444135Sgd78059 {
27454135Sgd78059 	lom2_mprog_t  mprog2;
27464135Sgd78059 	uint32_t base_addr;
27474135Sgd78059 	uint32_t data_size;
27484135Sgd78059 	uint32_t eeprom_size;
27494135Sgd78059 	int res = 0;
27504135Sgd78059 
27514135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
27524135Sgd78059 	    mode) < 0) {
27534135Sgd78059 		return (EFAULT);
27544135Sgd78059 	}
27554135Sgd78059 
27564135Sgd78059 	/*
27574135Sgd78059 	 * Note that originally this was accessed as 255 byte pages
27584135Sgd78059 	 * in address spaces 240-255. We have to emulate this behaviour.
27594135Sgd78059 	 */
27604135Sgd78059 	if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
27614135Sgd78059 		return (EINVAL);
27624135Sgd78059 	}
27634135Sgd78059 
27644135Sgd78059 	bscv_enter(ssp);
27654135Sgd78059 
27664135Sgd78059 	/* Calculate required data location */
27674135Sgd78059 	data_size = 255;
27684135Sgd78059 	base_addr = (mprog2.addr_space - 240) * data_size;
27694135Sgd78059 
27704135Sgd78059 	eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
27715107Seota 	    1024;
27724135Sgd78059 
27734135Sgd78059 	if (bscv_faulty(ssp)) {
27744135Sgd78059 		bscv_exit(ssp);
27754135Sgd78059 		return (EIO);
27764135Sgd78059 	} else if ((base_addr + data_size) > eeprom_size) {
27779644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2",
27784135Sgd78059 		    "Request extends past end of eeprom");
27794135Sgd78059 		bscv_exit(ssp);
27804135Sgd78059 		return (ENXIO);
27814135Sgd78059 	}
27824135Sgd78059 
27834135Sgd78059 	bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK1);
27844135Sgd78059 	if (bscv_faulty(ssp)) {
27859644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2", "ML1 Write failed");
27864135Sgd78059 		bscv_exit(ssp);
27874135Sgd78059 		return (EIO);
27884135Sgd78059 	}
27894135Sgd78059 
27904135Sgd78059 	bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK2);
27914135Sgd78059 	if (bscv_faulty(ssp)) {
27929644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2", "ML2 Write failed");
27934135Sgd78059 		bscv_exit(ssp);
27944135Sgd78059 		return (EIO);
27954135Sgd78059 	}
27964135Sgd78059 
27974135Sgd78059 	if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
27984135Sgd78059 	    data_size, B_TRUE /* write */) != 0) {
27994135Sgd78059 		res = EIO;
28004135Sgd78059 	}
28014135Sgd78059 
28024135Sgd78059 	/* Read a probe key to release the lock. */
28034135Sgd78059 	(void) bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
28044135Sgd78059 
28054135Sgd78059 	if (bscv_faulty(ssp)) {
28064135Sgd78059 		res = EIO;
28074135Sgd78059 	}
28084135Sgd78059 	bscv_exit(ssp);
28094135Sgd78059 
28104135Sgd78059 	return (res);
28114135Sgd78059 }
28124135Sgd78059 
28134135Sgd78059 /*
28144135Sgd78059  * LOMIOCMREAD2
28154135Sgd78059  */
28164135Sgd78059 static int
bscv_ioc_mread2(bscv_soft_state_t * ssp,intptr_t arg,int mode)28174135Sgd78059 bscv_ioc_mread2(bscv_soft_state_t *ssp, intptr_t arg, int mode)
28184135Sgd78059 {
28194135Sgd78059 	lom2_mprog_t  mprog2;
28204135Sgd78059 	uint32_t base_addr;
28214135Sgd78059 	uint32_t data_size;
28224135Sgd78059 	uint32_t eeprom_size;
28234135Sgd78059 	int res = 0;
28244135Sgd78059 
28254135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2),
28264135Sgd78059 	    mode) < 0) {
28274135Sgd78059 		return (EFAULT);
28284135Sgd78059 	}
28294135Sgd78059 
28304135Sgd78059 	/*
28314135Sgd78059 	 * Need to stop the queue and then just read
28324135Sgd78059 	 * the bytes blind to the relevant addresses.
28334135Sgd78059 	 * Note that originally this was accessed as 255 byte pages
28344135Sgd78059 	 * in address spaces 240-255. We have to emulate this behaviour.
28354135Sgd78059 	 */
28364135Sgd78059 	if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) {
28374135Sgd78059 		return (EINVAL);
28384135Sgd78059 	}
28394135Sgd78059 
28404135Sgd78059 	bscv_enter(ssp);
28414135Sgd78059 
28424135Sgd78059 	/* Calculate required data location */
28434135Sgd78059 	data_size = 255;
28444135Sgd78059 	base_addr = (mprog2.addr_space - 240) * data_size;
28454135Sgd78059 	eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) *
28465107Seota 	    1024;
28474135Sgd78059 
28484135Sgd78059 	if (bscv_faulty(ssp)) {
28494135Sgd78059 		bscv_exit(ssp);
28504135Sgd78059 		return (EIO);
28514135Sgd78059 	} else if ((base_addr + data_size) > eeprom_size) {
28529644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'M', "bscv_ioc_mread2",
28534135Sgd78059 		    "Request extends past end of eeprom");
28544135Sgd78059 		bscv_exit(ssp);
28554135Sgd78059 		return (ENXIO);
28564135Sgd78059 	}
28574135Sgd78059 
28584135Sgd78059 	if (bscv_eerw(ssp, base_addr, &mprog2.data[0],
28594135Sgd78059 	    data_size, B_FALSE /* read */) != 0) {
28604135Sgd78059 		res = EIO;
28614135Sgd78059 	}
28624135Sgd78059 
28634135Sgd78059 	if (bscv_faulty(ssp)) {
28644135Sgd78059 		res = EIO;
28654135Sgd78059 	}
28664135Sgd78059 	bscv_exit(ssp);
28674135Sgd78059 
28684135Sgd78059 	if ((res == 0) &&
28694135Sgd78059 	    (ddi_copyout((caddr_t)&mprog2, (caddr_t)arg, sizeof (mprog2),
28704135Sgd78059 	    mode) < 0)) {
28714135Sgd78059 		res = EFAULT;
28724135Sgd78059 	}
28734135Sgd78059 	return (res);
28744135Sgd78059 }
28754135Sgd78059 
28764135Sgd78059 static void
bscv_get_state_changes(bscv_soft_state_t * ssp)28774135Sgd78059 bscv_get_state_changes(bscv_soft_state_t *ssp)
28784135Sgd78059 {
28794135Sgd78059 	int i = STATUS_READ_LIMIT;
28804135Sgd78059 	uint8_t change;
28814135Sgd78059 	uint8_t detail;
28824135Sgd78059 
28834135Sgd78059 	ASSERT(bscv_held(ssp));
28844135Sgd78059 
28854135Sgd78059 	while (i-- && !ssp->cssp_prog) {
28864135Sgd78059 		/* Are there any changes to process? */
28874135Sgd78059 		change = bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
28884135Sgd78059 		change &= EBUS_STATE_MASK;
28894135Sgd78059 		if (!change)
28904135Sgd78059 			break;
28914135Sgd78059 
28924135Sgd78059 		/* Clarify the pending change */
28934135Sgd78059 		detail = bscv_get8(ssp, chan_general, EBUS_IDX_EVENT_DETAIL);
28944135Sgd78059 
28954135Sgd78059 		bscv_status(ssp, change, detail);
28964135Sgd78059 	}
28974135Sgd78059 
28989644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_get_state_changes",
28994135Sgd78059 	    "loop index %d ssp->cssp_prog 0x%x", i, ssp->cssp_prog);
29004135Sgd78059 }
29014135Sgd78059 
29024135Sgd78059 /*
29034135Sgd78059  * *********************************************************************
29044135Sgd78059  * Event Processing
29054135Sgd78059  * *********************************************************************
29064135Sgd78059  */
29074135Sgd78059 
29084135Sgd78059 /*
29094135Sgd78059  * function	- bscv_event_daemon
29104135Sgd78059  * description	- Perform periodic lom tasks in a separate thread.
29114135Sgd78059  * inputs	- LOM soft state structure pointer
29124135Sgd78059  * outputs	- none.
29134135Sgd78059  */
29144135Sgd78059 static void
bscv_event_daemon(void * arg)29154135Sgd78059 bscv_event_daemon(void *arg)
29164135Sgd78059 {
29174135Sgd78059 	bscv_soft_state_t	*ssp = (void *)arg;
29184135Sgd78059 	boolean_t do_events;
29194135Sgd78059 	boolean_t do_status;
29204135Sgd78059 	boolean_t do_nodename;
29214135Sgd78059 	boolean_t do_watchdog;
29224135Sgd78059 	uint32_t async_reg;
29234135Sgd78059 	uint32_t fault;
29244135Sgd78059 	clock_t poll_period = BSC_EVENT_POLL_NORMAL;
29254135Sgd78059 	int fault_cnt = 0;
29264135Sgd78059 
29279644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
29284135Sgd78059 	    "bscv_event_daemon: started");
29294135Sgd78059 
29304135Sgd78059 	/* Acquire task daemon lock. */
29314135Sgd78059 	mutex_enter(&ssp->task_mu);
29324135Sgd78059 
29334135Sgd78059 	ssp->task_flags |= TASK_ALIVE_FLG;
29344135Sgd78059 
29354135Sgd78059 	for (;;) {
29364135Sgd78059 		if ((ssp->task_flags & TASK_STOP_FLG) != 0) {
29374135Sgd78059 			/* Stop request seen - terminate */
29384135Sgd78059 			break;
29394135Sgd78059 		}
29404135Sgd78059 		if ((ssp->task_flags & TASK_PAUSE_FLG) == 0) {
29414135Sgd78059 			/* Poll for events reported to the nexus */
29424135Sgd78059 			mutex_exit(&ssp->task_mu);
29434135Sgd78059 			/* Probe and Check faults */
29444135Sgd78059 			bscv_enter(ssp);
29454135Sgd78059 			async_reg = bscv_probe(ssp, chan_general, &fault);
29469644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
29474135Sgd78059 			    "process event: async_reg 0x%x, fault 0x%x",
29484135Sgd78059 			    async_reg, fault);
29494135Sgd78059 
29504135Sgd78059 			if (!fault) {
29514135Sgd78059 				/* Treat non-fault conditions */
29524135Sgd78059 
29534135Sgd78059 				if (ssp->cssp_prog || ssp->prog_mode_only) {
29544135Sgd78059 					/*
29554135Sgd78059 					 * The BSC has become available again.
29564135Sgd78059 					 */
29574135Sgd78059 					fault_cnt = 0;
29584135Sgd78059 					ssp->cssp_prog = B_FALSE;
29594135Sgd78059 					ssp->prog_mode_only = B_FALSE;
29604135Sgd78059 					(void) bscv_attach_common(ssp);
29614135Sgd78059 				} else if (fault_cnt > 0) {
29624135Sgd78059 					/* Previous fault has cleared */
29634135Sgd78059 					bscv_clear_fault(ssp);
29644135Sgd78059 					fault_cnt = 0;
29654135Sgd78059 					cmn_err(CE_WARN,
29664135Sgd78059 					    "!bscv_event_daemon previous fault "
29674135Sgd78059 					    "cleared.");
29684135Sgd78059 				} else if (bscv_faulty(ssp)) {
29694135Sgd78059 					/* Previous fault has cleared */
29704135Sgd78059 					bscv_clear_fault(ssp);
29714135Sgd78059 					/* Sleep to avoid busy waiting */
29724135Sgd78059 					ssp->event_sleep = B_TRUE;
29734135Sgd78059 				}
29744135Sgd78059 				poll_period = BSC_EVENT_POLL_NORMAL;
29754135Sgd78059 
29764135Sgd78059 				if (async_reg) {
29774135Sgd78059 					ssp->status_change = B_TRUE;
29784135Sgd78059 					ssp->event_waiting = B_TRUE;
29794135Sgd78059 				}
29804135Sgd78059 			} else if (ssp->cssp_prog) {
29814135Sgd78059 				/*
29824135Sgd78059 				 * Expect radio silence or error values
29834135Sgd78059 				 * when the CSSP is upgrading the BSC firmware
29844135Sgd78059 				 * so throw away any fault indication.
29854135Sgd78059 				 */
29864135Sgd78059 				fault = B_FALSE;
29874135Sgd78059 			} else if (fault_cnt == BSC_PROBE_FAULT_LIMIT) {
29884135Sgd78059 				/* Count previous faults and maybe fail */
29894135Sgd78059 				/* Declare the lom broken */
29904135Sgd78059 				bscv_set_fault(ssp);
29914135Sgd78059 				poll_period = BSC_EVENT_POLL_FAULTY;
29924135Sgd78059 				cmn_err(CE_WARN,
29934135Sgd78059 				    "!bscv_event_daemon had faults probing "
29944135Sgd78059 				    "lom - marking it as faulty.");
29954135Sgd78059 				/*
29964135Sgd78059 				 * Increment fault_cnt to ensure that
29974135Sgd78059 				 * next time we do not report a message
29984135Sgd78059 				 * i.e. we drop out of the bottom
29994135Sgd78059 				 */
30004135Sgd78059 				fault_cnt = BSC_PROBE_FAULT_LIMIT + 1;
30014135Sgd78059 				ssp->event_sleep = B_TRUE;
30024135Sgd78059 			} else if (fault_cnt < BSC_PROBE_FAULT_LIMIT) {
30034135Sgd78059 				if (bscv_faulty(ssp)) {
30044135Sgd78059 					poll_period = BSC_EVENT_POLL_FAULTY;
30054135Sgd78059 					/*
30064135Sgd78059 					 * No recovery messages in this case
30074135Sgd78059 					 * because there was never a fault
30084135Sgd78059 					 * message here.
30094135Sgd78059 					 */
30104135Sgd78059 					fault_cnt = 0;
30114135Sgd78059 				} else {
30124135Sgd78059 					/* Getting ready to explode */
30134135Sgd78059 					fault_cnt++;
30144135Sgd78059 					cmn_err(CE_WARN,
30154135Sgd78059 					    "!bscv_event_daemon had fault 0x%x",
30164135Sgd78059 					    fault);
30174135Sgd78059 				}
30184135Sgd78059 				ssp->event_sleep = B_TRUE;
30194135Sgd78059 			}
30204135Sgd78059 			bscv_exit(ssp);
30214135Sgd78059 			mutex_enter(&ssp->task_mu);
30224135Sgd78059 		}
30234135Sgd78059 
30244135Sgd78059 #if defined(__i386) || defined(__amd64)
30254135Sgd78059 		/*
30264135Sgd78059 		 * we have no platmod hook on Solaris x86 to report
30274135Sgd78059 		 * a change to the nodename so we keep a copy so
30284135Sgd78059 		 * we can detect a change and request that the bsc
30294135Sgd78059 		 * be updated when appropriate.
30304135Sgd78059 		 */
30314135Sgd78059 		if (strcmp(ssp->last_nodename, utsname.nodename) != 0) {
30324135Sgd78059 
30339644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'X', "bscv_event_daemon",
30344135Sgd78059 			    "utsname.nodename='%s' possible change detected",
30354135Sgd78059 			    utsname.nodename);
30364135Sgd78059 			ssp->nodename_change = B_TRUE;
30374135Sgd78059 			(void) strncpy(ssp->last_nodename, utsname.nodename,
30385107Seota 			    sizeof (ssp->last_nodename));
30394135Sgd78059 			/* enforce null termination */
30404135Sgd78059 			ssp->last_nodename[sizeof (ssp->last_nodename) - 1] =
30414135Sgd78059 			    '\0';
30424135Sgd78059 		}
30434135Sgd78059 #endif /* __i386 || __amd64 */
30444135Sgd78059 
30454135Sgd78059 		if (((ssp->task_flags & TASK_PAUSE_FLG) == 0) &&
30464135Sgd78059 		    fault_cnt == 0 && ssp->cssp_prog == B_FALSE &&
30474135Sgd78059 		    (ssp->event_waiting || ssp->status_change ||
30485107Seota 		    ssp->nodename_change || ssp->watchdog_change)) {
30494135Sgd78059 
30504135Sgd78059 			do_events = ssp->event_waiting;
30514135Sgd78059 			ssp->event_waiting = B_FALSE;
30524135Sgd78059 			ssp->task_flags |= do_events ?
30535107Seota 			    TASK_EVENT_PENDING_FLG : 0;
30544135Sgd78059 			do_status = ssp->status_change;
30554135Sgd78059 			ssp->status_change = B_FALSE;
30564135Sgd78059 			do_nodename = ssp->nodename_change;
30574135Sgd78059 			ssp->nodename_change = B_FALSE;
30584135Sgd78059 			do_watchdog = ssp->watchdog_change;
30594135Sgd78059 			if (ssp->watchdog_change) {
30604135Sgd78059 				ssp->watchdog_change = B_FALSE;
30614135Sgd78059 			}
30624135Sgd78059 
30634135Sgd78059 			mutex_exit(&ssp->task_mu);
30644135Sgd78059 			/*
30654135Sgd78059 			 * We must not hold task_mu whilst processing
30664135Sgd78059 			 * events because this can lead to priority
30674135Sgd78059 			 * inversion and hence our interrupts getting
30684135Sgd78059 			 * locked out.
30694135Sgd78059 			 */
30704135Sgd78059 			bscv_enter(ssp);
30714135Sgd78059 			if (do_events) {
30724135Sgd78059 				bscv_event_process(ssp, do_events);
30734135Sgd78059 			}
30744135Sgd78059 			if (do_nodename) {
30759644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
30764135Sgd78059 				    "do_nodename task");
30774135Sgd78059 				bscv_setup_hostname(ssp);
30784135Sgd78059 			}
30794135Sgd78059 			if (do_watchdog) {
30809644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
30814135Sgd78059 				    "do_watchdog task");
30824135Sgd78059 				bscv_setup_watchdog(ssp);
30834135Sgd78059 			}
30844135Sgd78059 			/*
30854135Sgd78059 			 * Pending status changes are dealt with last because
30864135Sgd78059 			 * if we see that the BSC is about to be programmed,
30874135Sgd78059 			 * then it will expect us to to quiescent in the
30884135Sgd78059 			 * first second so it can cleanly tear down its comms
30894135Sgd78059 			 * protocols; this takes ~100 ms.
30904135Sgd78059 			 */
30914135Sgd78059 			if (do_status) {
30924135Sgd78059 				bscv_get_state_changes(ssp);
30934135Sgd78059 			}
30944135Sgd78059 			if (bscv_session_error(ssp)) {
30954135Sgd78059 				/*
30964135Sgd78059 				 * Had fault during event session. We always
30974135Sgd78059 				 * sleep after one of these because there
30984135Sgd78059 				 * may be a problem with the lom which stops
30994135Sgd78059 				 * us doing useful work in the event daemon.
31004135Sgd78059 				 * If we don't sleep then we may livelock.
31014135Sgd78059 				 */
31029644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
31034135Sgd78059 				    "had session error - sleeping");
31044135Sgd78059 				ssp->event_sleep = B_TRUE;
31054135Sgd78059 			}
31064135Sgd78059 			bscv_exit(ssp);
31074135Sgd78059 
31084135Sgd78059 			mutex_enter(&ssp->task_mu);
31094135Sgd78059 
31104135Sgd78059 			if (ssp->task_flags & TASK_EVENT_PENDING_FLG) {
31114135Sgd78059 				/*
31124135Sgd78059 				 * We have read any events which were
31134135Sgd78059 				 * pending. Let the consumer continue.
31144135Sgd78059 				 * Ignore the race condition with new events
31154135Sgd78059 				 * arriving - just let the consumer have
31164135Sgd78059 				 * whatever was pending when they asked.
31174135Sgd78059 				 */
31184135Sgd78059 				ssp->event_active_count++;
31194135Sgd78059 				ssp->task_flags &= ~(TASK_EVENT_PENDING_FLG |
31204135Sgd78059 				    TASK_EVENT_CONSUMER_FLG);
31214135Sgd78059 				cv_broadcast(&ssp->task_evnt_cv);
31224135Sgd78059 			}
31234135Sgd78059 		} else {
31244135Sgd78059 			/* There was nothing to do - sleep */
31254135Sgd78059 			ssp->event_sleep = B_TRUE;
31264135Sgd78059 		}
31274135Sgd78059 
31284135Sgd78059 		if (ssp->event_sleep) {
31294135Sgd78059 			ssp->task_flags |= TASK_SLEEPING_FLG;
31304135Sgd78059 			/* Sleep until there is something to do */
3131*11066Srafael.vanoni@sun.com 			(void) cv_reltimedwait(&ssp->task_cv,
3132*11066Srafael.vanoni@sun.com 			    &ssp->task_mu, poll_period, TR_CLOCK_TICK);
31334135Sgd78059 			ssp->task_flags &= ~TASK_SLEEPING_FLG;
31344135Sgd78059 			ssp->event_sleep = B_FALSE;
31354135Sgd78059 		}
31364135Sgd78059 	}
31374135Sgd78059 
31384135Sgd78059 	if (ssp->task_flags & TASK_EVENT_CONSUMER_FLG) {
31394135Sgd78059 		/*
31404135Sgd78059 		 * We are going away so wake up any event consumer.
31414135Sgd78059 		 * Pretend that any pending events have been processed.
31424135Sgd78059 		 */
31434135Sgd78059 		ssp->event_active_count += 2;
31444135Sgd78059 		cv_broadcast(&ssp->task_evnt_cv);
31454135Sgd78059 	}
31464135Sgd78059 
31474135Sgd78059 	ASSERT(!(ssp->task_flags & TASK_EVENT_PENDING_FLG));
31484135Sgd78059 	ssp->task_flags &=
31495107Seota 	    ~(TASK_STOP_FLG | TASK_ALIVE_FLG | TASK_EVENT_CONSUMER_FLG);
31504135Sgd78059 	mutex_exit(&ssp->task_mu);
31514135Sgd78059 
31529644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_event_daemon",
31534135Sgd78059 	    "exiting.");
31544135Sgd78059 }
31554135Sgd78059 
31564135Sgd78059 /*
31574135Sgd78059  * function	- bscv_start_event_daemon
31584135Sgd78059  * description	- Create the event daemon thread.
31594135Sgd78059  * inputs	- LOM soft state structure pointer
31604135Sgd78059  * outputs	- none
31614135Sgd78059  */
31624135Sgd78059 static void
bscv_start_event_daemon(bscv_soft_state_t * ssp)31634135Sgd78059 bscv_start_event_daemon(bscv_soft_state_t *ssp)
31644135Sgd78059 {
31654135Sgd78059 	if (ssp->progress & BSCV_THREAD)
31664135Sgd78059 		return;
31674135Sgd78059 
31684135Sgd78059 	/* Start the event thread after the queue has started */
31694135Sgd78059 	(void) thread_create(NULL, 0, (void (*)())bscv_event_daemon, ssp,
31704135Sgd78059 	    0, &p0, TS_RUN, minclsyspri);
31714135Sgd78059 
31724135Sgd78059 	ssp->progress |= BSCV_THREAD;
31734135Sgd78059 }
31744135Sgd78059 
31754135Sgd78059 /*
31764135Sgd78059  * function	- bscv_stop_event_daemon
31774135Sgd78059  * description	- Attempt to stop the event daemon thread.
31784135Sgd78059  * inputs	- LOM soft state structure pointer
31794135Sgd78059  * outputs	- DDI_SUCCESS OR DDI_FAILURE
31804135Sgd78059  */
31814135Sgd78059 static int
bscv_stop_event_daemon(bscv_soft_state_t * ssp)31824135Sgd78059 bscv_stop_event_daemon(bscv_soft_state_t *ssp)
31834135Sgd78059 {
31844135Sgd78059 	int try;
31854135Sgd78059 	int res = DDI_SUCCESS;
31864135Sgd78059 
31874135Sgd78059 	mutex_enter(&ssp->task_mu);
31884135Sgd78059 
31894135Sgd78059 	/* Wait for task daemon to stop running. */
31904135Sgd78059 	for (try = 0;
31915107Seota 	    ((ssp->task_flags & TASK_ALIVE_FLG) && try < 10);
31925107Seota 	    try++) {
31934135Sgd78059 		/* Signal that the task daemon should stop */
31944135Sgd78059 		ssp->task_flags |= TASK_STOP_FLG;
31954135Sgd78059 		cv_signal(&ssp->task_cv);
31964135Sgd78059 		/* Release task daemon lock. */
31974135Sgd78059 		mutex_exit(&ssp->task_mu);
31984135Sgd78059 		/*
31994135Sgd78059 		 * TODO - when the driver is modified to support
32004135Sgd78059 		 * system suspend or if this routine gets called
32014135Sgd78059 		 * during panic we should use drv_usecwait() rather
32024135Sgd78059 		 * than delay in those circumstances.
32034135Sgd78059 		 */
32044135Sgd78059 		delay(drv_usectohz(1000000));
32054135Sgd78059 		mutex_enter(&ssp->task_mu);
32064135Sgd78059 	}
32074135Sgd78059 
32084135Sgd78059 	if (ssp->task_flags & TASK_ALIVE_FLG) {
32094135Sgd78059 		res = DDI_FAILURE;
32104135Sgd78059 	}
32114135Sgd78059 	mutex_exit(&ssp->task_mu);
32124135Sgd78059 
32134135Sgd78059 	return (res);
32144135Sgd78059 }
32154135Sgd78059 
32164135Sgd78059 /*
32174135Sgd78059  * function	- bscv_pause_event_daemon
32184135Sgd78059  * description	- Attempt to pause the event daemon thread.
32194135Sgd78059  * inputs	- LOM soft state structure pointer
32204135Sgd78059  * outputs	- DDI_SUCCESS OR DDI_FAILURE
32214135Sgd78059  */
32224135Sgd78059 static int
bscv_pause_event_daemon(bscv_soft_state_t * ssp)32234135Sgd78059 bscv_pause_event_daemon(bscv_soft_state_t *ssp)
32244135Sgd78059 {
32254135Sgd78059 	int try;
32264135Sgd78059 
32274135Sgd78059 	if (!(ssp->progress & BSCV_THREAD)) {
32284135Sgd78059 		/* Nothing to do */
32294135Sgd78059 		return (BSCV_SUCCESS);
32304135Sgd78059 	}
32314135Sgd78059 
32329644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_pause_event_daemon",
32334135Sgd78059 	    "Attempting to pause event daemon");
32344135Sgd78059 
32354135Sgd78059 	mutex_enter(&ssp->task_mu);
32364135Sgd78059 	/* Signal that the task daemon should pause */
32374135Sgd78059 	ssp->task_flags |= TASK_PAUSE_FLG;
32384135Sgd78059 
32394135Sgd78059 	/* Wait for task daemon to pause. */
32404135Sgd78059 	for (try = 0;
32415107Seota 	    (!(ssp->task_flags & TASK_SLEEPING_FLG) &&
32425107Seota 	    (ssp->task_flags & TASK_ALIVE_FLG) &&
32435107Seota 	    try < 10);
32445107Seota 	    try++) {
32454135Sgd78059 		/* Paranoia */
32464135Sgd78059 		ssp->task_flags |= TASK_PAUSE_FLG;
32474135Sgd78059 		cv_signal(&ssp->task_cv);
32484135Sgd78059 		/* Release task daemon lock. */
32494135Sgd78059 		mutex_exit(&ssp->task_mu);
32504135Sgd78059 		delay(drv_usectohz(1000000));
32514135Sgd78059 		mutex_enter(&ssp->task_mu);
32524135Sgd78059 	}
32534135Sgd78059 	if ((ssp->task_flags & TASK_SLEEPING_FLG) ||
32544135Sgd78059 	    !(ssp->task_flags & TASK_ALIVE_FLG)) {
32554135Sgd78059 		mutex_exit(&ssp->task_mu);
32569644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'D', "bscv_pause_event_daemon",
32574135Sgd78059 		    "Pause event daemon - success");
32584135Sgd78059 		return (BSCV_SUCCESS);
32594135Sgd78059 	}
32604135Sgd78059 	mutex_exit(&ssp->task_mu);
32619644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_pause_event_daemon",
32624135Sgd78059 	    "Pause event daemon - failed");
32634135Sgd78059 	return (BSCV_FAILURE);
32644135Sgd78059 }
32654135Sgd78059 
32664135Sgd78059 /*
32674135Sgd78059  * function	- bscv_resume_event_daemon
32684135Sgd78059  * description	- Resumethe event daemon thread.
32694135Sgd78059  * inputs	- LOM soft state structure pointer
32704135Sgd78059  * outputs	- None.
32714135Sgd78059  */
32724135Sgd78059 static void
bscv_resume_event_daemon(bscv_soft_state_t * ssp)32734135Sgd78059 bscv_resume_event_daemon(bscv_soft_state_t *ssp)
32744135Sgd78059 {
32754135Sgd78059 	if (!(ssp->progress & BSCV_THREAD)) {
32764135Sgd78059 		/* Nothing to do */
32774135Sgd78059 		return;
32784135Sgd78059 	}
32794135Sgd78059 
32804135Sgd78059 	mutex_enter(&ssp->task_mu);
32814135Sgd78059 	/* Allow the task daemon to resume event processing */
32824135Sgd78059 	ssp->task_flags &= ~TASK_PAUSE_FLG;
32834135Sgd78059 	cv_signal(&ssp->task_cv);
32844135Sgd78059 	mutex_exit(&ssp->task_mu);
32854135Sgd78059 
32869644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_pause_event_daemon",
32874135Sgd78059 	    "Event daemon resumed");
32884135Sgd78059 }
32894135Sgd78059 
32904135Sgd78059 /*
32914135Sgd78059  * function	- bscv_event_process
32924135Sgd78059  * description	- process (report) events
32934135Sgd78059  * inputs	- Soft state ptr, process event request
32944135Sgd78059  * outputs	- none
32954135Sgd78059  */
32964135Sgd78059 static void
bscv_event_process(bscv_soft_state_t * ssp,boolean_t do_events)32974135Sgd78059 bscv_event_process(bscv_soft_state_t *ssp, boolean_t do_events)
32984135Sgd78059 {
32994135Sgd78059 	uint32_t currptr;
33004135Sgd78059 	unsigned int count;
33014135Sgd78059 
33024135Sgd78059 	/* Raw values read from the lom */
33034135Sgd78059 	uint8_t evcount;
33044135Sgd78059 	uint16_t logptr;
33054135Sgd78059 
33064135Sgd78059 	lom_event_t event;
33074135Sgd78059 
33084135Sgd78059 	if (do_events) {
33094135Sgd78059 		/*
33104135Sgd78059 		 * Read count, next event ptr MSB,LSB. Note a read of count
33114135Sgd78059 		 * latches values for the next event ptr
33124135Sgd78059 		 */
33134135Sgd78059 		evcount = bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS);
33144135Sgd78059 		logptr = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI);
33154135Sgd78059 
33164135Sgd78059 		/* Sanity check the values from the lom */
33174135Sgd78059 		count = bscv_event_validate(ssp, logptr, evcount);
33184135Sgd78059 
33194135Sgd78059 		if (count == -1) {
33204135Sgd78059 			/*
33214135Sgd78059 			 * Nothing to do - or badly configured event log.
33224135Sgd78059 			 * We really do not want to touch the lom in this
33234135Sgd78059 			 * case because any data that we access may be bad!
33244135Sgd78059 			 * This differs from zero because if we have zero
33254135Sgd78059 			 * to read the lom probably things that unread is
33264135Sgd78059 			 * non-zero and we want that to be set to zero!
33274135Sgd78059 			 * Signal event fault to make the thread wait
33284135Sgd78059 			 * before attempting to re-read the log.
33294135Sgd78059 			 */
33304135Sgd78059 			ssp->event_sleep = B_TRUE;
33314135Sgd78059 
33324135Sgd78059 			goto logdone;
33334135Sgd78059 		}
33344135Sgd78059 		if (ssp->event_fault_reported) {
33354135Sgd78059 			/* Clear down any old status - things are fixed */
33364135Sgd78059 			cmn_err(CE_NOTE, "Event pointer fault recovered.");
33374135Sgd78059 			ssp->event_fault_reported = B_FALSE;
33384135Sgd78059 		}
33394135Sgd78059 
33404135Sgd78059 		/* Compute the first entry that we need to read. */
33414135Sgd78059 		currptr = logptr - ssp->eventlog_start;
33424135Sgd78059 		currptr += ssp->eventlog_size;
33434135Sgd78059 		currptr -= (count * sizeof (event));
33444135Sgd78059 		currptr %= ssp->eventlog_size;
33454135Sgd78059 		currptr += ssp->eventlog_start;
33464135Sgd78059 
33479644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_event_process",
33484135Sgd78059 		    "processing %d events from 0x%x in 0x%x:0x%x",
33494135Sgd78059 		    count, currptr,
33504135Sgd78059 		    ssp->eventlog_start,
33514135Sgd78059 		    ssp->eventlog_start + ssp->eventlog_size);
33524135Sgd78059 
33534135Sgd78059 		for (; count > 0; count--) {
33544135Sgd78059 			/* Ensure window is positioned correctly */
33554135Sgd78059 			if (bscv_eerw(ssp, currptr, (uint8_t *)&event,
33564135Sgd78059 			    sizeof (event), B_FALSE /* read */) != 0) {
33574135Sgd78059 				/* Fault reading data - stop */
33584135Sgd78059 				break;
33594135Sgd78059 			}
33604135Sgd78059 
33614135Sgd78059 			bscv_event_process_one(ssp, &event);
33624135Sgd78059 			bscv_sysevent(ssp, &event);
33634135Sgd78059 
33644135Sgd78059 			currptr += sizeof (event);
33654135Sgd78059 			if (currptr >= ssp->eventlog_start +
33664135Sgd78059 			    ssp->eventlog_size) {
33674135Sgd78059 				currptr = ssp->eventlog_start;
33684135Sgd78059 			}
33694135Sgd78059 		}
33704135Sgd78059 		/*
33714135Sgd78059 		 * Clear event count - write the evcount value to remove that
33724135Sgd78059 		 * many from the unread total.
33734135Sgd78059 		 * Adjust the value to reflect how many we have left to
33744135Sgd78059 		 * read just in case we had a failure reading events.
33754135Sgd78059 		 */
33764135Sgd78059 		if (count == 0) {
33774135Sgd78059 			/*EMPTY*/
33784135Sgd78059 			ASSERT(logptr == currptr);
33794135Sgd78059 		} else if (count > evcount) {
33804135Sgd78059 			evcount = 0;
33814135Sgd78059 		} else {
33824135Sgd78059 			evcount -= count;
33834135Sgd78059 		}
33844135Sgd78059 		bscv_put8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS, evcount);
33854135Sgd78059 		    /* Remember where we were for next time */
33864135Sgd78059 		ssp->oldeeptr = currptr;
33874135Sgd78059 		ssp->oldeeptr_valid = B_TRUE;
33884135Sgd78059 logdone:
33894135Sgd78059 		;
33904135Sgd78059 	}
33914135Sgd78059 }
33924135Sgd78059 
33934135Sgd78059 /*
33944135Sgd78059  * function	- bscv_event_validate
33954135Sgd78059  * description	- validate the event data supplied by the lom and determine
33964135Sgd78059  *		  how many (if any) events to read.
33974135Sgd78059  *		  This function performs complex checks to ensure that
33984135Sgd78059  *		  events are not lost due to lom resets or host resets.
33994135Sgd78059  *		  A combination of lom reset and host reset (i.e. power fail)
34004135Sgd78059  *		  may cause some events to not be reported.
34014135Sgd78059  * inputs	- Soft state ptr, next event pointer, number of unread events.
34024135Sgd78059  * outputs	- the number of events to read. -1 on error.
34034135Sgd78059  *		  zero is a valid value because it forces the loms unread
34044135Sgd78059  *		  count to be cleared.
34054135Sgd78059  */
34064135Sgd78059 static int
bscv_event_validate(bscv_soft_state_t * ssp,uint32_t newptr,uint8_t unread)34074135Sgd78059 bscv_event_validate(bscv_soft_state_t *ssp, uint32_t newptr, uint8_t unread)
34084135Sgd78059 {
34094135Sgd78059 	uint32_t oldptr;
34104135Sgd78059 	unsigned int count;
34114135Sgd78059 
34124135Sgd78059 	if (!bscv_window_setup(ssp)) {
34134135Sgd78059 		/* Problem with lom eeprom setup we cannot do anything */
34144135Sgd78059 		return (-1);
34154135Sgd78059 	}
34164135Sgd78059 
34174135Sgd78059 	/* Sanity check the event pointers */
34184135Sgd78059 	if ((newptr < ssp->eventlog_start) ||
34194135Sgd78059 	    (newptr >= (ssp->eventlog_start + ssp->eventlog_size))) {
34204135Sgd78059 		if (!ssp->event_fault_reported) {
34214135Sgd78059 			cmn_err(CE_WARN, "Event pointer out of range. "
34225107Seota 			    "Cannot read events.");
34234135Sgd78059 			ssp->event_fault_reported = B_TRUE;
34244135Sgd78059 		}
34254135Sgd78059 		return (-1);
34264135Sgd78059 	}
34274135Sgd78059 	oldptr = ssp->oldeeptr;
34284135Sgd78059 	/* Now sanity check log pointer against count */
34294135Sgd78059 	if (newptr < oldptr) {
34304135Sgd78059 		/*
34314135Sgd78059 		 * Must have wrapped add eventlog_size to get the
34324135Sgd78059 		 * correct relative values - this makes the checks
34334135Sgd78059 		 * below work!
34344135Sgd78059 		 */
34354135Sgd78059 		newptr += ssp->eventlog_size;
34364135Sgd78059 	}
34374135Sgd78059 	if (!ssp->oldeeptr_valid) {
34384135Sgd78059 		/* We have just started up - we have to trust lom */
34394135Sgd78059 		count = unread;
34404135Sgd78059 	} else if ((unread == 0) && (newptr == oldptr)) {
34414135Sgd78059 		/* Nothing to do - we were just polling */
34424135Sgd78059 		return (-1);
34434135Sgd78059 	} else if (oldptr + (unread * sizeof (lom_event_t)) == newptr) {
34444135Sgd78059 		/* Ok - got as many events as we expected */
34454135Sgd78059 		count = unread;
34464135Sgd78059 	} else if (oldptr + (unread * sizeof (lom_event_t)) > newptr) {
34474135Sgd78059 		/*
34484135Sgd78059 		 * Errrm more messages than there should have been.
34494135Sgd78059 		 * Possible causes:
34504135Sgd78059 		 * 1.	the event log has filled - we have been
34514135Sgd78059 		 *	away for a long time
34524135Sgd78059 		 * 2.	software bug in lom or driver.
34534135Sgd78059 		 * 3.	something that I haven't thought of!
34544135Sgd78059 		 * Always warn about this we should really never
34554135Sgd78059 		 * see it!
34564135Sgd78059 		 */
34574135Sgd78059 		count = (newptr - oldptr) / sizeof (lom_event_t);
34589644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_event_process",
34594135Sgd78059 		    "bscv_event_process: lom reported "
34604135Sgd78059 		    "more events (%d) than expected (%d).",
34614135Sgd78059 		    unread, count);
34624135Sgd78059 		cmn_err(CE_CONT, "only processing %d events", count);
34634135Sgd78059 	} else {
34644135Sgd78059 		/* Less messages - perhaps the lom has been reset */
34654135Sgd78059 		count = (newptr - oldptr) / sizeof (lom_event_t);
34669644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_event_process",
34674135Sgd78059 		    "lom reported less events (%d) than expected (%d)"
34684135Sgd78059 		    " - the lom may have been reset",
34694135Sgd78059 		    unread, count);
34704135Sgd78059 	}
34714135Sgd78059 	/* Whatever happens only read a maximum of 255 entries */
34724135Sgd78059 	if ((count >= 0xff)) {
34734135Sgd78059 		cmn_err(CE_WARN,
34744135Sgd78059 		    "bscv_event_process: too many events (%d) to "
34754135Sgd78059 		    "process - some may have been lost", count);
34764135Sgd78059 		count = 0xff;
34774135Sgd78059 	}
34784135Sgd78059 	return (count);
34794135Sgd78059 }
34804135Sgd78059 
34814135Sgd78059 /*
34824135Sgd78059  * function	- bscv_event_process_one
34834135Sgd78059  * description	- reports on state changes to the host.
34844135Sgd78059  *
34854135Sgd78059  * inputs	- LOM soft state structure pointer.
34864135Sgd78059  *
34874135Sgd78059  * outputs	- none.
34884135Sgd78059  */
34894135Sgd78059 
34904135Sgd78059 static void
bscv_event_process_one(bscv_soft_state_t * ssp,lom_event_t * event)34914135Sgd78059 bscv_event_process_one(bscv_soft_state_t *ssp, lom_event_t *event)
34924135Sgd78059 {
34934135Sgd78059 	int level;
34944135Sgd78059 	char eventstr[100];
34954135Sgd78059 	int msg_type = 0;
34964135Sgd78059 
34974135Sgd78059 	if (bscv_is_null_event(ssp, event)) {
34984135Sgd78059 		/* Cleared entry - do not report it */
34994135Sgd78059 		return;
35004135Sgd78059 	}
35014135Sgd78059 
35024135Sgd78059 	level = bscv_level_of_event(event);
35034135Sgd78059 
35044135Sgd78059 	switch (level) {
35054135Sgd78059 	default:
35064135Sgd78059 		msg_type = CE_NOTE;
35074135Sgd78059 		break;
35084135Sgd78059 
35094135Sgd78059 	case EVENT_LEVEL_FATAL:
35104135Sgd78059 	case EVENT_LEVEL_FAULT:
35114135Sgd78059 		msg_type = CE_WARN;
35124135Sgd78059 		break;
35134135Sgd78059 	}
35144135Sgd78059 
35154135Sgd78059 	bscv_build_eventstring(ssp, event, eventstr, eventstr +
35164135Sgd78059 	    sizeof (eventstr));
35174135Sgd78059 
35184135Sgd78059 	if (level <= ssp->reporting_level) {
35194135Sgd78059 		/*
35204135Sgd78059 		 * The message is important enough to be shown on the console
35214135Sgd78059 		 * as well as the log.
35224135Sgd78059 		 */
35234135Sgd78059 		cmn_err(msg_type, "%s", eventstr);
35244135Sgd78059 	} else {
35254135Sgd78059 		/*
35264135Sgd78059 		 * The message goes only to the log.
35274135Sgd78059 		 */
35284135Sgd78059 		cmn_err(msg_type, "!%s", eventstr);
35294135Sgd78059 	}
35304135Sgd78059 }
35314135Sgd78059 
35324135Sgd78059 /*
35334135Sgd78059  * time formats
35344135Sgd78059  *
35354135Sgd78059  * The BSC represents times as seconds since epoch 1970.  Currently it gives
35364135Sgd78059  * us 32 bits, unsigned.  In the future this might change to a 64-bit count,
35374135Sgd78059  * to allow a greater range.
35384135Sgd78059  *
35394135Sgd78059  * Timestamp values below BSC_TIME_SANITY do not represent an absolute time,
35404135Sgd78059  * but instead represent an offset from the last reset.  This must be
35414135Sgd78059  * borne in mind by output routines.
35424135Sgd78059  */
35434135Sgd78059 
35444135Sgd78059 typedef uint32_t bsctime_t;
35454135Sgd78059 
35464135Sgd78059 #define	BSC_TIME_SANITY		1000000000
35474135Sgd78059 
35484135Sgd78059 /*
35494135Sgd78059  * render a formatted time for display
35504135Sgd78059  */
35514135Sgd78059 
35524135Sgd78059 static size_t
bscv_event_snprintgmttime(char * buf,size_t bufsz,todinfo_t t)35534135Sgd78059 bscv_event_snprintgmttime(char *buf, size_t bufsz, todinfo_t t)
35544135Sgd78059 {
35554135Sgd78059 	int year;
35564135Sgd78059 
35574135Sgd78059 	/* tod_year is base 1900 so this code needs to adjust */
35584135Sgd78059 	year = 1900 + t.tod_year;
35594135Sgd78059 
35604135Sgd78059 	return (snprintf(buf, bufsz, "%04d-%02d-%02d %02d:%02d:%02dZ",
35615107Seota 	    year, t.tod_month, t.tod_day, t.tod_hour,
35625107Seota 	    t.tod_min, t.tod_sec));
35634135Sgd78059 }
35644135Sgd78059 
35654135Sgd78059 /*
35664135Sgd78059  * function	- bscv_build_eventstring
35674135Sgd78059  * description	- reports on state changes to the host.
35684135Sgd78059  *
35694135Sgd78059  * inputs	- LOM soft state structure pointer.
35704135Sgd78059  *
35714135Sgd78059  * outputs	- none.
35724135Sgd78059  */
35734135Sgd78059 
35744135Sgd78059 static void
bscv_build_eventstring(bscv_soft_state_t * ssp,lom_event_t * event,char * buf,char * bufend)35754135Sgd78059 bscv_build_eventstring(bscv_soft_state_t *ssp, lom_event_t *event,
35764135Sgd78059     char *buf, char *bufend)
35774135Sgd78059 {
35784135Sgd78059 	uint8_t subsystem;
35794135Sgd78059 	uint8_t eventtype;
35804135Sgd78059 	bsctime_t bsctm;
35814135Sgd78059 
35829644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'S', "bscv_build_eventstring", "event %2x%2x%2x%2x",
35834135Sgd78059 	    event->ev_subsys, event->ev_event,
35844135Sgd78059 	    event->ev_resource, event->ev_detail);
35859644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'S', "bscv_build_eventstring", "time %2x%2x%2x%2x",
35864135Sgd78059 	    event->ev_data[0], event->ev_data[1],
35874135Sgd78059 	    event->ev_data[2], event->ev_data[3]);
35884135Sgd78059 
35894135Sgd78059 	/*
35904135Sgd78059 	 * We accept bad subsystems and event type codes here.
35914135Sgd78059 	 * The code decodes as much as possible and then produces
35924135Sgd78059 	 * suitable output.
35934135Sgd78059 	 */
35944135Sgd78059 	subsystem = EVENT_DECODE_SUBSYS(event->ev_subsys);
35954135Sgd78059 	eventtype = event->ev_event;
35964135Sgd78059 
35974135Sgd78059 	/* time */
35984135Sgd78059 	bsctm = (((uint32_t)event->ev_data[0]) << 24) |
35995107Seota 	    (((uint32_t)event->ev_data[1]) << 16) |
36005107Seota 	    (((uint32_t)event->ev_data[2]) << 8) |
36015107Seota 	    ((uint32_t)event->ev_data[3]);
36024135Sgd78059 	if (bsctm < BSC_TIME_SANITY) {
36034135Sgd78059 		/* offset */
36044135Sgd78059 		buf += snprintf(buf, bufend-buf, "+P%dd%02dh%02dm%02ds",
36055107Seota 		    (int)(bsctm/86400), (int)(bsctm/3600%24),
36065107Seota 		    (int)(bsctm/60%60), (int)(bsctm%60));
36074135Sgd78059 	} else {
36084135Sgd78059 		/* absolute time */
36094135Sgd78059 		mutex_enter(&tod_lock);
36104135Sgd78059 		buf += bscv_event_snprintgmttime(buf, bufend-buf,
36115107Seota 		    utc_to_tod(bsctm));
36124135Sgd78059 		mutex_exit(&tod_lock);
36134135Sgd78059 	}
36144135Sgd78059 	buf += snprintf(buf, bufend-buf, " ");
36154135Sgd78059 
36164135Sgd78059 	/* subsysp */
36174135Sgd78059 	if (subsystem <
36184135Sgd78059 	    (sizeof (eventSubsysStrings)/sizeof (*eventSubsysStrings))) {
36194135Sgd78059 		buf += snprintf(buf, bufend - buf, "%s",
36204135Sgd78059 		    eventSubsysStrings[subsystem]);
36214135Sgd78059 	} else {
36224135Sgd78059 		buf += snprintf(buf, bufend - buf,
36234135Sgd78059 		    "unknown subsystem %d ", subsystem);
36244135Sgd78059 	}
36254135Sgd78059 
36264135Sgd78059 	/* resource */
36274135Sgd78059 	switch (subsystem) {
36284135Sgd78059 	case EVENT_SUBSYS_ALARM:
36294135Sgd78059 	case EVENT_SUBSYS_TEMP:
36304135Sgd78059 	case EVENT_SUBSYS_OVERTEMP:
36314135Sgd78059 	case EVENT_SUBSYS_FAN:
36324135Sgd78059 	case EVENT_SUBSYS_SUPPLY:
36334135Sgd78059 	case EVENT_SUBSYS_BREAKER:
36344135Sgd78059 	case EVENT_SUBSYS_PSU:
36354135Sgd78059 		buf += snprintf(buf, bufend - buf, "%d ", event->ev_resource);
36364135Sgd78059 		break;
36374135Sgd78059 	case EVENT_SUBSYS_LED:
36384135Sgd78059 		buf += snprintf(buf, bufend - buf, "%s ", bscv_get_label(
36394135Sgd78059 		    ssp->led_names, MAX_LED_ID, event->ev_resource - 1));
36404135Sgd78059 		break;
36414135Sgd78059 	default:
36424135Sgd78059 		break;
36434135Sgd78059 	}
36444135Sgd78059 
36454135Sgd78059 	/* fatal */
36464135Sgd78059 	if (event->ev_subsys & EVENT_MASK_FAULT) {
36474135Sgd78059 		if (event->ev_subsys & EVENT_MASK_FATAL) {
36484135Sgd78059 			buf += snprintf(buf, bufend - buf, "FATAL FAULT: ");
36494135Sgd78059 		} else {
36504135Sgd78059 			buf += snprintf(buf, bufend - buf, "FAULT: ");
36514135Sgd78059 		}
36524135Sgd78059 	}
36534135Sgd78059 
36544135Sgd78059 	/* eventp */
36554135Sgd78059 	if (eventtype <
36564135Sgd78059 	    (sizeof (eventTypeStrings)/sizeof (*eventTypeStrings))) {
36574135Sgd78059 		buf += snprintf(buf, bufend - buf, "%s",
36584135Sgd78059 		    eventTypeStrings[eventtype]);
36594135Sgd78059 	} else {
36604135Sgd78059 		buf += snprintf(buf, bufend - buf,
36614135Sgd78059 		    "unknown event 0x%02x%02x%02x%02x",
36624135Sgd78059 		    event->ev_subsys, event->ev_event,
36634135Sgd78059 		    event->ev_resource, event->ev_detail);
36644135Sgd78059 	}
36654135Sgd78059 
36664135Sgd78059 	/* detail */
36674135Sgd78059 	switch (subsystem) {
36684135Sgd78059 	case EVENT_SUBSYS_TEMP:
36694135Sgd78059 		if ((eventtype != EVENT_RECOVERED) &&
36705107Seota 		    eventtype != EVENT_DEVICE_INACCESSIBLE) {
36714135Sgd78059 			buf += snprintf(buf, bufend - buf, " - %d degC",
36724135Sgd78059 			    (int8_t)event->ev_detail);
36734135Sgd78059 		}
36744135Sgd78059 		break;
36754135Sgd78059 	case EVENT_SUBSYS_FAN:
36764135Sgd78059 		if (eventtype == EVENT_FAILED) {
36774135Sgd78059 			buf += snprintf(buf, bufend - buf,
36784135Sgd78059 			    " %d%%", event->ev_detail);
36794135Sgd78059 		}
36804135Sgd78059 		break;
36814135Sgd78059 	case EVENT_SUBSYS_LOM:
36824135Sgd78059 		switch (eventtype) {
36834135Sgd78059 		case EVENT_FLASH_DOWNLOAD:
36844135Sgd78059 			buf += snprintf(buf, bufend - buf,
36854135Sgd78059 			    ": v%d.%d to v%d.%d",
36864135Sgd78059 			    (event->ev_resource >> 4),
36874135Sgd78059 			    (event->ev_resource & 0x0f),
36884135Sgd78059 			    (event->ev_detail >> 4),
36894135Sgd78059 			    (event->ev_detail & 0x0f));
36904135Sgd78059 			break;
36914135Sgd78059 		case EVENT_WATCHDOG_TRIGGER:
36924135Sgd78059 			buf += snprintf(buf, bufend - buf,
36934135Sgd78059 			    event->ev_detail ? "- soft" : " - hard");
36944135Sgd78059 			break;
36954135Sgd78059 		case EVENT_UNEXPECTED_RESET:
36964135Sgd78059 			if (event->ev_detail &
36974135Sgd78059 			    LOM_UNEXPECTEDRESET_MASK_BADTRAP) {
36984135Sgd78059 				buf += snprintf(buf, bufend - buf,
36994135Sgd78059 				    " - unclaimed exception 0x%x",
37004135Sgd78059 				    event->ev_detail &
37014135Sgd78059 				    ~LOM_UNEXPECTEDRESET_MASK_BADTRAP);
37024135Sgd78059 			}
37034135Sgd78059 			break;
37044135Sgd78059 		case EVENT_RESET:
37054135Sgd78059 			switch (event->ev_detail) {
37064135Sgd78059 			case LOM_RESET_DETAIL_BYUSER:
37074135Sgd78059 				buf += snprintf(buf, bufend - buf, " by user");
37084135Sgd78059 				break;
37094135Sgd78059 			case LOM_RESET_DETAIL_REPROGRAMMING:
37105107Seota 				buf += snprintf(buf, bufend - buf,
37114135Sgd78059 				" after flash download");
37124135Sgd78059 				break;
37134135Sgd78059 			default:
37144135Sgd78059 				buf += snprintf(buf, bufend - buf,
37154135Sgd78059 				    " - unknown reason");
37164135Sgd78059 				break;
37174135Sgd78059 			}
37184135Sgd78059 			break;
37194135Sgd78059 		default:
37204135Sgd78059 			break;
37214135Sgd78059 		}
37224135Sgd78059 		break;
37234135Sgd78059 	case EVENT_SUBSYS_LED:
37244135Sgd78059 		switch (event->ev_detail) {
37254135Sgd78059 		case LOM_LED_STATE_OFF:
37264135Sgd78059 			buf += snprintf(buf, bufend - buf, ": OFF");
37274135Sgd78059 			break;
37284135Sgd78059 		case LOM_LED_STATE_ON_STEADY:
37294135Sgd78059 			buf += snprintf(buf, bufend - buf, ": ON");
37304135Sgd78059 			break;
37314135Sgd78059 		case LOM_LED_STATE_ON_FLASHING:
37324135Sgd78059 		case LOM_LED_STATE_ON_SLOWFLASH:
37334135Sgd78059 			buf += snprintf(buf, bufend - buf, ": BLINKING");
37344135Sgd78059 			break;
37354135Sgd78059 		case LOM_LED_STATE_INACCESSIBLE:
37364135Sgd78059 			buf += snprintf(buf, bufend - buf, ": inaccessible");
37374135Sgd78059 			break;
37384135Sgd78059 		case LOM_LED_STATE_STANDBY:
37394135Sgd78059 			buf += snprintf(buf, bufend - buf, ": standby");
37404135Sgd78059 			break;
37414135Sgd78059 		case LOM_LED_STATE_NOT_PRESENT:
37424135Sgd78059 			buf += snprintf(buf, bufend - buf, ": not present");
37434135Sgd78059 			break;
37444135Sgd78059 		default:
37454135Sgd78059 			buf += snprintf(buf, bufend - buf, ": 0x%x",
37464135Sgd78059 			    event->ev_resource);
37474135Sgd78059 			break;
37484135Sgd78059 		}
37494135Sgd78059 		break;
37504135Sgd78059 	case EVENT_SUBSYS_USER:
37514135Sgd78059 		switch (eventtype) {
37524135Sgd78059 		case EVENT_USER_ADDED:
37534135Sgd78059 		case EVENT_USER_REMOVED:
37544135Sgd78059 		case EVENT_USER_PERMSCHANGED:
37554135Sgd78059 		case EVENT_USER_LOGIN:
37564135Sgd78059 		case EVENT_USER_PASSWORD_CHANGE:
37574135Sgd78059 		case EVENT_USER_LOGINFAIL:
37584135Sgd78059 		case EVENT_USER_LOGOUT:
37594135Sgd78059 			buf += snprintf(buf, bufend - buf, " %d",
37604135Sgd78059 			    event->ev_resource);
37614135Sgd78059 		default:
37624135Sgd78059 			break;
37634135Sgd78059 		}
37644135Sgd78059 		break;
37654135Sgd78059 	case EVENT_SUBSYS_PSU:
37664135Sgd78059 		if (event->ev_detail & LOM_PSU_NOACCESS) {
37674135Sgd78059 			buf += snprintf(buf, bufend - buf, " - inaccessible");
37684135Sgd78059 		} else if ((event->ev_detail & LOM_PSU_STATUS_MASK)
37694135Sgd78059 		    == LOM_PSU_STATUS_MASK) {
37704135Sgd78059 			buf += snprintf(buf, bufend - buf, " - OK");
37714135Sgd78059 		} else {
37724135Sgd78059 			buf += snprintf(buf, bufend - buf, " -");
37734135Sgd78059 			/*
37744135Sgd78059 			 * If both inputs are seen to have failed then simply
37754135Sgd78059 			 * indicate that the PSU input has failed
37764135Sgd78059 			 */
37774135Sgd78059 			if (!(event->ev_detail &
37784135Sgd78059 			    (LOM_PSU_INPUT_A_OK | LOM_PSU_INPUT_B_OK))) {
37794135Sgd78059 				buf += snprintf(buf, bufend - buf, " Input");
37804135Sgd78059 			} else {
37814135Sgd78059 				/* At least one input is ok */
37824135Sgd78059 				if (!(event->ev_detail & LOM_PSU_INPUT_A_OK)) {
37834135Sgd78059 					buf += snprintf(buf, bufend - buf,
37844135Sgd78059 					    " InA");
37854135Sgd78059 				}
37864135Sgd78059 				if (!(event->ev_detail & LOM_PSU_INPUT_B_OK)) {
37874135Sgd78059 					buf += snprintf(buf, bufend - buf,
37884135Sgd78059 					    " InB");
37894135Sgd78059 				}
37904135Sgd78059 				/*
37914135Sgd78059 				 * Only flag an output error if an input is
37924135Sgd78059 				 * still present
37934135Sgd78059 				 */
37944135Sgd78059 				if (!(event->ev_detail & LOM_PSU_OUTPUT_OK)) {
37954135Sgd78059 					buf += snprintf(buf, bufend - buf,
37964135Sgd78059 					    " Output");
37974135Sgd78059 				}
37984135Sgd78059 			}
37994135Sgd78059 			buf += snprintf(buf, bufend - buf, " failed");
38004135Sgd78059 		}
38014135Sgd78059 		break;
38024135Sgd78059 	case EVENT_SUBSYS_NONE:
38034135Sgd78059 		if (eventtype == EVENT_FAULT_LED) {
38044135Sgd78059 			switch (event->ev_detail) {
38054135Sgd78059 			case 0:
38064135Sgd78059 				buf += snprintf(buf, bufend - buf, " - ON");
38074135Sgd78059 				break;
38084135Sgd78059 			case 255:
38094135Sgd78059 				buf += snprintf(buf, bufend - buf, " - OFF");
38104135Sgd78059 				break;
38114135Sgd78059 			default:
38124135Sgd78059 				buf += snprintf(buf, bufend - buf,
38134135Sgd78059 				    " - %dHz", event->ev_detail);
38144135Sgd78059 				break;
38154135Sgd78059 			}
38164135Sgd78059 		}
38174135Sgd78059 		break;
38184135Sgd78059 	case EVENT_SUBSYS_HOST:
38194135Sgd78059 		if (eventtype == EVENT_BOOTMODE_CHANGE) {
38204135Sgd78059 			switch (event->ev_detail &
38214135Sgd78059 			    ~EBUS_BOOTMODE_FORCE_CONSOLE) {
38224135Sgd78059 			case EBUS_BOOTMODE_FORCE_NOBOOT:
38234135Sgd78059 				buf += snprintf(buf, bufend - buf,
38244135Sgd78059 				    " - no boot");
38254135Sgd78059 				break;
38264135Sgd78059 			case EBUS_BOOTMODE_RESET_DEFAULT:
38274135Sgd78059 				buf += snprintf(buf, bufend - buf,
38284135Sgd78059 				    " - reset defaults");
38294135Sgd78059 				break;
38304135Sgd78059 			case EBUS_BOOTMODE_FULLDIAG:
38314135Sgd78059 				buf += snprintf(buf, bufend - buf,
38324135Sgd78059 				    " - full diag");
38334135Sgd78059 				break;
38344135Sgd78059 			case EBUS_BOOTMODE_SKIPDIAG:
38354135Sgd78059 				buf += snprintf(buf, bufend - buf,
38364135Sgd78059 				    " - skip diag");
38374135Sgd78059 				break;
38384135Sgd78059 			default:
38394135Sgd78059 				break;
38404135Sgd78059 			}
38414135Sgd78059 		}
38424135Sgd78059 		if (eventtype == EVENT_SCC_STATUS) {
38434135Sgd78059 			switch (event->ev_detail) {
38444135Sgd78059 			case 0:
38454135Sgd78059 				buf += snprintf(buf, bufend - buf,
38464135Sgd78059 				    " - inserted");
38474135Sgd78059 				break;
38484135Sgd78059 			case 1:
38494135Sgd78059 				buf += snprintf(buf, bufend - buf,
38504135Sgd78059 				    " - removed");
38514135Sgd78059 				break;
38524135Sgd78059 			default:
38534135Sgd78059 				break;
38544135Sgd78059 			}
38554135Sgd78059 		}
38564135Sgd78059 		break;
38574135Sgd78059 
38584135Sgd78059 	default:
38594135Sgd78059 		break;
38604135Sgd78059 	}
38614135Sgd78059 
38624135Sgd78059 	/* shutd */
38634135Sgd78059 	if (event->ev_subsys & EVENT_MASK_SHUTDOWN_REQD) {
38644135Sgd78059 		buf += snprintf(buf, bufend - buf, " - shutdown req'd");
38654135Sgd78059 	}
38664135Sgd78059 
38674135Sgd78059 	buf += snprintf(buf, bufend - buf, "\n");
38684135Sgd78059 
38694135Sgd78059 	if (buf >= bufend) {
38704135Sgd78059 		/* Ensure newline at end of string */
38714135Sgd78059 		bufend[-2] = '\n';
38724135Sgd78059 		bufend[-1] = '\0';
38734135Sgd78059 #ifdef DEBUG
38744135Sgd78059 		cmn_err(CE_WARN, "!bscv_build_eventstring: buffer too small!");
38754135Sgd78059 #endif /* DEBUG */
38764135Sgd78059 	}
38774135Sgd78059 }
38784135Sgd78059 
38794135Sgd78059 /*
38804135Sgd78059  * function	- bscv_level_of_event
38814135Sgd78059  * description	- This routine determines which level an event should be
38824135Sgd78059  *		  reported at.
38834135Sgd78059  * inputs	- lom event structure pointer
38844135Sgd78059  * outputs	- event level.
38854135Sgd78059  */
38864135Sgd78059 static int
bscv_level_of_event(lom_event_t * event)38874135Sgd78059 bscv_level_of_event(lom_event_t *event)
38884135Sgd78059 {
38894135Sgd78059 	int level;
38904135Sgd78059 	/*
38914135Sgd78059 	 * This is the same criteria that the firmware uses except we
38924135Sgd78059 	 * log the fault led on as being EVENT_LEVEL_FAULT
38934135Sgd78059 	 */
38944135Sgd78059 	if (EVENT_DECODE_SUBSYS(event->ev_subsys) == EVENT_SUBSYS_USER) {
38954135Sgd78059 		level = EVENT_LEVEL_USER;
38964135Sgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
38974135Sgd78059 	    EVENT_SUBSYS_ALARM) && (event->ev_event == EVENT_STATE_ON)) {
38984135Sgd78059 		level = EVENT_LEVEL_FAULT;
38994135Sgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
39004135Sgd78059 	    EVENT_SUBSYS_NONE) &&
39014135Sgd78059 	    (event->ev_event == EVENT_FAULT_LED) &&
39024135Sgd78059 	    (event->ev_detail != 0xff)) {
39034135Sgd78059 		level = EVENT_LEVEL_FAULT;
39044135Sgd78059 	} else if ((EVENT_DECODE_SUBSYS(event->ev_subsys) ==
39054135Sgd78059 	    EVENT_SUBSYS_LOM) && event->ev_event == EVENT_TIME_REFERENCE) {
39064135Sgd78059 		level = EVENT_LEVEL_NOTICE;
39074135Sgd78059 	} else if (event->ev_event == EVENT_RECOVERED) {
39084135Sgd78059 		/*
39094135Sgd78059 		 * All recovery messages need to be reported to the console
39104135Sgd78059 		 * because during boot, the faults which occurred whilst
39114135Sgd78059 		 * Solaris was not running are relayed to the console.  There
39124135Sgd78059 		 * is a case whereby a fatal fault (eg. over temp) could
39134135Sgd78059 		 * have occurred and then recovered.  The recovery condition
39144135Sgd78059 		 * needs to be reported so the user doesn't think that the
39154135Sgd78059 		 * failure (over temp) is still present.
39164135Sgd78059 		 */
39174135Sgd78059 		level = EVENT_LEVEL_FAULT;
39184135Sgd78059 	} else if (EVENT_DECODE_FAULT(event->ev_subsys) == 0) {
39194135Sgd78059 		/* None of FAULT, FATAL or SHUTDOWN REQD are set */
39204135Sgd78059 		level = EVENT_LEVEL_NOTICE;
39214135Sgd78059 	} else if (EVENT_DECODE_FAULT(event->ev_subsys) == EVENT_MASK_FAULT) {
39224135Sgd78059 		/* Only FAULT set i.e not FATAL or SHUTDOWN REQD */
39234135Sgd78059 		level = EVENT_LEVEL_FAULT;
39244135Sgd78059 	} else {
39254135Sgd78059 		level = EVENT_LEVEL_FATAL;
39264135Sgd78059 	}
39274135Sgd78059 
39284135Sgd78059 	return (level);
39294135Sgd78059 }
39304135Sgd78059 
39314135Sgd78059 /*
39324135Sgd78059  * function	- bscv_status
39334135Sgd78059  * description	- This routine is called when any change in the LOMlite2 status
39344135Sgd78059  *		  is indicated by the status registers.
39354135Sgd78059  *
39364135Sgd78059  * inputs	- LOM soft state structure pointer
39374135Sgd78059  *
39384135Sgd78059  * outputs	- none.
39394135Sgd78059  */
39404135Sgd78059 static void
bscv_status(bscv_soft_state_t * ssp,uint8_t state_chng,uint8_t dev_no)39414135Sgd78059 bscv_status(bscv_soft_state_t *ssp, uint8_t state_chng, uint8_t dev_no)
39424135Sgd78059 {
39434135Sgd78059 	int8_t temp;
39444135Sgd78059 	uint8_t fanspeed;
39454135Sgd78059 
39464135Sgd78059 	ASSERT(bscv_held(ssp));
39474135Sgd78059 
39489644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'D', "bscv_status", "state_chng 0x%x dev_no 0x%x",
39494135Sgd78059 	    state_chng, dev_no);
39504135Sgd78059 
39514135Sgd78059 	/*
39524135Sgd78059 	 * The device that has changed is given by the state change
39534135Sgd78059 	 * register and the event detail register so react
39544135Sgd78059 	 * accordingly.
39554135Sgd78059 	 */
39564135Sgd78059 
39574135Sgd78059 	if (state_chng == EBUS_STATE_NOTIFY) {
39584135Sgd78059 		/*
39594135Sgd78059 		 * The BSC is indicating a self state change
39604135Sgd78059 		 */
39614135Sgd78059 		if (dev_no == EBUS_DETAIL_FLASH) {
39624135Sgd78059 			ssp->cssp_prog = B_TRUE;
39639644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'D', "bscv_status",
39644135Sgd78059 			    "ssp->cssp_prog changed to 0x%x",
39654135Sgd78059 			    ssp->cssp_prog);
39664135Sgd78059 			/*
39674135Sgd78059 			 * It takes the BSC at least 100 ms to
39684135Sgd78059 			 * clear down the comms protocol.
39694135Sgd78059 			 * We back-off from talking to the
39704135Sgd78059 			 * BSC during this period.
39714135Sgd78059 			 */
39724135Sgd78059 			delay(BSC_EVENT_POLL_NORMAL);
39739644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'D', "bscv_status",
39744135Sgd78059 			    "completed delay");
39754135Sgd78059 		} else if (dev_no == EBUS_DETAIL_RESET) {
39764135Sgd78059 			/*
39774135Sgd78059 			 * The bsc has reset
39784135Sgd78059 			 */
39799644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'D', "bscv_status",
39804135Sgd78059 			    "BSC reset occured, re-synching");
39814135Sgd78059 			(void) bscv_attach_common(ssp);
39829644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'D', "bscv_status",
39834135Sgd78059 			    "completed attach_common");
39844135Sgd78059 		}
39854135Sgd78059 
39864135Sgd78059 	}
39874135Sgd78059 
39884135Sgd78059 	if ((state_chng & EBUS_STATE_FAN) && ((dev_no - 1) < MAX_FANS)) {
39894135Sgd78059 		fanspeed = bscv_get8(ssp, chan_general,
39905107Seota 		    EBUS_IDX_FAN1_SPEED + dev_no - 1);
39914135Sgd78059 		/*
39924135Sgd78059 		 * Only remember fanspeeds which are real values or
39934135Sgd78059 		 * NOT PRESENT values.
39944135Sgd78059 		 */
39954135Sgd78059 		if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
39964135Sgd78059 		    (fanspeed == LOM_FAN_NOT_PRESENT)) {
39974135Sgd78059 			ssp->fanspeed[dev_no - 1] = fanspeed;
39984135Sgd78059 		}
39994135Sgd78059 	}
40004135Sgd78059 
40014135Sgd78059 	if ((state_chng & EBUS_STATE_PSU) && ((dev_no - 1) < MAX_PSUS)) {
40024135Sgd78059 		(void) bscv_get8(ssp, chan_general,
40035107Seota 		    EBUS_IDX_PSU1_STAT + dev_no - 1);
40044135Sgd78059 	}
40054135Sgd78059 
40064135Sgd78059 	if (state_chng & EBUS_STATE_GP) {
40074135Sgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_GPIP);
40084135Sgd78059 	}
40094135Sgd78059 
40104135Sgd78059 	if (state_chng & EBUS_STATE_CB) {
40114135Sgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_CBREAK_STATUS);
40124135Sgd78059 	}
40134135Sgd78059 
40144135Sgd78059 	if ((state_chng & EBUS_STATE_TEMPERATURE) &&
40154135Sgd78059 	    ((dev_no - 1) < MAX_TEMPS)) {
40164135Sgd78059 		temp = bscv_get8(ssp, chan_general,
40175107Seota 		    EBUS_IDX_TEMP1 + dev_no - 1);
40184135Sgd78059 		/*
40194135Sgd78059 		 * Only remember temperatures which are real values or
40204135Sgd78059 		 * a NOT PRESENT value.
40214135Sgd78059 		 */
40224135Sgd78059 		if ((temp <= LOM_TEMP_MAX_VALUE) ||
40234135Sgd78059 		    (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
40244135Sgd78059 			ssp->temps.temp[dev_no - 1] = temp;
40254135Sgd78059 		}
40264135Sgd78059 	}
40274135Sgd78059 
40284135Sgd78059 	if (state_chng & EBUS_STATE_RAIL) {
40294135Sgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_LO);
40304135Sgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_SUPPLY_HI);
40314135Sgd78059 	}
40324135Sgd78059 }
40334135Sgd78059 
40344135Sgd78059 char *
bscv_get_label(char labels[][MAX_LOM2_NAME_STR],int limit,int index)40354135Sgd78059 bscv_get_label(char labels[][MAX_LOM2_NAME_STR], int limit, int index)
40364135Sgd78059 {
40374135Sgd78059 
40384135Sgd78059 	if (labels == NULL)
40394135Sgd78059 		return ("");
40404135Sgd78059 
40414135Sgd78059 	if (limit < 0 || index < 0 || index > limit)
40424135Sgd78059 		return ("-");
40434135Sgd78059 
40444135Sgd78059 	return (labels[index]);
40454135Sgd78059 }
40464135Sgd78059 
40474135Sgd78059 static void
bscv_generic_sysevent(bscv_soft_state_t * ssp,char * class,char * subclass,char * fru_id,char * res_id,int32_t fru_state,char * msg)40484135Sgd78059 bscv_generic_sysevent(bscv_soft_state_t *ssp, char *class, char *subclass,
40494135Sgd78059     char *fru_id, char *res_id, int32_t fru_state, char *msg)
40504135Sgd78059 {
40514135Sgd78059 	int rv;
40524135Sgd78059 	nvlist_t *attr_list;
40534135Sgd78059 
40549644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent", "%s/%s:(%s,%s,%d) %s",
40554135Sgd78059 	    class, subclass, fru_id, res_id, fru_state, msg);
40564135Sgd78059 
40574135Sgd78059 
40584135Sgd78059 	if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE, KM_SLEEP)) {
40599644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40604135Sgd78059 		    "nvlist alloc failure");
40614135Sgd78059 		return;
40624135Sgd78059 	}
40634135Sgd78059 	if (nvlist_add_uint32(attr_list, ENV_VERSION, 1)) {
40649644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40654135Sgd78059 		    "nvlist ENV_VERSION failure");
40664135Sgd78059 		nvlist_free(attr_list);
40674135Sgd78059 		return;
40684135Sgd78059 	}
40694135Sgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_ID, fru_id)) {
40709644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40714135Sgd78059 		    "nvlist ENV_FRU_ID failure");
40724135Sgd78059 		nvlist_free(attr_list);
40734135Sgd78059 		return;
40744135Sgd78059 	}
40754135Sgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_RESOURCE_ID, res_id)) {
40769644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40774135Sgd78059 		    "nvlist ENV_FRU_RESOURCE_ID failure");
40784135Sgd78059 		nvlist_free(attr_list);
40794135Sgd78059 		return;
40804135Sgd78059 	}
40814135Sgd78059 	if (nvlist_add_string(attr_list, ENV_FRU_DEVICE, ENV_RESERVED_ATTR)) {
40829644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40834135Sgd78059 		    "nvlist ENV_FRU_DEVICE failure");
40844135Sgd78059 		nvlist_free(attr_list);
40854135Sgd78059 		return;
40864135Sgd78059 	}
40874135Sgd78059 	if (nvlist_add_int32(attr_list, ENV_FRU_STATE, fru_state)) {
40889644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40894135Sgd78059 		    "nvlist ENV_FRU_STATE failure");
40904135Sgd78059 		nvlist_free(attr_list);
40914135Sgd78059 		return;
40924135Sgd78059 	}
40934135Sgd78059 	if (nvlist_add_string(attr_list, ENV_MSG, msg)) {
40949644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent",
40954135Sgd78059 		    "nvlist ENV_MSG failure");
40964135Sgd78059 		nvlist_free(attr_list);
40974135Sgd78059 		return;
40984135Sgd78059 	}
40994135Sgd78059 
41004135Sgd78059 	rv = ddi_log_sysevent(ssp->dip, DDI_VENDOR_SUNW, class,
41014135Sgd78059 	    subclass, attr_list, NULL, DDI_SLEEP);
41024135Sgd78059 
41034135Sgd78059 	if (rv == DDI_SUCCESS) {
41049644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_generic_sysevent", "sent sysevent");
41054135Sgd78059 	} else {
41064135Sgd78059 		cmn_err(CE_WARN, "!cannot deliver sysevent");
41074135Sgd78059 	}
41084135Sgd78059 
41094135Sgd78059 	nvlist_free(attr_list);
41104135Sgd78059 }
41114135Sgd78059 
41124135Sgd78059 /*
41134135Sgd78059  * function	- bscv_sysevent
41144135Sgd78059  * description	- send out a sysevent on the given change if needed
41154135Sgd78059  * inputs	- soft state pointer, event to report
41164135Sgd78059  * outputs	- none
41174135Sgd78059  */
41184135Sgd78059 
41194135Sgd78059 static void
bscv_sysevent(bscv_soft_state_t * ssp,lom_event_t * event)41204135Sgd78059 bscv_sysevent(bscv_soft_state_t *ssp, lom_event_t *event)
41214135Sgd78059 {
41224135Sgd78059 	char *class = NULL;
41234135Sgd78059 	char *subclass = NULL;
41244135Sgd78059 	char *fru_id = "Blade";	/* The blade is only one FRU */
41254135Sgd78059 	char *res_id;
41264135Sgd78059 	int32_t fru_state = 0;
41274135Sgd78059 
41289644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'E', "bscv_sysevent", "processing event");
41294135Sgd78059 
41304135Sgd78059 	ASSERT(event != NULL);
41314135Sgd78059 
41324135Sgd78059 	/* Map ev_subsys to sysevent class/sub-class */
41334135Sgd78059 
41344135Sgd78059 	switch (EVENT_DECODE_SUBSYS(event->ev_subsys)) {
41355107Seota 		case EVENT_SUBSYS_NONE:
41364135Sgd78059 		break;
41375107Seota 		case EVENT_SUBSYS_ALARM:
41384135Sgd78059 		break;
41395107Seota 		case EVENT_SUBSYS_TEMP:
41404135Sgd78059 		class = EC_ENV, subclass = ESC_ENV_TEMP;
41414135Sgd78059 		res_id = bscv_get_label(ssp->temps.name, ssp->temps.num,
41424135Sgd78059 		    event->ev_resource - 1);
41434135Sgd78059 		switch (event->ev_event) {
41445107Seota 			case EVENT_SEVERE_OVERHEAT:
41454135Sgd78059 			fru_state = ENV_FAILED;
41464135Sgd78059 			break;
41475107Seota 			case EVENT_OVERHEAT:
41484135Sgd78059 			fru_state = ENV_WARNING;
41494135Sgd78059 			break;
41505107Seota 			case EVENT_NO_OVERHEAT:
41514135Sgd78059 			fru_state = ENV_OK;
41524135Sgd78059 			break;
41535107Seota 			default:
41544135Sgd78059 			return;
41554135Sgd78059 		}
41564135Sgd78059 		break;
41575107Seota 		case EVENT_SUBSYS_OVERTEMP:
41584135Sgd78059 		break;
41595107Seota 		case EVENT_SUBSYS_FAN:
41604135Sgd78059 		class = EC_ENV, subclass = ESC_ENV_FAN;
41614135Sgd78059 		res_id = bscv_get_label(ssp->fan_names, ssp->num_fans,
41624135Sgd78059 		    event->ev_resource - 1);
41634135Sgd78059 		switch (event->ev_event) {
41645107Seota 			case EVENT_FAILED:
41654135Sgd78059 			fru_state = ENV_FAILED;
41664135Sgd78059 			break;
41675107Seota 			case EVENT_RECOVERED:
41684135Sgd78059 			fru_state = ENV_OK;
41694135Sgd78059 			break;
41705107Seota 			default:
41714135Sgd78059 			return;
41724135Sgd78059 		}
41734135Sgd78059 		break;
41745107Seota 		case EVENT_SUBSYS_SUPPLY:
41754135Sgd78059 		class = EC_ENV, subclass = ESC_ENV_POWER;
41764135Sgd78059 		res_id = bscv_get_label(ssp->sflags.name, ssp->sflags.num,
41774135Sgd78059 		    event->ev_resource - 1);
41784135Sgd78059 		switch (event->ev_event) {
41795107Seota 			case EVENT_FAILED:
41804135Sgd78059 			fru_state = ENV_FAILED;
41814135Sgd78059 			break;
41825107Seota 			case EVENT_RECOVERED:
41834135Sgd78059 			fru_state = ENV_OK;
41844135Sgd78059 			break;
41855107Seota 			default:
41864135Sgd78059 			return;
41874135Sgd78059 		}
41884135Sgd78059 		break;
41895107Seota 		case EVENT_SUBSYS_BREAKER:
41904135Sgd78059 		break;
41915107Seota 		case EVENT_SUBSYS_PSU:
41924135Sgd78059 		break;
41935107Seota 		case EVENT_SUBSYS_USER:
41944135Sgd78059 		break;
41955107Seota 		case EVENT_SUBSYS_PHONEHOME:
41964135Sgd78059 		break;
41975107Seota 		case EVENT_SUBSYS_LOM:
41984135Sgd78059 		break;
41995107Seota 		case EVENT_SUBSYS_HOST:
42004135Sgd78059 		break;
42015107Seota 		case EVENT_SUBSYS_EVENTLOG:
42024135Sgd78059 		break;
42035107Seota 		case EVENT_SUBSYS_EXTRA:
42044135Sgd78059 		break;
42055107Seota 		case EVENT_SUBSYS_LED:
42064135Sgd78059 		if (event->ev_event != EVENT_FAULT_LED &&
42074135Sgd78059 		    event->ev_event != EVENT_STATE_CHANGE)
42084135Sgd78059 			return;
42094135Sgd78059 		/*
42104135Sgd78059 		 * There are 3 LEDs : Power, Service, Ready-to-Remove on a
42114135Sgd78059 		 * JBOS blade.  We'll never report the Power since Solaris
42124135Sgd78059 		 * won't be running when it is _switched_ ON.  Ready-to-Remove
42134135Sgd78059 		 * will only be lit when we're powered down which also means
42144135Sgd78059 		 * Solaris won't be running. We don't want to report it
42154135Sgd78059 		 * during system testing / Sun VTS exercising the LEDs.
42164135Sgd78059 		 *
42174135Sgd78059 		 * Therefore, we only report the Service Required LED.
42184135Sgd78059 		 */
42194135Sgd78059 		class = EC_ENV, subclass = ESC_ENV_LED;
42204135Sgd78059 		res_id = bscv_get_label(ssp->led_names, MAX_LED_ID,
42214135Sgd78059 		    event->ev_resource - 1);
42224135Sgd78059 
42234135Sgd78059 		switch (event->ev_detail) {
42245107Seota 			case LOM_LED_STATE_ON_STEADY:
42254135Sgd78059 			fru_state = ENV_LED_ON;
42264135Sgd78059 			break;
42275107Seota 			case LOM_LED_STATE_ON_FLASHING:
42285107Seota 			case LOM_LED_STATE_ON_SLOWFLASH:
42294135Sgd78059 			fru_state = ENV_LED_BLINKING;
42304135Sgd78059 			break;
42315107Seota 			case LOM_LED_STATE_OFF:
42324135Sgd78059 			fru_state = ENV_LED_OFF;
42334135Sgd78059 			break;
42345107Seota 			case LOM_LED_STATE_INACCESSIBLE:
42354135Sgd78059 			fru_state = ENV_LED_INACCESSIBLE;
42364135Sgd78059 			break;
42375107Seota 			case LOM_LED_STATE_STANDBY:
42384135Sgd78059 			fru_state = ENV_LED_STANDBY;
42394135Sgd78059 			break;
42405107Seota 			case LOM_LED_STATE_NOT_PRESENT:
42414135Sgd78059 			fru_state = ENV_LED_NOT_PRESENT;
42424135Sgd78059 			break;
42435107Seota 			default:
42444135Sgd78059 			fru_state = ENV_LED_INACCESSIBLE;
42454135Sgd78059 			break;
42464135Sgd78059 		}
42474135Sgd78059 		break;
42485107Seota 		default :
42494135Sgd78059 		break;
42504135Sgd78059 	}
42514135Sgd78059 
42524135Sgd78059 	if (class == NULL || subclass == NULL) {
42539644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'E', "bscv_sysevent", "class/subclass NULL");
42544135Sgd78059 		return;
42554135Sgd78059 	}
42564135Sgd78059 
42574135Sgd78059 	bscv_generic_sysevent(ssp, class, subclass, fru_id, res_id, fru_state,
42584135Sgd78059 	    ENV_RESERVED_ATTR);
42594135Sgd78059 }
42604135Sgd78059 
42614135Sgd78059 /*
42624135Sgd78059  * *********************************************************************
42634135Sgd78059  * Firmware download (programming)
42644135Sgd78059  * *********************************************************************
42654135Sgd78059  */
42664135Sgd78059 
42674135Sgd78059 /*
42684135Sgd78059  * function	- bscv_prog
42694135Sgd78059  * description	- LOMlite2 flash programming code.
42704135Sgd78059  *
42714135Sgd78059  *		  bscv_prog_image - download a complete image to the lom.
42724135Sgd78059  *		  bscv_prog_receive_image - receive data to build up a
42734135Sgd78059  *			complete image.
42744135Sgd78059  *		  bscv_prog_stop_lom - pause the event daemon and prepare
42754135Sgd78059  *			lom for firmware upgrade.
42764135Sgd78059  *		  bscv_prog_start_lom - reinit the driver/lom after upgrade
42774135Sgd78059  *			and restart the event daemon
42784135Sgd78059  *
42794135Sgd78059  * inputs	- soft state pointer, arg ptr, ioctl mode
42804135Sgd78059  * outputs	- status
42814135Sgd78059  */
42824135Sgd78059 
42834135Sgd78059 static int
bscv_prog(bscv_soft_state_t * ssp,intptr_t arg,int mode)42844135Sgd78059 bscv_prog(bscv_soft_state_t *ssp, intptr_t arg, int mode)
42854135Sgd78059 {
42864135Sgd78059 	lom_prog_t *prog;
42874135Sgd78059 	int res = 0;
42884135Sgd78059 
42894135Sgd78059 	/*
42904135Sgd78059 	 * We will get repeatedly called with bits of data first for
42914135Sgd78059 	 * loader, then for main image.
42924135Sgd78059 	 */
42934135Sgd78059 	prog = (lom_prog_t *)kmem_alloc(sizeof (lom_prog_t), KM_SLEEP);
42944135Sgd78059 
42954135Sgd78059 	if (ddi_copyin((caddr_t)arg, (caddr_t)prog, sizeof (*prog),
42964135Sgd78059 	    mode) < 0) {
42974135Sgd78059 		kmem_free((void *)prog, sizeof (*prog));
42984135Sgd78059 		return (EFAULT);
42994135Sgd78059 	}
43004135Sgd78059 
43019644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_prog",
43024135Sgd78059 	    "index 0x%x size 0x%x", prog->index, prog->size);
43034135Sgd78059 
43044135Sgd78059 	mutex_enter(&ssp->prog_mu);
43054135Sgd78059 	if (prog->size == 0) {
43064135Sgd78059 		if (prog->index == 2) {
43074135Sgd78059 			/*
43084135Sgd78059 			 * This is the initial request for the chip type so we
43094135Sgd78059 			 * know what we are programming.
43104135Sgd78059 			 * The type will have been read in at init so just
43114135Sgd78059 			 * return it in data[0].
43124135Sgd78059 			 */
43134135Sgd78059 			prog->data[0] = bscv_get8_cached(ssp,
43144135Sgd78059 			    EBUS_IDX_CPU_IDENT);
43154135Sgd78059 
43164135Sgd78059 			if (ddi_copyout((caddr_t)prog, (caddr_t)arg,
43174135Sgd78059 			    sizeof (lom_prog_t), mode) < 0) {
43184135Sgd78059 				res = EFAULT;
43194135Sgd78059 			}
43204135Sgd78059 		} else if (prog->index == 0) {
43214135Sgd78059 			res = bscv_prog_stop_lom(ssp);
43224135Sgd78059 		} else if (prog->index == 1) {
43234135Sgd78059 			res = bscv_prog_start_lom(ssp);
43244135Sgd78059 		} else {
43254135Sgd78059 			res = EINVAL;
43264135Sgd78059 		}
43274135Sgd78059 	} else {
43284135Sgd78059 		if (ssp->image == NULL) {
43294135Sgd78059 			ssp->image = (uint8_t *)kmem_zalloc(
43305107Seota 			    BSC_IMAGE_MAX_SIZE, KM_SLEEP);
43314135Sgd78059 		}
43324135Sgd78059 		res = bscv_prog_receive_image(ssp, prog,
43334135Sgd78059 		    ssp->image, BSC_IMAGE_MAX_SIZE);
43344135Sgd78059 	}
43354135Sgd78059 	mutex_exit(&ssp->prog_mu);
43364135Sgd78059 	kmem_free((void *)prog, sizeof (lom_prog_t));
43374135Sgd78059 
43384135Sgd78059 	return (res);
43394135Sgd78059 }
43404135Sgd78059 
43414135Sgd78059 static int
bscv_check_loader_config(bscv_soft_state_t * ssp,boolean_t is_image2)43424135Sgd78059 bscv_check_loader_config(bscv_soft_state_t *ssp, boolean_t is_image2)
43434135Sgd78059 {
43449644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_check_loader_config",
43454135Sgd78059 	    "loader_running %d, is_image2 %d",
43464135Sgd78059 	    ssp->loader_running, is_image2);
43474135Sgd78059 
43484135Sgd78059 	/*
43494135Sgd78059 	 * loader_running TRUE means that we have told the microcontroller to
43504135Sgd78059 	 * JUMP into the loader code which has been downloaded into its RAM.
43514135Sgd78059 	 * At this point its an error to try and download another loader.  We
43524135Sgd78059 	 * should be downloading the actual image at this point.
43534135Sgd78059 	 * Conversely, it is an error to download an image when the loader is
43544135Sgd78059 	 * not already downloaded and the microcontroller hasn't JUMPed into it.
43554135Sgd78059 	 * is_image2 TRUE means the image is being downloaded.
43564135Sgd78059 	 * is_image2 FALSE means the loader is being downloaded.
43574135Sgd78059 	 */
43584135Sgd78059 	if (ssp->loader_running && !is_image2) {
43594135Sgd78059 		cmn_err(CE_WARN, "Attempt to download loader image "
43604135Sgd78059 		    "with loader image already active");
43614135Sgd78059 		cmn_err(CE_CONT, "This maybe an attempt to restart a "
43624135Sgd78059 		    "failed firmware download - ignoring download attempt");
43634135Sgd78059 		return (B_FALSE);
43644135Sgd78059 	} else if (!ssp->loader_running && is_image2) {
43654135Sgd78059 		cmn_err(CE_WARN, "Attempt to download firmware image "
43664135Sgd78059 		    "without loader image active");
43674135Sgd78059 		return (B_FALSE);
43684135Sgd78059 
43694135Sgd78059 	}
43704135Sgd78059 
43714135Sgd78059 	return (B_TRUE);
43724135Sgd78059 }
43734135Sgd78059 
43744135Sgd78059 static uint32_t
bscv_get_pagesize(bscv_soft_state_t * ssp)43754135Sgd78059 bscv_get_pagesize(bscv_soft_state_t *ssp)
43764135Sgd78059 {
43774135Sgd78059 	uint32_t pagesize;
43784135Sgd78059 
43794135Sgd78059 	ASSERT(bscv_held(ssp));
43804135Sgd78059 
43814135Sgd78059 	pagesize = bscv_get32(ssp, chan_prog,
43825107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PAGE0));
43834135Sgd78059 
43849644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_get_pagesize", "pagesize 0x%x", pagesize);
43854135Sgd78059 
43864135Sgd78059 	return (pagesize);
43874135Sgd78059 }
43884135Sgd78059 
43894135Sgd78059 /*
43904135Sgd78059  * Sets the pagesize, returning the old value.
43914135Sgd78059  */
43924135Sgd78059 static uint32_t
bscv_set_pagesize(bscv_soft_state_t * ssp,uint32_t pagesize)43934135Sgd78059 bscv_set_pagesize(bscv_soft_state_t *ssp, uint32_t pagesize)
43944135Sgd78059 {
43954135Sgd78059 	uint32_t old_pagesize;
43964135Sgd78059 
43974135Sgd78059 	ASSERT(bscv_held(ssp));
43984135Sgd78059 
43994135Sgd78059 	old_pagesize = bscv_get_pagesize(ssp);
44004135Sgd78059 
44014135Sgd78059 	/*
44024135Sgd78059 	 * The microcontroller remembers this value until until someone
44034135Sgd78059 	 * changes it.
44044135Sgd78059 	 */
44054135Sgd78059 	bscv_put32(ssp, chan_prog,
44065107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0), pagesize);
44074135Sgd78059 
44084135Sgd78059 	return (old_pagesize);
44094135Sgd78059 }
44104135Sgd78059 
44114135Sgd78059 static uint8_t
bscv_enter_programming_mode(bscv_soft_state_t * ssp)44124135Sgd78059 bscv_enter_programming_mode(bscv_soft_state_t *ssp)
44134135Sgd78059 {
44144135Sgd78059 	uint8_t retval;
44154135Sgd78059 
44164135Sgd78059 	ASSERT(bscv_held(ssp));
44174135Sgd78059 
44184135Sgd78059 	bscv_put8(ssp, chan_prog,
44195107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
44205107Seota 	    EBUS_PROGRAM_PCR_PRGMODE_ON);
44214135Sgd78059 
44224135Sgd78059 	retval = bscv_get8(ssp, chan_prog, BSCVA(EBUS_CMD_SPACE_PROGRAM,
44235107Seota 	    EBUS_PROGRAM_PCSR));
44244135Sgd78059 
44254135Sgd78059 	return (retval);
44264135Sgd78059 }
44274135Sgd78059 
44284135Sgd78059 static void
bscv_leave_programming_mode(bscv_soft_state_t * ssp,boolean_t with_jmp)44294135Sgd78059 bscv_leave_programming_mode(bscv_soft_state_t *ssp, boolean_t with_jmp)
44304135Sgd78059 {
44314135Sgd78059 	uint8_t reg;
44324135Sgd78059 	ASSERT(bscv_held(ssp));
44334135Sgd78059 
44344135Sgd78059 	if (with_jmp) {
44354135Sgd78059 		reg = EBUS_PROGRAM_PCR_PROGOFF_JUMPTOADDR;
44369644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'U', "bscv_leave_programming_mode",
44374135Sgd78059 		    "jumptoaddr");
44384135Sgd78059 	} else {
44394135Sgd78059 		reg = EBUS_PROGRAM_PCR_PRGMODE_OFF;
44409644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'U', "bscv_leave_programming_mode",
44414135Sgd78059 		    "prgmode_off");
44424135Sgd78059 	}
44434135Sgd78059 
44444135Sgd78059 	bscv_put8(ssp, chan_prog,
44455107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR), reg);
44464135Sgd78059 }
44474135Sgd78059 
44484135Sgd78059 
44494135Sgd78059 static void
bscv_set_jump_to_addr(bscv_soft_state_t * ssp,uint32_t loadaddr)44504135Sgd78059 bscv_set_jump_to_addr(bscv_soft_state_t *ssp, uint32_t loadaddr)
44514135Sgd78059 {
44524135Sgd78059 	ASSERT(bscv_held(ssp));
44534135Sgd78059 
44544135Sgd78059 	bscv_put32(ssp, chan_prog,
44555107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0), loadaddr);
44564135Sgd78059 
44579644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_set_jump_to_addr",
44584135Sgd78059 	    "set jump to loadaddr 0x%x", loadaddr);
44594135Sgd78059 }
44604135Sgd78059 
44614135Sgd78059 static uint8_t
bscv_erase_once(bscv_soft_state_t * ssp,uint32_t loadaddr,uint32_t image_size)44624135Sgd78059 bscv_erase_once(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size)
44634135Sgd78059 {
44644135Sgd78059 	uint8_t retval;
44654135Sgd78059 
44664135Sgd78059 	ASSERT(bscv_held(ssp));
44674135Sgd78059 
44684135Sgd78059 	/*
44694135Sgd78059 	 * write PADR, PSIZ to define area to be erased
44704135Sgd78059 	 * We do not send erase for zero size because the current
44714135Sgd78059 	 * downloader gets this wrong
44724135Sgd78059 	 */
44734135Sgd78059 
44744135Sgd78059 	/*
44754135Sgd78059 	 * start at 0
44764135Sgd78059 	 */
44779644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_erase_once", "sending erase command");
44784135Sgd78059 
44794135Sgd78059 	bscv_put32(ssp, chan_prog,
44804135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
44814135Sgd78059 	    loadaddr);
44824135Sgd78059 
44834135Sgd78059 	/* set PSIZ to full size of image to be programmed */
44844135Sgd78059 	bscv_put32(ssp, chan_prog,
44854135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PSIZ0),
44864135Sgd78059 	    image_size);
44874135Sgd78059 
44884135Sgd78059 	/* write ERASE to PCSR */
44894135Sgd78059 	bscv_put8(ssp, chan_prog,
44904135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
44914135Sgd78059 	    EBUS_PROGRAM_PCR_ERASE);
44924135Sgd78059 
44934135Sgd78059 	/* read PCSR to check status */
44944135Sgd78059 	retval = bscv_get8(ssp, chan_prog,
44954135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
44964135Sgd78059 	return (retval);
44974135Sgd78059 }
44984135Sgd78059 
44994135Sgd78059 static uint8_t
bscv_do_erase(bscv_soft_state_t * ssp,uint32_t loadaddr,uint32_t image_size,boolean_t is_image2)45004135Sgd78059 bscv_do_erase(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
45014135Sgd78059     boolean_t is_image2)
45024135Sgd78059 {
45034135Sgd78059 	int retryable = BSC_ERASE_RETRY_LIMIT;
45044135Sgd78059 	uint8_t retval;
45054135Sgd78059 
45064135Sgd78059 	while (retryable--) {
45074135Sgd78059 		retval = bscv_erase_once(ssp, loadaddr, image_size);
45084135Sgd78059 		if (PSR_SUCCESS(retval))
45094135Sgd78059 			break;
45104135Sgd78059 		else
45114135Sgd78059 			cmn_err(CE_WARN, "erase error 0x%x, attempt %d"
45124135Sgd78059 			    ", base 0x%x, size 0x%x, %s image",
45134135Sgd78059 			    retval, BSC_ERASE_RETRY_LIMIT - retryable,
45144135Sgd78059 			    loadaddr, image_size,
45154135Sgd78059 			    is_image2 ? "main" : "loader");
45164135Sgd78059 	}
45174135Sgd78059 
45184135Sgd78059 	return (retval);
45194135Sgd78059 }
45204135Sgd78059 
45214135Sgd78059 static uint8_t
bscv_set_page(bscv_soft_state_t * ssp,uint32_t addr)45224135Sgd78059 bscv_set_page(bscv_soft_state_t *ssp, uint32_t addr)
45234135Sgd78059 {
45244135Sgd78059 	uint32_t retval;
45254135Sgd78059 	int retryable = BSC_PAGE_RETRY_LIMIT;
45264135Sgd78059 
45274135Sgd78059 	ASSERT(bscv_held(ssp));
45284135Sgd78059 
45294135Sgd78059 	while (retryable--) {
45304135Sgd78059 
45314135Sgd78059 		/*
45324135Sgd78059 		 * Write the page address and read it back for confirmation.
45334135Sgd78059 		 */
45344135Sgd78059 		bscv_put32(ssp, chan_prog,
45354135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0),
45364135Sgd78059 		    addr);
45374135Sgd78059 		retval = bscv_get32(ssp, chan_prog,
45384135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PADR0));
45394135Sgd78059 
45404135Sgd78059 		if (retval == addr)
45414135Sgd78059 			break;
45424135Sgd78059 		else {
45434135Sgd78059 			cmn_err(CE_WARN, "programmming error, attempt %d, "
45444135Sgd78059 			    "set page 0x%x, read back 0x%x",
45454135Sgd78059 			    BSC_PAGE_RETRY_LIMIT - retryable,
45464135Sgd78059 			    addr, retval);
45474135Sgd78059 		}
45484135Sgd78059 	}
45494135Sgd78059 	return ((addr == retval) ? EBUS_PROGRAM_PSR_SUCCESS :
45504135Sgd78059 	    EBUS_PROGRAM_PSR_INVALID_OPERATION);
45514135Sgd78059 }
45524135Sgd78059 
45534135Sgd78059 static uint8_t
bscv_do_page_data_once(bscv_soft_state_t * ssp,uint32_t index,uint32_t image_size,uint32_t pagesize,uint8_t * imagep,uint16_t * calcd_chksum)45544135Sgd78059 bscv_do_page_data_once(bscv_soft_state_t *ssp, uint32_t index,
45554135Sgd78059     uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
45564135Sgd78059     uint16_t *calcd_chksum)
45574135Sgd78059 {
45584135Sgd78059 	uint32_t size;
45594135Sgd78059 	uint16_t chksum;
45604135Sgd78059 	int i;
45614135Sgd78059 	uint8_t retval;
45624135Sgd78059 
45634135Sgd78059 	ASSERT(bscv_held(ssp));
45644135Sgd78059 
45659644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'P', "bscv_do_page_data_once", "index 0x%x", index);
45664135Sgd78059 
45674135Sgd78059 	/* write PSIZ bytes to PDAT */
45684135Sgd78059 	if (index + pagesize < image_size) {
45694135Sgd78059 		bscv_rep_rw8(ssp, chan_prog, imagep + index,
45704135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
45714135Sgd78059 		    pagesize, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
45724135Sgd78059 		size = pagesize;
45734135Sgd78059 	} else {
45749644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'P', "bscv_do_page_once",
45754135Sgd78059 		    "Sending last block, last 0x%x bytes",
45764135Sgd78059 		    (image_size % pagesize));
45774135Sgd78059 		size = (image_size - index);
45784135Sgd78059 		bscv_rep_rw8(ssp, chan_prog, imagep + index,
45794135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_DATA),
45804135Sgd78059 		    size, DDI_DEV_NO_AUTOINCR, B_TRUE /* write */);
45814135Sgd78059 		/* Now pad the rest of the page with zeros */
45824135Sgd78059 		for (i = size; i < pagesize; i++) {
45834135Sgd78059 			bscv_put8(ssp, chan_prog,
45844135Sgd78059 			    BSCVA(EBUS_CMD_SPACE_PROGRAM,
45855107Seota 			    EBUS_PROGRAM_DATA),
45864135Sgd78059 			    0);
45874135Sgd78059 		}
45884135Sgd78059 	}
45894135Sgd78059 
45904135Sgd78059 	/* write the checksum to PCSM */
45914135Sgd78059 	chksum = 0;
45924135Sgd78059 	for (i = 0; i < size; i++) {
45934135Sgd78059 		chksum = ((chksum << 3) | (chksum >> 13)) ^
45945107Seota 		    *(imagep + index + i);
45954135Sgd78059 	}
45964135Sgd78059 	/* Cope with non-pagesize sized bufers */
45974135Sgd78059 	for (; i < pagesize; i++) {
45984135Sgd78059 		chksum = ((chksum << 3) | (chksum >> 13)) ^ 0;
45994135Sgd78059 	}
46004135Sgd78059 	bscv_put16(ssp, chan_prog,
46014135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSM0), chksum);
46024135Sgd78059 
46034135Sgd78059 	bscv_put8(ssp, chan_prog,
46044135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
46054135Sgd78059 	    EBUS_PROGRAM_PCR_PROGRAM);
46064135Sgd78059 
46074135Sgd78059 	retval = bscv_get8(ssp, chan_prog,
46085107Seota 	    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR));
46094135Sgd78059 
46104135Sgd78059 	*calcd_chksum = chksum;
46114135Sgd78059 	return (retval);
46124135Sgd78059 }
46134135Sgd78059 
bscv_do_page(bscv_soft_state_t * ssp,uint32_t loadaddr,uint32_t index,uint32_t image_size,uint32_t pagesize,uint8_t * imagep,boolean_t is_image2)46144135Sgd78059 static uint8_t bscv_do_page(bscv_soft_state_t *ssp, uint32_t loadaddr,
46154135Sgd78059     uint32_t index, uint32_t image_size, uint32_t pagesize, uint8_t *imagep,
46164135Sgd78059     boolean_t is_image2)
46174135Sgd78059 {
46184135Sgd78059 	int retryable = BSC_PAGE_RETRY_LIMIT;
46194135Sgd78059 	uint8_t retval;
46204135Sgd78059 	uint16_t checksum;
46214135Sgd78059 
46229644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'P', "bscv_do_page", "index 0x%x", index);
46234135Sgd78059 
46244135Sgd78059 	while (retryable--) {
46254135Sgd78059 		/*
46264135Sgd78059 		 * Set the page address (with retries).  If this is not
46274135Sgd78059 		 * successful, then there is no point carrying on and sending
46284135Sgd78059 		 * the page's data since that could cause random memory
46294135Sgd78059 		 * corruption in the microcontroller.
46304135Sgd78059 		 */
46314135Sgd78059 		retval = bscv_set_page(ssp, loadaddr + index);
46324135Sgd78059 		if (!PSR_SUCCESS(retval)) {
46334135Sgd78059 			cmn_err(CE_WARN, "programming error 0x%x, "
46344135Sgd78059 			    "could not setup page address 0x%x, %s image",
46354135Sgd78059 			    retval, loadaddr + index,
46364135Sgd78059 			    is_image2 ? "main" : "loader");
46374135Sgd78059 			break;
46384135Sgd78059 		}
46394135Sgd78059 
46404135Sgd78059 		/*
46414135Sgd78059 		 * Send down the data for the page
46424135Sgd78059 		 */
46434135Sgd78059 
46449644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'P', "bscv_do_page", "sending data for page");
46454135Sgd78059 
46464135Sgd78059 		retval = bscv_do_page_data_once(ssp, index, image_size,
46474135Sgd78059 		    pagesize, imagep, &checksum);
46484135Sgd78059 		if (PSR_SUCCESS(retval))
46494135Sgd78059 			break;
46504135Sgd78059 		else
46514135Sgd78059 			cmn_err(CE_WARN, "programming error 0x%x,"
46524135Sgd78059 			    " attempt %d, index 0x%x, checksum 0x%x, %s image",
46534135Sgd78059 			    retval, BSC_PAGE_RETRY_LIMIT - retryable,
46544135Sgd78059 			    index, checksum, is_image2 ? "main" : "loader");
46554135Sgd78059 	}
46564135Sgd78059 
46579644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_do_page", "Returning 0x%x for index 0x%x,"
46584135Sgd78059 	    " checksum 0x%x, %s image", retval, index, checksum,
46594135Sgd78059 	    is_image2 ? "main" : "loader");
46604135Sgd78059 
46614135Sgd78059 	return (retval);
46624135Sgd78059 }
46634135Sgd78059 
46644135Sgd78059 static uint8_t
bscv_do_pages(bscv_soft_state_t * ssp,uint32_t loadaddr,uint32_t image_size,uint32_t pagesize,uint8_t * imagep,boolean_t is_image2)46654135Sgd78059 bscv_do_pages(bscv_soft_state_t *ssp, uint32_t loadaddr, uint32_t image_size,
46664135Sgd78059     uint32_t pagesize, uint8_t *imagep, boolean_t is_image2)
46674135Sgd78059 {
46684135Sgd78059 	uint8_t retval;
46694135Sgd78059 	uint32_t index;
46704135Sgd78059 
46719644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'P', "bscv_do_pages", "entered");
46724135Sgd78059 
46734135Sgd78059 	for (index = 0; index < image_size; index += pagesize) {
46744135Sgd78059 		retval = bscv_do_page(ssp, loadaddr, index, image_size,
46755107Seota 		    pagesize, imagep, is_image2);
46764135Sgd78059 		if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
46779644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'U', "bscv_do_pages",
46784135Sgd78059 			    "Failed to program lom (status 0x%x)", retval);
46794135Sgd78059 			break;
46804135Sgd78059 		}
46814135Sgd78059 	}
46824135Sgd78059 
46834135Sgd78059 	return (retval);
46844135Sgd78059 }
46854135Sgd78059 
46864135Sgd78059 static int
bscv_prog_image(bscv_soft_state_t * ssp,boolean_t is_image2,uint8_t * imagep,int image_size,uint32_t loadaddr)46874135Sgd78059 bscv_prog_image(bscv_soft_state_t *ssp, boolean_t is_image2,
46884135Sgd78059     uint8_t *imagep, int image_size, uint32_t loadaddr)
46894135Sgd78059 {
46904135Sgd78059 	uint32_t pagesize;
46914135Sgd78059 	int res = 0;
46924135Sgd78059 	uint8_t retval;
46934135Sgd78059 
46949644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_prog_image",
46954135Sgd78059 	    "image 0x%x, imagep %p, size 0x%x",
46964135Sgd78059 	    is_image2 ? 2 : 1, imagep, image_size);
46974135Sgd78059 
46984135Sgd78059 	if (!bscv_check_loader_config(ssp, is_image2))
46994135Sgd78059 		/*
47004135Sgd78059 		 * Return no error to allow userland to continue on with
47014135Sgd78059 		 * downloading the image.
47024135Sgd78059 		 */
47034135Sgd78059 		return (0);
47044135Sgd78059 
47054135Sgd78059 	bscv_enter(ssp);
47064135Sgd78059 
47074135Sgd78059 	pagesize = bscv_get_pagesize(ssp);
47084135Sgd78059 
47094135Sgd78059 	retval = bscv_enter_programming_mode(ssp);
47104135Sgd78059 	if (bscv_faulty(ssp) || !PSR_PROG(retval)) {
47114135Sgd78059 		cmn_err(CE_WARN, "lom: Failed to enter program mode, error 0x%x"
47124135Sgd78059 		    ", %s image", retval, is_image2 ? "main" : "loader");
47134135Sgd78059 		res = EIO;
47144135Sgd78059 		goto BSCV_PROG_IMAGE_END;
47154135Sgd78059 	}
47169644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'U', "bscv_prog_image", "entered programming mode");
47174135Sgd78059 
47184135Sgd78059 	/*
47194135Sgd78059 	 * Only issue an erase if we are downloading the image.  The loader
47204135Sgd78059 	 * does not need this step.
47214135Sgd78059 	 */
47224135Sgd78059 	if (is_image2 && (image_size != 0)) {
47234135Sgd78059 		retval = bscv_do_erase(ssp, loadaddr, image_size, is_image2);
47244135Sgd78059 		if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
47254135Sgd78059 			cmn_err(CE_WARN,
47264135Sgd78059 			    "lom: Erase failed during programming, status 0x%x",
47274135Sgd78059 			    retval);
47284135Sgd78059 			res = EIO;
47294135Sgd78059 			goto BSCV_PROG_IMAGE_END;
47304135Sgd78059 		} else {
47319644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'U', "bscv_prog_image",
47324135Sgd78059 			    "erase complete - programming...");
47334135Sgd78059 
47344135Sgd78059 		}
47354135Sgd78059 	}
47364135Sgd78059 
47374135Sgd78059 	(void) bscv_set_pagesize(ssp, pagesize);
47384135Sgd78059 
47394135Sgd78059 	retval = bscv_do_pages(ssp, loadaddr, image_size, pagesize, imagep,
47405107Seota 	    is_image2);
47414135Sgd78059 	if (bscv_faulty(ssp) || !PSR_SUCCESS(retval)) {
47429644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'U', "bscv_prog_image",
47434135Sgd78059 		    "Failed to program lom (status 0x%x)", retval);
47444135Sgd78059 		res = EIO;
47454135Sgd78059 		goto BSCV_PROG_IMAGE_END;
47464135Sgd78059 	}
47474135Sgd78059 
47484135Sgd78059 BSCV_PROG_IMAGE_END:
47494135Sgd78059 	if (res == 0 && !is_image2) {
47504135Sgd78059 		/*
47514135Sgd78059 		 * We've downloaded the loader successfully.  Now make the
47524135Sgd78059 		 * microcontroller jump to it.
47534135Sgd78059 		 */
47544135Sgd78059 		bscv_set_jump_to_addr(ssp, loadaddr);
47554135Sgd78059 		ssp->loader_running = B_TRUE;
47564135Sgd78059 		bscv_leave_programming_mode(ssp, B_TRUE);
47574135Sgd78059 	} else {
47584135Sgd78059 		/*
47594135Sgd78059 		 * We've just downloaded either the loader which failed, or
47604135Sgd78059 		 * the image (which may or may not have been successful).
47614135Sgd78059 		 */
47624135Sgd78059 		bscv_set_jump_to_addr(ssp, 0);
47634135Sgd78059 
47644135Sgd78059 		if (res != 0) {
47659644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'U', "bscv_prog_image",
47664135Sgd78059 			    "got error 0x%x - leaving programming mode",
47674135Sgd78059 			    res);
47684135Sgd78059 			cmn_err(CE_WARN, "programming error 0x%x, %s image",
47694135Sgd78059 			    res, is_image2 ? "main" : "loader");
47704135Sgd78059 		} else {
47719644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'U', "bscv_prog_image",
47724135Sgd78059 			    "programming complete - leaving programming mode");
47734135Sgd78059 		}
47744135Sgd78059 
47754135Sgd78059 		bscv_leave_programming_mode(ssp, B_FALSE);
47764135Sgd78059 		ssp->loader_running = B_FALSE;
47774135Sgd78059 	}
47784135Sgd78059 
47794135Sgd78059 	bscv_exit(ssp);
47804135Sgd78059 
47814135Sgd78059 	return (res);
47824135Sgd78059 }
47834135Sgd78059 
47844135Sgd78059 
47854135Sgd78059 static int
bscv_prog_receive_image(bscv_soft_state_t * ssp,lom_prog_t * prog,uint8_t * imagep,int max_size)47864135Sgd78059 bscv_prog_receive_image(bscv_soft_state_t *ssp, lom_prog_t *prog,
47874135Sgd78059     uint8_t *imagep, int max_size)
47884135Sgd78059 {
47894135Sgd78059 	int	res = 0;
47904135Sgd78059 	uint_t	size;
47914135Sgd78059 	int32_t loadaddr;
47924135Sgd78059 	lom_prog_data_t *prog_data;
47934135Sgd78059 
47944135Sgd78059 	if ((prog->index & 0x7FFF) != ssp->prog_index) {
47959644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'U', "bscv_prog_receive_image",
47964135Sgd78059 		    "Got wrong buffer 0x%x, expected 0x%x",
47974135Sgd78059 		    prog->index & 0x7fff, ssp->prog_index);
47984135Sgd78059 		return (EINVAL);
47994135Sgd78059 	}
48004135Sgd78059 
48014135Sgd78059 	/*
48024135Sgd78059 	 * We want to get the whole image and then do the download.
48034135Sgd78059 	 * It is assumed the device is now in programming mode.
48044135Sgd78059 	 */
48054135Sgd78059 
48064135Sgd78059 	if ((prog->index & 0x7fff) == 0) {
48074135Sgd78059 		/* Starting a new image */
48084135Sgd78059 		ssp->image_ptr = 0;
48094135Sgd78059 	}
48104135Sgd78059 
48114135Sgd78059 	if ((ssp->image_ptr + prog->size) > max_size) {
48124135Sgd78059 		cmn_err(CE_WARN,
48134135Sgd78059 		    "lom image exceeded maximum size: got 0x%x, maximum 0x%x",
48144135Sgd78059 		    (ssp->image_ptr + prog->size), max_size);
48154135Sgd78059 		return (EFAULT);
48164135Sgd78059 	}
48174135Sgd78059 	bcopy(prog->data, &imagep[ssp->image_ptr], prog->size);
48184135Sgd78059 	ssp->image_ptr += prog->size;
48194135Sgd78059 
48204135Sgd78059 	ssp->prog_index++;
48214135Sgd78059 
48224135Sgd78059 	if (prog->index & 0x8000) {
48234135Sgd78059 		/*
48244135Sgd78059 		 * OK we have the whole image so synch up and start download.
48254135Sgd78059 		 */
48264135Sgd78059 		prog_data = (lom_prog_data_t *)imagep;
48274135Sgd78059 		if (prog_data->header.magic != PROG_MAGIC) {
48284135Sgd78059 			/* Old style programming data */
48294135Sgd78059 			/* Take care image may not fill all of structure */
48304135Sgd78059 
48314135Sgd78059 			/* sign extend loadaddr from 16  to 32 bits */
48324135Sgd78059 			loadaddr = (int16_t)((uint16_t)((imagep[2] << 8) +
48334135Sgd78059 			    imagep[3]));
48344135Sgd78059 
48354135Sgd78059 			size = (imagep[0] << 8) + imagep[1];
48364135Sgd78059 			if (size != (ssp->image_ptr - 4)) {
48374135Sgd78059 				cmn_err(CE_WARN, "Image size mismatch:"
48384135Sgd78059 				    " expected 0x%x, got 0x%x",
48394135Sgd78059 				    size, (ssp->image_ptr - 1));
48404135Sgd78059 			}
48414135Sgd78059 
48424135Sgd78059 			res = bscv_prog_image(ssp,
48434135Sgd78059 			    ssp->image2_processing,
48444135Sgd78059 			    imagep + 4, ssp->image_ptr - 4, loadaddr);
48454135Sgd78059 
48464135Sgd78059 			/*
48474135Sgd78059 			 * Done the loading so set the flag to say we are doing
48484135Sgd78059 			 * the other image.
48494135Sgd78059 			 */
48504135Sgd78059 			ssp->image2_processing = !ssp->image2_processing;
48514135Sgd78059 		} else if ((ssp->image_ptr < sizeof (*prog_data)) ||
48524135Sgd78059 		    (prog_data->platform.bscv.size !=
48535107Seota 		    (ssp->image_ptr - sizeof (*prog_data)))) {
48544135Sgd78059 			/* Image too small for new style image */
48554135Sgd78059 			cmn_err(CE_WARN, "image too small");
48564135Sgd78059 			res = EINVAL;
48574135Sgd78059 		} else {
48584135Sgd78059 			/* New style programming image */
48594135Sgd78059 			switch (prog_data->platmagic) {
48604135Sgd78059 			case PROG_PLAT_BSCV_IMAGE:
48614135Sgd78059 				res = bscv_prog_image(ssp, B_TRUE,
48624135Sgd78059 				    imagep + sizeof (*prog_data),
48634135Sgd78059 				    prog_data->platform.bscv.size,
48644135Sgd78059 				    prog_data->platform.bscv.loadaddr);
48654135Sgd78059 				ssp->image2_processing = B_FALSE;
48664135Sgd78059 				break;
48674135Sgd78059 			case PROG_PLAT_BSCV_LOADER:
48684135Sgd78059 				res = bscv_prog_image(ssp, B_FALSE,
48694135Sgd78059 				    imagep + sizeof (*prog_data),
48704135Sgd78059 				    prog_data->platform.bscv.size,
48714135Sgd78059 				    prog_data->platform.bscv.loadaddr);
48724135Sgd78059 				ssp->image2_processing = B_TRUE;
48734135Sgd78059 				break;
48744135Sgd78059 			default:
48754135Sgd78059 				cmn_err(CE_WARN, "unknown platmagic 0x%x",
48764135Sgd78059 				    prog_data->platmagic);
48774135Sgd78059 				res = EINVAL;
48784135Sgd78059 				break;
48794135Sgd78059 			}
48804135Sgd78059 		}
48814135Sgd78059 		ssp->prog_index = 0;
48824135Sgd78059 		ssp->image_ptr = 0;
48834135Sgd78059 	}
48844135Sgd78059 	return (res);
48854135Sgd78059 }
48864135Sgd78059 
48874135Sgd78059 static int
bscv_prog_stop_lom(bscv_soft_state_t * ssp)48884135Sgd78059 bscv_prog_stop_lom(bscv_soft_state_t *ssp)
48894135Sgd78059 {
48904135Sgd78059 	if (ssp->programming) {
48914135Sgd78059 		/*
48924135Sgd78059 		 * Already programming - this may be a retry of a failed
48934135Sgd78059 		 * programming attempt or just a software error!
48944135Sgd78059 		 */
48954135Sgd78059 		goto queue_stopped;
48964135Sgd78059 	}
48974135Sgd78059 
48984135Sgd78059 	if (bscv_pause_event_daemon(ssp) == BSCV_FAILURE) {
48999644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'Q', "bscv_prog_stop_lom",
49004135Sgd78059 		    "failed to pause event daemon thread");
49014135Sgd78059 		return (EAGAIN);
49024135Sgd78059 	}
49034135Sgd78059 
49044135Sgd78059 	bscv_enter(ssp);
49054135Sgd78059 
49064135Sgd78059 	ssp->programming = B_TRUE;
49074135Sgd78059 
49084135Sgd78059 	bscv_exit(ssp);
49094135Sgd78059 
49104135Sgd78059 queue_stopped:
49114135Sgd78059 
49124135Sgd78059 	ssp->prog_index = 0;
49134135Sgd78059 	ssp->image2_processing = B_FALSE;
49144135Sgd78059 
49154135Sgd78059 	return (0);
49164135Sgd78059 }
49174135Sgd78059 
49184135Sgd78059 static int
bscv_prog_start_lom(bscv_soft_state_t * ssp)49194135Sgd78059 bscv_prog_start_lom(bscv_soft_state_t *ssp)
49204135Sgd78059 {
49214135Sgd78059 	int res = 0;
49224135Sgd78059 
49234135Sgd78059 	if (!ssp->programming) {
49244135Sgd78059 		/* Not programming so this is not a valid command */
49254135Sgd78059 		return (EINVAL);
49264135Sgd78059 	}
49274135Sgd78059 
49284135Sgd78059 	if (ssp->image != NULL) {
49294135Sgd78059 		kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
49304135Sgd78059 		ssp->image = NULL;
49314135Sgd78059 	}
49324135Sgd78059 
49334135Sgd78059 	/*
49344135Sgd78059 	 * OK we are out of reset now so:
49354135Sgd78059 	 * Probe the firmware and set everything up.
49364135Sgd78059 	 */
49374135Sgd78059 
49384135Sgd78059 	bscv_enter(ssp);
49394135Sgd78059 
49404135Sgd78059 	/* Explicit clear fault because things may have been mended now */
49414135Sgd78059 	bscv_clear_fault(ssp);
49424135Sgd78059 
49434135Sgd78059 	if (ssp->loader_running) {
49444135Sgd78059 		cmn_err(CE_WARN, "Firmware upgrade failed to exit loader - "
49454135Sgd78059 		    "performing forced exit");
49464135Sgd78059 		/* Must try to restart the lom here. */
49474135Sgd78059 		/* Ensure prog mode entry to enable PRGMODE_OFF */
49484135Sgd78059 		bscv_put8(ssp, chan_prog,
49494135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
49504135Sgd78059 		    EBUS_PROGRAM_PCR_PRGMODE_ON);
49514135Sgd78059 		bscv_put8(ssp, chan_prog,
49524135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_PROGRAM, EBUS_PROGRAM_PCSR),
49534135Sgd78059 		    EBUS_PROGRAM_PCR_PRGMODE_OFF);
49544135Sgd78059 		ssp->loader_running = B_FALSE;
49554135Sgd78059 		/* give the lom chance to recover */
49564135Sgd78059 		delay(drv_usectohz(5000000));	/* 5 seconds */
49574135Sgd78059 	}
49584135Sgd78059 
49594135Sgd78059 	ssp->prog_mode_only = B_FALSE;
49604135Sgd78059 	ssp->programming = B_FALSE;
49614135Sgd78059 
49624135Sgd78059 	if (bscv_attach_common(ssp) == DDI_FAILURE) {
49634135Sgd78059 		ssp->prog_mode_only = B_TRUE;
49644135Sgd78059 		res = EIO;
49654135Sgd78059 	}
49664135Sgd78059 
49674135Sgd78059 	bscv_exit(ssp);
49684135Sgd78059 
49694135Sgd78059 	if (!ssp->prog_mode_only) {
49704135Sgd78059 		/*
49714135Sgd78059 		 * Start the event thread after the queue has started
49724135Sgd78059 		 *
49734135Sgd78059 		 * Not sure if this is entirely correct because
49744135Sgd78059 		 * the other code at the end of bscv_attach()
49754135Sgd78059 		 * does not get run here.
49764135Sgd78059 		 */
49774135Sgd78059 		bscv_start_event_daemon(ssp);
49784135Sgd78059 		bscv_resume_event_daemon(ssp);
49794135Sgd78059 	}
49804135Sgd78059 
49814135Sgd78059 	return (res);
49824135Sgd78059 }
49834135Sgd78059 
49844135Sgd78059 
49854135Sgd78059 /*
49864135Sgd78059  * *********************************************************************
49874135Sgd78059  * Attach processing
49884135Sgd78059  * *********************************************************************
49894135Sgd78059  */
49904135Sgd78059 
49914135Sgd78059 /*
49924135Sgd78059  * function	- bscv_attach_common
49934135Sgd78059  * description	- this routine co-ordinates the initialisation of the
49944135Sgd78059  *		  driver both at attach time and after firmware programming.
49954135Sgd78059  * sequence	- bscv_setup_capability - read LOMlite2 capabilities
49964135Sgd78059  *		  bscv_probe_check - test comms and setup register cache
49974135Sgd78059  *		  bscv_setup_hostname - sync stored name in lom with nodename.
49984135Sgd78059  *		  bscv_setup_static_info - read device names etc.
49994135Sgd78059  *		  bscv_setup_events - start event daemon etc.
50004135Sgd78059  *
50014135Sgd78059  * inputs	- device information structure, DDI_ATTACH command
50024135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
50034135Sgd78059  */
50044135Sgd78059 
50054135Sgd78059 static int
bscv_attach_common(bscv_soft_state_t * ssp)50064135Sgd78059 bscv_attach_common(bscv_soft_state_t *ssp)
50074135Sgd78059 {
50084135Sgd78059 	ASSERT(bscv_held(ssp));
50094135Sgd78059 
50109644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_attach_common:", "");
50114135Sgd78059 
50124135Sgd78059 	/*
50134135Sgd78059 	 * Set the threshold for reporting messages to the console to
50144135Sgd78059 	 * Warnings or higher.
50154135Sgd78059 	 */
50164135Sgd78059 	ssp->reporting_level = 2;
50174135Sgd78059 
50184135Sgd78059 	/*
50194135Sgd78059 	 * When the system is not running the Operating System, make
50204135Sgd78059 	 * the microcontroller print event messages straight onto the
50214135Sgd78059 	 * console.
50224135Sgd78059 	 */
50234135Sgd78059 	ssp->serial_reporting = LOM_SER_EVENTS_DEF;
50244135Sgd78059 
50254135Sgd78059 	/* Setup capabilities */
50264135Sgd78059 	bscv_setup_capability(ssp);
50274135Sgd78059 
50284135Sgd78059 	if (bscv_probe_check(ssp) == DDI_FAILURE) {
50294135Sgd78059 		cmn_err(CE_WARN, "BSC chip not responding");
50304135Sgd78059 		/*
50314135Sgd78059 		 * We want lom -G to talk to this driver upon broken firmware
50324135Sgd78059 		 * so we prematurely return success here.
50334135Sgd78059 		 */
50344135Sgd78059 		return (DDI_SUCCESS);
50354135Sgd78059 	}
50364135Sgd78059 
50374135Sgd78059 	bscv_setup_hostname(ssp);
50384135Sgd78059 	bscv_setup_static_info(ssp);
50394135Sgd78059 	bscv_setup_events(ssp);
50404135Sgd78059 
50414135Sgd78059 #if defined(__i386) || defined(__amd64)
50424135Sgd78059 	bscv_inform_bsc(ssp, BSC_INFORM_ONLINE);
50434135Sgd78059 #endif /* __i386 || __amd64 */
50444135Sgd78059 	/*
50454135Sgd78059 	 * Watchdog configuration and CPU signatures are sent asynchronously
50464135Sgd78059 	 * with respect to attach so only inform the BSC if we've already
50474135Sgd78059 	 * sent the data in the past.
50484135Sgd78059 	 */
50494135Sgd78059 
50504135Sgd78059 	if (ssp->progress & BSCV_WDOG_CFG)
50514135Sgd78059 		bscv_setup_watchdog(ssp);
50524135Sgd78059 
50534135Sgd78059 #ifdef __sparc
50544135Sgd78059 	if (ssp->progress & BSCV_SIG_SENT)
50554135Sgd78059 		bscv_write_sig(ssp, ssp->last_sig);
50564135Sgd78059 #endif /* __sparc */
50574135Sgd78059 
50584135Sgd78059 	return (DDI_SUCCESS);
50594135Sgd78059 }
50604135Sgd78059 
50614135Sgd78059 /*
50624135Sgd78059  * function	- bscv_cleanup
50634135Sgd78059  * description	- routine that does the necessary tidying up if the attach
50644135Sgd78059  *		  request fails or the driver is to be detached.
50654135Sgd78059  *		  If the event thread has been started we may fail to
50664135Sgd78059  *		  stop it (because it is busy) so we fail the cleanup
50674135Sgd78059  *		  and hence the detach. All other calls to bscv_cleanup
50684135Sgd78059  *		  are done before the event daemon is started.
50694135Sgd78059  * inputs	- soft state structure address.
50704135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE.
50714135Sgd78059  */
50724135Sgd78059 
50734135Sgd78059 static int
bscv_cleanup(bscv_soft_state_t * ssp)50744135Sgd78059 bscv_cleanup(bscv_soft_state_t *ssp)
50754135Sgd78059 {
50764135Sgd78059 	int	instance;
50774135Sgd78059 	uint8_t bits2set;
50784135Sgd78059 	uint8_t bits2clear;
50794135Sgd78059 
50804135Sgd78059 	instance = ssp->instance;
50814135Sgd78059 
50824135Sgd78059 	if (ssp->progress & BSCV_LOCKS) {
50834135Sgd78059 		bscv_enter(ssp);
50844135Sgd78059 	}
50854135Sgd78059 
50864135Sgd78059 	if (ssp->progress & BSCV_THREAD) {
50874135Sgd78059 		if (bscv_stop_event_daemon(ssp) == DDI_FAILURE) {
50884135Sgd78059 			/* Fail the cleanup - may be able to cleanup later */
50894135Sgd78059 			if (ssp->progress & BSCV_LOCKS) {
50904135Sgd78059 				bscv_exit(ssp);
50914135Sgd78059 			}
50924135Sgd78059 			return (DDI_FAILURE);
50934135Sgd78059 		}
50944135Sgd78059 	}
50954135Sgd78059 
50964135Sgd78059 	if (ssp->progress & BSCV_NODES) {
50974135Sgd78059 		ddi_remove_minor_node(ssp->dip, NULL);
50984135Sgd78059 	}
50994135Sgd78059 
51004135Sgd78059 	if (ssp->progress & BSCV_MAPPED_REGS) {
51014135Sgd78059 		/*
51024135Sgd78059 		 * switch back on serial event reporting - cover all configs.
51034135Sgd78059 		 */
51044135Sgd78059 		bits2set = 0;
51054135Sgd78059 		bits2clear = 0;
51064135Sgd78059 		if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
51074135Sgd78059 			bits2clear |= EBUS_ALARM_NOEVENTS;
51084135Sgd78059 		} else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
51094135Sgd78059 			bits2set |= EBUS_ALARM_NOEVENTS;
51104135Sgd78059 		} else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
51114135Sgd78059 			bits2clear |= EBUS_ALARM_NOEVENTS;
51124135Sgd78059 		}
51134135Sgd78059 		bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
51144135Sgd78059 		    bits2set, bits2clear);
51154135Sgd78059 
51164135Sgd78059 		/*
51174135Sgd78059 		 * disable the reset function if we have enabled
51184135Sgd78059 		 * it. We don't want any nasty surprises like system
51194135Sgd78059 		 * rebooting unexpectedly.  If we timeout on the busy
51204135Sgd78059 		 * flag we just have to carry on.
51214135Sgd78059 		 */
51224135Sgd78059 
51239644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'W', "bscv_cleanup",
51244135Sgd78059 		    "bscv_cleanup - disable wdog");
51254135Sgd78059 		if (bscv_get8_cached(ssp, EBUS_IDX_WDOG_CTRL) &
51264135Sgd78059 		    EBUS_WDOG_ENABLE) {
51274135Sgd78059 			bscv_setclear8(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
51284135Sgd78059 			    0, EBUS_WDOG_RST | EBUS_WDOG_ENABLE);
51294135Sgd78059 		}
51304135Sgd78059 	}
51314135Sgd78059 
51324135Sgd78059 	/*
51334135Sgd78059 	 * unmap registers
51344135Sgd78059 	 */
51354135Sgd78059 
51364135Sgd78059 	if (ssp->progress & BSCV_MAPPED_REGS) {
51374135Sgd78059 		bscv_unmap_regs(ssp);
51384135Sgd78059 	}
51394135Sgd78059 
51404135Sgd78059 	/*
51414135Sgd78059 	 * release any memory allocated for mutexes and condition
51424135Sgd78059 	 * variables before deallocating the structures containing them
51434135Sgd78059 	 */
51444135Sgd78059 
51454135Sgd78059 	if (ssp->progress & BSCV_LOCKS) {
51464135Sgd78059 		bscv_exit(ssp);
51474135Sgd78059 		cv_destroy(&ssp->task_cv);
51484135Sgd78059 		cv_destroy(&ssp->task_evnt_cv);
51494135Sgd78059 		mutex_destroy(&ssp->task_mu);
51504135Sgd78059 		mutex_destroy(&ssp->prog_mu);
51514135Sgd78059 		mutex_destroy(&ssp->cmd_mutex);
51524135Sgd78059 	}
51534135Sgd78059 
51544135Sgd78059 	if (ssp->image != NULL) {
51554135Sgd78059 		kmem_free((void *)ssp->image, BSC_IMAGE_MAX_SIZE);
51564135Sgd78059 	}
51574135Sgd78059 
51584135Sgd78059 #if defined(__i386) || defined(__amd64)
51594135Sgd78059 	bscv_watchdog_cyclic_remove(ssp);
51604135Sgd78059 #endif /* __i386 || __amd64 */
51614135Sgd78059 	ddi_soft_state_free(bscv_statep, instance);
51624135Sgd78059 
51634135Sgd78059 	return (DDI_SUCCESS);
51644135Sgd78059 }
51654135Sgd78059 
51664135Sgd78059 /*
51674135Sgd78059  * function	- bscv_setup_capability
51684135Sgd78059  * description	- probe the lom find what capabilities are present for
51694135Sgd78059  *		  us to use.
51704135Sgd78059  * inputs	- soft state ptr
51714135Sgd78059  * outputs	- returns DDI_SUCCESS or DDI_FAILURE
51724135Sgd78059  */
bscv_setup_capability(bscv_soft_state_t * ssp)51734135Sgd78059 static void bscv_setup_capability(bscv_soft_state_t *ssp)
51744135Sgd78059 {
51754135Sgd78059 	ASSERT(bscv_held(ssp));
51764135Sgd78059 
51774135Sgd78059 	if (ssp->prog_mode_only) {
51784135Sgd78059 		/* Turn off all capabilities */
51794135Sgd78059 		ssp->cap0 = 0;
51804135Sgd78059 		ssp->cap1 = 0;
51814135Sgd78059 		ssp->cap2 = 0;
51824135Sgd78059 		return;
51834135Sgd78059 	}
51844135Sgd78059 
51854135Sgd78059 	ssp->cap0 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP0);
51864135Sgd78059 	ssp->cap1 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP1);
51874135Sgd78059 	ssp->cap2 = bscv_get8(ssp, chan_general, EBUS_IDX_CAP2);
51884135Sgd78059 	if (!bscv_faulty(ssp)) {
51899644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_setup_capability",
51904135Sgd78059 		    "Capability flags cap0=0x%x cap1=0x%x, cap2=0x%x",
51914135Sgd78059 		    ssp->cap0, ssp->cap1, ssp->cap2);
51924135Sgd78059 	} else {
51934135Sgd78059 		cmn_err(CE_WARN, "!Could not read capability flags");
51944135Sgd78059 		ssp->cap0 = 0; ssp->cap1 = 0; ssp->cap2 = 0;
51954135Sgd78059 	}
51964135Sgd78059 }
51974135Sgd78059 
51984135Sgd78059 /*
51994135Sgd78059  * function	- bscv_probe_check
52004135Sgd78059  * description	- probe the lom to check for correct operation
52014135Sgd78059  *		  has a side effect of setting up the cached registers and
52024135Sgd78059  *		  updates ssp->prog_mode_only.
52034135Sgd78059  * inputs	- soft state ptr
52044135Sgd78059  * outputs	- returns DDI_SUCCESS or DDI_FAILURE
52054135Sgd78059  */
52064135Sgd78059 
bscv_probe_check(bscv_soft_state_t * ssp)52074135Sgd78059 static int bscv_probe_check(bscv_soft_state_t *ssp)
52084135Sgd78059 {
52094135Sgd78059 	int i;
52104135Sgd78059 	uint8_t probeval;
52114135Sgd78059 
52124135Sgd78059 	ASSERT(bscv_held(ssp));
52134135Sgd78059 
52149644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_probe_check", "");
52154135Sgd78059 
52164135Sgd78059 	if (!ssp->prog_mode_only) {
52174135Sgd78059 		/*
52184135Sgd78059 		 * Make sure probe location is OK so that we are
52194135Sgd78059 		 * in sync.
52204135Sgd78059 		 * We want to make sure that this is not faulty so we
52214135Sgd78059 		 * do a bscv_clear_fault to clear any existing
52224135Sgd78059 		 * fault records down.
52234135Sgd78059 		 */
52244135Sgd78059 		bscv_clear_fault(ssp);
52254135Sgd78059 		probeval = bscv_get8(ssp, chan_general, EBUS_IDX_PROBEAA);
52264135Sgd78059 		if (bscv_faulty(ssp)) {
52274135Sgd78059 			ssp->prog_mode_only = B_TRUE;
52284135Sgd78059 		} else if (probeval != 0xAA) {
52299644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'A', "bscv_probe_check",
52304135Sgd78059 			    "LOMlite out of sync");
52314135Sgd78059 
52324135Sgd78059 			/*
52334135Sgd78059 			 * It may be that the LOMlite was out of
52344135Sgd78059 			 * sync so lets try the read again.
52354135Sgd78059 			 */
52364135Sgd78059 			probeval = bscv_get8(ssp, chan_general,
52375107Seota 			    EBUS_IDX_PROBEAA);
52384135Sgd78059 			if (bscv_faulty(ssp)) {
52399644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'A', "bscv_probe_check",
52404135Sgd78059 				    "Init readAA1 failed");
52414135Sgd78059 				ssp->prog_mode_only = B_TRUE;
52424135Sgd78059 			} else if (probeval != 0xAA) {
52434135Sgd78059 				/*
52444135Sgd78059 				 * OK that is twice we are out so I
52454135Sgd78059 				 * guess the LOMlite is in trouble
52464135Sgd78059 				 */
52479644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'A', "bscv_probe_check",
52484135Sgd78059 				    "Init readAA probe failed - got 0x%x",
52494135Sgd78059 				    probeval);
52504135Sgd78059 				ssp->prog_mode_only = B_TRUE;
52514135Sgd78059 			}
52524135Sgd78059 		}
52534135Sgd78059 	}
52544135Sgd78059 
52554135Sgd78059 	/*
52564135Sgd78059 	 * Read in all page zero lom registers.
52574135Sgd78059 	 * Read state change 1st so we dont miss anything and clear it.
52584135Sgd78059 	 * Note: we discard the values because we rely on bscv_get8 to
52594135Sgd78059 	 * setup the cache of register values.
52604135Sgd78059 	 */
52614135Sgd78059 
52624135Sgd78059 	if (!ssp->prog_mode_only) {
52634135Sgd78059 		(void) bscv_get8(ssp, chan_general, EBUS_IDX_STATE_CHNG);
52644135Sgd78059 		if (bscv_faulty(ssp)) {
52659644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'A', "bscv_probe_check",
52664135Sgd78059 			    "Read of state change register failed");
52674135Sgd78059 			ssp->prog_mode_only = B_TRUE;
52684135Sgd78059 		}
52694135Sgd78059 	}
52704135Sgd78059 
52714135Sgd78059 	if (!ssp->prog_mode_only) {
52724135Sgd78059 		for (i = 1; i < 0x80; i++) {
52734135Sgd78059 			switch (i) {
52744135Sgd78059 			case EBUS_IDX_STATE_CHNG:
52754135Sgd78059 			case EBUS_IDX_CMD_RES:
52764135Sgd78059 			case EBUS_IDX_HNAME_CHAR:
52774135Sgd78059 				/*
52784135Sgd78059 				 * Should not read these - they have side
52794135Sgd78059 				 * effects.
52804135Sgd78059 				 */
52814135Sgd78059 				break;
52824135Sgd78059 			default:
52834135Sgd78059 				(void) bscv_get8(ssp, chan_general, i);
52844135Sgd78059 				break;
52854135Sgd78059 			}
52864135Sgd78059 			if (bscv_faulty(ssp)) {
52879644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'A', "bscv_probe_check",
52884135Sgd78059 				    "Initial read or register %2x failed", i);
52894135Sgd78059 				ssp->prog_mode_only = B_TRUE;
52904135Sgd78059 				/* Might as well give up now! */
52914135Sgd78059 				break;
52924135Sgd78059 			}
52934135Sgd78059 		}
52944135Sgd78059 	}
52954135Sgd78059 
52964135Sgd78059 	/*
52974135Sgd78059 	 * Check the probe keys so we know the lom is OK
52984135Sgd78059 	 */
52994135Sgd78059 
53004135Sgd78059 	if (!ssp->prog_mode_only) {
53014135Sgd78059 		if ((bscv_get8_cached(ssp, EBUS_IDX_PROBE55) != 0x55) ||
53024135Sgd78059 		    (bscv_get8_cached(ssp, EBUS_IDX_PROBEAA) != 0xAA)) {
53034135Sgd78059 
53049644Sandrew.rutz@sun.com 			BSCV_TRACE(ssp, 'A', "bscv_probe_check",
53054135Sgd78059 			    "LOMlite Probe failed");
53064135Sgd78059 			for (i = 0; i < 0x8; i++) {
53079644Sandrew.rutz@sun.com 				BSCV_TRACE(ssp, 'A', "bscv_probe_check",
53084135Sgd78059 				    "%2x %2x %2x %2x %2x %2x %2x %2x %2x "
53094135Sgd78059 				    "%2x %2x %2x %2x %2x %2x %2x %2x %2x",
53104135Sgd78059 				    bscv_get8_cached(ssp, i),
53114135Sgd78059 				    bscv_get8_cached(ssp, i + 1),
53124135Sgd78059 				    bscv_get8_cached(ssp, i + 2),
53134135Sgd78059 				    bscv_get8_cached(ssp, i + 3),
53144135Sgd78059 				    bscv_get8_cached(ssp, i + 4),
53154135Sgd78059 				    bscv_get8_cached(ssp, i + 5),
53164135Sgd78059 				    bscv_get8_cached(ssp, i + 6),
53174135Sgd78059 				    bscv_get8_cached(ssp, i + 7),
53184135Sgd78059 				    bscv_get8_cached(ssp, i + 8),
53194135Sgd78059 				    bscv_get8_cached(ssp, i + 9),
53204135Sgd78059 				    bscv_get8_cached(ssp, i + 10),
53214135Sgd78059 				    bscv_get8_cached(ssp, i + 11),
53224135Sgd78059 				    bscv_get8_cached(ssp, i + 12),
53234135Sgd78059 				    bscv_get8_cached(ssp, i + 13),
53244135Sgd78059 				    bscv_get8_cached(ssp, i + 14),
53254135Sgd78059 				    bscv_get8_cached(ssp, i + 15));
53264135Sgd78059 			}
53274135Sgd78059 			ssp->prog_mode_only = B_TRUE;
53284135Sgd78059 		}
53294135Sgd78059 	}
53304135Sgd78059 
53314135Sgd78059 	return ((ssp->prog_mode_only == B_FALSE) ? DDI_SUCCESS : DDI_FAILURE);
53324135Sgd78059 }
53334135Sgd78059 
53344135Sgd78059 #ifdef __sparc
53354135Sgd78059 /*
53364135Sgd78059  * function	- bscv_idi_set
53374135Sgd78059  * description	- bscv inter driver interface set function
53384135Sgd78059  * inputs	- structure which defines type of service required and data
53394135Sgd78059  * ouputs	- none
53404135Sgd78059  *
53414135Sgd78059  * This is the Entry Point function for the platmod driver. It works out which
53424135Sgd78059  * X Bus channel ought to deliver the service requested.
53434135Sgd78059  */
53444135Sgd78059 void
bscv_idi_set(struct bscv_idi_info info)53454135Sgd78059 bscv_idi_set(struct bscv_idi_info info)
53464135Sgd78059 {
53474135Sgd78059 	struct bscv_idi_callout *tbl;
53484135Sgd78059 	boolean_t retval;
53494135Sgd78059 
53504135Sgd78059 	ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC);
53514135Sgd78059 
53524135Sgd78059 	if (bscv_idi_mgr.tbl == NULL) {
53534135Sgd78059 		if (bscv_idi_err())
53544135Sgd78059 			cmn_err(CE_WARN, "!bscv_idi_set : cannot find "
53554135Sgd78059 			    "bscv_callout_table");
53564135Sgd78059 		return;
53574135Sgd78059 	} else if (bscv_idi_mgr.valid_inst == (uint32_t)~0) {
53584135Sgd78059 		if (bscv_idi_err())
53594135Sgd78059 			/*
53604135Sgd78059 			 * This error message can appear in the context of
53614135Sgd78059 			 * another driver, say platmod or todblade.  We want
53624135Sgd78059 			 * to clearly indicate the culprit driver so put in
53634135Sgd78059 			 * the driver name.
53644135Sgd78059 			 */
53654135Sgd78059 			cmn_err(CE_WARN, "!bscv_idi_set : no valid "
53664135Sgd78059 			    "driver instance of "
53674135Sgd78059 			    MYNAME);
53684135Sgd78059 		return;
53694135Sgd78059 	}
53704135Sgd78059 
53714135Sgd78059 	tbl = bscv_idi_mgr.tbl;
53724135Sgd78059 
53734135Sgd78059 	while (tbl->type != BSCV_IDI_NULL) {
53744135Sgd78059 		if (tbl->type == info.type) {
53754135Sgd78059 			/*
53764135Sgd78059 			 * We service the request with a valid instance number
53774135Sgd78059 			 * for the driver.
53784135Sgd78059 			 */
53794135Sgd78059 			retval = ((tbl->fn) (info));
53804135Sgd78059 
53814135Sgd78059 			/*
53824135Sgd78059 			 * If the request was serviced, clear any accumulated
53834135Sgd78059 			 * error counters so future warnings will be reported if
53844135Sgd78059 			 * seen.
53854135Sgd78059 			 */
53864135Sgd78059 			if (retval == B_TRUE)
53874135Sgd78059 				bscv_idi_clear_err();
53884135Sgd78059 			return;
53894135Sgd78059 		} else {
53904135Sgd78059 			tbl++;
53914135Sgd78059 		}
53924135Sgd78059 	}
53934135Sgd78059 
53944135Sgd78059 	if (bscv_idi_err())
53954135Sgd78059 		cmn_err(CE_WARN, "!bscv_idi_set : cannot match info.type %d",
53964135Sgd78059 		    info.type);
53974135Sgd78059 }
53984135Sgd78059 
53994135Sgd78059 /*
54004135Sgd78059  * function     - bscv_nodename_set
54014135Sgd78059  * description  - notify the event thread that a nodename change has occurred.
54024135Sgd78059  * inputs       - data from client driver
54034135Sgd78059  * outputs	- none.
54044135Sgd78059  * side-effects - the event thread will schedule an update to the lom firmware.
54054135Sgd78059  */
54064135Sgd78059 /*ARGSUSED*/
54074135Sgd78059 static boolean_t
bscv_nodename_set(struct bscv_idi_info info)54084135Sgd78059 bscv_nodename_set(struct bscv_idi_info info)
54094135Sgd78059 {
54104135Sgd78059 	bscv_soft_state_t *ssp;
54114135Sgd78059 
54124135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
54134135Sgd78059 
54144135Sgd78059 	if (ssp == NULL) {
54154135Sgd78059 		if (bscv_idi_err())
54164135Sgd78059 			cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
54174135Sgd78059 		return (B_FALSE);
54184135Sgd78059 	}
54194135Sgd78059 
54204135Sgd78059 	/* Get a lock on the SSP, notify our change, then exit */
54214135Sgd78059 	mutex_enter(&ssp->task_mu);
54224135Sgd78059 	ssp->nodename_change = B_TRUE;
54234135Sgd78059 	cv_signal(&ssp->task_cv);
54244135Sgd78059 	mutex_exit(&ssp->task_mu);
54254135Sgd78059 
54264135Sgd78059 	return (B_TRUE);
54274135Sgd78059 }
54284135Sgd78059 
54294135Sgd78059 /*
54304135Sgd78059  * function	- bscv_sig_set
54314135Sgd78059  * description	- write a signature
54324135Sgd78059  * inputs	- data from client driver
54334135Sgd78059  * outputs	- none.
54344135Sgd78059  */
54354135Sgd78059 static boolean_t
bscv_sig_set(struct bscv_idi_info info)54364135Sgd78059 bscv_sig_set(struct bscv_idi_info info)
54374135Sgd78059 {
54384135Sgd78059 	bscv_soft_state_t *ssp;
54394135Sgd78059 	bscv_sig_t sig;
54404135Sgd78059 
54414135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
54424135Sgd78059 
54434135Sgd78059 	if (ssp == NULL) {
54444135Sgd78059 		if (bscv_idi_err())
54454135Sgd78059 			cmn_err(CE_WARN, "!blade_nodename_set: cannot get ssp");
54464135Sgd78059 		return (B_FALSE);
54474135Sgd78059 	}
54484135Sgd78059 
54494135Sgd78059 	/* Service the request */
54504135Sgd78059 	bcopy(info.data, &sig, sizeof (sig));
54514135Sgd78059 	bscv_enter(ssp);
54524135Sgd78059 	bscv_write_sig(ssp, sig);
54534135Sgd78059 	bscv_exit(ssp);
54544135Sgd78059 
54554135Sgd78059 	return (B_TRUE);
54564135Sgd78059 }
54574135Sgd78059 #endif /* __sparc */
54584135Sgd78059 
54594135Sgd78059 static void
bscv_wdog_do_pat(bscv_soft_state_t * ssp)54604135Sgd78059 bscv_wdog_do_pat(bscv_soft_state_t *ssp)
54614135Sgd78059 {
54624135Sgd78059 	uint8_t pat;
54634135Sgd78059 
54644135Sgd78059 	/*
54654135Sgd78059 	 * The value of the dog pat is a sequence number which wraps around,
54664135Sgd78059 	 * bounded by BSCV_WDOG_PAT_SEQ_MASK.
54674135Sgd78059 	 */
54684135Sgd78059 	pat = ssp->pat_seq++;
54694135Sgd78059 	pat &= EBUS_WDOG_NB_PAT_SEQ_MASK;
54704135Sgd78059 
54714135Sgd78059 	/* Set top nibble to indicate a pat */
54724135Sgd78059 	pat |= EBUS_WDOG_NB_PAT;
54734135Sgd78059 
54744135Sgd78059 	/*
54754135Sgd78059 	 * Now pat the dog.  This exercises a special protocol in the
54764135Sgd78059 	 * bus nexus that offers : non-blocking IO, and timely delivery,
54774135Sgd78059 	 * callable from high-level interrupt context.  The requirement
54784135Sgd78059 	 * on us is that the channel is not shared for any other use.
54794135Sgd78059 	 * This means for chan_wdogpat, nothing may use channel[chan].regs
54804135Sgd78059 	 * or channel.[chan].handle.
54814135Sgd78059 	 */
54824135Sgd78059 
54834135Sgd78059 	ddi_put8(ssp->channel[chan_wdogpat].handle,
54844135Sgd78059 	    ssp->channel[chan_wdogpat].regs, pat);
54854135Sgd78059 
54869644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'W', "bscv_wdog_pat", "patted the dog with seq %d",
54874135Sgd78059 	    pat);
54884135Sgd78059 }
54894135Sgd78059 
54904135Sgd78059 #ifdef __sparc
54914135Sgd78059 /*
54924135Sgd78059  * function	- bscv_wdog_pat
54934135Sgd78059  * description	- pat the watchdog
54944135Sgd78059  * inputs	- data from client driver
54954135Sgd78059  * outputs	- none.
54964135Sgd78059  */
54974135Sgd78059 /*ARGSUSED*/
54984135Sgd78059 static boolean_t
bscv_wdog_pat(struct bscv_idi_info info)54994135Sgd78059 bscv_wdog_pat(struct bscv_idi_info info)
55004135Sgd78059 {
55014135Sgd78059 	/*
55024135Sgd78059 	 * This function remembers if it has ever been called with the
55034135Sgd78059 	 * configure option set.
55044135Sgd78059 	 */
55054135Sgd78059 	bscv_soft_state_t *ssp;
55064135Sgd78059 
55074135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
55084135Sgd78059 
55094135Sgd78059 	if (ssp == NULL) {
55104135Sgd78059 		if (bscv_idi_err())
55114135Sgd78059 			cmn_err(CE_WARN, "!bscv_wdog_pat: cannot get ssp");
55124135Sgd78059 		return (B_FALSE);
55134135Sgd78059 	} else if (ssp->nchannels == 0) {
55144135Sgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
55154135Sgd78059 		if (bscv_idi_err())
55164135Sgd78059 			cmn_err(CE_WARN, "!bscv_wdog_pat: handle not mapped");
55174135Sgd78059 		return (B_FALSE);
55184135Sgd78059 	}
55194135Sgd78059 
55204135Sgd78059 	bscv_wdog_do_pat(ssp);
55214135Sgd78059 	return (B_TRUE);
55224135Sgd78059 }
55234135Sgd78059 
55244135Sgd78059 /*
55254135Sgd78059  * function	- bscv_wdog_cfg
55264135Sgd78059  * description	- configure the watchdog
55274135Sgd78059  * inputs	- data from client driver
55284135Sgd78059  * outputs	- none.
55294135Sgd78059  */
55304135Sgd78059 static boolean_t
bscv_wdog_cfg(struct bscv_idi_info info)55314135Sgd78059 bscv_wdog_cfg(struct bscv_idi_info info)
55324135Sgd78059 {
55334135Sgd78059 	bscv_soft_state_t *ssp;
55344135Sgd78059 
55354135Sgd78059 	ssp = ddi_get_soft_state(bscv_statep, bscv_idi_mgr.valid_inst);
55364135Sgd78059 
55374135Sgd78059 	if (ssp == NULL) {
55384135Sgd78059 		if (bscv_idi_err())
55394135Sgd78059 			cmn_err(CE_WARN, "!bscv_wdog_cfg: cannot get ssp");
55404135Sgd78059 		return (B_FALSE);
55414135Sgd78059 	} else if (ssp->nchannels == 0) {
55424135Sgd78059 		/* Didn't manage to map handles so ddi_{get,put}* broken */
55434135Sgd78059 		if (bscv_idi_err())
55444135Sgd78059 			cmn_err(CE_WARN, "!bscv_wdog_cfg: handle not mapped");
55454135Sgd78059 		return (B_FALSE);
55464135Sgd78059 	}
55474135Sgd78059 
55484135Sgd78059 	if (sizeof (bscv_wdog_t) != info.size) {
55499644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'W', "bscv_wdog_set", "data passed in is size"
55504135Sgd78059 		    " %d instead of %d", info.size,
55514135Sgd78059 		    sizeof (bscv_wdog_t));
55524135Sgd78059 		return (B_FALSE);
55534135Sgd78059 	}
55544135Sgd78059 
55559644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'W', "bscv_wdog_cfg", "enable_wdog %s, "
55564135Sgd78059 	    "wdog_timeout_s %d, reset_system_on_timeout %s",
55574135Sgd78059 	    ((bscv_wdog_t *)info.data)->enable_wdog ? "enabled" : "disabled",
55584135Sgd78059 	    ((bscv_wdog_t *)info.data)->wdog_timeout_s,
55594135Sgd78059 	    ((bscv_wdog_t *)info.data)->reset_system_on_timeout ? "yes" : "no");
55604135Sgd78059 	bscv_write_wdog_cfg(ssp,
55614135Sgd78059 	    ((bscv_wdog_t *)info.data)->wdog_timeout_s,
55624135Sgd78059 	    ((bscv_wdog_t *)info.data)->enable_wdog,
55634135Sgd78059 	    ((bscv_wdog_t *)info.data)->reset_system_on_timeout);
55644135Sgd78059 	return (B_TRUE);
55654135Sgd78059 }
55664135Sgd78059 #endif /* __sparc */
55674135Sgd78059 
55684135Sgd78059 static void
bscv_write_wdog_cfg(bscv_soft_state_t * ssp,uint_t wdog_timeout_s,boolean_t enable_wdog,uint8_t reset_system_on_timeout)55694135Sgd78059 bscv_write_wdog_cfg(bscv_soft_state_t *ssp,
55704135Sgd78059     uint_t wdog_timeout_s,
55714135Sgd78059     boolean_t enable_wdog,
55724135Sgd78059     uint8_t reset_system_on_timeout)
55734135Sgd78059 {
55744135Sgd78059 	uint8_t cfg = EBUS_WDOG_NB_CFG;
55754135Sgd78059 
55764135Sgd78059 	/*
55774135Sgd78059 	 * Configure the timeout value (1 to 127 seconds).
55784135Sgd78059 	 * Note that a policy is implemented at the bsc/ssp which bounds
55794135Sgd78059 	 * the value further. The bounding here is to fit the timeout value
55804135Sgd78059 	 * into the 7 bits the bsc uses.
55814135Sgd78059 	 */
55824135Sgd78059 	if (wdog_timeout_s < 1)
55834135Sgd78059 		ssp->watchdog_timeout = 1;
55844135Sgd78059 	else if (wdog_timeout_s > 127)
55854135Sgd78059 		ssp->watchdog_timeout = 127;
55864135Sgd78059 	else
55874135Sgd78059 		ssp->watchdog_timeout = wdog_timeout_s;
55884135Sgd78059 
55894135Sgd78059 	/*
55904135Sgd78059 	 * Configure the watchdog on or off.
55914135Sgd78059 	 */
55924135Sgd78059 	if (enable_wdog)
55934135Sgd78059 		cfg |= EBUS_WDOG_NB_CFG_ENB;
55944135Sgd78059 	else
55954135Sgd78059 		cfg &= ~EBUS_WDOG_NB_CFG_ENB;
55964135Sgd78059 
55974135Sgd78059 	/*
55984135Sgd78059 	 * Configure whether the microcontroller should reset the system when
55994135Sgd78059 	 * the watchdog expires.
56004135Sgd78059 	 */
56014135Sgd78059 	ssp->watchdog_reset_on_timeout = reset_system_on_timeout;
56024135Sgd78059 
56034135Sgd78059 	ddi_put8(ssp->channel[chan_wdogpat].handle,
56044135Sgd78059 	    ssp->channel[chan_wdogpat].regs, cfg);
56054135Sgd78059 
56064135Sgd78059 	/* have the event daemon set the timeout value and whether to reset */
56074135Sgd78059 	ssp->watchdog_change = B_TRUE;
56084135Sgd78059 
56099644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'W', "bscv_wdog_cfg",
56104135Sgd78059 	    "configured the dog with cfg 0x%x", cfg);
56114135Sgd78059 }
56124135Sgd78059 
56134135Sgd78059 /*
56144135Sgd78059  * function	- bscv_setup_watchdog
56154135Sgd78059  * description	- setup the  bsc watchdog
56164135Sgd78059  * inputs	- soft state ptr
56174135Sgd78059  * outputs	-
56184135Sgd78059  */
bscv_setup_watchdog(bscv_soft_state_t * ssp)56194135Sgd78059 static void bscv_setup_watchdog(bscv_soft_state_t *ssp)
56204135Sgd78059 {
56214135Sgd78059 	uint8_t set = 0;
56224135Sgd78059 	uint8_t clear = 0;
56234135Sgd78059 #ifdef __sparc
56244135Sgd78059 	extern int watchdog_activated;
56254135Sgd78059 #endif /* __sparc */
56264135Sgd78059 
56274135Sgd78059 	ASSERT(bscv_held(ssp));
56284135Sgd78059 
56294135Sgd78059 	/* Set the timeout */
56304135Sgd78059 	bscv_put8(ssp, chan_general,
56315107Seota 	    EBUS_IDX_WDOG_TIME, ssp->watchdog_timeout);
56324135Sgd78059 
56334135Sgd78059 	/* Set whether to reset the system on timeout */
56344135Sgd78059 	if (ssp->watchdog_reset_on_timeout) {
56354135Sgd78059 		set |= EBUS_WDOG_RST;
56364135Sgd78059 	} else {
56374135Sgd78059 		clear |= EBUS_WDOG_RST;
56384135Sgd78059 	}
56394135Sgd78059 
56404135Sgd78059 	if (watchdog_activated) {
56414135Sgd78059 		set |= EBUS_WDOG_ENABLE;
56424135Sgd78059 	} else {
56434135Sgd78059 		clear |= EBUS_WDOG_ENABLE;
56444135Sgd78059 	}
56454135Sgd78059 
56464135Sgd78059 	/* Set other host defaults */
56474135Sgd78059 	clear |= (EBUS_WDOG_BREAK_DISABLE | EBUS_WDOG_AL3_FANPSU
56484135Sgd78059 	    | EBUS_WDOG_AL3_WDOG);
56494135Sgd78059 
56504135Sgd78059 	bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_WDOG_CTRL,
56514135Sgd78059 	    set, clear);
56524135Sgd78059 
56534135Sgd78059 #if defined(__i386) || defined(__amd64)
56544135Sgd78059 	/* start the cyclic based watchdog patter */
56554135Sgd78059 	bscv_watchdog_cyclic_add(ssp);
56564135Sgd78059 #endif /* __i386 || __amd64 */
56574135Sgd78059 	ssp->progress |= BSCV_WDOG_CFG;
56584135Sgd78059 }
56594135Sgd78059 
56604135Sgd78059 
56614135Sgd78059 /*
56624135Sgd78059  * function	- bscv_setup_hostname
56634135Sgd78059  * description	- setup the lom hostname if different from the nodename
56644135Sgd78059  * inputs	- soft state ptr
56654135Sgd78059  * outputs	- none
56664135Sgd78059  */
56674135Sgd78059 
bscv_setup_hostname(bscv_soft_state_t * ssp)56684135Sgd78059 static void bscv_setup_hostname(bscv_soft_state_t *ssp)
56694135Sgd78059 {
56704135Sgd78059 	char	host_nodename[128];
56714135Sgd78059 	char	lom_nodename[128];
56724135Sgd78059 	size_t	hostlen;
56734135Sgd78059 	size_t	nodelen;
56744135Sgd78059 
56754135Sgd78059 	ASSERT(bscv_held(ssp));
56764135Sgd78059 
56774135Sgd78059 	/*
56784135Sgd78059 	 * Check machine label is the same as the
56794135Sgd78059 	 * system nodename.
56804135Sgd78059 	 */
56814135Sgd78059 	(void) strncpy(host_nodename, utsname.nodename,
56824135Sgd78059 	    sizeof (host_nodename));
56834135Sgd78059 
56844135Sgd78059 	/* read in lom hostname */
56854135Sgd78059 	bscv_read_hostname(ssp, lom_nodename);
56864135Sgd78059 
56874135Sgd78059 	/* Enforce null termination */
56884135Sgd78059 	host_nodename[sizeof (host_nodename) - 1] = '\0';
56894135Sgd78059 	lom_nodename[sizeof (lom_nodename) - 1] = '\0';
56904135Sgd78059 
56914135Sgd78059 	hostlen = (size_t)bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
56924135Sgd78059 	nodelen = (size_t)strlen(host_nodename);
56934135Sgd78059 	if ((nodelen > 0) &&
56944135Sgd78059 	    ((hostlen != nodelen) || (strcmp((const char *)&lom_nodename,
56954135Sgd78059 	    (const char *)&host_nodename)) ||
56964135Sgd78059 	    (hostlen == 0))) {
56979644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'A', "bscv_setup_hostname",
56984135Sgd78059 		    "nodename(%s,%d) != bsc label(%s,%d)",
56994135Sgd78059 		    host_nodename, nodelen, lom_nodename, hostlen);
57004135Sgd78059 
57014135Sgd78059 		/* Write new label into LOM EEPROM */
57024135Sgd78059 		bscv_write_hostname(ssp,
57034135Sgd78059 		    host_nodename,
57044135Sgd78059 		    (uint8_t)strlen(host_nodename));
57054135Sgd78059 	}
57064135Sgd78059 
57074135Sgd78059 	ssp->progress |= BSCV_HOSTNAME_DONE;
57084135Sgd78059 }
57094135Sgd78059 
57104135Sgd78059 /*
57114135Sgd78059  * function	- bscv_read_hostname
57124135Sgd78059  * description	- read the current hostname from the lom
57134135Sgd78059  * inputs	- soft state pointer and buffer to store the hostname in.
57144135Sgd78059  * outputs	- none
57154135Sgd78059  */
57164135Sgd78059 
57174135Sgd78059 static void
bscv_read_hostname(bscv_soft_state_t * ssp,char * lom_nodename)57184135Sgd78059 bscv_read_hostname(bscv_soft_state_t *ssp, char *lom_nodename)
57194135Sgd78059 {
57204135Sgd78059 	int num_failures;
57214135Sgd78059 	boolean_t needretry;
57224135Sgd78059 	int length;
57234135Sgd78059 	int i;
57244135Sgd78059 
57254135Sgd78059 	ASSERT(bscv_held(ssp));
57264135Sgd78059 
57274135Sgd78059 	/*
57284135Sgd78059 	 * We have a special failure case here because a retry of a read
57294135Sgd78059 	 * causes data to be lost. Thus we handle the retries ourselves
57304135Sgd78059 	 * and are also responsible for detemining if the lom is faulty
57314135Sgd78059 	 */
57324135Sgd78059 	for (num_failures = 0;
57334135Sgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
57344135Sgd78059 	    num_failures++) {
57354135Sgd78059 		bscv_clear_fault(ssp);
57364135Sgd78059 		length = bscv_get8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH);
57374135Sgd78059 		if (bscv_faulty(ssp)) {
57384135Sgd78059 			needretry = 1;
57394135Sgd78059 		} else {
57404135Sgd78059 			needretry = 0;
57414135Sgd78059 			for (i = 0; i < length; i++) {
57424135Sgd78059 				lom_nodename[i] = bscv_get8_once(ssp,
57434135Sgd78059 				    chan_general, EBUS_IDX_HNAME_CHAR);
57444135Sgd78059 				/* Retry on any error */
57454135Sgd78059 				if (bscv_retcode(ssp) != 0) {
57464135Sgd78059 					needretry = 1;
57474135Sgd78059 					break;
57484135Sgd78059 				}
57494135Sgd78059 			}
57504135Sgd78059 			/* null terminate for strcmp later */
57514135Sgd78059 			lom_nodename[length] = '\0';
57524135Sgd78059 		}
57534135Sgd78059 		if (!needretry) {
57544135Sgd78059 			break;
57554135Sgd78059 		}
57564135Sgd78059 		/* Force the nodename to be empty */
57574135Sgd78059 		lom_nodename[0] = '\0';
57584135Sgd78059 	}
57594135Sgd78059 
57604135Sgd78059 	if (needretry) {
57614135Sgd78059 		/* Failure - we ran out of retries */
57624135Sgd78059 		cmn_err(CE_WARN,
57634135Sgd78059 		    "bscv_read_hostname: retried %d times, giving up",
57644135Sgd78059 		    num_failures);
57654135Sgd78059 		ssp->had_fault = B_TRUE;
57664135Sgd78059 	} else if (num_failures > 0) {
57679644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'R', "bscv_read_hostname",
57684135Sgd78059 		    "retried %d times, succeeded", num_failures);
57694135Sgd78059 	}
57704135Sgd78059 }
57714135Sgd78059 
57724135Sgd78059 /*
57734135Sgd78059  * function	- bscv_write_hostname
57744135Sgd78059  * description	- write a new hostname to the lom
57754135Sgd78059  * inputs	- soft state pointer, pointer to new name, name length
57764135Sgd78059  * outputs	- none
57774135Sgd78059  */
57784135Sgd78059 static void
bscv_write_hostname(bscv_soft_state_t * ssp,char * host_nodename,uint8_t length)57794135Sgd78059 bscv_write_hostname(bscv_soft_state_t *ssp,
57804135Sgd78059     char *host_nodename, uint8_t length)
57814135Sgd78059 {
57824135Sgd78059 	int num_failures;
57834135Sgd78059 	boolean_t needretry;
57844135Sgd78059 	int i;
57854135Sgd78059 
57864135Sgd78059 	ASSERT(bscv_held(ssp));
57874135Sgd78059 
57884135Sgd78059 	/*
57894135Sgd78059 	 * We have a special failure case here because a retry of a read
57904135Sgd78059 	 * causes data to be lost. Thus we handle the retries ourselves
57914135Sgd78059 	 * and are also responsible for detemining if the lom is faulty
57924135Sgd78059 	 */
57934135Sgd78059 	for (num_failures = 0;
57944135Sgd78059 	    num_failures < BSC_FAILURE_RETRY_LIMIT;
57954135Sgd78059 	    num_failures++) {
57964135Sgd78059 		bscv_clear_fault(ssp);
57974135Sgd78059 		bscv_put8(ssp, chan_general, EBUS_IDX_HNAME_LENGTH, length);
57984135Sgd78059 		if (bscv_faulty(ssp)) {
57994135Sgd78059 			needretry = 1;
58004135Sgd78059 		} else {
58014135Sgd78059 			needretry = 0;
58024135Sgd78059 			for (i = 0; i < length; i++) {
58034135Sgd78059 				bscv_put8_once(ssp, chan_general,
58045107Seota 				    EBUS_IDX_HNAME_CHAR, host_nodename[i]);
58054135Sgd78059 				/* Retry on any error */
58064135Sgd78059 				if (bscv_retcode(ssp) != 0) {
58074135Sgd78059 					needretry = 1;
58084135Sgd78059 					break;
58094135Sgd78059 				}
58104135Sgd78059 			}
58114135Sgd78059 		}
58124135Sgd78059 		if (!needretry) {
58134135Sgd78059 			break;
58144135Sgd78059 		}
58154135Sgd78059 	}
58164135Sgd78059 
58174135Sgd78059 	if (needretry) {
58184135Sgd78059 		/* Failure - we ran out of retries */
58194135Sgd78059 		cmn_err(CE_WARN,
58204135Sgd78059 		    "bscv_write_hostname: retried %d times, giving up",
58214135Sgd78059 		    num_failures);
58224135Sgd78059 		ssp->had_fault = B_TRUE;
58234135Sgd78059 	} else if (num_failures > 0) {
58249644Sandrew.rutz@sun.com 		BSCV_TRACE(ssp, 'R', "bscv_write_hostname",
58254135Sgd78059 		    "retried %d times, succeeded", num_failures);
58264135Sgd78059 	}
58274135Sgd78059 }
58284135Sgd78059 
58294135Sgd78059 /*
58304135Sgd78059  * function	- bscv_setup_static_info
58314135Sgd78059  * description	- read in static information from the lom at attach time.
58324135Sgd78059  * inputs	- soft state ptr
58334135Sgd78059  * outputs	- none
58344135Sgd78059  */
58354135Sgd78059 
58364135Sgd78059 static void
bscv_setup_static_info(bscv_soft_state_t * ssp)58374135Sgd78059 bscv_setup_static_info(bscv_soft_state_t *ssp)
58384135Sgd78059 {
58394135Sgd78059 	uint8_t	addr_space_ptr;
58404135Sgd78059 	uint16_t mask;
58414135Sgd78059 	uint8_t fanspeed;
58424135Sgd78059 	int oldtemps[MAX_TEMPS];
58434135Sgd78059 	int8_t temp;
58444135Sgd78059 	int i;
58454135Sgd78059 
58464135Sgd78059 	ASSERT(bscv_held(ssp));
58474135Sgd78059 
58484135Sgd78059 	/*
58494135Sgd78059 	 * Finally read in some static info like device names,
58504135Sgd78059 	 * shutdown enabled, etc before the queue starts.
58514135Sgd78059 	 */
58524135Sgd78059 
58534135Sgd78059 	/*
58544135Sgd78059 	 * To get the volts static info we need address space 2
58554135Sgd78059 	 */
58564135Sgd78059 	bzero(&ssp->volts, sizeof (lom_volts_t));
58574135Sgd78059 	ssp->volts.num = EBUS_CONFIG2_NSUPPLY_DEC(
58585107Seota 	    bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
58594135Sgd78059 	if (ssp->volts.num > MAX_VOLTS) {
58604135Sgd78059 		cmn_err(CE_WARN,
58614135Sgd78059 		    "lom: firmware reported too many voltage lines. ");
58624135Sgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
58634135Sgd78059 		    ssp->volts.num, MAX_VOLTS);
58644135Sgd78059 		ssp->volts.num = MAX_VOLTS;
58654135Sgd78059 	}
58664135Sgd78059 
58679644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_setup_static_info",
58684135Sgd78059 	    "num volts %d", ssp->volts.num);
58694135Sgd78059 	(void) bscv_read_env_name(ssp,
58704135Sgd78059 	    EBUS_CMD_SPACE2,
58714135Sgd78059 	    EBUS_IDX2_SUPPLY_NAME_START,
58724135Sgd78059 	    EBUS_IDX2_SUPPLY_NAME_END,
58734135Sgd78059 	    ssp->volts.name,
58744135Sgd78059 	    ssp->volts.num);
58754135Sgd78059 
58764135Sgd78059 	mask = bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
58774135Sgd78059 	    EBUS_IDX2_SUPPLY_FATAL_MASK1)) << 8;
58784135Sgd78059 	mask |= bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE2,
58794135Sgd78059 	    EBUS_IDX2_SUPPLY_FATAL_MASK2));
58804135Sgd78059 
58814135Sgd78059 	for (i = 0; i < ssp->volts.num; i++) {
58824135Sgd78059 		ssp->volts.shutdown_enabled[i] =
58835107Seota 		    (((mask >> i) & 1) == 0) ? 0 : 1;
58844135Sgd78059 	}
58854135Sgd78059 
58864135Sgd78059 	/*
58874135Sgd78059 	 * Get the temperature static info and populate initial temperatures.
58884135Sgd78059 	 * Do not destroy old temperature values if the new value is not
58894135Sgd78059 	 * known i.e. if the device is inaccessible.
58904135Sgd78059 	 */
58914135Sgd78059 	bcopy(ssp->temps.temp, oldtemps, sizeof (oldtemps));
58924135Sgd78059 
58934135Sgd78059 	bzero(&ssp->temps, sizeof (lom_temp_t));
58944135Sgd78059 	ssp->temps.num = EBUS_CONFIG2_NTEMP_DEC(
58955107Seota 	    bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG2));
58964135Sgd78059 	if (ssp->temps.num > MAX_TEMPS) {
58974135Sgd78059 		cmn_err(CE_WARN,
58984135Sgd78059 		    "lom: firmware reported too many temperatures being "
58994135Sgd78059 		    "monitored.");
59004135Sgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
59014135Sgd78059 		    ssp->temps.num, MAX_TEMPS);
59024135Sgd78059 		ssp->temps.num = MAX_TEMPS;
59034135Sgd78059 	}
59044135Sgd78059 	ssp->temps.num_ov = EBUS_CONFIG3_NOTEMP_DEC(
59055107Seota 	    bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG3));
59064135Sgd78059 	if (ssp->temps.num_ov > MAX_TEMPS) {
59074135Sgd78059 		cmn_err(CE_WARN,
59084135Sgd78059 		    "lom: firmware reported too many over temperatures being "
59094135Sgd78059 		    "monitored.");
59104135Sgd78059 		cmn_err(CE_CONT, "Reported %d, maximum is %d",
59114135Sgd78059 		    ssp->temps.num_ov, MAX_TEMPS);
59124135Sgd78059 		ssp->temps.num_ov = MAX_TEMPS;
59134135Sgd78059 	}
59149644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_setup_static_info",
59154135Sgd78059 	    "num temps %d, over temps %d",
59164135Sgd78059 	    ssp->temps.num, ssp->temps.num_ov);
59174135Sgd78059 
59184135Sgd78059 	addr_space_ptr = bscv_read_env_name(ssp,
59194135Sgd78059 	    EBUS_CMD_SPACE4,
59204135Sgd78059 	    EBUS_IDX4_TEMP_NAME_START,
59214135Sgd78059 	    EBUS_IDX4_TEMP_NAME_END,
59224135Sgd78059 	    ssp->temps.name,
59234135Sgd78059 	    ssp->temps.num);
59244135Sgd78059 
59254135Sgd78059 	for (i = 0; i < ssp->temps.num; i++) {
59264135Sgd78059 		ssp->temps.warning[i] = (int8_t)bscv_get8(ssp, chan_general,
59274135Sgd78059 		    BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_WARN1 + i));
59284135Sgd78059 
59294135Sgd78059 		/*
59304135Sgd78059 		 * If shutdown is not enabled then set it as zero so
59314135Sgd78059 		 * it is not displayed by the utility.
59324135Sgd78059 		 */
59334135Sgd78059 		if ((bscv_get8(ssp, chan_general, BSCVA(EBUS_CMD_SPACE4,
59344135Sgd78059 		    EBUS_IDX4_TEMP_FATAL_MASK)) >> i) & 0x01) {
59354135Sgd78059 			ssp->temps.shutdown[i] = (int8_t)bscv_get8(ssp,
59364135Sgd78059 			    chan_general,
59374135Sgd78059 			    BSCVA(EBUS_CMD_SPACE4, EBUS_IDX4_TEMP_SDOWN1 + i));
59384135Sgd78059 		} else {
59394135Sgd78059 			ssp->temps.shutdown[i] = 0;
59404135Sgd78059 		}
59414135Sgd78059 	}
59424135Sgd78059 
59434135Sgd78059 	for (i = 0; i < ssp->temps.num; i++) {
59444135Sgd78059 		temp = bscv_get8(ssp, chan_general, EBUS_IDX_TEMP1 + i);
59454135Sgd78059 		if ((temp <= LOM_TEMP_MAX_VALUE) ||
59464135Sgd78059 		    (temp == LOM_TEMP_STATE_NOT_PRESENT)) {
59474135Sgd78059 			ssp->temps.temp[i] = temp;
59484135Sgd78059 		} else {
59494135Sgd78059 			/* New value is not known - use old value */
59504135Sgd78059 			ssp->temps.temp[i] = oldtemps[i];
59514135Sgd78059 		}
59524135Sgd78059 	}
59534135Sgd78059 
59544135Sgd78059 	/*
59554135Sgd78059 	 * Check for and skip a single 0xff character between the
59564135Sgd78059 	 * temperature and over temperature names
59574135Sgd78059 	 */
59584135Sgd78059 	if (bscv_get8(ssp, chan_general,
59594135Sgd78059 	    BSCVA(EBUS_CMD_SPACE4, addr_space_ptr)) == 0xff) {
59604135Sgd78059 		addr_space_ptr++;
59614135Sgd78059 	}
59624135Sgd78059 
59634135Sgd78059 	(void) bscv_read_env_name(ssp,
59644135Sgd78059 	    EBUS_CMD_SPACE4,
59654135Sgd78059 	    addr_space_ptr,
59664135Sgd78059 	    EBUS_IDX4_TEMP_NAME_END,
59674135Sgd78059 	    ssp->temps.name_ov,
59684135Sgd78059 	    ssp->temps.num_ov);
59694135Sgd78059 
59704135Sgd78059 	/*
59714135Sgd78059 	 * To get the CB static info we need address space 3
59724135Sgd78059 	 */
59734135Sgd78059 	bzero(&ssp->sflags, sizeof (lom_sflags_t));
59744135Sgd78059 	ssp->sflags.num = EBUS_CONFIG3_NBREAKERS_DEC(bscv_get8(ssp,
59754135Sgd78059 	    chan_general, EBUS_IDX_CONFIG3));
59764135Sgd78059 	if (ssp->sflags.num > MAX_STATS) {
59774135Sgd78059 		cmn_err(CE_WARN,
59784135Sgd78059 		    "lom: firmware reported too many status flags.");
59794135Sgd78059 		cmn_err(CE_CONT,
59804135Sgd78059 		    "Reported %d, maximum is %d",
59814135Sgd78059 		    ssp->sflags.num, MAX_STATS);
59824135Sgd78059 		ssp->sflags.num = MAX_STATS;
59834135Sgd78059 	}
59849644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_setup_static_info",
59854135Sgd78059 	    "num sflags %d", ssp->sflags.num);
59864135Sgd78059 
59874135Sgd78059 	(void) bscv_read_env_name(ssp,
59884135Sgd78059 	    EBUS_CMD_SPACE3,
59894135Sgd78059 	    EBUS_IDX3_BREAKER_NAME_START,
59904135Sgd78059 	    EBUS_IDX3_BREAKER_NAME_END,
59914135Sgd78059 	    ssp->sflags.name,
59924135Sgd78059 	    ssp->sflags.num);
59934135Sgd78059 
59944135Sgd78059 
59954135Sgd78059 	/*
59964135Sgd78059 	 * To get the fan static info we need address space 5
59974135Sgd78059 	 */
59984135Sgd78059 	ssp->num_fans = EBUS_CONFIG_NFAN_DEC(
59995107Seota 	    bscv_get8(ssp, chan_general, EBUS_IDX_CONFIG));
60004135Sgd78059 	if (ssp->num_fans > MAX_FANS) {
60014135Sgd78059 		cmn_err(CE_WARN,
60024135Sgd78059 		    "lom: firmware reported too many fans. ");
60034135Sgd78059 		cmn_err(CE_CONT,
60044135Sgd78059 		    "Reported %d, maximum is %d",
60054135Sgd78059 		    ssp->num_fans, MAX_FANS);
60064135Sgd78059 		ssp->num_fans = MAX_FANS;
60074135Sgd78059 	}
60084135Sgd78059 
60094135Sgd78059 	for (i = 0; i < ssp->num_fans; i++) {
60104135Sgd78059 		fanspeed = bscv_get8(ssp, chan_general,
60115107Seota 		    EBUS_IDX_FAN1_SPEED + i);
60124135Sgd78059 		if ((fanspeed <= LOM_FAN_MAX_SPEED) ||
60134135Sgd78059 		    (fanspeed == LOM_FAN_NOT_PRESENT)) {
60144135Sgd78059 			/*
60154135Sgd78059 			 * Do not destroy previous values unless the
60164135Sgd78059 			 * value is definitive.
60174135Sgd78059 			 */
60184135Sgd78059 			ssp->fanspeed[i] = fanspeed;
60194135Sgd78059 		}
60204135Sgd78059 	}
60214135Sgd78059 
60229644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_setup_static_info",
60234135Sgd78059 	    "num fans %d", ssp->num_fans);
60244135Sgd78059 
60254135Sgd78059 	(void) bscv_read_env_name(ssp,
60264135Sgd78059 	    EBUS_CMD_SPACE5,
60274135Sgd78059 	    EBUS_IDX5_FAN_NAME_START,
60284135Sgd78059 	    EBUS_IDX5_FAN_NAME_END,
60294135Sgd78059 	    ssp->fan_names,
60304135Sgd78059 	    ssp->num_fans);
60314135Sgd78059 
60324135Sgd78059 	/* Get led static information from address space 10 */
60334135Sgd78059 
60344135Sgd78059 	(void) bscv_read_env_name(ssp,
60354135Sgd78059 	    EBUS_CMD_SPACE_LEDS,
60364135Sgd78059 	    EBUS_IDX10_LED_NAME_START,
60374135Sgd78059 	    EBUS_IDX10_LED_NAME_END,
60384135Sgd78059 	    ssp->led_names,
60394135Sgd78059 	    MAX_LED_ID);
60404135Sgd78059 }
60414135Sgd78059 
60424135Sgd78059 /*
60434135Sgd78059  * function	- bscv_read_env_name
60444135Sgd78059  * description	- read in static environment names
60454135Sgd78059  *		  warning changes address space and the caller relies
60464135Sgd78059  *		  on this behaviour.
60474135Sgd78059  * inputs	- soft state ptr, chosen address space,
60484135Sgd78059  *		  start of name data, end of name data,
60494135Sgd78059  *		  name storage, number of names.
60504135Sgd78059  * outputs	- next address for reading name data.
60514135Sgd78059  */
60524135Sgd78059 
60534135Sgd78059 static uint8_t
bscv_read_env_name(bscv_soft_state_t * ssp,uint8_t addr_space,uint8_t addr_start,uint8_t addr_end,char namebuf[][MAX_LOM2_NAME_STR],int numnames)60544135Sgd78059 bscv_read_env_name(bscv_soft_state_t *ssp,
60554135Sgd78059     uint8_t addr_space,
60564135Sgd78059     uint8_t addr_start,
60574135Sgd78059     uint8_t addr_end,
60584135Sgd78059     char namebuf[][MAX_LOM2_NAME_STR],
60594135Sgd78059     int numnames)
60604135Sgd78059 {
60614135Sgd78059 	int i;
60624135Sgd78059 	int nameidx;
60634135Sgd78059 	int namemax;
60644135Sgd78059 	unsigned int addr_space_ptr;
60654135Sgd78059 	uint8_t this_char;
60664135Sgd78059 
60674135Sgd78059 	ASSERT(bscv_held(ssp));
60684135Sgd78059 
60699644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'A', "bscv_read_env_name",
60704135Sgd78059 	    "bscv_read_env_name, space %d, start 0x%x, end 0x%x, numnames %d",
60714135Sgd78059 	    addr_space, addr_start, addr_end, numnames);
60724135Sgd78059 
60734135Sgd78059 	addr_space_ptr = addr_start;
60744135Sgd78059 
60754135Sgd78059 	for (i = 0; i < numnames; i++) {
60764135Sgd78059 		nameidx = 0;
60774135Sgd78059 		namemax = sizeof (namebuf[i]);
60784135Sgd78059 		bzero(namebuf[i], namemax);
60794135Sgd78059 
60804135Sgd78059 		while (addr_space_ptr <= addr_end) {
60814135Sgd78059 			/*
60824135Sgd78059 			 * Read the current character.
60834135Sgd78059 			 */
60844135Sgd78059 			this_char = bscv_get8(ssp, chan_general,
60854135Sgd78059 			    BSCVA(addr_space, addr_space_ptr));
60864135Sgd78059 
60874135Sgd78059 			if (this_char == 0xff) {
60884135Sgd78059 				/*
60894135Sgd78059 				 * Ran out of names - this must
60904135Sgd78059 				 * be the end of the name.
60914135Sgd78059 				 * This is really an error because
60924135Sgd78059 				 * we have just seen either a non-NUL
60934135Sgd78059 				 * terminated string or the number of
60944135Sgd78059 				 * strings did not match what was
60954135Sgd78059 				 * reported.
60964135Sgd78059 				 */
60974135Sgd78059 				break;
60984135Sgd78059 			}
60994135Sgd78059 			/*
61004135Sgd78059 			 * We increment the buffer pointer now so that
61014135Sgd78059 			 * it is ready for the next read
61024135Sgd78059 			 */
61034135Sgd78059 			addr_space_ptr++;
61044135Sgd78059 
61054135Sgd78059 			if (this_char == '\0') {
61064135Sgd78059 				/* Found end of string - done */
61074135Sgd78059 				break;
61084135Sgd78059 			}
61094135Sgd78059 			if (nameidx < (namemax - 1)) {
61104135Sgd78059 				/*
61114135Sgd78059 				 * Buffer not full - record character
61124135Sgd78059 				 * NOTE we always leave room for the NUL
61134135Sgd78059 				 * terminator.
61144135Sgd78059 				 */
61154135Sgd78059 				namebuf[i][nameidx++] = this_char;
61164135Sgd78059 			}
61174135Sgd78059 		}
61184135Sgd78059 		/* Ensure null termination */
61194135Sgd78059 		namebuf[i][nameidx] = '\0';
61204135Sgd78059 	}
61214135Sgd78059 	/* Clamp addr_space_ptr to 0xff because we return uint8_t */
61224135Sgd78059 	if (addr_space_ptr > 0xff) {
61234135Sgd78059 		addr_space_ptr = 0xff;
61244135Sgd78059 	}
61254135Sgd78059 	return (addr_space_ptr);
61264135Sgd78059 }
61274135Sgd78059 
61284135Sgd78059 /*
61294135Sgd78059  * function	- bscv_setup_events
61304135Sgd78059  * description	- initialise the event reporting code
61314135Sgd78059  * inputs	- soft state ptr
61324135Sgd78059  * outputs	- DDI_SUCCESS or DDI_FAILURE
61334135Sgd78059  */
61344135Sgd78059 
61354135Sgd78059 static void
bscv_setup_events(bscv_soft_state_t * ssp)61364135Sgd78059 bscv_setup_events(bscv_soft_state_t *ssp)
61374135Sgd78059 {
61384135Sgd78059 	uint8_t bits2set;
61394135Sgd78059 	uint8_t bits2clear;
61404135Sgd78059 
61414135Sgd78059 	ASSERT(bscv_held(ssp));
61424135Sgd78059 
61434135Sgd78059 	/*
61444135Sgd78059 	 * deal with event reporting - cover all cases
61454135Sgd78059 	 */
61464135Sgd78059 
61474135Sgd78059 	bits2set = 0;
61484135Sgd78059 	bits2clear = 0;
61494135Sgd78059 	if (ssp->serial_reporting == LOM_SER_EVENTS_ON) {
61504135Sgd78059 		bits2clear |= EBUS_ALARM_NOEVENTS;
61514135Sgd78059 	} else if (ssp->serial_reporting == LOM_SER_EVENTS_OFF) {
61524135Sgd78059 		bits2set |= EBUS_ALARM_NOEVENTS;
61534135Sgd78059 	} else if (ssp->serial_reporting == LOM_SER_EVENTS_DEF) {
61544135Sgd78059 		bits2set |= EBUS_ALARM_NOEVENTS;
61554135Sgd78059 	}
61564135Sgd78059 	bscv_setclear8_volatile(ssp, chan_general, EBUS_IDX_ALARM,
61575107Seota 	    bits2set, bits2clear);
61584135Sgd78059 }
61594135Sgd78059 
61604135Sgd78059 #ifdef __sparc
61614135Sgd78059 /*
61624135Sgd78059  * function	- bscv_write_sig
61634135Sgd78059  * description	- write out a signature, taking care to deal with any strange
61644135Sgd78059  *		    values for CPU ID
61654135Sgd78059  * inputs	- soft state ptr, signature
61664135Sgd78059  * outputs	- none
61674135Sgd78059  */
61684135Sgd78059 static void
bscv_write_sig(bscv_soft_state_t * ssp,bscv_sig_t s)61694135Sgd78059 bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s)
61704135Sgd78059 {
61714135Sgd78059 	ASSERT(bscv_held(ssp));
61724135Sgd78059 
61734135Sgd78059 	/* Upload the signature */
61744135Sgd78059 	bscv_put32(ssp, chan_cpusig,
61754135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB),
61764135Sgd78059 	    s.sig_info.signature);
61774135Sgd78059 
61784135Sgd78059 	/*
61794135Sgd78059 	 * We always write the CPU ID last because this tells the firmware
61804135Sgd78059 	 * that the signature is fully uploaded and therefore to consume the
61814135Sgd78059 	 * data.  This is required since the signature is > 1 byte in size
61824135Sgd78059 	 * and we transmit data in single bytes.
61834135Sgd78059 	 */
61844135Sgd78059 	if (s.cpu == ~0) {
61854135Sgd78059 		/* ~0 means the signature applies to any CPU. */
61864135Sgd78059 		bscv_put8(ssp, chan_cpusig,
61874135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
61884135Sgd78059 		    EBUS_ANY_CPU_ID);
61894135Sgd78059 	} else {
61904135Sgd78059 		if (s.cpu > 255) {
61914135Sgd78059 			/*
61924135Sgd78059 			 * The CPU ID supplied is unexpectedly large.  Lets
61934135Sgd78059 			 * just use the bottom bits, in case other high order
61944135Sgd78059 			 * bits are being used for special meaning.
61954135Sgd78059 			 */
61964135Sgd78059 			cmn_err(CE_WARN, "CPU Signature ID 0x%x > 255", s.cpu);
61974135Sgd78059 			s.cpu %= 256;
61984135Sgd78059 			cmn_err(CE_CONT, "using ID 0x%x instead ", s.cpu);
61994135Sgd78059 		}
62004135Sgd78059 		bscv_put8(ssp, chan_cpusig,
62014135Sgd78059 		    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID),
62024135Sgd78059 		    (uint8_t)s.cpu);
62034135Sgd78059 	}
62044135Sgd78059 
62054135Sgd78059 	ssp->last_sig = s;
62064135Sgd78059 	ssp->progress |= BSCV_SIG_SENT;
62074135Sgd78059 }
62084135Sgd78059 #endif /* __sparc */
62094135Sgd78059 
62104135Sgd78059 #if defined(__i386) || defined(__amd64)
62114135Sgd78059 
62124135Sgd78059 /*
62134135Sgd78059  * function	- bscv_inform_bsc
62144135Sgd78059  * description	- inform bsc of driver state for logging purposes
62154135Sgd78059  * inputs	- driver soft state, state
62164135Sgd78059  * outputs	- none
62174135Sgd78059  *
62184135Sgd78059  */
62194135Sgd78059 static void
bscv_inform_bsc(bscv_soft_state_t * ssp,uint32_t state)62204135Sgd78059 bscv_inform_bsc(bscv_soft_state_t *ssp, uint32_t state)
62214135Sgd78059 {
62224135Sgd78059 	ASSERT(bscv_held(ssp));
62234135Sgd78059 
62249644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_inform_bsc",
62254135Sgd78059 	    "bscv_inform_bsc: state=%d", state);
62264135Sgd78059 
62274135Sgd78059 	bscv_put32(ssp, chan_general,
62284135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_SIG_MSB), state);
62294135Sgd78059 	bscv_put8(ssp, chan_cpusig,
62304135Sgd78059 	    BSCVA(EBUS_CMD_SPACE_CPUSIG, EBUS_IDX11_CPU_ID), EBUS_ANY_CPU_ID);
62314135Sgd78059 }
62324135Sgd78059 
62334135Sgd78059 /*
62344135Sgd78059  * function	- bscv_watchdog_pat_request
62354135Sgd78059  * description	- request a heartbeat pat
62364135Sgd78059  * inputs	- timeout value in seconds
62374135Sgd78059  * outputs	- none
62384135Sgd78059  */
62394135Sgd78059 static void
bscv_watchdog_pat_request(void * arg)62404135Sgd78059 bscv_watchdog_pat_request(void *arg)
62414135Sgd78059 {
62424135Sgd78059 	bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
62434135Sgd78059 
62444135Sgd78059 	bscv_wdog_do_pat(ssp);
62454135Sgd78059 }
62464135Sgd78059 
62474135Sgd78059 /*
62484135Sgd78059  * function	- bscv_watchdog_cfg_request
62494135Sgd78059  * description	- request configuration of the bsc hardware watchdog
62504135Sgd78059  * inputs	- new state (0=disabled, 1=enabled)
62514135Sgd78059  * outputs	- one if successful, zero if unsuccesful
62524135Sgd78059  */
62534135Sgd78059 static void
bscv_watchdog_cfg_request(bscv_soft_state_t * ssp,uint8_t new_state)62544135Sgd78059 bscv_watchdog_cfg_request(bscv_soft_state_t *ssp, uint8_t new_state)
62554135Sgd78059 {
62564135Sgd78059 	ASSERT(new_state == WDOG_ON || new_state == WDOG_OFF);
62574135Sgd78059 
62584135Sgd78059 	watchdog_activated = new_state;
62599644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_watchdog_cfg_request",
62604135Sgd78059 	    "watchdog_activated=%d", watchdog_activated);
62614135Sgd78059 	bscv_write_wdog_cfg(ssp,
62624135Sgd78059 	    bscv_watchdog_timeout_seconds,
62634135Sgd78059 	    new_state,
62644135Sgd78059 	    wdog_reset_on_timeout);
62654135Sgd78059 }
62664135Sgd78059 
62674135Sgd78059 /*
62684135Sgd78059  * function	- bscv_set_watchdog_timer
62694135Sgd78059  * description	- setup the heartbeat timeout value
62704135Sgd78059  * inputs	- timeout value in seconds
62714135Sgd78059  * outputs	- zero if the value was not changed
62724135Sgd78059  *                otherwise the current value
62734135Sgd78059  */
62744135Sgd78059 static uint_t
bscv_set_watchdog_timer(bscv_soft_state_t * ssp,uint_t timeoutval)62754135Sgd78059 bscv_set_watchdog_timer(bscv_soft_state_t *ssp, uint_t timeoutval)
62764135Sgd78059 {
62779644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_set_watchdog_timer:",
62784135Sgd78059 	    "timeout=%d", timeoutval);
62794135Sgd78059 
62804135Sgd78059 	/*
62814135Sgd78059 	 * We get started during bscv_attach only
62824135Sgd78059 	 * if bscv_watchdog_enable is set.
62834135Sgd78059 	 */
62844135Sgd78059 	if (bscv_watchdog_available && (!watchdog_activated ||
62854135Sgd78059 	    (watchdog_activated &&
62865107Seota 	    (timeoutval != bscv_watchdog_timeout_seconds)))) {
62874135Sgd78059 		bscv_watchdog_timeout_seconds = timeoutval;
62884135Sgd78059 		bscv_watchdog_cfg_request(ssp, WDOG_ON);
62894135Sgd78059 		return (bscv_watchdog_timeout_seconds);
62904135Sgd78059 	}
62914135Sgd78059 	return (0);
62924135Sgd78059 }
62934135Sgd78059 
62944135Sgd78059 /*
62954135Sgd78059  * function	- bscv_clear_watchdog_timer
62964135Sgd78059  * description	- add the watchdog patter cyclic
62974135Sgd78059  * inputs	- driver soft state
62984135Sgd78059  * outputs	- value of watchdog timeout in seconds
62994135Sgd78059  *
63004135Sgd78059  * This function is a copy of the SPARC implementation
63014135Sgd78059  * in the todblade clock driver.
63024135Sgd78059  */
63034135Sgd78059 static void
bscv_clear_watchdog_timer(bscv_soft_state_t * ssp)63044135Sgd78059 bscv_clear_watchdog_timer(bscv_soft_state_t *ssp)
63054135Sgd78059 {
63069644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_clear_watchdog_timer", "");
63074135Sgd78059 
63084135Sgd78059 	if (bscv_watchdog_available && watchdog_activated) {
63094135Sgd78059 		bscv_watchdog_enable = 0;
63104135Sgd78059 		bscv_watchdog_cfg_request(ssp, WDOG_OFF);
63114135Sgd78059 	}
63124135Sgd78059 }
63134135Sgd78059 
63144135Sgd78059 /*
63154135Sgd78059  * function	- bscv_panic_callback
63164135Sgd78059  * description	- called when we panic so we can disabled the watchdog
63174135Sgd78059  * inputs	- driver soft state pointer
63184135Sgd78059  * outputs	- DDI_SUCCESS
63194135Sgd78059  */
63204135Sgd78059 /*ARGSUSED1*/
63214135Sgd78059 static boolean_t
bscv_panic_callback(void * arg,int code)63224135Sgd78059 bscv_panic_callback(void *arg, int code)
63234135Sgd78059 {
63244135Sgd78059 	bscv_soft_state_t *ssp = (bscv_soft_state_t *)arg;
63254135Sgd78059 
63269644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_panic_callback",
63274135Sgd78059 	    "disabling watchdog");
63284135Sgd78059 
63294135Sgd78059 	bscv_clear_watchdog_timer(ssp);
63304135Sgd78059 	/*
63314135Sgd78059 	 * We dont get interrupts during the panic callback. But bscbus
63324135Sgd78059 	 * takes care of all this
63334135Sgd78059 	 */
63344135Sgd78059 	bscv_full_stop(ssp);
63354135Sgd78059 	return (DDI_SUCCESS);
63364135Sgd78059 }
63374135Sgd78059 
63384135Sgd78059 /*
63394135Sgd78059  * function	- bscv_watchdog_cyclic_add
63404135Sgd78059  * description	- add the watchdog patter cyclic
63414135Sgd78059  * inputs	- driver soft state
63424135Sgd78059  * outputs	- none
63434135Sgd78059  */
63444135Sgd78059 static void
bscv_watchdog_cyclic_add(bscv_soft_state_t * ssp)63454135Sgd78059 bscv_watchdog_cyclic_add(bscv_soft_state_t *ssp)
63464135Sgd78059 {
63475107Seota 	if (ssp->periodic_id != NULL) {
63484135Sgd78059 		return;
63494135Sgd78059 	}
63504135Sgd78059 
63515107Seota 	ssp->periodic_id = ddi_periodic_add(bscv_watchdog_pat_request, ssp,
63525107Seota 	    WATCHDOG_PAT_INTERVAL, DDI_IPL_10);
63534135Sgd78059 
63549644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_watchdog_cyclic_add:",
63554135Sgd78059 	    "cyclic added");
63564135Sgd78059 }
63574135Sgd78059 
63584135Sgd78059 /*
63594135Sgd78059  * function	- bscv_watchdog_cyclic_remove
63604135Sgd78059  * description	- remove the watchdog patter cyclic
63614135Sgd78059  * inputs	- soft state ptr
63624135Sgd78059  * outputs	- none
63634135Sgd78059  */
63644135Sgd78059 static void
bscv_watchdog_cyclic_remove(bscv_soft_state_t * ssp)63654135Sgd78059 bscv_watchdog_cyclic_remove(bscv_soft_state_t *ssp)
63664135Sgd78059 {
63675107Seota 	if (ssp->periodic_id == NULL) {
63684135Sgd78059 		return;
63694135Sgd78059 	}
63705107Seota 	ddi_periodic_delete(ssp->periodic_id);
63715107Seota 	ssp->periodic_id = NULL;
63729644Sandrew.rutz@sun.com 	BSCV_TRACE(ssp, 'X', "bscv_watchdog_cyclic_remove:",
63734135Sgd78059 	    "cyclic removed");
63744135Sgd78059 }
63754135Sgd78059 #endif /* __i386 || __amd64 */
63764135Sgd78059 
63774135Sgd78059 
63784135Sgd78059 /*
63794135Sgd78059  *  General utility routines ...
63804135Sgd78059  */
63814135Sgd78059 
63824135Sgd78059 #ifdef DEBUG
63834135Sgd78059 
63844135Sgd78059 static void
bscv_trace(bscv_soft_state_t * ssp,char code,const char * caller,const char * fmt,...)63854135Sgd78059 bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
63864135Sgd78059 	const char *fmt, ...)
63874135Sgd78059 {
63884135Sgd78059 	char buf[256];
63894135Sgd78059 	char *p;
63904135Sgd78059 	va_list va;
63914135Sgd78059 
63924135Sgd78059 	if (ssp->debug & (1 << (code-'@'))) {
63934135Sgd78059 		p = buf;
63944135Sgd78059 		(void) snprintf(p, sizeof (buf) - (p - buf),
63955107Seota 		    "%s/%s: ", MYNAME, caller);
63964135Sgd78059 		p += strlen(p);
63974135Sgd78059 
63984135Sgd78059 		va_start(va, fmt);
63994135Sgd78059 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
64004135Sgd78059 		va_end(va);
64014135Sgd78059 
64024135Sgd78059 		buf[sizeof (buf) - 1] = '\0';
64034135Sgd78059 		(void) strlog((short)ssp->majornum, (short)ssp->minornum, code,
64044135Sgd78059 		    SL_TRACE, buf);
64054135Sgd78059 	}
64064135Sgd78059 }
64074135Sgd78059 
64084135Sgd78059 #else /* DEBUG */
64094135Sgd78059 
64104135Sgd78059 _NOTE(ARGSUSED(0))
64114135Sgd78059 static void
bscv_trace(bscv_soft_state_t * ssp,char code,const char * caller,const char * fmt,...)64124135Sgd78059 bscv_trace(bscv_soft_state_t *ssp, char code, const char *caller,
64134135Sgd78059 	const char *fmt, ...)
64144135Sgd78059 {
64154135Sgd78059 }
64164135Sgd78059 
64174135Sgd78059 #endif /* DEBUG */
6418