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