| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| /* |
| * Copyright 2009 Sun Microsystems, Inc. All rights reserved. |
| * Use is subject to license terms. |
| */ |
| |
| |
| /* |
| * bscv.c - multi-threaded lom driver for the Stiletto platform. |
| */ |
| |
| /* |
| * Included files. |
| */ |
| |
| #include <sys/note.h> |
| #include <sys/types.h> |
| #include <sys/param.h> |
| #include <sys/uio.h> |
| #include <sys/open.h> |
| #include <sys/cred.h> |
| #include <sys/stream.h> |
| #include <sys/systm.h> |
| #include <sys/conf.h> |
| #include <sys/reboot.h> |
| #include <sys/modctl.h> |
| #include <sys/mkdev.h> |
| #include <sys/errno.h> |
| #include <sys/debug.h> |
| #include <sys/kmem.h> |
| #include <sys/consdev.h> |
| #include <sys/file.h> |
| #include <sys/stat.h> |
| #include <sys/disp.h> |
| #include <sys/ddi.h> |
| #include <sys/sunddi.h> |
| #include <sys/stream.h> |
| #include <sys/strlog.h> |
| #include <sys/log.h> |
| #include <sys/utsname.h> |
| #include <sys/callb.h> |
| #include <sys/sysevent.h> |
| #include <sys/nvpair.h> |
| #include <sys/sysevent/eventdefs.h> |
| #include <sys/sysevent/domain.h> |
| #include <sys/sysevent/env.h> |
| #include <sys/sysevent/dr.h> |
| |
| #include <sys/lom_io.h> |
| #include <sys/bscbus.h> |
| #include <sys/bscv_impl.h> |
| |
| /* |
| * Variables defined here and visible internally only |
| */ |
| |
| static void *bscv_statep = NULL; |
| |
| /* |
| * Forward declarations |
| */ |
| |
| static int bscv_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); |
| static int bscv_attach(dev_info_t *, ddi_attach_cmd_t); |
| static int bscv_detach(dev_info_t *, ddi_detach_cmd_t); |
| static int bscv_quiesce(dev_info_t *); |
| static int bscv_map_regs(bscv_soft_state_t *); |
| static void bscv_unmap_regs(bscv_soft_state_t *); |
| static void bscv_map_chan_logical_physical(bscv_soft_state_t *); |
| |
| static int bscv_open(dev_t *, int, int, cred_t *); |
| static int bscv_close(dev_t, int, int, cred_t *); |
| static void bscv_full_stop(bscv_soft_state_t *); |
| |
| static void bscv_enter(bscv_soft_state_t *); |
| static int bscv_tryenter(bscv_soft_state_t *ssp); |
| static void bscv_exit(bscv_soft_state_t *); |
| #ifdef DEBUG |
| static int bscv_held(bscv_soft_state_t *); |
| #endif /* DEBUG */ |
| |
| static void bscv_put8(bscv_soft_state_t *, int, bscv_addr_t, uint8_t); |
| static void bscv_put16(bscv_soft_state_t *, int, bscv_addr_t, uint16_t); |
| static void bscv_put32(bscv_soft_state_t *, int, bscv_addr_t, uint32_t); |
| static uint8_t bscv_get8(bscv_soft_state_t *, int, bscv_addr_t); |
| static uint16_t bscv_get16(bscv_soft_state_t *, int, bscv_addr_t); |
| static uint32_t bscv_get32(bscv_soft_state_t *, int, bscv_addr_t); |
| static void bscv_setclear8(bscv_soft_state_t *, int, |
| bscv_addr_t, uint8_t, uint8_t); |
| static void bscv_setclear8_volatile(bscv_soft_state_t *, int, |
| bscv_addr_t, uint8_t, uint8_t); |
| static void bscv_rep_rw8(bscv_soft_state_t *, int, |
| uint8_t *, bscv_addr_t, size_t, uint_t, boolean_t); |
| static uint8_t bscv_get8_cached(bscv_soft_state_t *, bscv_addr_t); |
| |
| static uint8_t bscv_get8_locked(bscv_soft_state_t *, int, bscv_addr_t, int *); |
| static void bscv_rep_get8_locked(bscv_soft_state_t *, int, |
| uint8_t *, bscv_addr_t, size_t, uint_t, int *); |
| |
| static boolean_t bscv_faulty(bscv_soft_state_t *); |
| static void bscv_clear_fault(bscv_soft_state_t *); |
| static void bscv_set_fault(bscv_soft_state_t *); |
| static boolean_t bscv_session_error(bscv_soft_state_t *); |
| static int bscv_retcode(bscv_soft_state_t *); |
| static int bscv_should_retry(bscv_soft_state_t *); |
| static void bscv_locked_result(bscv_soft_state_t *, int *); |
| |
| static void bscv_put8_once(bscv_soft_state_t *, int, bscv_addr_t, uint8_t); |
| static uint8_t bscv_get8_once(bscv_soft_state_t *, int, bscv_addr_t); |
| static uint32_t bscv_probe(bscv_soft_state_t *, int, uint32_t *); |
| static void bscv_resync_comms(bscv_soft_state_t *, int); |
| |
| static boolean_t bscv_window_setup(bscv_soft_state_t *); |
| static int bscv_eerw(bscv_soft_state_t *, uint32_t, uint8_t *, |
| unsigned, boolean_t); |
| |
| static int bscv_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); |
| static int bscv_ioc_dogstate(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_psustate(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_fanstate(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_fledstate(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_ledstate(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_info(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_mread(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_volts(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_stats(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_temp(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_cons(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_eventlog2(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_info2(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_test(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_mprog2(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_ioc_mread2(bscv_soft_state_t *, intptr_t, int); |
| |
| static void bscv_event_daemon(void *); |
| static void bscv_start_event_daemon(bscv_soft_state_t *); |
| static int bscv_stop_event_daemon(bscv_soft_state_t *); |
| static int bscv_pause_event_daemon(bscv_soft_state_t *); |
| static void bscv_resume_event_daemon(bscv_soft_state_t *); |
| static void bscv_event_process(bscv_soft_state_t *ssp, boolean_t); |
| static int bscv_event_validate(bscv_soft_state_t *, uint32_t, uint8_t); |
| static void bscv_event_process_one(bscv_soft_state_t *, lom_event_t *); |
| static void bscv_build_eventstring(bscv_soft_state_t *, |
| lom_event_t *, char *, char *); |
| static int bscv_level_of_event(lom_event_t *); |
| static void bscv_status(bscv_soft_state_t *, uint8_t, uint8_t); |
| char *bscv_get_label(char [][MAX_LOM2_NAME_STR], int, int); |
| static void bscv_generic_sysevent(bscv_soft_state_t *, char *, char *, char *, |
| char *, int32_t, char *); |
| static void bscv_sysevent(bscv_soft_state_t *, lom_event_t *); |
| |
| static int bscv_prog(bscv_soft_state_t *, intptr_t, int); |
| static int bscv_prog_image(bscv_soft_state_t *, boolean_t, |
| uint8_t *, int, uint32_t); |
| static int bscv_prog_receive_image(bscv_soft_state_t *, lom_prog_t *, |
| uint8_t *, int); |
| static void bscv_leave_programming_mode(bscv_soft_state_t *, boolean_t); |
| static int bscv_prog_stop_lom(bscv_soft_state_t *); |
| static int bscv_prog_start_lom(bscv_soft_state_t *); |
| |
| static int bscv_attach_common(bscv_soft_state_t *); |
| static int bscv_cleanup(bscv_soft_state_t *); |
| static void bscv_setup_capability(bscv_soft_state_t *); |
| static int bscv_probe_check(bscv_soft_state_t *); |
| static void bscv_setup_hostname(bscv_soft_state_t *); |
| static void bscv_read_hostname(bscv_soft_state_t *, char *); |
| static void bscv_write_hostname(bscv_soft_state_t *, char *, uint8_t); |
| static void bscv_setup_static_info(bscv_soft_state_t *); |
| static uint8_t bscv_read_env_name(bscv_soft_state_t *, uint8_t, |
| uint8_t, uint8_t, char [][MAX_LOM2_NAME_STR], int); |
| static void bscv_setup_events(bscv_soft_state_t *); |
| |
| static void bscv_trace(bscv_soft_state_t *, char, const char *, |
| const char *, ...); |
| |
| #ifdef __sparc |
| static void bscv_idi_init(); |
| static void bscv_idi_fini(); |
| static void bscv_idi_new_instance(dev_info_t *dip); |
| static void bscv_idi_clear_err(); |
| void bscv_idi_set(struct bscv_idi_info info); |
| static boolean_t bscv_idi_err(); |
| static boolean_t bscv_nodename_set(struct bscv_idi_info info); |
| static boolean_t bscv_sig_set(struct bscv_idi_info info); |
| static boolean_t bscv_wdog_pat(struct bscv_idi_info info); |
| static boolean_t bscv_wdog_cfg(struct bscv_idi_info info); |
| static void bscv_write_sig(bscv_soft_state_t *ssp, bscv_sig_t s); |
| #endif /* __sparc */ |
| |
| static void bscv_setup_watchdog(bscv_soft_state_t *ssp); |
| static void bscv_write_wdog_cfg(bscv_soft_state_t *, |
| uint_t, boolean_t, uint8_t); |
| |
| #if defined(__i386) || defined(__amd64) |
| static void bscv_inform_bsc(bscv_soft_state_t *, uint32_t); |
| static void bscv_watchdog_pat_request(void *); |
| static void bscv_watchdog_cfg_request(bscv_soft_state_t *, uint8_t); |
| static uint_t bscv_set_watchdog_timer(bscv_soft_state_t *, uint_t); |
| static void bscv_clear_watchdog_timer(bscv_soft_state_t *); |
| |
| static boolean_t bscv_panic_callback(void *, int); |
| static void bscv_watchdog_cyclic_add(bscv_soft_state_t *); |
| static void bscv_watchdog_cyclic_remove(bscv_soft_state_t *); |
| |
| static uint8_t wdog_reset_on_timeout = 1; |
| |
| #define WDOG_ON 1 |
| #define WDOG_OFF 0 |
| #define CLK_WATCHDOG_DEFAULT 10 /* 10 seconds */ |
| #define WATCHDOG_PAT_INTERVAL 1000000000 /* 1 second */ |
| |
| static int bscv_watchdog_enable; |
| static int bscv_watchdog_available; |
| static int watchdog_activated; |
| static uint_t bscv_watchdog_timeout_seconds; |
| #endif /* __i386 || __amd64 */ |
| |
| #ifdef __sparc |
| struct bscv_idi_callout bscv_idi_callout_table[] = { |
| {BSCV_IDI_NODENAME, &bscv_nodename_set }, |
| {BSCV_IDI_SIG, &bscv_sig_set }, |
| {BSCV_IDI_WDOG_PAT, &bscv_wdog_pat }, |
| {BSCV_IDI_WDOG_CFG, &bscv_wdog_cfg }, |
| {BSCV_IDI_NULL, NULL } |
| }; |
| |
| static struct bscv_idi_callout_mgr bscv_idi_mgr; |
| #endif /* __sparc */ |
| |
| /* |
| * Local Definitions |
| */ |
| #define STATUS_READ_LIMIT 8 /* Read up to 8 status changes at a time */ |
| #define MYNAME "bscv" |
| #define BSCV_INST_TO_MINOR(i) (i) |
| #define BSCV_MINOR_TO_INST(m) (m) |
| |
| /* |
| * Strings for daemon event reporting |
| */ |
| |
| static char *eventSubsysStrings[] = |
| { "", /* 00 */ |
| "Alarm ", /* 01 */ |
| "temperature sensor ", /* 02 */ |
| "overheat sensor ", /* 03 */ |
| "Fan ", /* 04 */ |
| "supply rail ", /* 05 */ |
| "circuit breaker ", /* 06 */ |
| "PSU ", /* 07 */ |
| "user ", /* 08 */ |
| "phonehome ", /* 09; unutilized */ |
| "LOM ", /* 0a */ |
| "host ", /* 0b */ |
| "event log ", /* 0c */ |
| "", /* 0d; EVENT_SUBSYS_EXTRA unutilized */ |
| "LED ", /* 0e */ |
| }; |
| |
| static char *eventTypeStrings[] = |
| { |
| "[null event]", /* 00 */ |
| "ON", /* 01 */ |
| "OFF", /* 02 */ |
| "state change", /* 03 */ |
| "power on", /* 04 */ |
| "power off", /* 05 */ |
| "powered off unexpectedly", /* 06 */ |
| "reset unexpectedly", /* 07 */ |
| "booted", /* 08 */ |
| "watchdog enabled", /* 09 */ |
| "watchdog disabled", /* 0a */ |
| "watchdog triggered", /* 0b */ |
| "failed", /* 0c */ |
| "recovered", /* 0d */ |
| "reset", /* 0e */ |
| "XIR reset", /* 0f */ |
| "console selected", /* 10 */ |
| "time reference", /* 11 */ |
| "script failure", /* 12 */ |
| "modem access failure", /* 13 */ |
| "modem dialing failure", /* 14 */ |
| "bad checksum", /* 15 */ |
| "added", /* 16 */ |
| "removed", /* 17 */ |
| "changed", /* 18 */ |
| "login", /* 19 */ |
| "password changed", /* 1a */ |
| "login failed", /* 1b */ |
| "logout", /* 1c */ |
| "flash download", /* 1d */ |
| "data lost", /* 1e */ |
| "device busy", /* 1f */ |
| "fault led state", /* 20 */ |
| "overheat", /* 21 */ |
| "severe overheat", /* 22 */ |
| "no overheat", /* 23 */ |
| "SCC", /* 24 */ |
| "device inaccessible", /* 25 */ |
| "Hostname change", /* 26 */ |
| "CPU signature timeout", /* 27 */ |
| "Bootmode change", /* 28 */ |
| "Watchdog change policy", /* 29 */ |
| "Watchdog change timeout", /* 2a */ |
| }; |
| |
| /* |
| * These store to mapping between the logical service, e.g. chan_prog for |
| * programming, and the actual Xbus channel which carries that traffic. |
| * Any services can be shared on the same channel apart from chan_wdogpat. |
| */ |
| static int chan_general; /* General Traffic */ |
| static int chan_wdogpat; /* Watchdog Patting */ |
| static int chan_cpusig; /* CPU signatures */ |
| static int chan_eeprom; /* EEPROM I/O */ |
| static int chan_prog; /* Programming */ |
| |
| /* |
| * cb_ops structure defining the driver entry points |
| */ |
| |
| static struct cb_ops bscv_cb_ops = { |
| bscv_open, /* open */ |
| bscv_close, /* close */ |
| nodev, /* strategy */ |
| nodev, /* print */ |
| nodev, /* dump */ |
| nodev, /* read */ |
| nodev, /* write */ |
| bscv_ioctl, /* ioctl */ |
| nodev, /* devmap */ |
| nodev, /* mmap */ |
| nodev, /* segmap */ |
| nochpoll, /* poll */ |
| ddi_prop_op, /* prop op */ |
| NULL, /* ! STREAMS */ |
| D_NEW | D_MP /* MT/MP Safe */ |
| }; |
| |
| /* |
| * dev_ops structure defining autoconfiguration driver autoconfiguration |
| * routines |
| */ |
| |
| static struct dev_ops bscv_dev_ops = { |
| DEVO_REV, /* devo_rev */ |
| 0, /* devo_refcnt */ |
| bscv_getinfo, /* devo_getinfo */ |
| nulldev, /* devo_identify */ |
| nulldev, /* devo_probe */ |
| bscv_attach, /* devo_attach */ |
| bscv_detach, /* devo_detach */ |
| nodev, /* devo_reset */ |
| &bscv_cb_ops, /* devo_cb_ops */ |
| (struct bus_ops *)0, /* devo_bus_ops */ |
| NULL, /* devo_power */ |
| bscv_quiesce, /* devo_quiesce */ |
| }; |
| |
| /* |
| * module configuration section |
| */ |
| |
| #ifdef DEBUG |
| #define BSCV_VERSION_STRING "bscv driver - Debug" |
| #else /* DEBUG */ |
| #define BSCV_VERSION_STRING "bscv driver" |
| #endif /* DEBUG */ |
| |
| static struct modldrv modldrv = { |
| &mod_driverops, |
| BSCV_VERSION_STRING, |
| &bscv_dev_ops, |
| }; |
| |
| static struct modlinkage modlinkage = { |
| MODREV_1, |
| &modldrv, |
| NULL |
| }; |
| |
| #ifdef DEBUG |
| /* Tracing is enabled if value is non-zero. */ |
| static int bscv_trace_flag = 1; |
| |
| #define BSCV_TRACE if (bscv_trace_flag != 0) bscv_trace |
| #else |
| #define BSCV_TRACE |
| #endif |
| |
| /* |
| * kernel accessible routines. These routines are necessarily global so the |
| * driver can be loaded, and unloaded successfully |
| */ |
| |
| /* |
| * function - _init |
| * description - initializes the driver state structure and installs the |
| * driver module into the kernel |
| * inputs - none |
| * outputs - success or failure of module installation |
| */ |
| |
| int |
| _init(void) |
| { |
| register int e; |
| |
| if ((e = ddi_soft_state_init(&bscv_statep, |
| sizeof (bscv_soft_state_t), 1)) != 0) { |
| return (e); |
| } |
| |
| if ((e = mod_install(&modlinkage)) != 0) { |
| ddi_soft_state_fini(&bscv_statep); |
| } |
| |
| #ifdef __sparc |
| if (e == 0) bscv_idi_init(); |
| #endif /* __sparc */ |
| return (e); |
| } |
| |
| /* |
| * function - _info |
| * description - provide information about a kernel loaded module |
| * inputs - module infomation |
| * outputs - success or failure of information request |
| */ |
| |
| int |
| _info(struct modinfo *modinfop) |
| { |
| return (mod_info(&modlinkage, modinfop)); |
| } |
| |
| /* |
| * function - _fini |
| * description - removes a module from the kernel and frees the driver soft |
| * state memory |
| * inputs - none |
| * outputs - success or failure of module removal |
| */ |
| |
| int |
| _fini(void) |
| { |
| register int e; |
| |
| if ((e = mod_remove(&modlinkage)) != 0) { |
| return (e); |
| } |
| |
| #ifdef __sparc |
| bscv_idi_fini(); |
| #endif /* __sparc */ |
| ddi_soft_state_fini(&bscv_statep); |
| |
| return (e); |
| } |
| |
| /* |
| * function - bscv_getinfo |
| * description - routine used to provide information on the driver |
| * inputs - device information structure, command, command arg, storage |
| * area for the result |
| * outputs - DDI_SUCCESS or DDI_FAILURE |
| */ |
| |
| /*ARGSUSED*/ |
| static int |
| bscv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) |
| { |
| bscv_soft_state_t *ssp; |
| dev_t dev = (dev_t)arg; |
| int instance; |
| int error; |
| |
| instance = DEVICETOINSTANCE(dev); |
| |
| switch (cmd) { |
| case DDI_INFO_DEVT2INSTANCE: |
| *result = (void *)(uintptr_t)instance; |
| error = DDI_SUCCESS; |
| break; |
| |
| case DDI_INFO_DEVT2DEVINFO: |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| if (ssp == NULL) |
| return (DDI_FAILURE); |
| *result = (void *) ssp->dip; |
| error = DDI_SUCCESS; |
| break; |
| |
| default: |
| error = DDI_FAILURE; |
| break; |
| } |
| |
| return (error); |
| } |
| |
| #ifdef __sparc |
| void |
| bscv_idi_init() |
| { |
| bscv_idi_mgr.valid_inst = (uint32_t)~0; /* No valid instances */ |
| bscv_idi_mgr.tbl = bscv_idi_callout_table; |
| bscv_idi_mgr.errs = 0; |
| |
| /* |
| * Now that all fields are initialized, set the magic flag. This is |
| * a kind of integrity check for the data structure. |
| */ |
| bscv_idi_mgr.magic = BSCV_IDI_CALLOUT_MAGIC; |
| } |
| |
| static void |
| bscv_idi_clear_err() |
| { |
| ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC); |
| |
| bscv_idi_mgr.errs = 0; |
| } |
| |
| /* |
| * function - bscv_idi_err |
| * description - error messaging service which throttles the number of error |
| * messages to avoid overflowing storage |
| * inputs - none |
| * returns - boolean to indicate whether a message should be reported |
| * side-effects - updates the error number counter |
| */ |
| static boolean_t |
| bscv_idi_err() |
| { |
| ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC); |
| |
| bscv_idi_mgr.errs++; |
| |
| if (bscv_idi_mgr.errs++ < BSCV_IDI_ERR_MSG_THRESHOLD) |
| return (B_TRUE); |
| |
| return (B_FALSE); |
| } |
| |
| void |
| bscv_idi_new_instance(dev_info_t *dip) |
| { |
| ASSERT(bscv_idi_mgr.magic == BSCV_IDI_CALLOUT_MAGIC); |
| |
| /* |
| * We don't care how many instances we have, or their value, so long |
| * as we have at least one valid value. This is so service routines |
| * can get any required locks via a soft state pointer. |
| */ |
| if (bscv_idi_mgr.valid_inst == (uint32_t)~0) { |
| bscv_idi_mgr.valid_inst = ddi_get_instance(dip); |
| } |
| } |
| |
| void |
| bscv_idi_fini() |
| { |
| bscv_idi_mgr.valid_inst = (uint32_t)~0; /* No valid instances */ |
| bscv_idi_mgr.tbl = NULL; |
| } |
| #endif /* __sparc */ |
| |
| /* |
| * function - bscv_attach |
| * description - this routine is responsible for setting aside memory for the |
| * driver data structures, initialising the mutexes and creating |
| * the device minor nodes. Additionally, this routine calls the |
| * the callback routine. |
| * inputs - device information structure, DDI_ATTACH command |
| * outputs - DDI_SUCCESS or DDI_FAILURE |
| */ |
| |
| int |
| bscv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) |
| { |
| bscv_soft_state_t *ssp; |
| int instance; |
| |
| switch (cmd) { |
| case DDI_ATTACH: |
| |
| instance = ddi_get_instance(dip); |
| |
| if (ddi_soft_state_zalloc(bscv_statep, instance) != |
| DDI_SUCCESS) { |
| return (DDI_FAILURE); |
| } |
| |
| |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| |
| ssp->progress = 0; |
| |
| ssp->dip = dip; |
| ssp->instance = instance; |
| ssp->event_waiting = B_FALSE; |
| ssp->status_change = B_FALSE; |
| ssp->nodename_change = B_FALSE; |
| ssp->cap0 = 0; |
| ssp->cap1 = 0; |
| ssp->cap2 = 0; |
| ssp->prog_mode_only = B_FALSE; |
| ssp->programming = B_FALSE; |
| ssp->cssp_prog = B_FALSE; |
| ssp->task_flags = 0; |
| ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip, |
| DDI_PROP_DONTPASS, "debug", 0); |
| ssp->majornum = ddi_driver_major(dip); |
| ssp->minornum = BSCV_INST_TO_MINOR(instance); |
| #if defined(__i386) || defined(__amd64) |
| ssp->last_nodename[0] = '\0'; |
| #endif /* __i386 || __amd64 */ |
| |
| /* |
| * initialise the mutexes |
| */ |
| |
| mutex_init(&ssp->cmd_mutex, NULL, MUTEX_DRIVER, NULL); |
| |
| mutex_init(&ssp->task_mu, NULL, MUTEX_DRIVER, NULL); |
| cv_init(&ssp->task_cv, NULL, CV_DRIVER, NULL); |
| cv_init(&ssp->task_evnt_cv, NULL, CV_DRIVER, NULL); |
| mutex_init(&ssp->prog_mu, NULL, MUTEX_DRIVER, NULL); |
| ssp->progress |= BSCV_LOCKS; |
| |
| BSCV_TRACE(ssp, 'A', "bscv_attach", |
| "bscv_attach: mutexes and condition vars initialised"); |
| |
| /* Map in physical communication channels */ |
| |
| if (bscv_map_regs(ssp) != DDI_SUCCESS) { |
| (void) bscv_cleanup(ssp); |
| return (DDI_FAILURE); |
| } |
| ssp->progress |= BSCV_MAPPED_REGS; |
| |
| /* Associate logical channels to physical channels */ |
| |
| bscv_map_chan_logical_physical(ssp); |
| |
| bscv_enter(ssp); |
| |
| bscv_leave_programming_mode(ssp, B_FALSE); |
| |
| if (bscv_attach_common(ssp) == DDI_FAILURE) { |
| bscv_exit(ssp); |
| (void) bscv_cleanup(ssp); |
| return (DDI_FAILURE); |
| } |
| |
| #ifdef __sparc |
| /* |
| * At this point the inter-driver-interface is made available. |
| * The IDI uses the event thread service which |
| * bscv_attach_common() sets up. |
| */ |
| bscv_idi_new_instance(dip); |
| #endif /* __sparc */ |
| |
| bscv_exit(ssp); |
| |
| /* |
| * now create the minor nodes |
| */ |
| if (ddi_create_minor_node(ssp->dip, "lom", S_IFCHR, |
| BSCV_INST_TO_MINOR(instance), |
| DDI_PSEUDO, 0) != DDI_SUCCESS) { |
| (void) bscv_cleanup(ssp); |
| return (DDI_FAILURE); |
| } |
| BSCV_TRACE(ssp, 'A', "bscv_attach", |
| "bscv_attach: device minor nodes created"); |
| ssp->progress |= BSCV_NODES; |
| |
| if (!ssp->prog_mode_only) |
| bscv_start_event_daemon(ssp); |
| |
| #if defined(__i386) || defined(__amd64) |
| bscv_watchdog_enable = 1; |
| bscv_watchdog_available = 1; |
| watchdog_activated = 0; |
| bscv_watchdog_timeout_seconds = CLK_WATCHDOG_DEFAULT; |
| |
| if (bscv_watchdog_enable && (boothowto & RB_DEBUG)) { |
| bscv_watchdog_available = 0; |
| cmn_err(CE_WARN, "bscv: kernel debugger " |
| "detected: hardware watchdog disabled"); |
| } |
| |
| /* |
| * Before we enable the watchdog - register the panic |
| * callback so that we get called to stop the watchdog |
| * in the case of a panic. |
| */ |
| ssp->callb_id = callb_add(bscv_panic_callback, |
| (void *)ssp, CB_CL_PANIC, ""); |
| |
| if (bscv_watchdog_available) { |
| (void) bscv_set_watchdog_timer(ssp, |
| CLK_WATCHDOG_DEFAULT); |
| bscv_enter(ssp); |
| bscv_setup_watchdog(ssp); /* starts cyclic callback */ |
| bscv_exit(ssp); |
| } |
| #endif /* __i386 || __amd64 */ |
| ddi_report_dev(dip); |
| return (DDI_SUCCESS); |
| default: |
| return (DDI_FAILURE); |
| } |
| } |
| |
| /* |
| * function - bscv_detach |
| * description - routine that prepares a module to be unloaded. It undoes all |
| * the work done by the bscv_attach)() routine. This is |
| * facilitated by the use of the progress indicator |
| * inputs - device information structure, DDI_DETACH command |
| * outputs - DDI_SUCCESS or DDI_FAILURE |
| */ |
| |
| /*ARGSUSED*/ |
| static int |
| bscv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) |
| { |
| return (DDI_FAILURE); |
| } |
| |
| /* |
| * quiesce(9E) entry point. |
| * |
| * This function is called when the system is single-threaded at high |
| * PIL with preemption disabled. Therefore, this function must not be |
| * blocked. |
| * |
| * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. |
| * DDI_FAILURE indicates an error condition and should almost never happen. |
| */ |
| static int |
| bscv_quiesce(dev_info_t *dip) |
| { |
| bscv_soft_state_t *ssp; |
| int instance; |
| |
| |
| instance = ddi_get_instance(dip); |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| if (ssp == NULL) { |
| return (DDI_FAILURE); |
| } |
| #ifdef DEBUG |
| /* Disable tracing, as we are executing at High-Interrupt level */ |
| bscv_trace_flag = 0; |
| #endif |
| /* quiesce the device */ |
| bscv_full_stop(ssp); |
| |
| return (DDI_SUCCESS); |
| } |
| |
| /* |
| * cb_ops routines |
| */ |
| |
| /* |
| * function - bscv_open |
| * description - routine to provide association between user fd and device |
| * minor number. This routine is necessarily simple since a |
| * read/write interface is not provided. Additionally, the |
| * driver does not enforce exclusive access (FEXCL) or |
| * non-blocking during an open (FNDELAY). Deferred attach is |
| * supported. |
| * inputs - device number, flag specifying open type, device type, |
| * permissions |
| * outputs - success or failure of operation |
| */ |
| |
| /*ARGSUSED*/ |
| static int |
| bscv_open(dev_t *devp, int flag, int otype, cred_t *cred) |
| { |
| bscv_soft_state_t *ssp; |
| int instance; |
| |
| instance = DEVICETOINSTANCE(*devp); |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| if (ssp == NULL) { |
| return (ENXIO); /* not attached yet */ |
| } |
| BSCV_TRACE(ssp, 'O', "bscv_open", "instance 0x%x", instance); |
| |
| if (otype != OTYP_CHR) { |
| return (EINVAL); |
| } |
| |
| return (0); |
| } |
| |
| /* |
| * function - bscv_close |
| * description - routine to perform the final close on the device. As per the |
| * open routine, neither FEXCL or FNDELAY accesses are enforced |
| * by the driver. |
| * inputs - device number,flag specifying open type, device type, |
| * permissions |
| * outputs - success or failure of operation |
| */ |
| |
| /*ARGSUSED1*/ |
| static int |
| bscv_close(dev_t dev, int flag, int otype, cred_t *cred) |
| { |
| bscv_soft_state_t *ssp; |
| int instance; |
| |
| instance = DEVICETOINSTANCE(dev); |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| if (ssp == NULL) { |
| return (ENXIO); |
| } |
| BSCV_TRACE(ssp, 'O', "bscv_close", "instance 0x%x", instance); |
| |
| return (0); |
| } |
| |
| static int |
| bscv_map_regs(bscv_soft_state_t *ssp) |
| { |
| int i; |
| int retval; |
| int *props; |
| unsigned int nelements; |
| |
| ASSERT(ssp); |
| |
| ssp->nchannels = 0; |
| |
| /* |
| * Work out how many channels are available by looking at the number |
| * of elements of the regs property array. |
| */ |
| retval = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, ssp->dip, |
| DDI_PROP_DONTPASS, "reg", &props, &nelements); |
| |
| /* We don't need props anymore. Free memory if it was allocated */ |
| if (retval == DDI_PROP_SUCCESS) |
| ddi_prop_free(props); |
| |
| /* Check for sanity of nelements */ |
| if (retval != DDI_PROP_SUCCESS) { |
| BSCV_TRACE(ssp, 'A', "bscv_map_regs", "lookup reg returned" |
| " 0x%x", retval); |
| goto cleanup_exit; |
| } else if (nelements % LOMBUS_REGSPEC_SIZE != 0) { |
| BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d not" |
| " a multiple of %d", nelements, LOMBUS_REGSPEC_SIZE); |
| goto cleanup_exit; |
| } else if (nelements > BSCV_MAXCHANNELS * LOMBUS_REGSPEC_SIZE) { |
| BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d too large" |
| ", probably a misconfiguration", nelements); |
| goto cleanup_exit; |
| } else if (nelements < BSCV_MINCHANNELS * LOMBUS_REGSPEC_SIZE) { |
| BSCV_TRACE(ssp, 'A', "bscv_map_regs", "nelements %d too small" |
| ", need to have at least a general and a wdog channel", |
| nelements); |
| goto cleanup_exit; |
| } |
| |
| ssp->nchannels = nelements / LOMBUS_REGSPEC_SIZE; |
| |
| ssp->attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; |
| ssp->attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; |
| ssp->attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; |
| |
| for (i = 0; i < ssp->nchannels; i++) { |
| retval = ddi_regs_map_setup(ssp->dip, i, |
| (caddr_t *)&ssp->channel[i].regs, |
| 0, 0, &ssp->attr, &ssp->channel[i].handle); |
| if (retval != DDI_SUCCESS) { |
| BSCV_TRACE(ssp, 'A', "bscv_map_regs", "map failure" |
| " 0x%x on space %d", retval, i); |
| |
| /* Rewind all current mappings - avoiding failed one */ |
| i--; |
| for (; i >= 0; i--) { |
| ddi_regs_map_free(&ssp->channel[i].handle); |
| } |
| |
| goto cleanup_exit; |
| } |
| } |
| |
| return (DDI_SUCCESS); |
| |
| cleanup_exit: |
| /* |
| * It is important to set nchannels to 0 even if, say, only one of |
| * the two required handles was mapped. If we cannot achieve our |
| * minimum config its not safe to do any IO; this keeps our failure |
| * mode handling simpler. |
| */ |
| ssp->nchannels = 0; |
| return (DDI_FAILURE); |
| } |
| |
| static void |
| bscv_unmap_regs(bscv_soft_state_t *ssp) |
| { |
| int i; |
| |
| ASSERT(ssp); |
| |
| for (i = 0; i < ssp->nchannels; i++) { |
| ddi_regs_map_free(&ssp->channel[i].handle); |
| } |
| } |
| |
| /* |
| * Map logical services onto physical XBus channels. |
| */ |
| static void |
| bscv_map_chan_logical_physical(bscv_soft_state_t *ssp) |
| { |
| ASSERT(ssp); |
| |
| /* |
| * We can assert that there will always be at least two channels, |
| * to allow watchdog pats to be segregated from all other traffic. |
| */ |
| chan_general = 0; |
| chan_wdogpat = 1; |
| |
| /* |
| * By default move all other services onto the generic channel unless |
| * the hardware supports additional channels. |
| */ |
| |
| chan_cpusig = chan_eeprom = chan_prog = chan_general; |
| |
| if (ssp->nchannels > 2) |
| chan_cpusig = 2; |
| if (ssp->nchannels > 3) |
| chan_eeprom = 3; |
| if (ssp->nchannels > 4) |
| chan_prog = 4; |
| } |
| |
| |
| /* |
| * function - bscv_full_stop |
| * description - gracefully shut the lom down during panic or reboot. |
| * Disables the watchdog and sets up serial event reporting. |
| * inputs - soft state pointer |
| * outputs - none |
| */ |
| void |
| bscv_full_stop(bscv_soft_state_t *ssp) |
| { |
| uint8_t bits2set = 0; |
| uint8_t bits2clear = 0; |
| int obtained_lock; |
| |
| BSCV_TRACE(ssp, 'W', "bscv_full_stop", |
| "turning off watchdog"); |
| |
| /* |
| * Obtain the softstate lock only if it is not already owned, |
| * as this function can be called from a High-level interrupt |
| * context. As a result, our thread cannot sleep. |
| * At end of function, our thread releases the lock only if |
| * it acquired the lock. |
| */ |
| obtained_lock = (bscv_tryenter(ssp) != 0); |
| |
| #if defined(__i386) || defined(__amd64) |
| if (ddi_in_panic()) { |
| bscv_inform_bsc(ssp, BSC_INFORM_PANIC); |
| } else { |
| bscv_inform_bsc(ssp, BSC_INFORM_OFFLINE); |
| } |
| #endif /* __i386 || __amd64 */ |
| |
| /* set serial event reporting */ |
| switch (ssp->serial_reporting) { |
| case LOM_SER_EVENTS_ON: |
| case LOM_SER_EVENTS_DEF: |
| /* Make sure serial event reporting is on */ |
| bits2clear = EBUS_ALARM_NOEVENTS; |
| break; |
| case LOM_SER_EVENTS_OFF: |
| /* Make sure serial event reporting is on */ |
| bits2set = EBUS_ALARM_NOEVENTS; |
| break; |
| default: |
| break; |
| } |
| bscv_setclear8_volatile(ssp, chan_general, |
| EBUS_IDX_ALARM, bits2set, bits2clear); |
| |
| /* Do not free the lock if our thread did not obtain it. */ |
| if (obtained_lock != 0) { |
| bscv_exit(ssp); |
| } |
| } |
| |
| /* |
| * LOM I/O routines. |
| * |
| * locking |
| * |
| * Two sets of routines are provided: |
| * normal - must be called after acquiring an appropriate lock. |
| * locked - perform all the locking required and return any error |
| * code in the supplied 'res' argument. If there is no |
| * error 'res' is not changed. |
| * The locked routines are designed for use in ioctl commands where |
| * only a single operation needs to be performed and the overhead of |
| * locking and result checking adds significantly to code complexity. |
| * |
| * locking primitives |
| * |
| * bscv_enter() - acquires an I/O lock for the calling thread. |
| * bscv_tryenter() - conditionally acquires an I/O lock for calling thread. |
| * bscv_exit() - releases an I/O lock acquired by bscv_enter(). |
| * bscv_held() - used to assert ownership of an I/O lock. |
| * |
| * normal I/O routines |
| * |
| * Note bscv_{put|get}{16|32} routines are big-endian. This assumes that |
| * the firmware works that way too. |
| * |
| * bscv_put8(), bscv_put16, bscv_put32 - write values to the LOM |
| * and handle any retries if necessary. |
| * 16 and 32 bit values are big-endian. |
| * bscv_get8(), bscv_get16, bscv_get32 - read values from the LOM |
| * and handle any retries if necessary. |
| * 16 and 32 bit values are big-endian. |
| * bscv_setclear8() - set or clear the specified bits in the register |
| * at the supplied address. |
| * bscv_setclear8_volatile() - set or clear the specified bits in the |
| * register at the supplied address. If the lom reports |
| * that the registers has changed since the last read |
| * re-read and apply the set or clear to the new bits. |
| * bscv_get8_cached() - Return a cached register value (addr < 0x80). |
| * Does not access the hardware. A read of the hardware |
| * automatically updates this cache. |
| * |
| * locked I/O routines |
| * |
| * bscv_get8_locked(), bscv_rep_get8_locked(). |
| * |
| * Call the indicated function from above, but wrapping it with |
| * bscv_enter()/bscv_exit(). |
| * |
| * |
| * Fault management |
| * |
| * LOM communications fault are grouped into three categories: |
| * 1) Faulty - the LOM is not responding and no attempt to communicate |
| * with it should be made. |
| * 2) Transient fault - something which might recover after a retry |
| * but which doesn't affect our ability to perform other |
| * commands. |
| * 3) Command error - an inappropriate command was executed. A retry |
| * will not fix it but the command failed. |
| * |
| * The current implementation of the bscv driver is not very good at |
| * noticing command errors due to the structure of the original code |
| * that it is based on. It is possible to extend the driver to do this |
| * and would probably involve having a concept of a "session error" |
| * which is less severe than a fault but means that a sequence of |
| * commands had some fault which cannot be recovered. |
| * |
| * |
| * faults |
| * |
| * bscv_faulty() - returns B_TRUE if the LOM (communications) have been |
| * declared faulty. |
| * bscv_clear_fault() - marks the LOM as not faulty. |
| * bscv_set_fault() - marks the LOM as being faulty. |
| * |
| * bscv_clear_fault and bscv_set_fault should generally not be called |
| * directly. |
| * |
| * command errors/transient faults |
| * |
| * bscv_retcode() - returns the actual error code of the last operation. |
| * bscv_should_retry() - determines if last operation may suceed if |
| * retried. |
| * bscv_locked_result() - Set the result of a locked register access. |
| * |
| * low level I/O primitives |
| * |
| * These are generally not called directly. These perform a single |
| * access to the LOM device. They do not handle retries. |
| * |
| * bscv_put8_once() |
| * bscv_get8_once() |
| * bscv_probe() - perform a probe (NOP) operation to check out lom comms. |
| * bscv_resync_comms() - resynchronise communications after a transient fault. |
| */ |
| |
| static void |
| bscv_enter(bscv_soft_state_t *ssp) |
| { |
| BSCV_TRACE(ssp, '@', "bscv_enter", ""); |
| mutex_enter(&ssp->cmd_mutex); |
| ssp->had_session_error = B_FALSE; |
| } |
| |
| static int |
| bscv_tryenter(bscv_soft_state_t *ssp) |
| { |
| int rv; |
| |
| BSCV_TRACE(ssp, '@', "bscv_tryenter", ""); |
| if ((rv = mutex_tryenter(&ssp->cmd_mutex)) != 0) { |
| ssp->had_session_error = B_FALSE; |
| } |
| return (rv); |
| } |
| |
| static void |
| bscv_exit(bscv_soft_state_t *ssp) |
| { |
| mutex_exit(&ssp->cmd_mutex); |
| BSCV_TRACE(ssp, '@', "bscv_exit", ""); |
| } |
| |
| #ifdef DEBUG |
| static int |
| bscv_held(bscv_soft_state_t *ssp) |
| { |
| return (mutex_owned(&ssp->cmd_mutex)); |
| } |
| #endif /* DEBUG */ |
| |
| static void |
| bscv_put8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val) |
| { |
| boolean_t needretry; |
| int num_failures; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| if (bscv_faulty(ssp)) { |
| return; |
| } |
| |
| BSCV_TRACE(ssp, '@', "bscv_put8", |
| "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val); |
| |
| for (num_failures = 0; |
| num_failures < BSC_FAILURE_RETRY_LIMIT; |
| num_failures++) { |
| bscv_put8_once(ssp, chan, addr, val); |
| needretry = bscv_should_retry(ssp); |
| if (!needretry) { |
| break; |
| } |
| } |
| if (ssp->command_error != 0) { |
| ssp->had_session_error = B_TRUE; |
| } |
| |
| if (needretry) { |
| /* Failure - we ran out of retries */ |
| cmn_err(CE_WARN, "bscv_put8: addr 0x%x.%02x retried " |
| "write %d times, giving up", |
| addr >> 8, addr & 0xff, num_failures); |
| bscv_set_fault(ssp); |
| } else if (num_failures > 0) { |
| BSCV_TRACE(ssp, 'R', "bscv_put8", |
| "addr 0x%x.%02x retried write %d times, succeeded", |
| addr >> 8, addr & 0xff, num_failures); |
| } |
| } |
| |
| static void |
| bscv_put16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint16_t val) |
| { |
| ASSERT(bscv_held(ssp)); |
| BSCV_TRACE(ssp, '@', "bscv_put16", |
| "addr 0x%x.%02x <= %04x", addr >> 8, addr & 0xff, val); |
| bscv_put8(ssp, chan, addr, val >> 8); |
| bscv_put8(ssp, chan, addr + 1, val & 0xff); |
| } |
| |
| static void |
| bscv_put32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint32_t val) |
| { |
| ASSERT(bscv_held(ssp)); |
| BSCV_TRACE(ssp, '@', "bscv_put32", |
| "addr 0x%x.%02x <= %08x", addr >> 8, addr & 0xff, val); |
| bscv_put8(ssp, chan, addr, (val >> 24) & 0xff); |
| bscv_put8(ssp, chan, addr + 1, (val >> 16) & 0xff); |
| bscv_put8(ssp, chan, addr + 2, (val >> 8) & 0xff); |
| bscv_put8(ssp, chan, addr + 3, val & 0xff); |
| } |
| |
| static uint8_t |
| bscv_get8(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr) |
| { |
| uint8_t retval; |
| boolean_t needretry; |
| int num_failures; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| if (bscv_faulty(ssp)) { |
| return (0); |
| } |
| |
| for (num_failures = 0; |
| num_failures < BSC_FAILURE_RETRY_LIMIT; |
| num_failures++) { |
| retval = bscv_get8_once(ssp, chan, addr); |
| needretry = bscv_should_retry(ssp); |
| if (!needretry) { |
| break; |
| } |
| } |
| if (ssp->command_error != 0) { |
| ssp->had_session_error = B_TRUE; |
| } |
| |
| if (needretry) { |
| /* Failure */ |
| cmn_err(CE_WARN, "bscv_get8: addr 0x%x.%02x retried " |
| "read %d times, giving up", |
| addr >> 8, addr & 0xff, num_failures); |
| bscv_set_fault(ssp); |
| } else if (num_failures > 0) { |
| BSCV_TRACE(ssp, 'R', "bscv_get8", |
| "addr 0x%x.%02x retried read %d times, succeeded", |
| addr >> 8, addr & 0xff, num_failures); |
| } |
| |
| BSCV_TRACE(ssp, '@', "bscv_get8", |
| "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval); |
| return (retval); |
| } |
| |
| static uint16_t |
| bscv_get16(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr) |
| { |
| uint16_t retval; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| retval = bscv_get8(ssp, chan, addr) << 8; |
| retval |= bscv_get8(ssp, chan, addr + 1); |
| |
| BSCV_TRACE(ssp, '@', "bscv_get16", |
| "addr 0x%x.%02x => %04x", addr >> 8, addr & 0xff, retval); |
| return (retval); |
| } |
| |
| static uint32_t |
| bscv_get32(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr) |
| { |
| uint32_t retval; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| retval = bscv_get8(ssp, chan, addr) << 24; |
| retval |= bscv_get8(ssp, chan, addr + 1) << 16; |
| retval |= bscv_get8(ssp, chan, addr + 2) << 8; |
| retval |= bscv_get8(ssp, chan, addr + 3); |
| |
| BSCV_TRACE(ssp, '@', "bscv_get32", |
| "addr 0x%x.%02x => %08x", addr >> 8, addr & 0xff, retval); |
| return (retval); |
| } |
| |
| static void |
| bscv_setclear8(bscv_soft_state_t *ssp, int chan, |
| bscv_addr_t addr, uint8_t set, uint8_t clear) |
| { |
| uint8_t val; |
| |
| ASSERT(bscv_held(ssp)); |
| ASSERT(addr < BSC_ADDR_CACHE_LIMIT); |
| |
| val = ssp->lom_regs[addr] | set; |
| val &= ~clear; |
| |
| BSCV_TRACE(ssp, '@', "bscv_setclear8", |
| "addr 0x%x.%02x, set %02x, clear %02x => %02x", |
| addr >> 8, addr & 0xff, |
| set, clear, val); |
| |
| bscv_put8(ssp, chan, addr, val); |
| } |
| |
| static void |
| bscv_setclear8_volatile(bscv_soft_state_t *ssp, int chan, |
| bscv_addr_t addr, uint8_t set, uint8_t clear) |
| { |
| uint8_t val; |
| boolean_t needretry; |
| int num_failures; |
| |
| ASSERT(bscv_held(ssp)); |
| ASSERT(addr < BSC_ADDR_CACHE_LIMIT); |
| |
| if (bscv_faulty(ssp)) { |
| return; |
| } |
| |
| BSCV_TRACE(ssp, '@', "bscv_setclear8_volatile", |
| "addr 0x%x.%02x => set %02x clear %02x", |
| addr >> 8, addr & 0xff, set, clear); |
| |
| val = bscv_get8_cached(ssp, addr); |
| for (num_failures = 0; |
| num_failures < BSC_FAILURE_RETRY_LIMIT; |
| num_failures++) { |
| val |= set; |
| val &= ~clear; |
| bscv_put8_once(ssp, chan, addr, val); |
| if (ssp->command_error == EBUS_ERROR_STALEDATA) { |
| /* Re-read the stale register from the lom */ |
| val = bscv_get8_once(ssp, chan, addr); |
| needretry = 1; |
| } else { |
| needretry = bscv_should_retry(ssp); |
| if (!needretry) { |
| break; |
| } |
| } |
| } |
| if (ssp->command_error != 0) { |
| ssp->had_session_error = B_TRUE; |
| } |
| |
| if (needretry) { |
| /* Failure */ |
| cmn_err(CE_WARN, "bscv_setclear8_volatile: addr 0x%x.%02x " |
| "retried write %d times, giving up", |
| addr >> 8, addr & 0xff, num_failures); |
| if (ssp->command_error != EBUS_ERROR_STALEDATA) { |
| bscv_set_fault(ssp); |
| } |
| } else if (num_failures > 0) { |
| BSCV_TRACE(ssp, 'R', "bscv_setclear8_volatile", |
| "addr 0x%x.%02x retried write %d times, succeeded", |
| addr >> 8, addr & 0xff, num_failures); |
| } |
| } |
| |
| static void |
| bscv_rep_rw8(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr, |
| bscv_addr_t dev_addr, size_t repcount, uint_t flags, |
| boolean_t is_write) |
| { |
| size_t inc; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; |
| for (; repcount--; dev_addr += inc) { |
| if (flags & DDI_DEV_AUTOINCR) { |
| if (is_write) { |
| bscv_put8(ssp, chan, dev_addr, *host_addr++); |
| } else { |
| *host_addr++ = bscv_get8(ssp, chan, dev_addr); |
| } |
| } else { |
| if (is_write) { |
| bscv_put8_once(ssp, chan, |
| dev_addr, *host_addr++); |
| } else { |
| *host_addr++ = bscv_get8_once(ssp, chan, |
| dev_addr); |
| } |
| /* We need this because _once routines don't do it */ |
| if (ssp->command_error != 0) { |
| ssp->had_session_error = B_TRUE; |
| } |
| } |
| if (bscv_faulty(ssp) || bscv_session_error(ssp)) { |
| /* |
| * No retry here. If we were AUTOINCR then get/put |
| * will have retried. For NO_AUTOINCR we cannot retry |
| * because the data would be corrupted. |
| */ |
| break; |
| } |
| } |
| } |
| |
| static uint8_t |
| bscv_get8_cached(bscv_soft_state_t *ssp, bscv_addr_t addr) |
| { |
| ASSERT(addr < BSC_ADDR_CACHE_LIMIT); |
| /* Can be called with or without the lock held */ |
| |
| return (ssp->lom_regs[addr]); |
| } |
| |
| static uint8_t |
| bscv_get8_locked(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, int *res) |
| { |
| uint8_t retval; |
| |
| ASSERT(addr < BSC_ADDR_CACHE_LIMIT); |
| bscv_enter(ssp); |
| retval = bscv_get8(ssp, chan, addr); |
| bscv_locked_result(ssp, res); |
| bscv_exit(ssp); |
| BSCV_TRACE(ssp, '@', "bscv_get8_locked", |
| "addr 0x%x.%02x => %02x", addr >> 8, addr & 0xff, retval); |
| return (retval); |
| } |
| |
| static void |
| bscv_rep_get8_locked(bscv_soft_state_t *ssp, int chan, uint8_t *host_addr, |
| bscv_addr_t dev_addr, size_t repcount, uint_t flags, int *res) |
| { |
| bscv_enter(ssp); |
| bscv_rep_rw8(ssp, chan, host_addr, dev_addr, repcount, |
| flags, B_FALSE /* read */); |
| bscv_locked_result(ssp, res); |
| bscv_exit(ssp); |
| } |
| |
| static boolean_t |
| bscv_faulty(bscv_soft_state_t *ssp) |
| { |
| ASSERT(bscv_held(ssp)); |
| return (ssp->had_fault); |
| } |
| |
| static void |
| bscv_clear_fault(bscv_soft_state_t *ssp) |
| { |
| ASSERT(bscv_held(ssp)); |
| BSCV_TRACE(ssp, 'J', "bscv_clear_fault", "clearing fault flag"); |
| ssp->had_fault = B_FALSE; |
| ssp->had_session_error = B_FALSE; |
| } |
| |
| static void |
| bscv_set_fault(bscv_soft_state_t *ssp) |
| { |
| ASSERT(bscv_held(ssp)); |
| BSCV_TRACE(ssp, 'J', "bscv_set_fault", "setting fault flag"); |
| ssp->had_fault = B_TRUE; |
| } |
| |
| static boolean_t |
| bscv_session_error(bscv_soft_state_t *ssp) |
| { |
| ASSERT(bscv_held(ssp)); |
| return (ssp->had_session_error); |
| } |
| |
| static int |
| bscv_retcode(bscv_soft_state_t *ssp) |
| { |
| BSCV_TRACE(ssp, '@', "bscv_retcode", |
| "code 0x%x", ssp->command_error); |
| return (ssp->command_error); |
| } |
| |
| static int |
| bscv_should_retry(bscv_soft_state_t *ssp) |
| { |
| if ((ssp->command_error == EBUS_ERROR_DEVICEFAIL) || |
| (ssp->command_error >= LOMBUS_ERR_BASE)) { |
| /* This command is due to an I/O fault - retry might fix */ |
| return (1); |
| } else { |
| /* |
| * The command itself was bad - there is no point in fixing |
| * Note. Whatever happens we should know that if we were |
| * doing EBUS_IDX_SELFTEST0..EBUS_IDX_SELFTEST7 and we |
| * had 0x80 set then this is a test error not a retry |
| * error. |
| */ |
| return (0); |
| } |
| } |
| |
| static void |
| bscv_locked_result(bscv_soft_state_t *ssp, int *res) |
| { |
| if (bscv_faulty(ssp) || (bscv_retcode(ssp) != 0)) { |
| *res = EIO; |
| } |
| } |
| |
| static void |
| bscv_put8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr, uint8_t val) |
| { |
| uint32_t fault; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| ssp->command_error = 0; |
| |
| if (bscv_faulty(ssp)) { |
| /* Bail out things are not working */ |
| return; |
| } else if (ssp->nchannels == 0) { |
| /* Didn't manage to map handles so ddi_{get,put}* broken */ |
| BSCV_TRACE(ssp, '@', "bscv_put8_once", |
| "nchannels is 0x0 so cannot do IO"); |
| return; |
| } |
| |
| /* Clear any pending fault */ |
| ddi_put32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0); |
| |
| /* Do the access and get fault code - may take a long time */ |
| ddi_put8(ssp->channel[chan].handle, |
| &ssp->channel[chan].regs[addr], val); |
| fault = ddi_get32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG)); |
| |
| ssp->command_error = fault; |
| |
| if (fault == 0) { |
| /* Things were ok - update cache entry */ |
| if (addr < BSC_ADDR_CACHE_LIMIT) { |
| /* Store cacheable entries */ |
| ssp->lom_regs[addr] = val; |
| } |
| } else if (fault >= LOMBUS_ERR_BASE) { |
| /* lombus problem - do a resync session */ |
| cmn_err(CE_WARN, "!bscv_put8_once: Had comms fault " |
| "for address 0x%x.%02x - data 0x%x, fault 0x%x", |
| addr >> 8, addr & 0xff, val, fault); |
| /* Attempt to resync with the lom */ |
| bscv_resync_comms(ssp, chan); |
| /* |
| * Note: we do not set fault status here. That |
| * is done if our caller decides to give up talking to |
| * the lom. The observant might notice that this means |
| * that if we mend things on the last attempt we still |
| * get the fault set - we just live with that! |
| */ |
| } |
| |
| BSCV_TRACE(ssp, '@', "bscv_put8_once", |
| "addr 0x%x.%02x <= 0x%02x", addr >> 8, addr & 0xff, val); |
| } |
| |
| static uint8_t |
| bscv_get8_once(bscv_soft_state_t *ssp, int chan, bscv_addr_t addr) |
| { |
| uint8_t val; |
| uint32_t fault; |
| |
| ASSERT(bscv_held(ssp)); |
| |
| ssp->command_error = 0; |
| |
| if (bscv_faulty(ssp)) { |
| /* Bail out things are not working */ |
| return (0xff); |
| } else if (ssp->nchannels == 0) { |
| /* Didn't manage to map handles so ddi_{get,put}* broken */ |
| BSCV_TRACE(ssp, '@', "bscv_get8_once", |
| "nchannels is 0x0 so cannot do IO"); |
| return (0xff); |
| } |
| |
| /* Clear any pending fault */ |
| ddi_put32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0); |
| |
| /* Do the access and get fault code - may take a long time */ |
| val = ddi_get8(ssp->channel[chan].handle, |
| &ssp->channel[chan].regs[addr]); |
| fault = ddi_get32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG)); |
| ssp->command_error = fault; |
| |
| if (fault >= LOMBUS_ERR_BASE) { |
| /* lombus problem - do a resync session */ |
| cmn_err(CE_WARN, "!bscv_get8_once: Had comms fault " |
| "for address 0x%x.%02x - data 0x%x, fault 0x%x", |
| addr >> 8, addr & 0xff, val, fault); |
| /* Attempt to resync with the lom */ |
| bscv_resync_comms(ssp, chan); |
| /* |
| * Note: we do not set fault status here. That |
| * is done if our caller decides to give up talking to |
| * the lom. The observant might notice that this means |
| * that if we mend things on the last attempt we still |
| * get the fault set - we just live with that! |
| */ |
| } |
| /* |
| * FIXME - should report error if you get |
| * EBUS_ERROR_DEVICEFAIL reported from the BSC. That gets |
| * logged as a failure in bscv_should_retry and may contribute |
| * to a permanent failure. Reference issues seen by Mitac. |
| */ |
| |
| if (!bscv_faulty(ssp)) { |
| if (addr < BSC_ADDR_CACHE_LIMIT) { |
| /* Store cacheable entries */ |
| ssp->lom_regs[addr] = val; |
| } |
| } |
| |
| BSCV_TRACE(ssp, '@', "bscv_get8_once", |
| "addr 0x%x.%02x => 0x%02x", addr >> 8, addr & 0xff, val); |
| return (val); |
| } |
| |
| static uint32_t |
| bscv_probe(bscv_soft_state_t *ssp, int chan, uint32_t *fault) |
| { |
| uint32_t async_reg; |
| |
| if (ssp->nchannels == 0) { |
| /* |
| * Failed to map handles, so cannot do any IO. Set the |
| * fault indicator and return a dummy value. |
| */ |
| BSCV_TRACE(ssp, '@', "bscv_probe", |
| "nchannels is 0x0 so cannot do any IO"); |
| *fault = LOMBUS_ERR_REG_NUM; |
| return ((~(int8_t)0)); |
| } |
| |
| /* Clear faults */ |
| ddi_put32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_FAULT_REG), 0); |
| /* Probe and Check faults */ |
| *fault = ddi_get32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_PROBE_REG)); |
| /* Read status */ |
| async_reg = ddi_get32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, LOMBUS_ASYNC_REG)); |
| |
| BSCV_TRACE(ssp, '@', "bscv_probe", |
| "async status 0x%x, fault 0x%x", async_reg, *fault); |
| return (async_reg); |
| } |
| |
| static void |
| bscv_resync_comms(bscv_soft_state_t *ssp, int chan) |
| { |
| int try; |
| uint32_t command_error = ssp->command_error; |
| uint32_t fault = 0; |
| |
| if (ssp->nchannels == 0) { |
| /* |
| * Didn't manage to map handles so ddi_{get,put}* broken. |
| * Therefore, there is no way to resync comms. |
| */ |
| BSCV_TRACE(ssp, '@', "bscv_resync_comms", |
| "nchannels is 0x0 so not possible to resync comms"); |
| return; |
| } |
| if (command_error >= LOMBUS_ERR_BASE && |
| command_error != LOMBUS_ERR_REG_NUM && |
| command_error != LOMBUS_ERR_REG_SIZE && |
| command_error != LOMBUS_ERR_TIMEOUT) { |
| /* Resync here to make sure that the lom is talking */ |
| cmn_err(CE_WARN, "!bscv_resync_comms: " |
| "Attempting comms resync after comms fault 0x%x", |
| command_error); |
| for (try = 1; try <= 8; try++) { |
| /* Probe */ |
| fault = ddi_get32(ssp->channel[chan].handle, |
| (uint32_t *)BSC_NEXUS_ADDR(ssp, chan, 0, |
| LOMBUS_PROBE_REG)); |
| |
| if (fault == 0) { |
| break; |
| } else { |
| cmn_err(CE_WARN, "!bscv_resync_comms: " |
| "comms resync (probing) - try 0x%x " |
| "had fault 0x%x", try, fault); |
| } |
| } |
| if (fault != 0) { |
| cmn_err(CE_WARN, "!bscv_resync_comms: " |
| "Failed to resync comms - giving up"); |
| ssp->bad_resync++; |
| } else { |
| cmn_err(CE_WARN, "!bscv_resync_comms: " |
| "resync comms after 0x%x tries", try); |
| ssp->bad_resync = 0; |
| } |
| } |
| |
| } |
| |
| |
| /* |
| * LOMLite configuration/event eeprom access routines |
| * |
| * bscv_window_setup() - Read/Sanity check the eeprom parameters. |
| * This must be called prior to calling bscv_eerw(). |
| * bscv_eerw() - Read/write data from/to the eeprom. |
| */ |
| |
| /* |
| * function - bscv_window_setup |
| * description - this routine reads the eeprom parameters and sanity |
| * checks them to ensure that the lom is talking sense. |
| * inputs - soft state ptr |
| * outputs - B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK. |
| */ |
| static boolean_t |
| bscv_window_setup(bscv_soft_state_t *ssp) |
| { |
| ASSERT(bscv_held(ssp)); |
| |
| if (ssp->eeinfo_valid) { |
| /* Already have good cached values */ |
| return (ssp->eeinfo_valid); |
| } |
| ssp->eeprom_size = |
| bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) * 1024; |
| ssp->eventlog_start = bscv_get16(ssp, chan_general, |
| EBUS_IDX_LOG_START_HI); |
| |
| /* |
| * The log does not run to the end of the EEPROM because it is a |
| * logical partition. The last 8K partition is reserved for FRUID |
| * usage. |
| */ |
| ssp->eventlog_size = EBUS_LOG_END - ssp->eventlog_start; |
| |
| BSCV_TRACE(ssp, 'I', "bscv_window_setup", "eeprom size 0x%x log_start" |
| " 0x%x log_size 0x%x", ssp->eeprom_size, ssp->eventlog_start, |
| ssp->eventlog_size); |
| |
| if (bscv_faulty(ssp) || bscv_session_error(ssp)) { |
| ssp->eeinfo_valid = B_FALSE; |
| } else if ((ssp->eeprom_size == 0) || |
| (ssp->eventlog_start >= ssp->eeprom_size)) { |
| /* Sanity check values */ |
| cmn_err(CE_WARN, |
| "!bscv_window_setup: read invalid eeprom parameters"); |
| ssp->eeinfo_valid = B_FALSE; |
| } else { |
| ssp->eeinfo_valid = B_TRUE; |
| } |
| |
| BSCV_TRACE(ssp, 'I', "bscv_window_setup", "returning eeinfo_valid %s", |
| ssp->eeinfo_valid ? "true" : "false"); |
| return (ssp->eeinfo_valid); |
| } |
| |
| /* |
| * function - bscv_eerw |
| * description - this routine reads/write data from/to the eeprom. |
| * It takes care of setting the window on the eeprom correctly. |
| * inputs - soft state ptr, eeprom offset, data buffer, size, read/write |
| * outputs - B_TRUE if the eeprom is ok, B_FALSE if the eeprom is not OK. |
| */ |
| static int |
| bscv_eerw(bscv_soft_state_t *ssp, uint32_t eeoffset, uint8_t *buf, |
| unsigned size, boolean_t is_write) |
| { |
| uint32_t blk_addr = eeoffset; |
| unsigned remaining = size; |
| uint8_t page_idx; |
| uint8_t this_page; |
| uint8_t blk_size; |
| int res = 0; |
| |
| while (remaining > 0) { |
| page_idx = blk_addr & 0xff; |
| if ((page_idx + remaining) > 0x100) { |
| blk_size = 0x100 - page_idx; |
| } else { |
| blk_size = remaining; |
| } |
| |
| /* Select correct eeprom page */ |
| this_page = blk_addr >> 8; |
| bscv_put8(ssp, chan_eeprom, EBUS_IDX_EEPROM_PAGESEL, this_page); |
| |
| BSCV_TRACE(ssp, 'M', "lom_eerw", |
| "%s data @0x%x.%02x, size 0x%x, 0x%x bytes remaining", |
| is_write ? "writing" : "reading", |
| this_page, page_idx, blk_size, remaining - blk_size); |
| |
| bscv_rep_rw8(ssp, chan_eeprom, |
| buf, BSCVA(EBUS_CMD_SPACE_EEPROM, page_idx), |
| blk_size, DDI_DEV_AUTOINCR, is_write); |
| |
| if (bscv_faulty(ssp) || bscv_session_error(ssp)) { |
| res = EIO; |
| break; |
| } |
| |
| remaining -= blk_size; |
| blk_addr += blk_size; |
| buf += blk_size; |
| } |
| |
| return (res); |
| } |
| |
| static boolean_t |
| bscv_is_null_event(bscv_soft_state_t *ssp, lom_event_t *e) |
| { |
| ASSERT(e != NULL); |
| |
| if (EVENT_DECODE_SUBSYS(e->ev_subsys) == EVENT_SUBSYS_NONE && |
| e->ev_event == EVENT_NONE) { |
| /* |
| * This marks a NULL event. |
| */ |
| BSCV_TRACE(ssp, 'E', "bscv_is_null_event", |
| "EVENT_SUBSYS_NONE/EVENT_NONE null event"); |
| return (B_TRUE); |
| } else if (e->ev_subsys == 0xff && e->ev_event == 0xff) { |
| /* |
| * Under some circumstances, we've seen all 1s to represent |
| * a manually cleared event log at the BSC prompt. Only |
| * a test/diagnosis environment is likely to show this. |
| */ |
| BSCV_TRACE(ssp, 'E', "bscv_is_null_event", "0xffff null event"); |
| return (B_TRUE); |
| } else { |
| /* |
| * Not a NULL event. |
| */ |
| BSCV_TRACE(ssp, 'E', "bscv_is_null_event", "returning False"); |
| return (B_FALSE); |
| } |
| } |
| |
| /* |
| * ********************************************************************* |
| * IOCTL Processing |
| * ********************************************************************* |
| */ |
| |
| /* |
| * function - bscv_ioctl |
| * description - routine that acts as a high level manager for ioctls. It |
| * calls the appropriate handler for ioctls on the alarm:mon and |
| * alarm:ctl minor nodes respectively |
| * |
| * Unsupported ioctls (now deprecated) |
| * LOMIOCALCTL |
| * LOMIOCALSTATE |
| * LOMIOCCLEARLOG |
| * LOMIOCCTL |
| * LOMIOCCTL2 |
| * LOMIOCDAEMON |
| * LOMIOCDMON |
| * LOMIOCDOGCTL, TSIOCDOGCTL |
| * LOMIOCDOGPAT, TSIOCDOGPAT |
| * LOMIOCDOGTIME, TSIOCDOGTIME |
| * LOMIOCEVENTLOG |
| * LOMIOCEVNT |
| * LOMIOCGETMASK |
| * LOMIOCMPROG |
| * LOMIOCNBMON, TSIOCNBMON |
| * LOMIOCSLEEP |
| * LOMIOCUNLOCK, TSIOCUNLOCK |
| * LOMIOCWTMON, TSIOCWTMON |
| * |
| * Supported ioctls |
| * LOMIOCDOGSTATE, TSIOCDOGSTATE |
| * LOMIOCPROG |
| * LOMIOCPSUSTATE |
| * LOMIOCFANSTATE |
| * LOMIOCFLEDSTATE |
| * LOMIOCINFO |
| * LOMIOCMREAD |
| * LOMIOCVOLTS |
| * LOMIOCSTATS |
| * LOMIOCTEMP |
| * LOMIOCCONS |
| * LOMIOCEVENTLOG2 |
| * LOMIOCINFO2 |
| * LOMIOCTEST |
| * LOMIOCMPROG2 |
| * LOMIOCMREAD2 |
| * |
| * inputs - device number, command, user space arg, filemode, user |
| * credentials, return value |
| * outputs - the return value propagated back by the lower level routines. |
| */ |
| |
| /*ARGSUSED*/ |
| static int |
| bscv_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred, int *rvalp) |
| { |
| bscv_soft_state_t *ssp; |
| int instance; |
| int res = 0; |
| |
| instance = DEVICETOINSTANCE(dev); |
| ssp = ddi_get_soft_state(bscv_statep, instance); |
| if (ssp == NULL) { |
| return (ENXIO); |
| } |
| |
| /* |
| * The Combined Switch and Service Processor takes care of configuration |
| * and control. The CSSP tells the BSC chip about it; therefore the |
| * bscv driver doesn't send such configuration and control to the BSC. |
| * Additionally Watchdog configuration is no longer done from userland |
| * lom. |
| */ |
| switch (cmd) { |
| case LOMIOCALCTL: |
| case LOMIOCALSTATE: |
| case LOMIOCCLEARLOG: |
| case LOMIOCCTL: |
| case LOMIOCCTL2: |
| case LOMIOCDAEMON: |
| case LOMIOCDMON: |
| case LOMIOCDOGCTL: |
| case LOMIOCDOGPAT: |
| case LOMIOCDOGTIME: |
| case LOMIOCEVENTLOG: |
| case LOMIOCEVNT: |
| case LOMIOCGETMASK: |
| case LOMIOCMPROG: |
| case LOMIOCNBMON: |
| case LOMIOCSLEEP: |
| case LOMIOCUNLOCK: |
| case LOMIOCWTMON: |
| return (ENOTSUP); |
| } |
| |
| /* |
| * set the default result. |
| */ |
| |
| *rvalp = 0; |
| |
| if (ssp->cssp_prog) { |
| return (ENXIO); |
| } else if ((ssp->prog_mode_only || ssp->programming) && |
| cmd != LOMIOCPROG) { |
| return (ENXIO); |
| } |
| |
| /* |
| * Check that the caller has appropriate access permissions |
| * (FWRITE set in mode) for those ioctls which change lom |
| * state |
| */ |
| if (!(mode & FWRITE)) { |
| switch (cmd) { |
| case LOMIOCMPROG2: |
| case LOMIOCMREAD2: |
| case LOMIOCPROG: |
| case LOMIOCTEST: |
| return (EACCES); |
| /* NOTREACHED */ |
| default: |
| /* Does not require write access */ |
| break; |
| } |
| } |
| |
| switch (cmd) { |
| |
| case LOMIOCDOGSTATE: |
| res = bscv_ioc_dogstate(ssp, arg, mode); |
| break; |
| |
| case LOMIOCPROG: |
| res = bscv_prog(ssp, arg, mode); |
| break; |
| |
| case LOMIOCPSUSTATE: |
| res = bscv_ioc_psustate(ssp, arg, mode); |
| break; |
| |
| case LOMIOCFANSTATE: |
| res = bscv_ioc_fanstate(ssp, arg, mode); |
| break; |
| |
| case LOMIOCFLEDSTATE: |
| res = bscv_ioc_fledstate(ssp, arg, mode); |
| break; |
| |
| case LOMIOCLEDSTATE: |
| res = bscv_ioc_ledstate(ssp, arg, mode); |
| break; |
| |
| case LOMIOCINFO: |
| res = bscv_ioc_info(ssp, arg, mode); |
| break; |
| |
| case LOMIOCMREAD: |
| res = bscv_ioc_mread(ssp, arg, mode); |
| break; |
| |
| case LOMIOCVOLTS: |
| res = bscv_ioc_volts(ssp, arg, mode); |
| break; |
| |
| case LOMIOCSTATS: |
| res = bscv_ioc_stats(ssp, arg, mode); |
| break; |
| |
| case LOMIOCTEMP: |
| res = bscv_ioc_temp(ssp, arg, mode); |
| break; |
| |
| case LOMIOCCONS: |
| res = bscv_ioc_cons(ssp, arg, mode); |
| break; |
| |
| case LOMIOCEVENTLOG2: |
| res = bscv_ioc_eventlog2(ssp, arg, mode); |
| break; |
| |
| case LOMIOCINFO2: |
| res = bscv_ioc_info2(ssp, arg, mode); |
| break; |
| |
| case LOMIOCTEST: |
| res = bscv_ioc_test(ssp, arg, mode); |
| break; |
| |
| case LOMIOCMPROG2: |
| res = bscv_ioc_mprog2(ssp, arg, mode); |
| break; |
| |
| case LOMIOCMREAD2: |
| res = bscv_ioc_mread2(ssp, arg, mode); |
| break; |
| |
| default: |
| BSCV_TRACE(ssp, 'I', "bscv_ioctl", "Invalid IOCTL 0x%x", cmd); |
| res = EINVAL; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCDOGSTATE |
| * TSIOCDOGSTATE - indicate whether the alarm watchdog and reset |
| * circuitry is enabled or not. |
| */ |
| static int |
| bscv_ioc_dogstate(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_dogstate_t dogstate; |
| uint8_t dogval; |
| int res = 0; |
| |
| dogval = bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res); |
| dogstate.dog_enable = (dogval & EBUS_WDOG_ENABLE) ? 1 : 0; |
| dogstate.reset_enable = (dogval & EBUS_WDOG_RST) ? 1 : 0; |
| dogstate.dog_timeout = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_WDOG_TIME, &res); |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&dogstate, |
| (caddr_t)arg, sizeof (dogstate), mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCPSUSTATE - returns full information for 4 PSUs. All this |
| * information is available from two bytes of LOMlite RAM, but if |
| * on the first read it is noticed that two or more of the PSUs are |
| * not present only 1 byte will be read subsequently. |
| */ |
| static int |
| bscv_ioc_psustate(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_psudata_t psudata; |
| uint8_t psustat; |
| int i; |
| int res = 0; |
| |
| for (i = 0; i < MAX_PSUS; i++) { |
| psustat = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_PSU1_STAT + i, &res); |
| psudata.fitted[i] = psustat & EBUS_PSU_PRESENT; |
| psudata.output[i] = psustat & EBUS_PSU_OUTPUT; |
| psudata.supplyb[i] = psustat & EBUS_PSU_INPUTB; |
| psudata.supplya[i] = psustat & EBUS_PSU_INPUTA; |
| psudata.standby[i] = psustat & EBUS_PSU_STANDBY; |
| } |
| |
| if (ddi_copyout((caddr_t)&psudata, (caddr_t)arg, sizeof (psudata), |
| mode) < 0) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCFANSTATE - returns full information including speed for 4 |
| * fans and the minimum and maximum operating speeds for each fan as |
| * stored in the READ ONLY EEPROM data. As this EEPROM data is set |
| * at manufacture time, this data should only be read by the driver |
| * once and stored locally. |
| */ |
| static int |
| bscv_ioc_fanstate(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_fandata_t fandata; |
| int numfans; |
| int i; |
| int res = 0; |
| |
| bzero(&fandata, sizeof (lom_fandata_t)); |
| numfans = EBUS_CONFIG_NFAN_DEC(bscv_get8_locked(ssp, |
| chan_general, EBUS_IDX_CONFIG, &res)); |
| for (i = 0; (i < numfans) && (res == 0); i++) { |
| if (ssp->fanspeed[i] != LOM_FAN_NOT_PRESENT) { |
| fandata.fitted[i] = 1; |
| fandata.speed[i] = ssp->fanspeed[i]; |
| fandata.minspeed[i] = bscv_get8_cached(ssp, |
| EBUS_IDX_FAN1_LOW + i); |
| } |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&fandata, (caddr_t)arg, sizeof (fandata), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCFLEDSTATE - returns the state of the fault LED |
| */ |
| static int |
| bscv_ioc_fledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_fled_info_t fled_info; |
| uint8_t fledstate; |
| int res = 0; |
| |
| fledstate = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res); |
| |
| /* Decode of 0x0F is off and 0x00-0x07 is on. */ |
| if (EBUS_ALARM_LED_DEC(fledstate) == 0x0F) { |
| fled_info.on = 0; |
| } else { |
| /* has +1 here - not 2 as in the info ioctl */ |
| fled_info.on = EBUS_ALARM_LED_DEC(fledstate) + 1; |
| } |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&fled_info, (caddr_t)arg, |
| sizeof (fled_info), mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCLEDSTATE - returns the state of the requested LED |
| */ |
| static int |
| bscv_ioc_ledstate(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_led_state_t led_state; |
| int fw_led_state; |
| int res = 0; |
| |
| /* copy in arguments supplied */ |
| if (ddi_copyin((caddr_t)arg, (caddr_t)&led_state, |
| sizeof (lom_led_state_t), mode) < 0) { |
| return (EFAULT); |
| } |
| |
| /* |
| * check if led index is -1, if so set it to max value for |
| * this implementation. |
| */ |
| if (led_state.index == -1) { |
| led_state.index = MAX_LED_ID; |
| } |
| |
| /* is the index in a valid range */ |
| if ((led_state.index > MAX_LED_ID) || (led_state.index < 0)) { |
| led_state.state = LOM_LED_OUTOFRANGE; |
| } else { |
| /* read the relevant led info */ |
| fw_led_state = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_LED1_STATUS + led_state.index, &res); |
| |
| /* set the state values accordingly */ |
| switch (fw_led_state) { |
| case LOM_LED_STATE_OFF: |
| led_state.state = LOM_LED_OFF; |
| led_state.colour = LOM_LED_COLOUR_ANY; |
| break; |
| case LOM_LED_STATE_ON_STEADY: |
| led_state.state = LOM_LED_ON; |
| led_state.colour = LOM_LED_COLOUR_ANY; |
| break; |
| case LOM_LED_STATE_ON_FLASHING: |
| case LOM_LED_STATE_ON_SLOWFLASH: |
| led_state.state = LOM_LED_BLINKING; |
| led_state.colour = LOM_LED_COLOUR_ANY; |
| break; |
| case LOM_LED_STATE_NOT_PRESENT: |
| led_state.state = LOM_LED_NOT_IMPLEMENTED; |
| led_state.colour = LOM_LED_COLOUR_NONE; |
| break; |
| case LOM_LED_STATE_INACCESSIBLE: |
| case LOM_LED_STATE_STANDBY: |
| default: |
| led_state.state = LOM_LED_ACCESS_ERROR; |
| led_state.colour = LOM_LED_COLOUR_NONE; |
| break; |
| } |
| |
| /* set the label info */ |
| (void) strcpy(led_state.label, |
| ssp->led_names[led_state.index]); |
| } |
| |
| /* copy out lom_state */ |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&led_state, (caddr_t)arg, |
| sizeof (lom_led_state_t), mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCINFO - returns with a structure containing any information |
| * stored on the LOMlite which a user should not need to access but |
| * may be useful for diagnostic problems. The structure contains: the |
| * serial escape character, alarm3 mode, version and checksum read from |
| * RAM and the Product revision and ID read from EEPROM. |
| */ |
| static int |
| bscv_ioc_info(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_info_t info; |
| int i; |
| uint16_t csum; |
| int res = 0; |
| |
| info.ser_char = bscv_get8_locked(ssp, chan_general, EBUS_IDX_ESCAPE, |
| &res); |
| info.a3mode = WATCHDOG; |
| info.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res); |
| csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res) |
| << 8; |
| csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res); |
| info.fchksum = csum; |
| info.prod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV, |
| &res); |
| for (i = 0; i < sizeof (info.prod_id); i++) { |
| info.prod_id[i] = bscv_get8_locked(ssp, |
| chan_general, EBUS_IDX_MODEL_ID1 + i, &res); |
| } |
| if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_ALARM, &res) & |
| EBUS_ALARM_NOEVENTS) { |
| info.events = OFF; |
| } else { |
| info.events = ON; |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&info, (caddr_t)arg, sizeof (info), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCMREAD - used to query the LOMlite configuration parameters |
| */ |
| static int |
| bscv_ioc_mread(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_mprog_t mprog; |
| int i; |
| int fanz; |
| int res = 0; |
| |
| for (i = 0; i < sizeof (mprog.mod_id); i++) { |
| mprog.mod_id[i] = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_MODEL_ID1 + i, &res); |
| } |
| mprog.mod_rev = bscv_get8_locked(ssp, chan_general, EBUS_IDX_MODEL_REV, |
| &res); |
| mprog.config = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG, |
| &res); |
| |
| /* Read the fan calibration values */ |
| fanz = sizeof (mprog.fanhz) / sizeof (mprog.fanhz[0]); |
| for (i = 0; i < fanz; i++) { |
| mprog.fanhz[i] = bscv_get8_cached(ssp, |
| EBUS_IDX_FAN1_CAL + i); |
| mprog.fanmin[i] = bscv_get8_cached(ssp, |
| EBUS_IDX_FAN1_LOW + i); |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&mprog, (caddr_t)arg, sizeof (mprog), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCVOLTS |
| */ |
| static int |
| bscv_ioc_volts(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| int i; |
| uint16_t supply; |
| int res = 0; |
| |
| supply = (bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_HI, &res) |
| << 8) | bscv_get8_locked(ssp, chan_general, EBUS_IDX_SUPPLY_LO, |
| &res); |
| |
| for (i = 0; i < ssp->volts.num; i++) { |
| ssp->volts.status[i] = (supply >> i) & 1; |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&ssp->volts, (caddr_t)arg, |
| sizeof (ssp->volts), mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCSTATS |
| */ |
| static int |
| bscv_ioc_stats(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| int i; |
| uint8_t status; |
| int res = 0; |
| |
| status = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CBREAK_STATUS, |
| &res); |
| for (i = 0; i < ssp->sflags.num; i++) { |
| ssp->sflags.status[i] = (int)((status >> i) & 1); |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&ssp->sflags, (caddr_t)arg, |
| sizeof (ssp->sflags), mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCTEMP |
| */ |
| static int |
| bscv_ioc_temp(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| int i; |
| int idx; |
| uint8_t status_ov; |
| lom_temp_t temps; |
| int res = 0; |
| |
| bzero(&temps, sizeof (temps)); |
| idx = 0; |
| for (i = 0; i < ssp->temps.num; i++) { |
| if (ssp->temps.temp[i] != LOM_TEMP_STATE_NOT_PRESENT) { |
| temps.temp[idx] = ssp->temps.temp[i]; |
| bcopy(ssp->temps.name[i], temps.name[idx], |
| sizeof (temps.name[idx])); |
| temps.warning[idx] = ssp->temps.warning[i]; |
| temps.shutdown[idx] = ssp->temps.shutdown[i]; |
| idx++; |
| } |
| } |
| temps.num = idx; |
| |
| bcopy(ssp->temps.name_ov, temps.name_ov, sizeof (temps.name_ov)); |
| temps.num_ov = ssp->temps.num_ov; |
| status_ov = bscv_get8_locked(ssp, chan_general, EBUS_IDX_OTEMP_STATUS, |
| &res); |
| for (i = 0; i < ssp->temps.num_ov; i++) { |
| ssp->temps.status_ov[i] = (status_ov >> i) & 1; |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&temps, (caddr_t)arg, sizeof (temps), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCCONS |
| */ |
| static int |
| bscv_ioc_cons(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_cbuf_t cbuf; |
| int datasize; |
| int res = 0; |
| |
| bzero(&cbuf, sizeof (cbuf)); |
| datasize = EBUS_IDX1_CONS_BUF_END - EBUS_IDX1_CONS_BUF_START + 1; |
| /* Ensure that we do not overfill cbuf and that it is NUL terminated */ |
| if (datasize > (sizeof (cbuf) - 1)) { |
| datasize = sizeof (cbuf) - 1; |
| } |
| bscv_rep_get8_locked(ssp, chan_general, (uint8_t *)cbuf.lrbuf, |
| BSCVA(EBUS_CMD_SPACE1, (EBUS_IDX1_CONS_BUF_END - datasize + 1)), |
| datasize, DDI_DEV_AUTOINCR, &res); |
| /* This is always within the array due to the checks above */ |
| cbuf.lrbuf[datasize] = '\0'; |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&cbuf, (caddr_t)arg, sizeof (cbuf), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCEVENTLOG2 |
| */ |
| static int |
| bscv_ioc_eventlog2(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom_eventlog2_t *eventlog2; |
| int events_recorded; |
| int level; |
| uint16_t next_offset; |
| lom_event_t event; |
| int res = 0; |
| |
| eventlog2 = (lom_eventlog2_t *)kmem_zalloc(sizeof (*eventlog2), |
| KM_SLEEP); |
| |
| /* |
| * First get number of events and level requested. |
| */ |
| |
| if (ddi_copyin((caddr_t)arg, (caddr_t)eventlog2, |
| sizeof (lom_eventlog2_t), mode) < 0) { |
| kmem_free((void *)eventlog2, sizeof (*eventlog2)); |
| return (EFAULT); |
| } |
| |
| bscv_enter(ssp); |
| |
| /* |
| * OK we have full private access to the LOM now so loop |
| * over the eventlog addr spaces until we get the required |
| * number of events. |
| */ |
| |
| if (!bscv_window_setup(ssp)) { |
| res = EIO; |
| bscv_exit(ssp); |
| kmem_free((void *)eventlog2, sizeof (*eventlog2)); |
| return (res); |
| } |
| |
| /* |
| * Read count, next event ptr MSB,LSB. Note a read of count |
| * is necessary to latch values for the next event ptr |
| */ |
| (void) bscv_get8(ssp, chan_general, EBUS_IDX_UNREAD_EVENTS); |
| next_offset = bscv_get16(ssp, chan_general, EBUS_IDX_LOG_PTR_HI); |
| BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "log_ptr_hi 0x%x", |
| next_offset); |
| |
| events_recorded = 0; |
| |
| while (events_recorded < eventlog2->num) { |
| /* |
| * Working backwards - read an event at a time. |
| * next_offset is one event on from where we want to be! |
| * Decrement next_offset and maybe wrap to the end of the |
| * buffer. |
| * Note the unsigned arithmetic, so check values first! |
| */ |
| if (next_offset <= ssp->eventlog_start) { |
| /* Wrap to the end of the buffer */ |
| next_offset = ssp->eventlog_start + ssp->eventlog_size; |
| BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "wrapping" |
| " around to end of buffer; next_offset 0x%x", |
| next_offset); |
| } |
| next_offset -= sizeof (event); |
| |
| if (bscv_eerw(ssp, next_offset, (uint8_t *)&event, |
| sizeof (event), B_FALSE /* read */) != 0) { |
| /* Fault reading data - stop */ |
| BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "read" |
| " failure for offset 0x%x", next_offset); |
| res = EIO; |
| break; |
| } |
| |
| if (bscv_is_null_event(ssp, &event)) { |
| /* |
| * No more events in this log so give up. |
| */ |
| BSCV_TRACE(ssp, 'I', "bscv_ioc_eventlog2", "no more" |
| " events left at offset 0x%x", next_offset); |
| break; |
| } |
| |
| /* |
| * Are we interested in this event |
| */ |
| |
| level = bscv_level_of_event(&event); |
| if (level <= eventlog2->level) { |
| /* Arggh why the funny byte ordering 3, 2, 0, 1 */ |
| eventlog2->code[events_recorded] = |
| ((unsigned)event.ev_event | |
| ((unsigned)event.ev_subsys << 8) | |
| ((unsigned)event.ev_resource << 16) | |
| ((unsigned)event.ev_detail << 24)); |
| |
| eventlog2->time[events_recorded] = |
| ((unsigned)event.ev_data[0] | |
| ((unsigned)event.ev_data[1] << 8) | |
| ((unsigned)event.ev_data[3] << 16) | |
| ((unsigned)event.ev_data[2] << 24)); |
| |
| bscv_build_eventstring(ssp, |
| &event, eventlog2->string[events_recorded], |
| eventlog2->string[events_recorded] + |
| sizeof (eventlog2->string[events_recorded])); |
| events_recorded++; |
| } |
| } |
| |
| eventlog2->num = events_recorded; |
| |
| bscv_exit(ssp); |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)eventlog2, (caddr_t)arg, |
| sizeof (lom_eventlog2_t), mode) < 0)) { |
| res = EFAULT; |
| } |
| |
| kmem_free((void *)eventlog2, sizeof (lom_eventlog2_t)); |
| return (res); |
| } |
| |
| /* |
| * LOMIOCINFO2 |
| */ |
| static int |
| bscv_ioc_info2(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom2_info_t info2; |
| int i; |
| uint16_t csum; |
| int res = 0; |
| |
| bzero(&info2, sizeof (info2)); |
| |
| (void) strncpy(info2.escape_chars, ssp->escape_chars, |
| sizeof (info2.escape_chars)); |
| info2.serial_events = ssp->reporting_level | ssp->serial_reporting; |
| info2.a3mode = WATCHDOG; |
| |
| info2.fver = bscv_get8_locked(ssp, chan_general, EBUS_IDX_FW_REV, &res); |
| csum = bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_HI, &res) |
| << 8; |
| csum |= bscv_get8_locked(ssp, chan_general, EBUS_IDX_CHECK_LO, &res); |
| info2.fchksum = csum; |
| info2.prod_rev = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_MODEL_REV, &res); |
| for (i = 0; i < sizeof (info2.prod_id); i++) { |
| info2.prod_id[i] = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_MODEL_ID1 + i, &res); |
| } |
| info2.serial_config = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_SER_TIMEOUT, &res); |
| if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) & |
| EBUS_CONFIG_MISC_SECURITY_ENABLED) { |
| info2.serial_config |= LOM_SER_SECURITY; |
| } |
| if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_CONFIG_MISC, &res) & |
| EBUS_CONFIG_MISC_AUTO_CONSOLE) { |
| info2.serial_config |= LOM_SER_RETURN; |
| } |
| if (bscv_get8_locked(ssp, chan_general, EBUS_IDX_WDOG_CTRL, &res) & |
| EBUS_WDOG_BREAK_DISABLE) { |
| info2.serial_config |= LOM_DISABLE_WDOG_BREAK; |
| } |
| info2.baud_rate = bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_SER_BAUD, &res); |
| info2.serial_hw_config = |
| ((int)bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_SER_CHARMODE, &res) | |
| ((int)bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_SER_FLOWCTL, &res) << 8) | |
| ((int)bscv_get8_locked(ssp, chan_general, |
| EBUS_IDX_SER_MODEMTYPE, &res) << 16)); |
| |
| /* |
| * There is no phone home support on the blade platform. We hardcode |
| * FALSE and NUL for config and script respectively. |
| */ |
| info2.phone_home_config = B_FALSE; |
| info2.phone_home_script[0] = '\0'; |
| |
| for (i = 0; i < ssp->num_fans; i++) { |
| (void) strcpy(info2.fan_names[i], ssp->fan_names[i]); |
| } |
| |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&info2, (caddr_t)arg, sizeof (info2), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCTEST |
| */ |
| static int |
| bscv_ioc_test(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| uint32_t test; |
| uint8_t testnum; |
| uint8_t testarg; |
| int res = 0; |
| |
| if (ddi_copyin((caddr_t)arg, (caddr_t)&test, sizeof (test), |
| mode) < 0) { |
| return (EFAULT); |
| } |
| |
| /* |
| * Extract num iterations. |
| */ |
| |
| testarg = (test & 0xff00) >> 8; |
| testnum = test & 0xff; |
| |
| BSCV_TRACE(ssp, 'F', "bscv_ioc_test", |
| "LOMIOCTEST data 0x%x (test 0x%x, arg 0x%x)", |
| test, (EBUS_IDX_SELFTEST0 + testnum), testarg); |
| |
| switch (testnum + EBUS_IDX_SELFTEST0) { |
| default: |
| /* Invalid test */ |
| res = EINVAL; |
| break; |
| |
| case EBUS_IDX_SELFTEST0: /* power on self-test result */ |
| case EBUS_IDX_SELFTEST1: /* not used currently */ |
| case EBUS_IDX_SELFTEST2: /* not used currently */ |
| case EBUS_IDX_SELFTEST3: /* not used currently */ |
| case EBUS_IDX_SELFTEST4: /* not used currently */ |
| case EBUS_IDX_SELFTEST5: /* not used currently */ |
| case EBUS_IDX_SELFTEST6: /* LED self-test */ |
| case EBUS_IDX_SELFTEST7: /* platform-specific tests */ |
| /* Run the test */ |
| |
| /* Stop other things and then run the test */ |
| bscv_enter(ssp); |
| |
| /* |
| * Then we simply write the argument to the relevant register |
| * and wait for the return code. |
| */ |
| bscv_put8(ssp, chan_general, |
| EBUS_IDX_SELFTEST0 + testnum, testarg); |
| if (bscv_faulty(ssp)) { |
| res = EIO; |
| } else { |
| /* Get hold of the SunVTS error code */ |
| test = bscv_retcode(ssp); |
| } |
| |
| bscv_exit(ssp); |
| break; |
| } |
| |
| BSCV_TRACE(ssp, 'F', "bscv_ioc_test", |
| "LOMIOCTEST status 0x%x, res 0x%x", test, res); |
| if ((res == 0) && |
| (ddi_copyout((caddr_t)&test, (caddr_t)arg, sizeof (test), |
| mode) < 0)) { |
| res = EFAULT; |
| } |
| return (res); |
| } |
| |
| /* |
| * LOMIOCMPROG2 |
| */ |
| static int |
| bscv_ioc_mprog2(bscv_soft_state_t *ssp, intptr_t arg, int mode) |
| { |
| lom2_mprog_t mprog2; |
| uint32_t base_addr; |
| uint32_t data_size; |
| uint32_t eeprom_size; |
| int res = 0; |
| |
| if (ddi_copyin((caddr_t)arg, (caddr_t)&mprog2, sizeof (mprog2), |
| mode) < 0) { |
| return (EFAULT); |
| } |
| |
| /* |
| * Note that originally this was accessed as 255 byte pages |
| * in address spaces 240-255. We have to emulate this behaviour. |
| */ |
| if ((mprog2.addr_space < 240) || (mprog2.addr_space > 255)) { |
| return (EINVAL); |
| } |
| |
| bscv_enter(ssp); |
| |
| /* Calculate required data location */ |
| data_size = 255; |
| base_addr = (mprog2.addr_space - 240) * data_size; |
| |
| eeprom_size = bscv_get8(ssp, chan_general, EBUS_IDX_EEPROM_SIZE_KB) * |
| 1024; |
| |
| if (bscv_faulty(ssp)) { |
| bscv_exit(ssp); |
| return (EIO); |
| } else if ((base_addr + data_size) > eeprom_size) { |
| BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2", |
| "Request extends past end of eeprom"); |
| bscv_exit(ssp); |
| return (ENXIO); |
| } |
| |
| bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK1); |
| if (bscv_faulty(ssp)) { |
| BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2", "ML1 Write failed"); |
| bscv_exit(ssp); |
| return (EIO); |
| } |
| |
| bscv_put8(ssp, chan_general, EBUS_IDX_CMD_RES, EBUS_CMD_UNLOCK2); |
| if (bscv_faulty(ssp)) { |
| BSCV_TRACE(ssp, 'M', "bscv_ioc_mprog2", "ML2 Write failed"); |
| bscv_exit(ssp); |
| return (EIO); |
| } |
| |
| if (bscv_eerw(ssp, base_addr, &mprog2.data[0], |
| data_size, B_TRUE /* write */) != 0) { |
| res = EIO; |
| } |
| |
| <
|