11c42de6dSgd78059 /* 21c42de6dSgd78059 * CDDL HEADER START 31c42de6dSgd78059 * 41c42de6dSgd78059 * The contents of this file are subject to the terms of the 51c42de6dSgd78059 * Common Development and Distribution License (the "License"). 61c42de6dSgd78059 * You may not use this file except in compliance with the License. 71c42de6dSgd78059 * 81c42de6dSgd78059 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 91c42de6dSgd78059 * or http://www.opensolaris.org/os/licensing. 101c42de6dSgd78059 * See the License for the specific language governing permissions 111c42de6dSgd78059 * and limitations under the License. 121c42de6dSgd78059 * 131c42de6dSgd78059 * When distributing Covered Code, include this CDDL HEADER in each 141c42de6dSgd78059 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 151c42de6dSgd78059 * If applicable, add the following below this CDDL HEADER, with the 161c42de6dSgd78059 * fields enclosed by brackets "[]" replaced with your own identifying 171c42de6dSgd78059 * information: Portions Copyright [yyyy] [name of copyright owner] 181c42de6dSgd78059 * 191c42de6dSgd78059 * CDDL HEADER END 201c42de6dSgd78059 */ 211c42de6dSgd78059 /* 22*19397407SSherry Moore * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 231c42de6dSgd78059 * Use is subject to license terms. 241c42de6dSgd78059 * 251c42de6dSgd78059 * The "bscbus" driver provides access to the LOMlite2 virtual registers, 261c42de6dSgd78059 * so that its clients (children) need not be concerned with the details 271c42de6dSgd78059 * of the access mechanism, which in this case is implemented via a 281c42de6dSgd78059 * packet-based protocol over a Xbus (similar to ebus) parallel link to the 291c42de6dSgd78059 * H8 host interface registers. 301c42de6dSgd78059 * 311c42de6dSgd78059 * On the other hand, this driver doesn't generally know what the virtual 321c42de6dSgd78059 * registers signify - only the clients need this information. 331c42de6dSgd78059 */ 341c42de6dSgd78059 351c42de6dSgd78059 361c42de6dSgd78059 #include <sys/note.h> 371c42de6dSgd78059 #include <sys/types.h> 381c42de6dSgd78059 #include <sys/conf.h> 391c42de6dSgd78059 #include <sys/debug.h> 401c42de6dSgd78059 #include <sys/errno.h> 411c42de6dSgd78059 #include <sys/file.h> 421c42de6dSgd78059 431c42de6dSgd78059 #if defined(__sparc) 441c42de6dSgd78059 #include <sys/intr.h> 451c42de6dSgd78059 #include <sys/membar.h> 461c42de6dSgd78059 #endif 471c42de6dSgd78059 481c42de6dSgd78059 #include <sys/kmem.h> 491c42de6dSgd78059 #include <sys/modctl.h> 501c42de6dSgd78059 #include <sys/note.h> 511c42de6dSgd78059 #include <sys/open.h> 521c42de6dSgd78059 #include <sys/poll.h> 531c42de6dSgd78059 #include <sys/spl.h> 541c42de6dSgd78059 #include <sys/stat.h> 551c42de6dSgd78059 #include <sys/strlog.h> 561c42de6dSgd78059 #include <sys/atomic.h> 571c42de6dSgd78059 581c42de6dSgd78059 #include <sys/ddi.h> 591c42de6dSgd78059 #include <sys/sunddi.h> 601c42de6dSgd78059 #include <sys/sunndi.h> 611c42de6dSgd78059 621c42de6dSgd78059 #include <sys/bscbus.h> 631c42de6dSgd78059 641c42de6dSgd78059 #if defined(NDI_ACC_HDL_V2) 651c42de6dSgd78059 661c42de6dSgd78059 /* 671c42de6dSgd78059 * Compiling for Solaris 10+ with access handle enhancements 681c42de6dSgd78059 */ 691c42de6dSgd78059 #define HANDLE_TYPE ndi_acc_handle_t 701c42de6dSgd78059 #define HANDLE_ADDR(hdlp) (hdlp->ah_addr) 711c42de6dSgd78059 #define HANDLE_FAULT(hdlp) (hdlp->ah_fault) 721c42de6dSgd78059 #define HANDLE_MAPLEN(hdlp) (hdlp->ah_len) 731c42de6dSgd78059 #define HANDLE_PRIVATE(hdlp) (hdlp->ah_bus_private) 741c42de6dSgd78059 751c42de6dSgd78059 #else 761c42de6dSgd78059 771c42de6dSgd78059 /* 781c42de6dSgd78059 * Compatibility definitions for backport to Solaris 8/9 791c42de6dSgd78059 */ 801c42de6dSgd78059 #define HANDLE_TYPE ddi_acc_impl_t 811c42de6dSgd78059 #define HANDLE_ADDR(hdlp) (hdlp->ahi_common.ah_addr) 821c42de6dSgd78059 #define HANDLE_FAULT(hdlp) (hdlp->ahi_fault) 831c42de6dSgd78059 #define HANDLE_MAPLEN(hdlp) (hdlp->ahi_common.ah_len) 841c42de6dSgd78059 #define HANDLE_PRIVATE(hdlp) (hdlp->ahi_common.ah_bus_private) 851c42de6dSgd78059 861c42de6dSgd78059 #define ddi_driver_major(dip) ddi_name_to_major(ddi_binding_name(dip)) 871c42de6dSgd78059 881c42de6dSgd78059 #endif /* NDI_ACC_HDL_V2 */ 891c42de6dSgd78059 901c42de6dSgd78059 911c42de6dSgd78059 /* 921c42de6dSgd78059 * Local definitions 931c42de6dSgd78059 */ 941c42de6dSgd78059 #define MYNAME "bscbus" 951c42de6dSgd78059 #define NOMAJOR (~(major_t)0) 961c42de6dSgd78059 #define DUMMY_VALUE (~(int8_t)0) 971c42de6dSgd78059 981c42de6dSgd78059 #define BSCBUS_INST_TO_MINOR(i) (i) 991c42de6dSgd78059 #define BSCBUS_MINOR_TO_INST(m) (m) 1001c42de6dSgd78059 1011c42de6dSgd78059 #define BSCBUS_MAX_CHANNELS (4) 1021c42de6dSgd78059 1031c42de6dSgd78059 #define BSCBUS_DUMMY_ADDRESS ((caddr_t)0x0CADD1ED) 1041c42de6dSgd78059 #define ADDR_TO_OFFSET(a, hdlp) ((caddr_t)(a) - HANDLE_ADDR(hdlp)) 1051c42de6dSgd78059 #define ADDR_TO_VREG(a) ((caddr_t)(a) - BSCBUS_DUMMY_ADDRESS) 1061c42de6dSgd78059 #define VREG_TO_ADDR(v) (BSCBUS_DUMMY_ADDRESS + (v)) 1071c42de6dSgd78059 1081c42de6dSgd78059 #ifdef DEBUG 1091c42de6dSgd78059 #define BSCBUS_LOGSTATUS 1101c42de6dSgd78059 #endif /* DEBUG */ 1111c42de6dSgd78059 1121c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 1131c42de6dSgd78059 /* 1141c42de6dSgd78059 * BSC command logging routines. 1151c42de6dSgd78059 * Record the data passing to and from the BSC 1161c42de6dSgd78059 */ 1171c42de6dSgd78059 1181c42de6dSgd78059 typedef enum { 1191c42de6dSgd78059 BSC_CMD_BUSY = 1, /* bsc reports busy */ 1201c42de6dSgd78059 BSC_CMD_CLEARING = 2, /* clearing bsc busy */ 1211c42de6dSgd78059 BSC_CMD_CLEARED = 3, /* cleared bsc busy */ 1221c42de6dSgd78059 BSC_CMD_SENDING = 4, /* sending next byte */ 1231c42de6dSgd78059 BSC_CMD_SENT = 5, /* sending last byte */ 1241c42de6dSgd78059 BSC_CMD_PENDING = 6, /* got sent byte ack */ 1251c42de6dSgd78059 BSC_CMD_REPLY = 7, /* got reply byte */ 1261c42de6dSgd78059 BSC_CMD_COMPLETE = 8, /* command complete */ 1271c42de6dSgd78059 BSC_CMD_ERROR_SEQ = 9, /* error status */ 1281c42de6dSgd78059 BSC_CMD_ERROR_STATUS = 10, /* error status */ 1291c42de6dSgd78059 BSC_CMD_ERROR_OFLOW = 11, /* error status */ 1301c42de6dSgd78059 BSC_CMD_ERROR_TOUT = 12, /* error status */ 1311c42de6dSgd78059 1321c42de6dSgd78059 BSC_CMD_PROCESS = 13, /* async intr */ 1331c42de6dSgd78059 BSC_CMD_V1INTR = 14, /* v1 intr */ 1341c42de6dSgd78059 BSC_CMD_V1INTRUNCL = 15, /* v1 intr unclaim */ 1351c42de6dSgd78059 BSC_CMD_DOGPAT = 17 /* watchdog pat */ 1361c42de6dSgd78059 } bsc_cmd_stamp_t; 1371c42de6dSgd78059 1381c42de6dSgd78059 typedef struct { 1391c42de6dSgd78059 hrtime_t bcl_now; 1401c42de6dSgd78059 int bcl_seq; 1411c42de6dSgd78059 bsc_cmd_stamp_t bcl_cat; 1421c42de6dSgd78059 uint8_t bcl_chno; 1431c42de6dSgd78059 uint8_t bcl_cmdstate; 1441c42de6dSgd78059 uint8_t bcl_status; 1451c42de6dSgd78059 uint8_t bcl_data; 1461c42de6dSgd78059 } bsc_cmd_log_t; 1471c42de6dSgd78059 1481c42de6dSgd78059 uint32_t bscbus_cmd_log_size = 1024; 1491c42de6dSgd78059 1501c42de6dSgd78059 uint32_t bscbus_cmd_log_flags = 0xffffffff; 1511c42de6dSgd78059 1521c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 1531c42de6dSgd78059 1541c42de6dSgd78059 /* 1551c42de6dSgd78059 * The following definitions are taken from the Hardware Manual for 1561c42de6dSgd78059 * the Hitachi H8S/2148 in conjunction with the hardware specification 1571c42de6dSgd78059 * for the Stiletto blade. 1581c42de6dSgd78059 * 1591c42de6dSgd78059 * Each instance of the host interface has 3 registers on the H8: 1601c42de6dSgd78059 * IDRn - Input Data Register - write-only for Solaris. 1611c42de6dSgd78059 * writes to this can be done via two 1621c42de6dSgd78059 * addresses - control and data. 1631c42de6dSgd78059 * The H8 can determine which address was 1641c42de6dSgd78059 * written by examining the C/D bit in 1651c42de6dSgd78059 * the status register. 1661c42de6dSgd78059 * ODRn - Output Data Register - read-only for Solaris. 1671c42de6dSgd78059 * A read has the side effect of acknowledging 1681c42de6dSgd78059 * interrupts. 1691c42de6dSgd78059 * STRn - Status Register - read-only for Solaris. 1701c42de6dSgd78059 * 1711c42de6dSgd78059 * 1721c42de6dSgd78059 * 1731c42de6dSgd78059 * In terms of host access to this the Input and Output data registers are 1741c42de6dSgd78059 * mapped at the same address. 1751c42de6dSgd78059 */ 1761c42de6dSgd78059 #define H8_IDRD 0 1771c42de6dSgd78059 #define H8_IDRC 1 1781c42de6dSgd78059 #define H8_ODR 0 1791c42de6dSgd78059 #define H8_STR 1 1801c42de6dSgd78059 1811c42de6dSgd78059 #define H8_STR_OBF 0x01 /* data available in ODR */ 1821c42de6dSgd78059 #define H8_STR_IBF 0x02 /* data for H8 in IDR */ 1831c42de6dSgd78059 #define H8_STR_IDRC 0x08 /* last write to IDR was to IDRC */ 1841c42de6dSgd78059 /* 0=data, 1=command */ 1851c42de6dSgd78059 #define H8_STR_BUSY 0x04 /* H8 busy processing command */ 1861c42de6dSgd78059 #define H8_STR_TOKENPROTOCOL 0x80 /* token-passing protocol */ 1871c42de6dSgd78059 1881c42de6dSgd78059 /* 1891c42de6dSgd78059 * Packet format ... 1901c42de6dSgd78059 */ 1911c42de6dSgd78059 #define BSCBUS_MASK 0xc0 /* Byte-type bits */ 1921c42de6dSgd78059 #define BSCBUS_PARAM 0x00 /* Parameter byte: 0b0xxxxxxx */ 1931c42de6dSgd78059 #define BSCBUS_LAST 0x80 /* Last byte of packet */ 1941c42de6dSgd78059 #define BSCBUS_CMD 0x80 /* Command byte: 0b10###XWV */ 1951c42de6dSgd78059 #define BSCBUS_STATUS 0xc0 /* Status byte: 0b11###AEV */ 1961c42de6dSgd78059 1971c42de6dSgd78059 #define BSCBUS_SEQ 0x38 /* Sequence number bits */ 1981c42de6dSgd78059 #define BSCBUS_SEQ_LSB 0x08 /* Sequence number LSB */ 1991c42de6dSgd78059 #define BSCBUS_CMD_XADDR 0x04 /* Extended (2-byte) addressing */ 2001c42de6dSgd78059 #define BSCBUS_CMD_WRITE 0x02 /* Write command */ 2011c42de6dSgd78059 #define BSCBUS_CMD_WMSB 0x01 /* Set MSB on Write */ 2021c42de6dSgd78059 #define BSCBUS_CMD_READ 0x01 /* Read command */ 2031c42de6dSgd78059 #define BSCBUS_CMD_NOP 0x00 /* NOP command */ 2041c42de6dSgd78059 2051c42de6dSgd78059 #define BSCBUS_STATUS_ASYNC 0x04 /* Asynchronous event pending */ 2061c42de6dSgd78059 #define BSCBUS_STATUS_ERR 0x02 /* Error in command processing */ 2071c42de6dSgd78059 #define BSCBUS_STATUS_MSB 0x01 /* MSB of Value read */ 2081c42de6dSgd78059 2091c42de6dSgd78059 #define BSCBUS_VREG_LO(x) ((x) & ((1 << 7) - 1)) 2101c42de6dSgd78059 #define BSCBUS_VREG_HI(x) ((x) >> 7) 2111c42de6dSgd78059 2121c42de6dSgd78059 #define BSCBUS_BUFSIZE 8 2131c42de6dSgd78059 2141c42de6dSgd78059 #define BSCBUS_CHANNEL_TO_OFFSET(chno) ((chno) * 2) /* Register offset */ 2151c42de6dSgd78059 2161c42de6dSgd78059 /* 2171c42de6dSgd78059 * Time periods, in nanoseconds 2181c42de6dSgd78059 * 2191c42de6dSgd78059 * Note that LOMBUS_ONE_SEC and some other time 2201c42de6dSgd78059 * periods are defined in <sys/lombus.h> 2211c42de6dSgd78059 */ 2221c42de6dSgd78059 #define BSCBUS_CMD_POLL (LOMBUS_ONE_SEC) 2231c42de6dSgd78059 #define BSCBUS_CMD_POLLNOINTS (LOMBUS_ONE_SEC/20) 2241c42de6dSgd78059 #define BSCBUS_HWRESET_POLL (LOMBUS_ONE_SEC/20) 2251c42de6dSgd78059 #define BSCBUS_HWRESET_TIMEOUT (LOMBUS_ONE_SEC*2) 2261c42de6dSgd78059 2271c42de6dSgd78059 #define BSCBUS_DOG_PAT_POLL_LIMIT (1000) 2281c42de6dSgd78059 #define BSCBUS_DOG_PAT_POLL (1) 2291c42de6dSgd78059 #define BSCBUS_PAT_RETRY_LIMIT 5 2301c42de6dSgd78059 2311c42de6dSgd78059 /* 2321c42de6dSgd78059 * Local datatypes 2331c42de6dSgd78059 */ 2341c42de6dSgd78059 enum bscbus_cmdstate { 2351c42de6dSgd78059 BSCBUS_CMDSTATE_IDLE, /* No transaction in progress */ 2361c42de6dSgd78059 BSCBUS_CMDSTATE_BUSY, /* Setting up command */ 2371c42de6dSgd78059 BSCBUS_CMDSTATE_CLEARING, /* Clearing firmware busy status */ 2381c42de6dSgd78059 BSCBUS_CMDSTATE_SENDING, /* Waiting to send data to f/w */ 2391c42de6dSgd78059 BSCBUS_CMDSTATE_PENDING, /* Waiting for ack from f/w */ 2401c42de6dSgd78059 BSCBUS_CMDSTATE_WAITING, /* Waiting for status from f/w */ 2411c42de6dSgd78059 BSCBUS_CMDSTATE_READY, /* Status received/command done */ 2421c42de6dSgd78059 BSCBUS_CMDSTATE_ERROR /* Command failed with error */ 2431c42de6dSgd78059 }; 2441c42de6dSgd78059 2451c42de6dSgd78059 struct bscbus_channel_state { 2461c42de6dSgd78059 /* Changes to these are protected by the instance ch_mutex mutex */ 2471c42de6dSgd78059 struct bscbus_state *ssp; 2481c42de6dSgd78059 uint8_t *ch_regs; 2491c42de6dSgd78059 ddi_acc_handle_t ch_handle; /* per channel access handle */ 2501c42de6dSgd78059 unsigned int chno; 2511c42de6dSgd78059 unsigned int map_count; /* Number of mappings to channel */ 2521c42de6dSgd78059 boolean_t map_dog; /* channel is mapped for watchdog */ 2531c42de6dSgd78059 2541c42de6dSgd78059 /* 2551c42de6dSgd78059 * Flag to indicate that we've incurred a hardware fault on 2561c42de6dSgd78059 * accesses to the H8; once this is set, we fake all further 2571c42de6dSgd78059 * accesses in order not to provoke additional bus errors. 2581c42de6dSgd78059 */ 2591c42de6dSgd78059 boolean_t xio_fault; 2601c42de6dSgd78059 2611c42de6dSgd78059 /* 2621c42de6dSgd78059 * Data protected by the dog_mutex: the watchdog-patting 2631c42de6dSgd78059 * protocol data (since the dog can be patted from a high-level 2641c42de6dSgd78059 * cyclic), and the interrupt-enabled flag. 2651c42de6dSgd78059 */ 2661c42de6dSgd78059 kmutex_t dog_mutex[1]; 2671c42de6dSgd78059 unsigned int pat_retry_count; 2681c42de6dSgd78059 unsigned int pat_fail_count; 2691c42de6dSgd78059 2701c42de6dSgd78059 /* 2711c42de6dSgd78059 * Serial protocol state data, protected by lo_mutex 2721c42de6dSgd78059 * (which is initialised using <lo_iblk>) 2731c42de6dSgd78059 */ 2741c42de6dSgd78059 kmutex_t lo_mutex[1]; 2751c42de6dSgd78059 ddi_iblock_cookie_t lo_iblk; 2761c42de6dSgd78059 kcondvar_t lo_cv[1]; 2771c42de6dSgd78059 int unclaimed_count; 2781c42de6dSgd78059 2791c42de6dSgd78059 volatile enum bscbus_cmdstate cmdstate; 2801c42de6dSgd78059 clock_t deadline; 2811c42de6dSgd78059 clock_t poll_hz; 2821c42de6dSgd78059 boolean_t interrupt_failed; 2831c42de6dSgd78059 uint8_t cmdbuf[BSCBUS_BUFSIZE]; 2841c42de6dSgd78059 uint8_t *cmdp; /* Points to last tx'd in cmdbuf */ 2851c42de6dSgd78059 uint8_t reply[BSCBUS_BUFSIZE]; 2861c42de6dSgd78059 uint8_t async; 2871c42de6dSgd78059 uint8_t index; 2881c42de6dSgd78059 uint8_t result; 2891c42de6dSgd78059 uint8_t sequence; 2901c42de6dSgd78059 uint32_t error; 2911c42de6dSgd78059 }; 2921c42de6dSgd78059 2931c42de6dSgd78059 #define BSCBUS_TX_PENDING(csp) ((csp)->cmdp > (csp)->cmdbuf) 2941c42de6dSgd78059 2951c42de6dSgd78059 /* 2961c42de6dSgd78059 * This driver's soft-state structure 2971c42de6dSgd78059 */ 2981c42de6dSgd78059 2991c42de6dSgd78059 struct bscbus_state { 3001c42de6dSgd78059 /* 3011c42de6dSgd78059 * Configuration data, set during attach 3021c42de6dSgd78059 */ 3031c42de6dSgd78059 dev_info_t *dip; 3041c42de6dSgd78059 major_t majornum; 3051c42de6dSgd78059 int instance; 3061c42de6dSgd78059 3071c42de6dSgd78059 ddi_acc_handle_t h8_handle; 3081c42de6dSgd78059 uint8_t *h8_regs; 3091c42de6dSgd78059 3101c42de6dSgd78059 /* 3111c42de6dSgd78059 * Parameters derived from .conf properties 3121c42de6dSgd78059 */ 3131c42de6dSgd78059 uint32_t debug; 3141c42de6dSgd78059 3151c42de6dSgd78059 /* 3161c42de6dSgd78059 * Flag to indicate that we are using per channel 3171c42de6dSgd78059 * mapping of the register sets and interrupts. 3181c42de6dSgd78059 * reg set 0 is chan 0 3191c42de6dSgd78059 * reg set 1 is chan 1 ... 3201c42de6dSgd78059 * 3211c42de6dSgd78059 * Interrupts are specified in that order but later 3221c42de6dSgd78059 * channels may not have interrupts. 3231c42de6dSgd78059 */ 3241c42de6dSgd78059 boolean_t per_channel_regs; 3251c42de6dSgd78059 3261c42de6dSgd78059 /* 3271c42de6dSgd78059 * channel state data, protected by ch_mutex 3281c42de6dSgd78059 * channel claim/release requests are protected by this mutex. 3291c42de6dSgd78059 */ 3301c42de6dSgd78059 kmutex_t ch_mutex[1]; 3311c42de6dSgd78059 struct bscbus_channel_state channel[BSCBUS_MAX_CHANNELS]; 3321c42de6dSgd78059 3331c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 3341c42de6dSgd78059 /* 3351c42de6dSgd78059 * Command logging buffer for recording transactions with the 3361c42de6dSgd78059 * BSC. This is useful for debugging failed transactions and other 3371c42de6dSgd78059 * such funnies. 3381c42de6dSgd78059 */ 3391c42de6dSgd78059 bsc_cmd_log_t *cmd_log; 3401c42de6dSgd78059 uint32_t cmd_log_idx; 3411c42de6dSgd78059 uint32_t cmd_log_size; 3421c42de6dSgd78059 uint32_t cmd_log_flags; 3431c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 3441c42de6dSgd78059 }; 3451c42de6dSgd78059 3461c42de6dSgd78059 /* 3471c42de6dSgd78059 * The auxiliary structure attached to each child 3481c42de6dSgd78059 * (the child's parent-private-data points to this). 3491c42de6dSgd78059 */ 3501c42de6dSgd78059 struct bscbus_child_info { 3511c42de6dSgd78059 lombus_regspec_t *rsp; 3521c42de6dSgd78059 int nregs; 3531c42de6dSgd78059 }; 3541c42de6dSgd78059 3551c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 3561c42de6dSgd78059 void bscbus_cmd_log(struct bscbus_channel_state *, bsc_cmd_stamp_t, 3571c42de6dSgd78059 uint8_t, uint8_t); 3581c42de6dSgd78059 #else /* BSCBUS_LOGSTATUS */ 3591c42de6dSgd78059 #define bscbus_cmd_log(state, stamp, status, data) 3601c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 3611c42de6dSgd78059 3621c42de6dSgd78059 3631c42de6dSgd78059 /* 3641c42de6dSgd78059 * Local data 3651c42de6dSgd78059 */ 3661c42de6dSgd78059 3671c42de6dSgd78059 static void *bscbus_statep; 3681c42de6dSgd78059 3691c42de6dSgd78059 static major_t bscbus_major = NOMAJOR; 3701c42de6dSgd78059 3711c42de6dSgd78059 static ddi_device_acc_attr_t bscbus_dev_acc_attr[1] = { 3721c42de6dSgd78059 DDI_DEVICE_ATTR_V0, 3731c42de6dSgd78059 DDI_STRUCTURE_LE_ACC, 3741c42de6dSgd78059 DDI_STRICTORDER_ACC 3751c42de6dSgd78059 }; 3761c42de6dSgd78059 3771c42de6dSgd78059 3781c42de6dSgd78059 /* 3791c42de6dSgd78059 * General utility routines ... 3801c42de6dSgd78059 */ 3811c42de6dSgd78059 3821c42de6dSgd78059 #ifdef DEBUG 3831c42de6dSgd78059 static void 3841c42de6dSgd78059 bscbus_trace(struct bscbus_channel_state *csp, char code, const char *caller, 3851c42de6dSgd78059 const char *fmt, ...) 3861c42de6dSgd78059 { 3871c42de6dSgd78059 char buf[256]; 3881c42de6dSgd78059 char *p; 3891c42de6dSgd78059 va_list va; 3901c42de6dSgd78059 3911c42de6dSgd78059 if (csp->ssp->debug & (1 << (code-'@'))) { 3921c42de6dSgd78059 p = buf; 3931c42de6dSgd78059 (void) snprintf(p, sizeof (buf) - (p - buf), 3941c42de6dSgd78059 "%s/%s: ", MYNAME, caller); 3951c42de6dSgd78059 p += strlen(p); 3961c42de6dSgd78059 3971c42de6dSgd78059 va_start(va, fmt); 3981c42de6dSgd78059 (void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va); 3991c42de6dSgd78059 va_end(va); 4001c42de6dSgd78059 4011c42de6dSgd78059 buf[sizeof (buf) - 1] = '\0'; 4021c42de6dSgd78059 (void) strlog(csp->ssp->majornum, csp->ssp->instance, 4031c42de6dSgd78059 code, SL_TRACE, buf); 4041c42de6dSgd78059 } 4051c42de6dSgd78059 } 4061c42de6dSgd78059 #else /* DEBUG */ 4071c42de6dSgd78059 #define bscbus_trace 4081c42de6dSgd78059 #endif /* DEBUG */ 4091c42de6dSgd78059 4101c42de6dSgd78059 static struct bscbus_state * 4111c42de6dSgd78059 bscbus_getstate(dev_info_t *dip, int instance, const char *caller) 4121c42de6dSgd78059 { 4131c42de6dSgd78059 struct bscbus_state *ssp = NULL; 4141c42de6dSgd78059 dev_info_t *sdip = NULL; 4151c42de6dSgd78059 major_t dmaj = NOMAJOR; 4161c42de6dSgd78059 4171c42de6dSgd78059 if (dip != NULL) { 4181c42de6dSgd78059 /* 4191c42de6dSgd78059 * Use the instance number from the <dip>; also, 4201c42de6dSgd78059 * check that it really corresponds to this driver 4211c42de6dSgd78059 */ 4221c42de6dSgd78059 instance = ddi_get_instance(dip); 4231c42de6dSgd78059 dmaj = ddi_driver_major(dip); 4241c42de6dSgd78059 if (bscbus_major == NOMAJOR && dmaj != NOMAJOR) 4251c42de6dSgd78059 bscbus_major = dmaj; 4261c42de6dSgd78059 else if (dmaj != bscbus_major) { 4271c42de6dSgd78059 cmn_err(CE_WARN, 4281c42de6dSgd78059 "%s: major number mismatch (%d vs. %d) in %s()," 4291c42de6dSgd78059 "probably due to child misconfiguration", 4301c42de6dSgd78059 MYNAME, bscbus_major, dmaj, caller); 4311c42de6dSgd78059 instance = -1; 4321c42de6dSgd78059 } 4331c42de6dSgd78059 } 4341c42de6dSgd78059 4351c42de6dSgd78059 if (instance >= 0) 4361c42de6dSgd78059 ssp = ddi_get_soft_state(bscbus_statep, instance); 4371c42de6dSgd78059 if (ssp != NULL) { 4381c42de6dSgd78059 sdip = ssp->dip; 4391c42de6dSgd78059 if (dip == NULL && sdip == NULL) 4401c42de6dSgd78059 ssp = NULL; 4411c42de6dSgd78059 else if (dip != NULL && sdip != NULL && sdip != dip) { 4421c42de6dSgd78059 cmn_err(CE_WARN, 4431c42de6dSgd78059 "%s: devinfo mismatch (%p vs. %p) in %s(), " 4441c42de6dSgd78059 "probably due to child misconfiguration", 4451c42de6dSgd78059 MYNAME, (void *)dip, (void *)sdip, caller); 4461c42de6dSgd78059 ssp = NULL; 4471c42de6dSgd78059 } 4481c42de6dSgd78059 } 4491c42de6dSgd78059 4501c42de6dSgd78059 return (ssp); 4511c42de6dSgd78059 } 4521c42de6dSgd78059 4531c42de6dSgd78059 /* 4541c42de6dSgd78059 * Lowest-level I/O register read/write 4551c42de6dSgd78059 */ 4561c42de6dSgd78059 4571c42de6dSgd78059 static void 4581c42de6dSgd78059 bscbus_put_reg(struct bscbus_channel_state *csp, uint_t reg, uint8_t val) 4591c42de6dSgd78059 { 4601c42de6dSgd78059 if (csp->ch_handle != NULL && !csp->xio_fault) { 4611c42de6dSgd78059 ddi_put8(csp->ch_handle, 4621c42de6dSgd78059 csp->ch_regs + reg, val); 4631c42de6dSgd78059 } 4641c42de6dSgd78059 } 4651c42de6dSgd78059 4661c42de6dSgd78059 static uint8_t 4671c42de6dSgd78059 bscbus_get_reg(struct bscbus_channel_state *csp, uint_t reg) 4681c42de6dSgd78059 { 4691c42de6dSgd78059 uint8_t val; 4701c42de6dSgd78059 4711c42de6dSgd78059 if (csp->ch_handle != NULL && !csp->xio_fault) 4721c42de6dSgd78059 val = ddi_get8(csp->ch_handle, 4731c42de6dSgd78059 csp->ch_regs + reg); 4741c42de6dSgd78059 else 4751c42de6dSgd78059 val = DUMMY_VALUE; 4761c42de6dSgd78059 4771c42de6dSgd78059 return (val); 4781c42de6dSgd78059 } 4791c42de6dSgd78059 4801c42de6dSgd78059 static void 4811c42de6dSgd78059 bscbus_check_fault_status(struct bscbus_channel_state *csp) 4821c42de6dSgd78059 { 4831c42de6dSgd78059 csp->xio_fault = 4841c42de6dSgd78059 ddi_check_acc_handle(csp->ch_handle) != DDI_SUCCESS; 4851c42de6dSgd78059 } 4861c42de6dSgd78059 4871c42de6dSgd78059 static boolean_t 4881c42de6dSgd78059 bscbus_faulty(struct bscbus_channel_state *csp) 4891c42de6dSgd78059 { 4901c42de6dSgd78059 if (!csp->xio_fault) 4911c42de6dSgd78059 bscbus_check_fault_status(csp); 4921c42de6dSgd78059 return (csp->xio_fault); 4931c42de6dSgd78059 } 4941c42de6dSgd78059 4951c42de6dSgd78059 /* 4961c42de6dSgd78059 * Write data into h8 registers 4971c42de6dSgd78059 */ 4981c42de6dSgd78059 static void 4991c42de6dSgd78059 bscbus_pat_dog(struct bscbus_channel_state *csp, uint8_t val) 5001c42de6dSgd78059 { 5011c42de6dSgd78059 uint8_t status; 5021c42de6dSgd78059 uint32_t doglimit = BSCBUS_DOG_PAT_POLL_LIMIT; 5031c42de6dSgd78059 5041c42de6dSgd78059 bscbus_trace(csp, 'W', "bscbus_pat_dog:", ""); 5051c42de6dSgd78059 5061c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_DOGPAT, 0, val); 5071c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 5081c42de6dSgd78059 while (status & H8_STR_IBF) { 5091c42de6dSgd78059 if (csp->pat_retry_count > BSCBUS_PAT_RETRY_LIMIT) { 5101c42de6dSgd78059 /* 5111c42de6dSgd78059 * Previous attempts to contact BSC have failed. 5121c42de6dSgd78059 * Do not bother waiting for it to eat previous 5131c42de6dSgd78059 * data. 5141c42de6dSgd78059 * Pat anyway just in case the BSC is really alive 5151c42de6dSgd78059 * and the IBF bit is lying. 5161c42de6dSgd78059 */ 5171c42de6dSgd78059 bscbus_put_reg(csp, H8_IDRC, val); 5181c42de6dSgd78059 bscbus_trace(csp, 'W', "bscbus_pat_dog:", 5191c42de6dSgd78059 "retry count exceeded"); 5201c42de6dSgd78059 return; 5211c42de6dSgd78059 } 5221c42de6dSgd78059 if (--doglimit == 0) { 5231c42de6dSgd78059 /* The BSC is not responding - give up */ 5241c42de6dSgd78059 csp->pat_fail_count++; 5251c42de6dSgd78059 csp->pat_retry_count++; 5261c42de6dSgd78059 /* Pat anyway just in case the BSC is really alive */ 5271c42de6dSgd78059 bscbus_put_reg(csp, H8_IDRC, val); 5281c42de6dSgd78059 bscbus_trace(csp, 'W', "bscbus_pat_dog:", 5291c42de6dSgd78059 "poll limit exceeded"); 5301c42de6dSgd78059 return; 5311c42de6dSgd78059 } 5321c42de6dSgd78059 drv_usecwait(BSCBUS_DOG_PAT_POLL); 5331c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 5341c42de6dSgd78059 } 5351c42de6dSgd78059 bscbus_put_reg(csp, H8_IDRC, val); 5361c42de6dSgd78059 csp->pat_retry_count = 0; 5371c42de6dSgd78059 } 5381c42de6dSgd78059 5391c42de6dSgd78059 /* 5401c42de6dSgd78059 * State diagrams for how bscbus_process works. 5411c42de6dSgd78059 * BSCBUS_CMDSTATE_IDLE No transaction in progress 5421c42de6dSgd78059 * BSCBUS_CMDSTATE_BUSY Setting up command 5431c42de6dSgd78059 * BSCBUS_CMDSTATE_CLEARING Clearing firmware busy status 5441c42de6dSgd78059 * BSCBUS_CMDSTATE_SENDING Waiting to send data to f/w 5451c42de6dSgd78059 * BSCBUS_CMDSTATE_PENDING Waiting for ack from f/w 5461c42de6dSgd78059 * BSCBUS_CMDSTATE_WAITING Waiting for status from f/w 5471c42de6dSgd78059 * BSCBUS_CMDSTATE_READY Status received/command done 5481c42de6dSgd78059 * BSCBUS_CMDSTATE_ERROR Command failed with error 5491c42de6dSgd78059 * 5501c42de6dSgd78059 * +----------+ 5511c42de6dSgd78059 * | | 5521c42de6dSgd78059 * | IDLE/BUSY| 5531c42de6dSgd78059 * | (0/1) | abnormal 5541c42de6dSgd78059 * +----------+ state 5551c42de6dSgd78059 * | \ detected 5561c42de6dSgd78059 * | \------>------+ +----<---+ 5571c42de6dSgd78059 * bsc | | | | 5581c42de6dSgd78059 * is | V V | 5591c42de6dSgd78059 * ready| +----------+ | 5601c42de6dSgd78059 * | | | ^ 5611c42de6dSgd78059 * | | CLEARING | | 5621c42de6dSgd78059 * | | (2) | | 5631c42de6dSgd78059 * | +----------+ | 5641c42de6dSgd78059 * | cleared / | \ | more to clear 5651c42de6dSgd78059 * | / | \-->--+ 5661c42de6dSgd78059 * | +-------<-------/ V 5671c42de6dSgd78059 * | | | 5681c42de6dSgd78059 * V V |timeout 5691c42de6dSgd78059 * +----------+ timeout | 5701c42de6dSgd78059 * | |------>---------+--------+ 5711c42de6dSgd78059 * | SENDING | | 5721c42de6dSgd78059 * | (3) |------<-------+ | 5731c42de6dSgd78059 * +----------+ | V 5741c42de6dSgd78059 * sent| \ send ^ack | 5751c42de6dSgd78059 * last| \ next |received | 5761c42de6dSgd78059 * | \ +----------+ | 5771c42de6dSgd78059 * | \ | | | 5781c42de6dSgd78059 * | \------>| PENDING |-->-+ 5791c42de6dSgd78059 * | | (4) | | 5801c42de6dSgd78059 * | +----------+ |timeout 5811c42de6dSgd78059 * | +---<----+ | 5821c42de6dSgd78059 * | | | | 5831c42de6dSgd78059 * V V | | 5841c42de6dSgd78059 * +----------+ | | 5851c42de6dSgd78059 * | | | | 5861c42de6dSgd78059 * | WAITING | ^ | 5871c42de6dSgd78059 * | (5) | | | 5881c42de6dSgd78059 * +----------+ | | 5891c42de6dSgd78059 * | | |more | | 5901c42de6dSgd78059 * | V |required| | 5911c42de6dSgd78059 * done| | +--->----+ | 5921c42de6dSgd78059 * | +--->--------------+ +---<---+ 5931c42de6dSgd78059 * | error/timeout | | 5941c42de6dSgd78059 * V V V 5951c42de6dSgd78059 * +----------+ +----------+ 5961c42de6dSgd78059 * | | | | 5971c42de6dSgd78059 * | READY | | ERROR | 5981c42de6dSgd78059 * | (7) | | (6) | 5991c42de6dSgd78059 * +----------+ +----------+ 6001c42de6dSgd78059 * | | 6011c42de6dSgd78059 * V V 6021c42de6dSgd78059 * | | 6031c42de6dSgd78059 * +------>---+---<------+ 6041c42de6dSgd78059 * | 6051c42de6dSgd78059 * | 6061c42de6dSgd78059 * Back to 6071c42de6dSgd78059 * Idle 6081c42de6dSgd78059 */ 6091c42de6dSgd78059 6101c42de6dSgd78059 static void 6111c42de6dSgd78059 bscbus_process_sending(struct bscbus_channel_state *csp, uint8_t status) 6121c42de6dSgd78059 { 6131c42de6dSgd78059 /* 6141c42de6dSgd78059 * When we get here we actually expect H8_STR_IBF to 6151c42de6dSgd78059 * be clear but we check just in case of problems. 6161c42de6dSgd78059 */ 6171c42de6dSgd78059 ASSERT(BSCBUS_TX_PENDING(csp)); 6181c42de6dSgd78059 if (!(status & H8_STR_IBF)) { 6191c42de6dSgd78059 bscbus_put_reg(csp, H8_IDRD, *--csp->cmdp); 6201c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process_sending", 6211c42de6dSgd78059 "state %d; val $%x", 6221c42de6dSgd78059 csp->cmdstate, *csp->cmdp); 6231c42de6dSgd78059 if (!BSCBUS_TX_PENDING(csp)) { 6241c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_SENT, 6251c42de6dSgd78059 status, *csp->cmdp); 6261c42de6dSgd78059 /* No more pending - move to waiting state */ 6271c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process_sending", 6281c42de6dSgd78059 "moving to waiting"); 6291c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_WAITING; 6301c42de6dSgd78059 /* Extend deadline because time has moved on */ 6311c42de6dSgd78059 csp->deadline = ddi_get_lbolt() + 6321c42de6dSgd78059 drv_usectohz(LOMBUS_CMD_TIMEOUT/1000); 6331c42de6dSgd78059 } else { 6341c42de6dSgd78059 /* Wait for ack of this byte */ 6351c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_SENDING, 6361c42de6dSgd78059 status, *csp->cmdp); 6371c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_PENDING; 6381c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process_sending", 6391c42de6dSgd78059 "moving to pending"); 6401c42de6dSgd78059 } 6411c42de6dSgd78059 } 6421c42de6dSgd78059 } 6431c42de6dSgd78059 6441c42de6dSgd78059 static void 6451c42de6dSgd78059 bscbus_process_clearing(struct bscbus_channel_state *csp, 6461c42de6dSgd78059 uint8_t status, uint8_t data) 6471c42de6dSgd78059 { 6481c42de6dSgd78059 /* 6491c42de6dSgd78059 * We only enter this state if H8_STR_BUSY was set when 6501c42de6dSgd78059 * we started the transaction. We just ignore all received 6511c42de6dSgd78059 * data until we see OBF set AND BUSY cleared. 6521c42de6dSgd78059 * It is not good enough to see BUSY clear on its own 6531c42de6dSgd78059 */ 6541c42de6dSgd78059 if ((status & H8_STR_OBF) && !(status & H8_STR_BUSY)) { 6551c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_CLEARED, status, data); 6561c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_SENDING; 6571c42de6dSgd78059 /* Throw away any data received up until now */ 6581c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process_clearing", 6591c42de6dSgd78059 "busy cleared"); 6601c42de6dSgd78059 /* 6611c42de6dSgd78059 * Send the next byte immediately. 6621c42de6dSgd78059 * At this stage we should clear the OBF flag because that 6631c42de6dSgd78059 * data has been used. IBF is still valid so do not clear that. 6641c42de6dSgd78059 */ 6651c42de6dSgd78059 status &= ~(H8_STR_OBF); 6661c42de6dSgd78059 bscbus_process_sending(csp, status); 6671c42de6dSgd78059 } else { 6681c42de6dSgd78059 if (status & H8_STR_OBF) { 6691c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_CLEARING, status, data); 6701c42de6dSgd78059 } 6711c42de6dSgd78059 } 6721c42de6dSgd78059 } 6731c42de6dSgd78059 6741c42de6dSgd78059 static void 6751c42de6dSgd78059 bscbus_process_pending(struct bscbus_channel_state *csp, uint8_t status) 6761c42de6dSgd78059 { 6771c42de6dSgd78059 /* We are waiting for an acknowledgement of a byte */ 6781c42de6dSgd78059 if (status & H8_STR_OBF) { 6791c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_PENDING, 6801c42de6dSgd78059 status, *csp->cmdp); 6811c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process_pending", 6821c42de6dSgd78059 "moving to sending"); 6831c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_SENDING; 6841c42de6dSgd78059 /* 6851c42de6dSgd78059 * Send the next byte immediately. 6861c42de6dSgd78059 * At this stage we should clear the OBF flag because that 6871c42de6dSgd78059 * data has been used. IBF is still valid so do not clear that. 6881c42de6dSgd78059 */ 6891c42de6dSgd78059 status &= ~(H8_STR_OBF); 6901c42de6dSgd78059 bscbus_process_sending(csp, status); 6911c42de6dSgd78059 } 6921c42de6dSgd78059 } 6931c42de6dSgd78059 6941c42de6dSgd78059 static boolean_t 6951c42de6dSgd78059 bscbus_process_waiting(struct bscbus_channel_state *csp, 6961c42de6dSgd78059 uint8_t status, uint8_t data) 6971c42de6dSgd78059 { 6981c42de6dSgd78059 uint8_t rcvd = 0; 6991c42de6dSgd78059 boolean_t ready = B_FALSE; 7001c42de6dSgd78059 uint8_t tmp; 7011c42de6dSgd78059 7021c42de6dSgd78059 if (status & H8_STR_OBF) { 7031c42de6dSgd78059 csp->reply[rcvd = csp->index] = data; 7041c42de6dSgd78059 if (++rcvd < BSCBUS_BUFSIZE) 7051c42de6dSgd78059 csp->index = rcvd; 7061c42de6dSgd78059 7071c42de6dSgd78059 bscbus_trace(csp, 'D', "bscbus_process_waiting", 7081c42de6dSgd78059 "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x", 7091c42de6dSgd78059 rcvd, 7101c42de6dSgd78059 csp->reply[0], csp->reply[1], 7111c42de6dSgd78059 csp->reply[2], csp->reply[3], 7121c42de6dSgd78059 csp->reply[4], csp->reply[5], 7131c42de6dSgd78059 csp->reply[6], csp->reply[7]); 7141c42de6dSgd78059 } 7151c42de6dSgd78059 7161c42de6dSgd78059 if (rcvd == 0) { 7171c42de6dSgd78059 /* 7181c42de6dSgd78059 * No bytes received this time through (though there 7191c42de6dSgd78059 * might be a partial packet sitting in the buffer). 7201c42de6dSgd78059 */ 7211c42de6dSgd78059 /* EMPTY */ 7221c42de6dSgd78059 ; 7231c42de6dSgd78059 } else if (rcvd >= BSCBUS_BUFSIZE) { 7241c42de6dSgd78059 /* 7251c42de6dSgd78059 * Buffer overflow; discard the data & treat as an error 7261c42de6dSgd78059 * (even if the last byte read did claim to terminate a 7271c42de6dSgd78059 * packet, it can't be a valid one 'cos it's too long!) 7281c42de6dSgd78059 */ 7291c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_ERROR_OFLOW, status, data); 7301c42de6dSgd78059 csp->index = 0; 7311c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_ERROR; 7321c42de6dSgd78059 csp->error = LOMBUS_ERR_OFLOW; 7331c42de6dSgd78059 ready = B_TRUE; 7341c42de6dSgd78059 } else if ((data & BSCBUS_LAST) == 0) { 7351c42de6dSgd78059 /* 7361c42de6dSgd78059 * Packet not yet complete; leave the partial packet in 7371c42de6dSgd78059 * the buffer for later ... 7381c42de6dSgd78059 */ 7391c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_REPLY, status, data); 7401c42de6dSgd78059 } else if ((data & BSCBUS_MASK) != BSCBUS_STATUS) { 7411c42de6dSgd78059 /* Invalid "status" byte - maybe an echo of the command? */ 7421c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_ERROR_STATUS, status, data); 7431c42de6dSgd78059 7441c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_ERROR; 7451c42de6dSgd78059 csp->error = LOMBUS_ERR_BADSTATUS; 7461c42de6dSgd78059 ready = B_TRUE; 7471c42de6dSgd78059 } else if ((data & BSCBUS_SEQ) != csp->sequence) { 7481c42de6dSgd78059 /* Wrong sequence number! Flag this as an error */ 7491c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_ERROR_SEQ, status, data); 7501c42de6dSgd78059 7511c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_ERROR; 7521c42de6dSgd78059 csp->error = LOMBUS_ERR_SEQUENCE; 7531c42de6dSgd78059 ready = B_TRUE; 7541c42de6dSgd78059 } else { 7551c42de6dSgd78059 /* 7561c42de6dSgd78059 * Finally, we know that's it's a valid reply to our 7571c42de6dSgd78059 * last command. Update the ASYNC status, derive the 7581c42de6dSgd78059 * reply parameter (if any), and check the ERROR bit 7591c42de6dSgd78059 * to find out what the parameter means. 7601c42de6dSgd78059 * 7611c42de6dSgd78059 * Note that not all the values read/assigned here 7621c42de6dSgd78059 * are meaningful, but it doesn't matter; the waiting 7631c42de6dSgd78059 * thread will know which one(s) it should check. 7641c42de6dSgd78059 */ 7651c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_COMPLETE, status, data); 7661c42de6dSgd78059 csp->async = (data & BSCBUS_STATUS_ASYNC) ? 1 : 0; 7671c42de6dSgd78059 7681c42de6dSgd78059 tmp = ((data & BSCBUS_STATUS_MSB) ? 0x80 : 0) | csp->reply[0]; 7691c42de6dSgd78059 if (data & BSCBUS_STATUS_ERR) { 7701c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_ERROR; 7711c42de6dSgd78059 csp->error = tmp; 7721c42de6dSgd78059 } else { 7731c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_READY; 7741c42de6dSgd78059 csp->result = tmp; 7751c42de6dSgd78059 } 7761c42de6dSgd78059 ready = B_TRUE; 7771c42de6dSgd78059 } 7781c42de6dSgd78059 return (ready); 7791c42de6dSgd78059 } 7801c42de6dSgd78059 7811c42de6dSgd78059 /* 7821c42de6dSgd78059 * Packet receive handler 7831c42de6dSgd78059 * 7841c42de6dSgd78059 * This routine should be called from the low-level softint, 7851c42de6dSgd78059 * or bscbus_cmd() (for polled operation), with the 7861c42de6dSgd78059 * low-level mutex already held. 7871c42de6dSgd78059 */ 7881c42de6dSgd78059 static void 7891c42de6dSgd78059 bscbus_process(struct bscbus_channel_state *csp, 7901c42de6dSgd78059 uint8_t status, uint8_t data) 7911c42de6dSgd78059 { 7921c42de6dSgd78059 boolean_t ready = B_FALSE; 7931c42de6dSgd78059 7941c42de6dSgd78059 ASSERT(mutex_owned(csp->lo_mutex)); 7951c42de6dSgd78059 7961c42de6dSgd78059 if ((status & H8_STR_OBF) || (status & H8_STR_IBF)) { 7971c42de6dSgd78059 bscbus_trace(csp, 'D', "bscbus_process", 7981c42de6dSgd78059 "state %d; error $%x", 7991c42de6dSgd78059 csp->cmdstate, csp->error); 8001c42de6dSgd78059 } 8011c42de6dSgd78059 8021c42de6dSgd78059 switch (csp->cmdstate) { 8031c42de6dSgd78059 case BSCBUS_CMDSTATE_CLEARING: 8041c42de6dSgd78059 bscbus_process_clearing(csp, status, data); 8051c42de6dSgd78059 break; 8061c42de6dSgd78059 case BSCBUS_CMDSTATE_SENDING: 8071c42de6dSgd78059 bscbus_process_sending(csp, status); 8081c42de6dSgd78059 break; 8091c42de6dSgd78059 case BSCBUS_CMDSTATE_PENDING: 8101c42de6dSgd78059 bscbus_process_pending(csp, status); 8111c42de6dSgd78059 break; 8121c42de6dSgd78059 case BSCBUS_CMDSTATE_WAITING: 8131c42de6dSgd78059 ready = bscbus_process_waiting(csp, status, data); 8141c42de6dSgd78059 break; 8151c42de6dSgd78059 default: 8161c42de6dSgd78059 /* Nothing to do */ 8171c42de6dSgd78059 break; 8181c42de6dSgd78059 } 8191c42de6dSgd78059 8201c42de6dSgd78059 /* 8211c42de6dSgd78059 * Check for timeouts - but only if the command has not yet 8221c42de6dSgd78059 * completed (ready is true when command completes in this 8231c42de6dSgd78059 * call to bscbus_process OR cmdstate is READY or ERROR if 8241c42de6dSgd78059 * this is a spurious call to bscbus_process i.e. a spurious 8251c42de6dSgd78059 * interrupt) 8261c42de6dSgd78059 */ 8271c42de6dSgd78059 if (!ready && 8281c42de6dSgd78059 ((ddi_get_lbolt() - csp->deadline) > 0) && 8291c42de6dSgd78059 csp->cmdstate != BSCBUS_CMDSTATE_READY && 8301c42de6dSgd78059 csp->cmdstate != BSCBUS_CMDSTATE_ERROR) { 8311c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_process", 8321c42de6dSgd78059 "timeout previous state %d; error $%x", 8331c42de6dSgd78059 csp->cmdstate, csp->error); 8341c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_ERROR_TOUT, status, data); 8351c42de6dSgd78059 if (csp->cmdstate == BSCBUS_CMDSTATE_CLEARING) { 8361c42de6dSgd78059 /* Move onto sending because busy might be stuck */ 8371c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_SENDING; 8381c42de6dSgd78059 /* Extend timeout relative to original start time */ 8391c42de6dSgd78059 csp->deadline += drv_usectohz(LOMBUS_CMD_TIMEOUT/1000); 8401c42de6dSgd78059 } else if (csp->cmdstate != BSCBUS_CMDSTATE_IDLE) { 8411c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_ERROR; 8421c42de6dSgd78059 csp->error = LOMBUS_ERR_TIMEOUT; 8431c42de6dSgd78059 } 8441c42de6dSgd78059 ready = B_TRUE; 8451c42de6dSgd78059 } 8461c42de6dSgd78059 8471c42de6dSgd78059 if ((status & H8_STR_OBF) || (status & H8_STR_IBF) || ready) { 8481c42de6dSgd78059 bscbus_trace(csp, 'D', "bscbus_process", 8491c42de6dSgd78059 "last $%02x; state %d; error $%x; ready %d", 8501c42de6dSgd78059 data, csp->cmdstate, csp->error, ready); 8511c42de6dSgd78059 } 8521c42de6dSgd78059 if (ready) 8531c42de6dSgd78059 cv_broadcast(csp->lo_cv); 8541c42de6dSgd78059 } 8551c42de6dSgd78059 8561c42de6dSgd78059 static uint_t 8571c42de6dSgd78059 bscbus_hwintr(caddr_t arg) 8581c42de6dSgd78059 { 8591c42de6dSgd78059 struct bscbus_channel_state *csp = (void *)arg; 8601c42de6dSgd78059 8611c42de6dSgd78059 uint8_t status; 8621c42de6dSgd78059 uint8_t data = 0xb0 /* Dummy value */; 8631c42de6dSgd78059 8641c42de6dSgd78059 mutex_enter(csp->lo_mutex); 8651c42de6dSgd78059 /* 8661c42de6dSgd78059 * Read the registers to ensure that the interrupt is cleared. 8671c42de6dSgd78059 * Status must be read first because reading data changes the 8681c42de6dSgd78059 * status. 8691c42de6dSgd78059 * We always read the data because that clears the interrupt down. 8701c42de6dSgd78059 * This is horrible hardware semantics but we have to do it! 8711c42de6dSgd78059 */ 8721c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 8731c42de6dSgd78059 data = bscbus_get_reg(csp, H8_ODR); 8741c42de6dSgd78059 if (!(status & H8_STR_OBF)) { 8751c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_V1INTRUNCL, status, data); 8761c42de6dSgd78059 csp->unclaimed_count++; 8771c42de6dSgd78059 } else { 8781c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_V1INTR, status, data); 8791c42de6dSgd78059 } 8801c42de6dSgd78059 if (status & H8_STR_TOKENPROTOCOL) { 8811c42de6dSgd78059 bscbus_process(csp, status, data); 8821c42de6dSgd78059 if (csp->interrupt_failed) { 8831c42de6dSgd78059 bscbus_trace(csp, 'I', "bscbus_hwintr:", 8841c42de6dSgd78059 "interrupt fault cleared channel %d", csp->chno); 8851c42de6dSgd78059 csp->interrupt_failed = B_FALSE; 8861c42de6dSgd78059 csp->poll_hz = drv_usectohz(BSCBUS_CMD_POLL / 1000); 8871c42de6dSgd78059 } 8881c42de6dSgd78059 } 8891c42de6dSgd78059 8901c42de6dSgd78059 mutex_exit(csp->lo_mutex); 8911c42de6dSgd78059 return (DDI_INTR_CLAIMED); 8921c42de6dSgd78059 } 8931c42de6dSgd78059 8941c42de6dSgd78059 void 8951c42de6dSgd78059 bscbus_poll(struct bscbus_channel_state *csp) 8961c42de6dSgd78059 { 8971c42de6dSgd78059 /* 8981c42de6dSgd78059 * This routine is only called if we timeout in userland 8991c42de6dSgd78059 * waiting for an interrupt. This generally means that we have 9001c42de6dSgd78059 * lost interrupt capabilities or that something has gone 9011c42de6dSgd78059 * wrong. In this case we are allowed to access the hardware 9021c42de6dSgd78059 * and read the data register if necessary. 9031c42de6dSgd78059 * If interrupts return then recovery actions should mend us! 9041c42de6dSgd78059 */ 9051c42de6dSgd78059 uint8_t status; 9061c42de6dSgd78059 uint8_t data = 0xfa; /* Dummy value */ 9071c42de6dSgd78059 9081c42de6dSgd78059 ASSERT(mutex_owned(csp->lo_mutex)); 9091c42de6dSgd78059 9101c42de6dSgd78059 /* Should look for data to receive */ 9111c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 9121c42de6dSgd78059 if (status & H8_STR_OBF) { 9131c42de6dSgd78059 /* There is data available */ 9141c42de6dSgd78059 data = bscbus_get_reg(csp, H8_ODR); 9151c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_PROCESS, status, data); 9161c42de6dSgd78059 } 9171c42de6dSgd78059 bscbus_process(csp, status, data); 9181c42de6dSgd78059 } 9191c42de6dSgd78059 9201c42de6dSgd78059 /* 9211c42de6dSgd78059 * Serial protocol 9221c42de6dSgd78059 * 9231c42de6dSgd78059 * This routine builds a command and sets it in progress. 9241c42de6dSgd78059 */ 9251c42de6dSgd78059 static uint8_t 9261c42de6dSgd78059 bscbus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd) 9271c42de6dSgd78059 { 9281c42de6dSgd78059 struct bscbus_channel_state *csp; 9291c42de6dSgd78059 clock_t start; 9301c42de6dSgd78059 clock_t tick; 9311c42de6dSgd78059 uint8_t status; 9321c42de6dSgd78059 9331c42de6dSgd78059 /* 9341c42de6dSgd78059 * First of all, wait for the interface to be available. 9351c42de6dSgd78059 * 9361c42de6dSgd78059 * NOTE: we blow through all the mutex/cv/state checking and 9371c42de6dSgd78059 * preempt any command in progress if the system is panicking! 9381c42de6dSgd78059 */ 9391c42de6dSgd78059 csp = HANDLE_PRIVATE(hdlp); 9401c42de6dSgd78059 mutex_enter(csp->lo_mutex); 9411c42de6dSgd78059 while (csp->cmdstate != BSCBUS_CMDSTATE_IDLE && !ddi_in_panic()) 9421c42de6dSgd78059 cv_wait(csp->lo_cv, csp->lo_mutex); 9431c42de6dSgd78059 9441c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_BUSY; 9451c42de6dSgd78059 csp->sequence = (csp->sequence + BSCBUS_SEQ_LSB) & BSCBUS_SEQ; 9461c42de6dSgd78059 9471c42de6dSgd78059 /* 9481c42de6dSgd78059 * We have exclusive ownership, so assemble the command (backwards): 9491c42de6dSgd78059 * 9501c42de6dSgd78059 * [byte 0] Command: modified by XADDR and/or WMSB bits 9511c42de6dSgd78059 * [Optional] Parameter: Value to write (low 7 bits) 9521c42de6dSgd78059 * [Optional] Parameter: Register number (high 7 bits) 9531c42de6dSgd78059 * [Optional] Parameter: Register number (low 7 bits) 9541c42de6dSgd78059 */ 9551c42de6dSgd78059 csp->cmdp = &csp->cmdbuf[0]; 9561c42de6dSgd78059 *csp->cmdp++ = BSCBUS_CMD | csp->sequence | cmd; 9571c42de6dSgd78059 switch (cmd) { 9581c42de6dSgd78059 case BSCBUS_CMD_WRITE: 9591c42de6dSgd78059 *csp->cmdp++ = val & 0x7f; 9601c42de6dSgd78059 if (val >= 0x80) 9611c42de6dSgd78059 csp->cmdbuf[0] |= BSCBUS_CMD_WMSB; 9621c42de6dSgd78059 /*FALLTHRU*/ 9631c42de6dSgd78059 case BSCBUS_CMD_READ: 9641c42de6dSgd78059 if (BSCBUS_VREG_HI(vreg) != 0) { 9651c42de6dSgd78059 *csp->cmdp++ = BSCBUS_VREG_HI(vreg); 9661c42de6dSgd78059 csp->cmdbuf[0] |= BSCBUS_CMD_XADDR; 9671c42de6dSgd78059 } 9681c42de6dSgd78059 *csp->cmdp++ = BSCBUS_VREG_LO(vreg); 9691c42de6dSgd78059 /*FALLTHRU*/ 9701c42de6dSgd78059 case BSCBUS_CMD_NOP: 9711c42de6dSgd78059 break; 9721c42de6dSgd78059 } 9731c42de6dSgd78059 9741c42de6dSgd78059 /* 9751c42de6dSgd78059 * Check and update the H8 h/w fault status before accessing 9761c42de6dSgd78059 * the chip registers. If there's a (new or previous) fault, 9771c42de6dSgd78059 * we'll run through the protocol but won't really touch the 9781c42de6dSgd78059 * hardware and all commands will timeout. If a previously 9791c42de6dSgd78059 * discovered fault has now gone away (!), then we can (try to) 9801c42de6dSgd78059 * proceed with the new command (probably a probe). 9811c42de6dSgd78059 */ 9821c42de6dSgd78059 bscbus_check_fault_status(csp); 9831c42de6dSgd78059 9841c42de6dSgd78059 /* 9851c42de6dSgd78059 * Prepare for the command (to be processed by the interrupt 9861c42de6dSgd78059 * handler and/or polling loop below), and wait for a response 9871c42de6dSgd78059 * or timeout. 9881c42de6dSgd78059 */ 9891c42de6dSgd78059 start = ddi_get_lbolt(); 9901c42de6dSgd78059 csp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000); 9911c42de6dSgd78059 csp->error = 0; 9921c42de6dSgd78059 csp->index = 0; 9931c42de6dSgd78059 csp->result = DUMMY_VALUE; 9941c42de6dSgd78059 9951c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 9961c42de6dSgd78059 if (status & H8_STR_BUSY) { 9971c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0xfd); 9981c42de6dSgd78059 /* 9991c42de6dSgd78059 * Must ensure that the busy state has cleared before 10001c42de6dSgd78059 * sending the command 10011c42de6dSgd78059 */ 10021c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_CLEARING; 10031c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_cmd", 10041c42de6dSgd78059 "h8 reporting status (%x) busy - clearing", status); 10051c42de6dSgd78059 } else { 10061c42de6dSgd78059 /* It is clear to send the command immediately */ 10071c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_SENDING; 10081c42de6dSgd78059 bscbus_trace(csp, 'P', "bscbus_cmd", 10091c42de6dSgd78059 "sending first byte of command, status %x", status); 10101c42de6dSgd78059 bscbus_poll(csp); 10111c42de6dSgd78059 } 10121c42de6dSgd78059 10131c42de6dSgd78059 csp->poll_hz = drv_usectohz( 10141c42de6dSgd78059 (csp->interrupt_failed ? 10151c42de6dSgd78059 BSCBUS_CMD_POLLNOINTS : BSCBUS_CMD_POLL) / 1000); 10161c42de6dSgd78059 10171c42de6dSgd78059 while ((csp->cmdstate != BSCBUS_CMDSTATE_READY) && 10181c42de6dSgd78059 (csp->cmdstate != BSCBUS_CMDSTATE_ERROR)) { 10191c42de6dSgd78059 ASSERT(csp->cmdstate != BSCBUS_CMDSTATE_IDLE); 10201c42de6dSgd78059 10211c42de6dSgd78059 tick = ddi_get_lbolt() + csp->poll_hz; 10221c42de6dSgd78059 if ((cv_timedwait(csp->lo_cv, csp->lo_mutex, tick) == -1) && 10231c42de6dSgd78059 csp->cmdstate != BSCBUS_CMDSTATE_READY && 10241c42de6dSgd78059 csp->cmdstate != BSCBUS_CMDSTATE_ERROR) { 10251c42de6dSgd78059 if (!csp->interrupt_failed) { 10261c42de6dSgd78059 bscbus_trace(csp, 'I', "bscbus_cmd:", 10271c42de6dSgd78059 "interrupt_failed channel %d", csp->chno); 10281c42de6dSgd78059 csp->interrupt_failed = B_TRUE; 10291c42de6dSgd78059 csp->poll_hz = drv_usectohz( 10301c42de6dSgd78059 BSCBUS_CMD_POLLNOINTS / 1000); 10311c42de6dSgd78059 } 10321c42de6dSgd78059 bscbus_poll(csp); 10331c42de6dSgd78059 } 10341c42de6dSgd78059 } 10351c42de6dSgd78059 10361c42de6dSgd78059 /* 10371c42de6dSgd78059 * The return value may not be meaningful but retrieve it anyway 10381c42de6dSgd78059 */ 10391c42de6dSgd78059 val = csp->result; 10401c42de6dSgd78059 if (bscbus_faulty(csp)) { 10411c42de6dSgd78059 val = DUMMY_VALUE; 10421c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW; 10431c42de6dSgd78059 } else if (csp->cmdstate != BSCBUS_CMDSTATE_READY) { 10441c42de6dSgd78059 /* 10451c42de6dSgd78059 * Some problem here ... transfer the error code from 10461c42de6dSgd78059 * the per-instance state to the per-handle fault flag. 10471c42de6dSgd78059 * The error code shouldn't be zero! 10481c42de6dSgd78059 */ 10491c42de6dSgd78059 if (csp->error != 0) 10501c42de6dSgd78059 HANDLE_FAULT(hdlp) = csp->error; 10511c42de6dSgd78059 else 10521c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE; 10531c42de6dSgd78059 } 10541c42de6dSgd78059 10551c42de6dSgd78059 /* 10561c42de6dSgd78059 * All done now! 10571c42de6dSgd78059 */ 10581c42de6dSgd78059 csp->index = 0; 10591c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_IDLE; 10601c42de6dSgd78059 cv_broadcast(csp->lo_cv); 10611c42de6dSgd78059 mutex_exit(csp->lo_mutex); 10621c42de6dSgd78059 10631c42de6dSgd78059 return (val); 10641c42de6dSgd78059 } 10651c42de6dSgd78059 10661c42de6dSgd78059 /* 10671c42de6dSgd78059 * Space 0 - LOM virtual register access 10681c42de6dSgd78059 * Only 8-bit accesses are supported. 10691c42de6dSgd78059 */ 10701c42de6dSgd78059 static uint8_t 10711c42de6dSgd78059 bscbus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 10721c42de6dSgd78059 { 10731c42de6dSgd78059 ptrdiff_t offset; 10741c42de6dSgd78059 10751c42de6dSgd78059 /* 10761c42de6dSgd78059 * Check the offset that the caller has added to the base address 10771c42de6dSgd78059 * against the length of the mapping originally requested. 10781c42de6dSgd78059 */ 10791c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 10801c42de6dSgd78059 if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 10811c42de6dSgd78059 /* 10821c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 10831c42de6dSgd78059 */ 10841c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 10851c42de6dSgd78059 return (DUMMY_VALUE); 10861c42de6dSgd78059 } 10871c42de6dSgd78059 10881c42de6dSgd78059 /* 10891c42de6dSgd78059 * Derive the virtual register number and run the command 10901c42de6dSgd78059 */ 10911c42de6dSgd78059 return (bscbus_cmd(hdlp, ADDR_TO_VREG(addr), 0, BSCBUS_CMD_READ)); 10921c42de6dSgd78059 } 10931c42de6dSgd78059 10941c42de6dSgd78059 static void 10951c42de6dSgd78059 bscbus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 10961c42de6dSgd78059 { 10971c42de6dSgd78059 ptrdiff_t offset; 10981c42de6dSgd78059 10991c42de6dSgd78059 /* 11001c42de6dSgd78059 * Check the offset that the caller has added to the base address 11011c42de6dSgd78059 * against the length of the mapping originally requested. 11021c42de6dSgd78059 */ 11031c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 11041c42de6dSgd78059 if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 11051c42de6dSgd78059 /* 11061c42de6dSgd78059 * Invalid access - flag a fault and return 11071c42de6dSgd78059 */ 11081c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 11091c42de6dSgd78059 return; 11101c42de6dSgd78059 } 11111c42de6dSgd78059 11121c42de6dSgd78059 /* 11131c42de6dSgd78059 * Derive the virtual register number and run the command 11141c42de6dSgd78059 */ 11151c42de6dSgd78059 (void) bscbus_cmd(hdlp, ADDR_TO_VREG(addr), val, BSCBUS_CMD_WRITE); 11161c42de6dSgd78059 } 11171c42de6dSgd78059 11181c42de6dSgd78059 static void 11191c42de6dSgd78059 bscbus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 11201c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 11211c42de6dSgd78059 { 11221c42de6dSgd78059 size_t inc; 11231c42de6dSgd78059 11241c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 11251c42de6dSgd78059 for (; repcount--; dev_addr += inc) 11261c42de6dSgd78059 *host_addr++ = bscbus_vreg_get8(hdlp, dev_addr); 11271c42de6dSgd78059 } 11281c42de6dSgd78059 11291c42de6dSgd78059 static void 11301c42de6dSgd78059 bscbus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 11311c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 11321c42de6dSgd78059 { 11331c42de6dSgd78059 size_t inc; 11341c42de6dSgd78059 11351c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 11361c42de6dSgd78059 for (; repcount--; dev_addr += inc) 11371c42de6dSgd78059 bscbus_vreg_put8(hdlp, dev_addr, *host_addr++); 11381c42de6dSgd78059 } 11391c42de6dSgd78059 11401c42de6dSgd78059 11411c42de6dSgd78059 /* 11421c42de6dSgd78059 * Space 1 - LOM watchdog pat register access 11431c42de6dSgd78059 * Only 8-bit accesses are supported. 11441c42de6dSgd78059 * 11451c42de6dSgd78059 * Reads have no effect and return 0. 11461c42de6dSgd78059 * 11471c42de6dSgd78059 * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient 11481c42de6dSgd78059 * way of zeroing the destination area ;-) and still won't pat the dog. 11491c42de6dSgd78059 * 11501c42de6dSgd78059 * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly 11511c42de6dSgd78059 * only count as a single pat, no matter how many bytes the caller 11521c42de6dSgd78059 * says to write, as the inter-pat time is VERY long compared with 11531c42de6dSgd78059 * the time it will take to read the memory source area. 11541c42de6dSgd78059 */ 11551c42de6dSgd78059 11561c42de6dSgd78059 static uint8_t 11571c42de6dSgd78059 bscbus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 11581c42de6dSgd78059 { 11591c42de6dSgd78059 ptrdiff_t offset; 11601c42de6dSgd78059 11611c42de6dSgd78059 /* 11621c42de6dSgd78059 * Check the offset that the caller has added to the base address 11631c42de6dSgd78059 * against the length of the mapping originally requested. 11641c42de6dSgd78059 */ 11651c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 11661c42de6dSgd78059 if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 11671c42de6dSgd78059 /* 11681c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 11691c42de6dSgd78059 */ 11701c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 11711c42de6dSgd78059 return (DUMMY_VALUE); 11721c42de6dSgd78059 } 11731c42de6dSgd78059 11741c42de6dSgd78059 return (0); 11751c42de6dSgd78059 } 11761c42de6dSgd78059 11771c42de6dSgd78059 static void 11781c42de6dSgd78059 bscbus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 11791c42de6dSgd78059 { 11801c42de6dSgd78059 struct bscbus_channel_state *csp; 11811c42de6dSgd78059 ptrdiff_t offset; 11821c42de6dSgd78059 11831c42de6dSgd78059 /* 11841c42de6dSgd78059 * Check the offset that the caller has added to the base address 11851c42de6dSgd78059 * against the length of the mapping originally requested. 11861c42de6dSgd78059 */ 11871c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 11881c42de6dSgd78059 if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 11891c42de6dSgd78059 /* 11901c42de6dSgd78059 * Invalid access - flag a fault and return 11911c42de6dSgd78059 */ 11921c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 11931c42de6dSgd78059 return; 11941c42de6dSgd78059 } 11951c42de6dSgd78059 11961c42de6dSgd78059 csp = HANDLE_PRIVATE(hdlp); 11971c42de6dSgd78059 mutex_enter(csp->dog_mutex); 11981c42de6dSgd78059 bscbus_pat_dog(csp, val); 11991c42de6dSgd78059 mutex_exit(csp->dog_mutex); 12001c42de6dSgd78059 } 12011c42de6dSgd78059 12021c42de6dSgd78059 static void 12031c42de6dSgd78059 bscbus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 12041c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 12051c42de6dSgd78059 { 12061c42de6dSgd78059 size_t inc; 12071c42de6dSgd78059 12081c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 12091c42de6dSgd78059 for (; repcount--; dev_addr += inc) 12101c42de6dSgd78059 *host_addr++ = bscbus_pat_get8(hdlp, dev_addr); 12111c42de6dSgd78059 } 12121c42de6dSgd78059 12131c42de6dSgd78059 static void 12141c42de6dSgd78059 bscbus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 12151c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 12161c42de6dSgd78059 { 12171c42de6dSgd78059 size_t inc; 12181c42de6dSgd78059 12191c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 12201c42de6dSgd78059 for (; repcount--; dev_addr += inc) 12211c42de6dSgd78059 bscbus_pat_put8(hdlp, dev_addr, *host_addr++); 12221c42de6dSgd78059 } 12231c42de6dSgd78059 12241c42de6dSgd78059 12251c42de6dSgd78059 /* 12261c42de6dSgd78059 * Space 2 - LOM async event flag register access 12271c42de6dSgd78059 * Only 16-bit accesses are supported. 12281c42de6dSgd78059 */ 12291c42de6dSgd78059 static uint16_t 12301c42de6dSgd78059 bscbus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr) 12311c42de6dSgd78059 { 12321c42de6dSgd78059 struct bscbus_channel_state *csp; 12331c42de6dSgd78059 ptrdiff_t offset; 12341c42de6dSgd78059 12351c42de6dSgd78059 /* 12361c42de6dSgd78059 * Check the offset that the caller has added to the base address 12371c42de6dSgd78059 * against the length of the mapping orignally requested. 12381c42de6dSgd78059 */ 12391c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 12401c42de6dSgd78059 if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) { 12411c42de6dSgd78059 /* 12421c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 12431c42de6dSgd78059 */ 12441c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 12451c42de6dSgd78059 return (DUMMY_VALUE); 12461c42de6dSgd78059 } 12471c42de6dSgd78059 12481c42de6dSgd78059 /* 12491c42de6dSgd78059 * Return the value of the asynchronous-event-pending flag 12501c42de6dSgd78059 * as passed back by the LOM at the end of the last command. 12511c42de6dSgd78059 */ 12521c42de6dSgd78059 csp = HANDLE_PRIVATE(hdlp); 12531c42de6dSgd78059 return (csp->async); 12541c42de6dSgd78059 } 12551c42de6dSgd78059 12561c42de6dSgd78059 static void 12571c42de6dSgd78059 bscbus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val) 12581c42de6dSgd78059 { 12591c42de6dSgd78059 ptrdiff_t offset; 12601c42de6dSgd78059 12611c42de6dSgd78059 _NOTE(ARGUNUSED(val)) 12621c42de6dSgd78059 12631c42de6dSgd78059 /* 12641c42de6dSgd78059 * Check the offset that the caller has added to the base address 12651c42de6dSgd78059 * against the length of the mapping originally requested. 12661c42de6dSgd78059 */ 12671c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 12681c42de6dSgd78059 if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) { 12691c42de6dSgd78059 /* 12701c42de6dSgd78059 * Invalid access - flag a fault and return 12711c42de6dSgd78059 */ 12721c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 12731c42de6dSgd78059 return; 12741c42de6dSgd78059 } 12751c42de6dSgd78059 12761c42de6dSgd78059 /* 12771c42de6dSgd78059 * The user can't overwrite the asynchronous-event-pending flag! 12781c42de6dSgd78059 */ 12791c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO; 12801c42de6dSgd78059 } 12811c42de6dSgd78059 12821c42de6dSgd78059 static void 12831c42de6dSgd78059 bscbus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 12841c42de6dSgd78059 uint16_t *dev_addr, size_t repcount, uint_t flags) 12851c42de6dSgd78059 { 12861c42de6dSgd78059 size_t inc; 12871c42de6dSgd78059 12881c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 12891c42de6dSgd78059 for (; repcount--; dev_addr += inc) 12901c42de6dSgd78059 *host_addr++ = bscbus_event_get16(hdlp, dev_addr); 12911c42de6dSgd78059 } 12921c42de6dSgd78059 12931c42de6dSgd78059 static void 12941c42de6dSgd78059 bscbus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 12951c42de6dSgd78059 uint16_t *dev_addr, size_t repcount, uint_t flags) 12961c42de6dSgd78059 { 12971c42de6dSgd78059 size_t inc; 12981c42de6dSgd78059 12991c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 13001c42de6dSgd78059 for (; repcount--; dev_addr += inc) 13011c42de6dSgd78059 bscbus_event_put16(hdlp, dev_addr, *host_addr++); 13021c42de6dSgd78059 } 13031c42de6dSgd78059 13041c42de6dSgd78059 13051c42de6dSgd78059 /* 13061c42de6dSgd78059 * All spaces - access handle fault information 13071c42de6dSgd78059 * Only 32-bit accesses are supported. 13081c42de6dSgd78059 */ 13091c42de6dSgd78059 static uint32_t 13101c42de6dSgd78059 bscbus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr) 13111c42de6dSgd78059 { 13121c42de6dSgd78059 struct bscbus_channel_state *csp; 13131c42de6dSgd78059 ptrdiff_t offset; 13141c42de6dSgd78059 13151c42de6dSgd78059 /* 13161c42de6dSgd78059 * Derive the offset that the caller has added to the base 13171c42de6dSgd78059 * address originally returned, and use it to determine 13181c42de6dSgd78059 * which meta-register is to be accessed ... 13191c42de6dSgd78059 */ 13201c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 13211c42de6dSgd78059 switch (offset) { 13221c42de6dSgd78059 case LOMBUS_FAULT_REG: 13231c42de6dSgd78059 /* 13241c42de6dSgd78059 * This meta-register provides a code for the most 13251c42de6dSgd78059 * recent virtual register access fault, if any. 13261c42de6dSgd78059 */ 13271c42de6dSgd78059 return (HANDLE_FAULT(hdlp)); 13281c42de6dSgd78059 13291c42de6dSgd78059 case LOMBUS_PROBE_REG: 13301c42de6dSgd78059 /* 13311c42de6dSgd78059 * Reading this meta-register clears any existing fault 13321c42de6dSgd78059 * (at the virtual, not the hardware access layer), then 13331c42de6dSgd78059 * runs a NOP command and returns the fault code from that. 13341c42de6dSgd78059 */ 13351c42de6dSgd78059 HANDLE_FAULT(hdlp) = 0; 13361c42de6dSgd78059 (void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP); 13371c42de6dSgd78059 return (HANDLE_FAULT(hdlp)); 13381c42de6dSgd78059 13391c42de6dSgd78059 case LOMBUS_ASYNC_REG: 13401c42de6dSgd78059 /* 13411c42de6dSgd78059 * Obsolescent - but still supported for backwards 13421c42de6dSgd78059 * compatibility. This is an alias for the newer 13431c42de6dSgd78059 * LOMBUS_EVENT_REG, but doesn't require a separate 13441c42de6dSgd78059 * "reg" entry and ddi_regs_map_setup() call. 13451c42de6dSgd78059 * 13461c42de6dSgd78059 * It returns the value of the asynchronous-event-pending 13471c42de6dSgd78059 * flag as passed back by the BSC at the end of the last 13481c42de6dSgd78059 * completed command. 13491c42de6dSgd78059 */ 13501c42de6dSgd78059 csp = HANDLE_PRIVATE(hdlp); 13511c42de6dSgd78059 return (csp->async); 13521c42de6dSgd78059 13531c42de6dSgd78059 default: 13541c42de6dSgd78059 /* 13551c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 13561c42de6dSgd78059 */ 13571c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13581c42de6dSgd78059 return (DUMMY_VALUE); 13591c42de6dSgd78059 } 13601c42de6dSgd78059 } 13611c42de6dSgd78059 13621c42de6dSgd78059 static void 13631c42de6dSgd78059 bscbus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val) 13641c42de6dSgd78059 { 13651c42de6dSgd78059 ptrdiff_t offset; 13661c42de6dSgd78059 13671c42de6dSgd78059 /* 13681c42de6dSgd78059 * Derive the offset that the caller has added to the base 13691c42de6dSgd78059 * address originally returned, and use it to determine 13701c42de6dSgd78059 * which meta-register is to be accessed ... 13711c42de6dSgd78059 */ 13721c42de6dSgd78059 offset = ADDR_TO_OFFSET(addr, hdlp); 13731c42de6dSgd78059 switch (offset) { 13741c42de6dSgd78059 case LOMBUS_FAULT_REG: 13751c42de6dSgd78059 /* 13761c42de6dSgd78059 * This meta-register contains a code for the most 13771c42de6dSgd78059 * recent virtual register access fault, if any. 13781c42de6dSgd78059 * It can be cleared simply by writing 0 to it. 13791c42de6dSgd78059 */ 13801c42de6dSgd78059 HANDLE_FAULT(hdlp) = val; 13811c42de6dSgd78059 return; 13821c42de6dSgd78059 13831c42de6dSgd78059 case LOMBUS_PROBE_REG: 13841c42de6dSgd78059 /* 13851c42de6dSgd78059 * Writing this meta-register clears any existing fault 13861c42de6dSgd78059 * (at the virtual, not the hardware acess layer), then 13871c42de6dSgd78059 * runs a NOP command. The caller can check the fault 13881c42de6dSgd78059 * code later if required. 13891c42de6dSgd78059 */ 13901c42de6dSgd78059 HANDLE_FAULT(hdlp) = 0; 13911c42de6dSgd78059 (void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP); 13921c42de6dSgd78059 return; 13931c42de6dSgd78059 13941c42de6dSgd78059 default: 13951c42de6dSgd78059 /* 13961c42de6dSgd78059 * Invalid access - flag a fault 13971c42de6dSgd78059 */ 13981c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13991c42de6dSgd78059 return; 14001c42de6dSgd78059 } 14011c42de6dSgd78059 } 14021c42de6dSgd78059 14031c42de6dSgd78059 static void 14041c42de6dSgd78059 bscbus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr, 14051c42de6dSgd78059 uint32_t *dev_addr, size_t repcount, uint_t flags) 14061c42de6dSgd78059 { 14071c42de6dSgd78059 size_t inc; 14081c42de6dSgd78059 14091c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 14101c42de6dSgd78059 for (; repcount--; dev_addr += inc) 14111c42de6dSgd78059 *host_addr++ = bscbus_meta_get32(hdlp, dev_addr); 14121c42de6dSgd78059 } 14131c42de6dSgd78059 14141c42de6dSgd78059 static void 14151c42de6dSgd78059 bscbus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr, 14161c42de6dSgd78059 uint32_t *dev_addr, size_t repcount, uint_t flags) 14171c42de6dSgd78059 { 14181c42de6dSgd78059 size_t inc; 14191c42de6dSgd78059 14201c42de6dSgd78059 inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 14211c42de6dSgd78059 for (; repcount--; dev_addr += inc) 14221c42de6dSgd78059 bscbus_meta_put32(hdlp, dev_addr, *host_addr++); 14231c42de6dSgd78059 } 14241c42de6dSgd78059 14251c42de6dSgd78059 14261c42de6dSgd78059 /* 14271c42de6dSgd78059 * Finally, some dummy functions for all unsupported access 14281c42de6dSgd78059 * space/size/mode combinations ... 14291c42de6dSgd78059 */ 14301c42de6dSgd78059 static uint8_t 14311c42de6dSgd78059 bscbus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 14321c42de6dSgd78059 { 14331c42de6dSgd78059 _NOTE(ARGUNUSED(addr)) 14341c42de6dSgd78059 14351c42de6dSgd78059 /* 14361c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 14371c42de6dSgd78059 */ 14381c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14391c42de6dSgd78059 return (DUMMY_VALUE); 14401c42de6dSgd78059 } 14411c42de6dSgd78059 14421c42de6dSgd78059 static void 14431c42de6dSgd78059 bscbus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 14441c42de6dSgd78059 { 14451c42de6dSgd78059 _NOTE(ARGUNUSED(addr, val)) 14461c42de6dSgd78059 14471c42de6dSgd78059 /* 14481c42de6dSgd78059 * Invalid access - flag a fault 14491c42de6dSgd78059 */ 14501c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14511c42de6dSgd78059 } 14521c42de6dSgd78059 14531c42de6dSgd78059 static void 14541c42de6dSgd78059 bscbus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 14551c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 14561c42de6dSgd78059 { 14571c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14581c42de6dSgd78059 14591c42de6dSgd78059 /* 14601c42de6dSgd78059 * Invalid access - flag a fault 14611c42de6dSgd78059 */ 14621c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14631c42de6dSgd78059 } 14641c42de6dSgd78059 14651c42de6dSgd78059 static void 14661c42de6dSgd78059 bscbus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 14671c42de6dSgd78059 uint8_t *dev_addr, size_t repcount, uint_t flags) 14681c42de6dSgd78059 { 14691c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14701c42de6dSgd78059 14711c42de6dSgd78059 /* 14721c42de6dSgd78059 * Invalid access - flag a fault 14731c42de6dSgd78059 */ 14741c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14751c42de6dSgd78059 } 14761c42de6dSgd78059 14771c42de6dSgd78059 static uint16_t 14781c42de6dSgd78059 bscbus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr) 14791c42de6dSgd78059 { 14801c42de6dSgd78059 _NOTE(ARGUNUSED(addr)) 14811c42de6dSgd78059 14821c42de6dSgd78059 /* 14831c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 14841c42de6dSgd78059 */ 14851c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14861c42de6dSgd78059 return (DUMMY_VALUE); 14871c42de6dSgd78059 } 14881c42de6dSgd78059 14891c42de6dSgd78059 static void 14901c42de6dSgd78059 bscbus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val) 14911c42de6dSgd78059 { 14921c42de6dSgd78059 _NOTE(ARGUNUSED(addr, val)) 14931c42de6dSgd78059 14941c42de6dSgd78059 /* 14951c42de6dSgd78059 * Invalid access - flag a fault 14961c42de6dSgd78059 */ 14971c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14981c42de6dSgd78059 } 14991c42de6dSgd78059 15001c42de6dSgd78059 static void 15011c42de6dSgd78059 bscbus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 15021c42de6dSgd78059 uint16_t *dev_addr, size_t repcount, uint_t flags) 15031c42de6dSgd78059 { 15041c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 15051c42de6dSgd78059 15061c42de6dSgd78059 /* 15071c42de6dSgd78059 * Invalid access - flag a fault 15081c42de6dSgd78059 */ 15091c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15101c42de6dSgd78059 } 15111c42de6dSgd78059 15121c42de6dSgd78059 static void 15131c42de6dSgd78059 bscbus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 15141c42de6dSgd78059 uint16_t *dev_addr, size_t repcount, uint_t flags) 15151c42de6dSgd78059 { 15161c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 15171c42de6dSgd78059 15181c42de6dSgd78059 /* 15191c42de6dSgd78059 * Invalid access - flag a fault 15201c42de6dSgd78059 */ 15211c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15221c42de6dSgd78059 } 15231c42de6dSgd78059 15241c42de6dSgd78059 static uint64_t 15251c42de6dSgd78059 bscbus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr) 15261c42de6dSgd78059 { 15271c42de6dSgd78059 _NOTE(ARGUNUSED(addr)) 15281c42de6dSgd78059 15291c42de6dSgd78059 /* 15301c42de6dSgd78059 * Invalid access - flag a fault and return a dummy value 15311c42de6dSgd78059 */ 15321c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15331c42de6dSgd78059 return (DUMMY_VALUE); 15341c42de6dSgd78059 } 15351c42de6dSgd78059 15361c42de6dSgd78059 static void 15371c42de6dSgd78059 bscbus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val) 15381c42de6dSgd78059 { 15391c42de6dSgd78059 _NOTE(ARGUNUSED(addr, val)) 15401c42de6dSgd78059 15411c42de6dSgd78059 /* 15421c42de6dSgd78059 * Invalid access - flag a fault 15431c42de6dSgd78059 */ 15441c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15451c42de6dSgd78059 } 15461c42de6dSgd78059 15471c42de6dSgd78059 static void 15481c42de6dSgd78059 bscbus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr, 15491c42de6dSgd78059 uint64_t *dev_addr, size_t repcount, uint_t flags) 15501c42de6dSgd78059 { 15511c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 15521c42de6dSgd78059 15531c42de6dSgd78059 /* 15541c42de6dSgd78059 * Invalid access - flag a fault 15551c42de6dSgd78059 */ 15561c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15571c42de6dSgd78059 } 15581c42de6dSgd78059 15591c42de6dSgd78059 static void 15601c42de6dSgd78059 bscbus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr, 15611c42de6dSgd78059 uint64_t *dev_addr, size_t repcount, uint_t flags) 15621c42de6dSgd78059 { 15631c42de6dSgd78059 _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 15641c42de6dSgd78059 15651c42de6dSgd78059 /* 15661c42de6dSgd78059 * Invalid access - flag a fault 15671c42de6dSgd78059 */ 15681c42de6dSgd78059 HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 15691c42de6dSgd78059 } 15701c42de6dSgd78059 15711c42de6dSgd78059 static int 15721c42de6dSgd78059 bscbus_acc_fault_check(HANDLE_TYPE *hdlp) 15731c42de6dSgd78059 { 15741c42de6dSgd78059 return (HANDLE_FAULT(hdlp) != 0); 15751c42de6dSgd78059 } 15761c42de6dSgd78059 15771c42de6dSgd78059 /* 15781c42de6dSgd78059 * Hardware setup - ensure that there are no pending transactions and 15791c42de6dSgd78059 * hence no pending interrupts. We do this be ensuring that the BSC is 15801c42de6dSgd78059 * not reporting a busy condition and that it does not have any data 15811c42de6dSgd78059 * pending in its output buffer. 15821c42de6dSgd78059 * This is important because if we have pending interrupts at attach 15831c42de6dSgd78059 * time Solaris will hang due to bugs in ddi_get_iblock_cookie. 15841c42de6dSgd78059 */ 15851c42de6dSgd78059 static void 15861c42de6dSgd78059 bscbus_hw_reset(struct bscbus_channel_state *csp) 15871c42de6dSgd78059 { 15881c42de6dSgd78059 int64_t timeout; 15891c42de6dSgd78059 uint8_t status; 15901c42de6dSgd78059 15911c42de6dSgd78059 if (csp->map_count == 0) { 15921c42de6dSgd78059 /* No-one using this instance - no need to reset hardware */ 15931c42de6dSgd78059 return; 15941c42de6dSgd78059 } 15951c42de6dSgd78059 15961c42de6dSgd78059 bscbus_trace(csp, 'R', "bscbus_hw_reset", 15971c42de6dSgd78059 "resetting channel %d", csp->chno); 15981c42de6dSgd78059 15991c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 16001c42de6dSgd78059 if (status & H8_STR_BUSY) { 16011c42de6dSgd78059 /* 16021c42de6dSgd78059 * Give the h8 time to complete a reply. 16031c42de6dSgd78059 * In practice we should never worry about this 16041c42de6dSgd78059 * because whenever we get here it will have been 16051c42de6dSgd78059 * long enough for the h8 to complete a reply 16061c42de6dSgd78059 */ 16071c42de6dSgd78059 bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0); 16081c42de6dSgd78059 bscbus_trace(csp, 'R', "bscbus_hw_reset", 16091c42de6dSgd78059 "h8 reporting status (%x) busy - waiting", status); 16101c42de6dSgd78059 if (ddi_in_panic()) { 16111c42de6dSgd78059 drv_usecwait(BSCBUS_HWRESET_POLL/1000); 16121c42de6dSgd78059 } else { 16131c42de6dSgd78059 delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000)); 16141c42de6dSgd78059 } 16151c42de6dSgd78059 } 16161c42de6dSgd78059 /* Reply should be completed by now. Try to clear busy status */ 16171c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 16181c42de6dSgd78059 if (status & (H8_STR_BUSY | H8_STR_OBF)) { 16191c42de6dSgd78059 bscbus_trace(csp, 'R', "bscbus_hw_reset", 16201c42de6dSgd78059 "clearing busy status for channel %d", csp->chno); 16211c42de6dSgd78059 16221c42de6dSgd78059 for (timeout = BSCBUS_HWRESET_TIMEOUT; 16231c42de6dSgd78059 (timeout > 0); 16241c42de6dSgd78059 timeout -= BSCBUS_HWRESET_POLL) { 16251c42de6dSgd78059 if (status & H8_STR_OBF) { 16261c42de6dSgd78059 (void) bscbus_get_reg(csp, H8_ODR); 16271c42de6dSgd78059 if (!(status & H8_STR_BUSY)) { 16281c42de6dSgd78059 /* We are done */ 16291c42de6dSgd78059 break; 16301c42de6dSgd78059 } 16311c42de6dSgd78059 } 16321c42de6dSgd78059 if (ddi_in_panic()) { 16331c42de6dSgd78059 drv_usecwait(BSCBUS_HWRESET_POLL/1000); 16341c42de6dSgd78059 } else { 16351c42de6dSgd78059 delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000)); 16361c42de6dSgd78059 } 16371c42de6dSgd78059 status = bscbus_get_reg(csp, H8_STR); 16381c42de6dSgd78059 } 16391c42de6dSgd78059 if (timeout <= 0) { 16401c42de6dSgd78059 cmn_err(CE_WARN, "bscbus_hw_reset: timed out " 16411c42de6dSgd78059 "clearing busy status"); 16421c42de6dSgd78059 } 16431c42de6dSgd78059 } 16441c42de6dSgd78059 /* 16451c42de6dSgd78059 * We read ODR just in case there is a pending interrupt with 16461c42de6dSgd78059 * no data. This is potentially dangerous because we could get 16471c42de6dSgd78059 * out of sync due to race conditions BUT at this point the 16481c42de6dSgd78059 * channel should be idle so it is safe. 16491c42de6dSgd78059 */ 16501c42de6dSgd78059 (void) bscbus_get_reg(csp, H8_ODR); 16511c42de6dSgd78059 } 16521c42de6dSgd78059 16531c42de6dSgd78059 /* 16541c42de6dSgd78059 * Higher-level setup & teardown 16551c42de6dSgd78059 */ 16561c42de6dSgd78059 16571c42de6dSgd78059 static void 16581c42de6dSgd78059 bscbus_offline(struct bscbus_state *ssp) 16591c42de6dSgd78059 { 16601c42de6dSgd78059 if (ssp->h8_handle != NULL) 16611c42de6dSgd78059 ddi_regs_map_free(&ssp->h8_handle); 16621c42de6dSgd78059 ssp->h8_handle = NULL; 16631c42de6dSgd78059 ssp->h8_regs = NULL; 16641c42de6dSgd78059 } 16651c42de6dSgd78059 16661c42de6dSgd78059 static int 16671c42de6dSgd78059 bscbus_online(struct bscbus_state *ssp) 16681c42de6dSgd78059 { 16691c42de6dSgd78059 ddi_acc_handle_t h; 16701c42de6dSgd78059 caddr_t p; 16711c42de6dSgd78059 int nregs; 16721c42de6dSgd78059 int err; 16731c42de6dSgd78059 16741c42de6dSgd78059 ssp->h8_handle = NULL; 16751c42de6dSgd78059 ssp->h8_regs = (void *)NULL; 16761c42de6dSgd78059 ssp->per_channel_regs = B_FALSE; 16771c42de6dSgd78059 16781c42de6dSgd78059 if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS) 16791c42de6dSgd78059 nregs = 0; 16801c42de6dSgd78059 16811c42de6dSgd78059 switch (nregs) { 16821c42de6dSgd78059 case 1: 16831c42de6dSgd78059 /* 16841c42de6dSgd78059 * regset 0 represents the H8 interface registers 16851c42de6dSgd78059 */ 16861c42de6dSgd78059 err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0, 16871c42de6dSgd78059 bscbus_dev_acc_attr, &h); 16881c42de6dSgd78059 if (err != DDI_SUCCESS) 16891c42de6dSgd78059 return (EIO); 16901c42de6dSgd78059 16911c42de6dSgd78059 ssp->h8_handle = h; 16921c42de6dSgd78059 ssp->h8_regs = (void *)p; 16931c42de6dSgd78059 break; 16941c42de6dSgd78059 16951c42de6dSgd78059 case 0: 16961c42de6dSgd78059 /* 16971c42de6dSgd78059 * If no registers are defined, succeed vacuously; 16981c42de6dSgd78059 * commands will be accepted, but we fake the accesses. 16991c42de6dSgd78059 */ 17001c42de6dSgd78059 break; 17011c42de6dSgd78059 17021c42de6dSgd78059 default: 17031c42de6dSgd78059 /* 17041c42de6dSgd78059 * Remember that we are using the new register scheme. 17051c42de6dSgd78059 * reg set 0 is chan 0 17061c42de6dSgd78059 * reg set 1 is chan 1 ... 17071c42de6dSgd78059 * Interrupts are specified in that order but later 17081c42de6dSgd78059 * channels may not have interrupts. 17091c42de6dSgd78059 * We map the regs later on a per channel basis. 17101c42de6dSgd78059 */ 17111c42de6dSgd78059 ssp->per_channel_regs = B_TRUE; 17121c42de6dSgd78059 break; 17131c42de6dSgd78059 } 17141c42de6dSgd78059 return (0); 17151c42de6dSgd78059 } 17161c42de6dSgd78059 17171c42de6dSgd78059 static int 17181c42de6dSgd78059 bscbus_claim_channel(struct bscbus_channel_state *csp, boolean_t map_dog) 17191c42de6dSgd78059 { 17201c42de6dSgd78059 int err; 17211c42de6dSgd78059 17221c42de6dSgd78059 mutex_enter(csp->ssp->ch_mutex); 17231c42de6dSgd78059 csp->map_count++; 17241c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_claim_channel", 17251c42de6dSgd78059 "claim channel for channel %d, count %d", 17261c42de6dSgd78059 csp->chno, csp->map_count); 17271c42de6dSgd78059 17281c42de6dSgd78059 if (csp->map_count == 1) { 17291c42de6dSgd78059 /* No-one is using this channel - initialise it */ 17301c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_claim_channel", 17311c42de6dSgd78059 "initialise channel %d, count %d", 17321c42de6dSgd78059 csp->chno, csp->map_count); 17331c42de6dSgd78059 17341c42de6dSgd78059 mutex_init(csp->dog_mutex, NULL, MUTEX_DRIVER, 17351c42de6dSgd78059 (void *)(uintptr_t)__ipltospl(SPL7 - 1)); 17361c42de6dSgd78059 csp->map_dog = map_dog; 17371c42de6dSgd78059 csp->interrupt_failed = B_FALSE; 17381c42de6dSgd78059 csp->cmdstate = BSCBUS_CMDSTATE_IDLE; 17391c42de6dSgd78059 csp->pat_retry_count = 0; 17401c42de6dSgd78059 csp->pat_fail_count = 0; 17411c42de6dSgd78059 17421c42de6dSgd78059 /* Map appropriate register set for this channel */ 17431c42de6dSgd78059 if (csp->ssp->per_channel_regs == B_TRUE) { 17441c42de6dSgd78059 ddi_acc_handle_t h; 17451c42de6dSgd78059 caddr_t p; 17461c42de6dSgd78059 17471c42de6dSgd78059 err = ddi_regs_map_setup(csp->ssp->dip, csp->chno, 17481c42de6dSgd78059 &p, 0, 0, bscbus_dev_acc_attr, &h); 17491c42de6dSgd78059 17501c42de6dSgd78059 if (err != DDI_SUCCESS) { 17511c42de6dSgd78059 goto failed1; 17521c42de6dSgd78059 } 17531c42de6dSgd78059 17541c42de6dSgd78059 csp->ch_handle = h; 17551c42de6dSgd78059 csp->ch_regs = (void *)p; 17561c42de6dSgd78059 17571c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_claim_channel", 17581c42de6dSgd78059 "mapped chno=%d ch_handle=%d ch_regs=%p", 17591c42de6dSgd78059 csp->chno, h, p); 17601c42de6dSgd78059 } else { 17611c42de6dSgd78059 /* 17621c42de6dSgd78059 * if using the old reg property scheme use the 17631c42de6dSgd78059 * common mapping. 17641c42de6dSgd78059 */ 17651c42de6dSgd78059 csp->ch_handle = csp->ssp->h8_handle; 17661c42de6dSgd78059 csp->ch_regs = 17671c42de6dSgd78059 csp->ssp->h8_regs + 17681c42de6dSgd78059 BSCBUS_CHANNEL_TO_OFFSET(csp->chno); 17691c42de6dSgd78059 } 17701c42de6dSgd78059 17711c42de6dSgd78059 /* Ensure no interrupts pending prior to getting iblk cookie */ 17721c42de6dSgd78059 bscbus_hw_reset(csp); 17731c42de6dSgd78059 17741c42de6dSgd78059 if (csp->map_dog == 1) { 17751c42de6dSgd78059 /* 17761c42de6dSgd78059 * we don't want lo_mutex to be initialised 17771c42de6dSgd78059 * with an iblock cookie if we are the wdog, 17781c42de6dSgd78059 * because we don't use interrupts. 17791c42de6dSgd78059 */ 17801c42de6dSgd78059 mutex_init(csp->lo_mutex, NULL, 17811c42de6dSgd78059 MUTEX_DRIVER, NULL); 17821c42de6dSgd78059 cv_init(csp->lo_cv, NULL, 17831c42de6dSgd78059 CV_DRIVER, NULL); 17841c42de6dSgd78059 csp->unclaimed_count = 0; 17851c42de6dSgd78059 } else { 17861c42de6dSgd78059 int ninterrupts; 17871c42de6dSgd78059 17881c42de6dSgd78059 /* 17891c42de6dSgd78059 * check that there is an interrupt for this 17901c42de6dSgd78059 * this channel. If we fail to setup interrupts we 17911c42de6dSgd78059 * must unmap the registers and fail. 17921c42de6dSgd78059 */ 17931c42de6dSgd78059 err = ddi_dev_nintrs(csp->ssp->dip, &ninterrupts); 17941c42de6dSgd78059 17951c42de6dSgd78059 if (err != DDI_SUCCESS) { 17961c42de6dSgd78059 ninterrupts = 0; 17971c42de6dSgd78059 } 17981c42de6dSgd78059 17991c42de6dSgd78059 if (ninterrupts <= csp->chno) { 18001c42de6dSgd78059 cmn_err(CE_WARN, 18011c42de6dSgd78059 "no interrupt available for " 18021c42de6dSgd78059 "bscbus channel %d", csp->chno); 18031c42de6dSgd78059 goto failed2; 18041c42de6dSgd78059 } 18051c42de6dSgd78059 18061c42de6dSgd78059 if (ddi_intr_hilevel(csp->ssp->dip, csp->chno) != 0) { 18071c42de6dSgd78059 cmn_err(CE_WARN, 18081c42de6dSgd78059 "bscbus interrupts are high " 18091c42de6dSgd78059 "level - channel not usable."); 18101c42de6dSgd78059 goto failed2; 18111c42de6dSgd78059 } else { 18121c42de6dSgd78059 err = ddi_get_iblock_cookie(csp->ssp->dip, 18131c42de6dSgd78059 csp->chno, &csp->lo_iblk); 18141c42de6dSgd78059 if (err != DDI_SUCCESS) { 18151c42de6dSgd78059 goto failed2; 18161c42de6dSgd78059 } 18171c42de6dSgd78059 18181c42de6dSgd78059 mutex_init(csp->lo_mutex, NULL, 18191c42de6dSgd78059 MUTEX_DRIVER, csp->lo_iblk); 18201c42de6dSgd78059 cv_init(csp->lo_cv, NULL, 18211c42de6dSgd78059 CV_DRIVER, NULL); 18221c42de6dSgd78059 csp->unclaimed_count = 0; 18231c42de6dSgd78059 18241c42de6dSgd78059 err = ddi_add_intr(csp->ssp->dip, csp->chno, 18251c42de6dSgd78059 &csp->lo_iblk, NULL, 18261c42de6dSgd78059 bscbus_hwintr, (caddr_t)csp); 18271c42de6dSgd78059 if (err != DDI_SUCCESS) { 18281c42de6dSgd78059 cv_destroy(csp->lo_cv); 18291c42de6dSgd78059 mutex_destroy(csp->lo_mutex); 18301c42de6dSgd78059 goto failed2; 18311c42de6dSgd78059 } 18321c42de6dSgd78059 } 18331c42de6dSgd78059 } 18341c42de6dSgd78059 /* 18351c42de6dSgd78059 * The channel is now live and may 18361c42de6dSgd78059 * receive interrupts 18371c42de6dSgd78059 */ 18381c42de6dSgd78059 } else if (csp->map_dog != map_dog) { 18391c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_claim_channel", 18401c42de6dSgd78059 "request conflicts with previous mapping. old %x, new %x.", 18411c42de6dSgd78059 csp->map_dog, map_dog); 18421c42de6dSgd78059 goto failed1; 18431c42de6dSgd78059 } 18441c42de6dSgd78059 mutex_exit(csp->ssp->ch_mutex); 18451c42de6dSgd78059 return (1); 18461c42de6dSgd78059 18471c42de6dSgd78059 failed2: 18481c42de6dSgd78059 /* unmap regs for failed channel */ 18491c42de6dSgd78059 if (csp->ssp->per_channel_regs == B_TRUE) { 18501c42de6dSgd78059 ddi_regs_map_free(&csp->ch_handle); 18511c42de6dSgd78059 } 18521c42de6dSgd78059 csp->ch_handle = NULL; 18531c42de6dSgd78059 csp->ch_regs = (void *)NULL; 18541c42de6dSgd78059 failed1: 18551c42de6dSgd78059 csp->map_count--; 18561c42de6dSgd78059 mutex_exit(csp->ssp->ch_mutex); 18571c42de6dSgd78059 return (0); 18581c42de6dSgd78059 } 18591c42de6dSgd78059 18601c42de6dSgd78059 static void 18611c42de6dSgd78059 bscbus_release_channel(struct bscbus_channel_state *csp) 18621c42de6dSgd78059 { 18631c42de6dSgd78059 mutex_enter(csp->ssp->ch_mutex); 18641c42de6dSgd78059 if (csp->map_count == 1) { 18651c42de6dSgd78059 /* No-one is now using this channel - shutdown channel */ 18661c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_release_channel", 18671c42de6dSgd78059 "shutdown channel %d, count %d", 18681c42de6dSgd78059 csp->chno, csp->map_count); 18691c42de6dSgd78059 18701c42de6dSgd78059 if (csp->map_dog == 0) { 18711c42de6dSgd78059 ASSERT(!ddi_intr_hilevel(csp->ssp->dip, csp->chno)); 1872*19397407SSherry Moore ddi_remove_intr(csp->ssp->dip, csp->chno, csp->lo_iblk); 18731c42de6dSgd78059 } 18741c42de6dSgd78059 cv_destroy(csp->lo_cv); 18751c42de6dSgd78059 mutex_destroy(csp->lo_mutex); 18761c42de6dSgd78059 mutex_destroy(csp->dog_mutex); 18771c42de6dSgd78059 bscbus_hw_reset(csp); 18781c42de6dSgd78059 18791c42de6dSgd78059 /* unmap registers if using the new register scheme */ 18801c42de6dSgd78059 if (csp->ssp->per_channel_regs == B_TRUE) { 18811c42de6dSgd78059 ddi_regs_map_free(&csp->ch_handle); 18821c42de6dSgd78059 } 18831c42de6dSgd78059 csp->ch_handle = NULL; 18841c42de6dSgd78059 csp->ch_regs = (void *)NULL; 18851c42de6dSgd78059 } 18861c42de6dSgd78059 csp->map_count--; 18871c42de6dSgd78059 bscbus_trace(csp, 'C', "bscbus_release_channel", 18881c42de6dSgd78059 "release channel %d, count %d", 18891c42de6dSgd78059 csp->chno, csp->map_count); 18901c42de6dSgd78059 mutex_exit(csp->ssp->ch_mutex); 18911c42de6dSgd78059 } 18921c42de6dSgd78059 18931c42de6dSgd78059 18941c42de6dSgd78059 /* 18951c42de6dSgd78059 * Nexus routines 18961c42de6dSgd78059 */ 18971c42de6dSgd78059 18981c42de6dSgd78059 #if defined(NDI_ACC_HDL_V2) 18991c42de6dSgd78059 19001c42de6dSgd78059 static const ndi_acc_fns_t bscbus_vreg_acc_fns = { 19011c42de6dSgd78059 NDI_ACC_FNS_CURRENT, 19021c42de6dSgd78059 NDI_ACC_FNS_V1, 19031c42de6dSgd78059 19041c42de6dSgd78059 bscbus_vreg_get8, 19051c42de6dSgd78059 bscbus_vreg_put8, 19061c42de6dSgd78059 bscbus_vreg_rep_get8, 19071c42de6dSgd78059 bscbus_vreg_rep_put8, 19081c42de6dSgd78059 19091c42de6dSgd78059 bscbus_no_get16, 19101c42de6dSgd78059 bscbus_no_put16, 19111c42de6dSgd78059 bscbus_no_rep_get16, 19121c42de6dSgd78059 bscbus_no_rep_put16, 19131c42de6dSgd78059 19141c42de6dSgd78059 bscbus_meta_get32, 19151c42de6dSgd78059 bscbus_meta_put32, 19161c42de6dSgd78059 bscbus_meta_rep_get32, 19171c42de6dSgd78059 bscbus_meta_rep_put32, 19181c42de6dSgd78059 19191c42de6dSgd78059 bscbus_no_get64, 19201c42de6dSgd78059 bscbus_no_put64, 19211c42de6dSgd78059 bscbus_no_rep_get64, 19221c42de6dSgd78059 bscbus_no_rep_put64, 19231c42de6dSgd78059 19241c42de6dSgd78059 bscbus_acc_fault_check 19251c42de6dSgd78059 }; 19261c42de6dSgd78059 19271c42de6dSgd78059 static const ndi_acc_fns_t bscbus_pat_acc_fns = { 19281c42de6dSgd78059 NDI_ACC_FNS_CURRENT, 19291c42de6dSgd78059 NDI_ACC_FNS_V1, 19301c42de6dSgd78059 19311c42de6dSgd78059 bscbus_pat_get8, 19321c42de6dSgd78059 bscbus_pat_put8, 19331c42de6dSgd78059 bscbus_pat_rep_get8, 19341c42de6dSgd78059 bscbus_pat_rep_put8, 19351c42de6dSgd78059 19361c42de6dSgd78059 bscbus_no_get16, 19371c42de6dSgd78059 bscbus_no_put16, 19381c42de6dSgd78059 bscbus_no_rep_get16, 19391c42de6dSgd78059 bscbus_no_rep_put16, 19401c42de6dSgd78059 19411c42de6dSgd78059 bscbus_meta_get32, 19421c42de6dSgd78059 bscbus_meta_put32, 19431c42de6dSgd78059 bscbus_meta_rep_get32, 19441c42de6dSgd78059 bscbus_meta_rep_put32, 19451c42de6dSgd78059 19461c42de6dSgd78059 bscbus_no_get64, 19471c42de6dSgd78059 bscbus_no_put64, 19481c42de6dSgd78059 bscbus_no_rep_get64, 19491c42de6dSgd78059 bscbus_no_rep_put64, 19501c42de6dSgd78059 19511c42de6dSgd78059 bscbus_acc_fault_check 19521c42de6dSgd78059 }; 19531c42de6dSgd78059 19541c42de6dSgd78059 static const ndi_acc_fns_t bscbus_event_acc_fns = { 19551c42de6dSgd78059 NDI_ACC_FNS_CURRENT, 19561c42de6dSgd78059 NDI_ACC_FNS_V1, 19571c42de6dSgd78059 19581c42de6dSgd78059 bscbus_no_get8, 19591c42de6dSgd78059 bscbus_no_put8, 19601c42de6dSgd78059 bscbus_no_rep_get8, 19611c42de6dSgd78059 bscbus_no_rep_put8, 19621c42de6dSgd78059 19631c42de6dSgd78059 bscbus_event_get16, 19641c42de6dSgd78059 bscbus_event_put16, 19651c42de6dSgd78059 bscbus_event_rep_get16, 19661c42de6dSgd78059 bscbus_event_rep_put16, 19671c42de6dSgd78059 19681c42de6dSgd78059 bscbus_meta_get32, 19691c42de6dSgd78059 bscbus_meta_put32, 19701c42de6dSgd78059 bscbus_meta_rep_get32, 19711c42de6dSgd78059 bscbus_meta_rep_put32, 19721c42de6dSgd78059 19731c42de6dSgd78059 bscbus_no_get64, 19741c42de6dSgd78059 bscbus_no_put64, 19751c42de6dSgd78059 bscbus_no_rep_get64, 19761c42de6dSgd78059 bscbus_no_rep_put64, 19771c42de6dSgd78059 19781c42de6dSgd78059 bscbus_acc_fault_check 19791c42de6dSgd78059 }; 19801c42de6dSgd78059 19811c42de6dSgd78059 static int 19821c42de6dSgd78059 bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op, 19831c42de6dSgd78059 int space, caddr_t vaddr, off_t len, 19841c42de6dSgd78059 ndi_acc_handle_t *hdlp, caddr_t *addrp) 19851c42de6dSgd78059 { 19861c42de6dSgd78059 switch (op) { 19871c42de6dSgd78059 default: 19881c42de6dSgd78059 return (DDI_ME_UNIMPLEMENTED); 19891c42de6dSgd78059 19901c42de6dSgd78059 case DDI_MO_MAP_LOCKED: 19911c42de6dSgd78059 if (bscbus_claim_channel(csp, 19921c42de6dSgd78059 (space == LOMBUS_PAT_SPACE)) == 0) { 19931c42de6dSgd78059 return (DDI_ME_GENERIC); 19941c42de6dSgd78059 } 19951c42de6dSgd78059 19961c42de6dSgd78059 switch (space) { 19971c42de6dSgd78059 default: 19981c42de6dSgd78059 return (DDI_ME_REGSPEC_RANGE); 19991c42de6dSgd78059 20001c42de6dSgd78059 case LOMBUS_VREG_SPACE: 20011c42de6dSgd78059 ndi_set_acc_fns(hdlp, &bscbus_vreg_acc_fns); 20021c42de6dSgd78059 break; 20031c42de6dSgd78059 20041c42de6dSgd78059 case LOMBUS_PAT_SPACE: 20051c42de6dSgd78059 ndi_set_acc_fns(hdlp, &bscbus_pat_acc_fns); 20061c42de6dSgd78059 break; 20071c42de6dSgd78059 20081c42de6dSgd78059 case LOMBUS_EVENT_SPACE: 20091c42de6dSgd78059 ndi_set_acc_fns(hdlp, &bscbus_event_acc_fns); 20101c42de6dSgd78059 break; 20111c42de6dSgd78059 } 20121c42de6dSgd78059 hdlp->ah_addr = *addrp = vaddr; 20131c42de6dSgd78059 hdlp->ah_len = len; 20141c42de6dSgd78059 hdlp->ah_bus_private = csp; 20151c42de6dSgd78059 return (DDI_SUCCESS); 20161c42de6dSgd78059 20171c42de6dSgd78059 case DDI_MO_UNMAP: 20181c42de6dSgd78059 *addrp = NULL; 20191c42de6dSgd78059 hdlp->ah_bus_private = NULL; 20201c42de6dSgd78059 bscbus_release_channel(csp); 20211c42de6dSgd78059 return (DDI_SUCCESS); 20221c42de6dSgd78059 } 20231c42de6dSgd78059 } 20241c42de6dSgd78059 20251c42de6dSgd78059 #else 20261c42de6dSgd78059 20271c42de6dSgd78059 static int 20281c42de6dSgd78059 bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op, 20291c42de6dSgd78059 int space, caddr_t vaddr, off_t len, 20301c42de6dSgd78059 ddi_acc_hdl_t *hdlp, caddr_t *addrp) 20311c42de6dSgd78059 { 20321c42de6dSgd78059 ddi_acc_impl_t *aip = hdlp->ah_platform_private; 20331c42de6dSgd78059 20341c42de6dSgd78059 switch (op) { 20351c42de6dSgd78059 default: 20361c42de6dSgd78059 return (DDI_ME_UNIMPLEMENTED); 20371c42de6dSgd78059 20381c42de6dSgd78059 case DDI_MO_MAP_LOCKED: 20391c42de6dSgd78059 if (bscbus_claim_channel(csp, 20401c42de6dSgd78059 (space == LOMBUS_PAT_SPACE)) == 0) { 20411c42de6dSgd78059 return (DDI_ME_GENERIC); 20421c42de6dSgd78059 } 20431c42de6dSgd78059 20441c42de6dSgd78059 switch (space) { 20451c42de6dSgd78059 default: 20461c42de6dSgd78059 return (DDI_ME_REGSPEC_RANGE); 20471c42de6dSgd78059 20481c42de6dSgd78059 case LOMBUS_VREG_SPACE: 20491c42de6dSgd78059 aip->ahi_get8 = bscbus_vreg_get8; 20501c42de6dSgd78059 aip->ahi_put8 = bscbus_vreg_put8; 20511c42de6dSgd78059 aip->ahi_rep_get8 = bscbus_vreg_rep_get8; 20521c42de6dSgd78059 aip->ahi_rep_put8 = bscbus_vreg_rep_put8; 20531c42de6dSgd78059 20541c42de6dSgd78059 aip->ahi_get16 = bscbus_no_get16; 20551c42de6dSgd78059 aip->ahi_put16 = bscbus_no_put16; 20561c42de6dSgd78059 aip->ahi_rep_get16 = bscbus_no_rep_get16; 20571c42de6dSgd78059 aip->ahi_rep_put16 = bscbus_no_rep_put16; 20581c42de6dSgd78059 20591c42de6dSgd78059 aip->ahi_get32 = bscbus_meta_get32; 20601c42de6dSgd78059 aip->ahi_put32 = bscbus_meta_put32; 20611c42de6dSgd78059 aip->ahi_rep_get32 = bscbus_meta_rep_get32; 20621c42de6dSgd78059 aip->ahi_rep_put32 = bscbus_meta_rep_put32; 20631c42de6dSgd78059 20641c42de6dSgd78059 aip->ahi_get64 = bscbus_no_get64; 20651c42de6dSgd78059 aip->ahi_put64 = bscbus_no_put64; 20661c42de6dSgd78059 aip->ahi_rep_get64 = bscbus_no_rep_get64; 20671c42de6dSgd78059 aip->ahi_rep_put64 = bscbus_no_rep_put64; 20681c42de6dSgd78059 20691c42de6dSgd78059 aip->ahi_fault_check = bscbus_acc_fault_check; 20701c42de6dSgd78059 break; 20711c42de6dSgd78059 20721c42de6dSgd78059 case LOMBUS_PAT_SPACE: 20731c42de6dSgd78059 aip->ahi_get8 = bscbus_pat_get8; 20741c42de6dSgd78059 aip->ahi_put8 = bscbus_pat_put8; 20751c42de6dSgd78059 aip->ahi_rep_get8 = bscbus_pat_rep_get8; 20761c42de6dSgd78059 aip->ahi_rep_put8 = bscbus_pat_rep_put8; 20771c42de6dSgd78059 20781c42de6dSgd78059 aip->ahi_get16 = bscbus_no_get16; 20791c42de6dSgd78059 aip->ahi_put16 = bscbus_no_put16; 20801c42de6dSgd78059 aip->ahi_rep_get16 = bscbus_no_rep_get16; 20811c42de6dSgd78059 aip->ahi_rep_put16 = bscbus_no_rep_put16; 20821c42de6dSgd78059 20831c42de6dSgd78059 aip->ahi_get32 = bscbus_meta_get32; 20841c42de6dSgd78059 aip->ahi_put32 = bscbus_meta_put32; 20851c42de6dSgd78059 aip->ahi_rep_get32 = bscbus_meta_rep_get32; 20861c42de6dSgd78059 aip->ahi_rep_put32 = bscbus_meta_rep_put32; 20871c42de6dSgd78059 20881c42de6dSgd78059 aip->ahi_get64 = bscbus_no_get64; 20891c42de6dSgd78059 aip->ahi_put64 = bscbus_no_put64; 20901c42de6dSgd78059 aip->ahi_rep_get64 = bscbus_no_rep_get64; 20911c42de6dSgd78059 aip->ahi_rep_put64 = bscbus_no_rep_put64; 20921c42de6dSgd78059 20931c42de6dSgd78059 aip->ahi_fault_check = bscbus_acc_fault_check; 20941c42de6dSgd78059 break; 20951c42de6dSgd78059 20961c42de6dSgd78059 case LOMBUS_EVENT_SPACE: 20971c42de6dSgd78059 aip->ahi_get8 = bscbus_no_get8; 20981c42de6dSgd78059 aip->ahi_put8 = bscbus_no_put8; 20991c42de6dSgd78059 aip->ahi_rep_get8 = bscbus_no_rep_get8; 21001c42de6dSgd78059 aip->ahi_rep_put8 = bscbus_no_rep_put8; 21011c42de6dSgd78059 21021c42de6dSgd78059 aip->ahi_get16 = bscbus_event_get16; 21031c42de6dSgd78059 aip->ahi_put16 = bscbus_event_put16; 21041c42de6dSgd78059 aip->ahi_rep_get16 = bscbus_event_rep_get16; 21051c42de6dSgd78059 aip->ahi_rep_put16 = bscbus_event_rep_put16; 21061c42de6dSgd78059 21071c42de6dSgd78059 aip->ahi_get32 = bscbus_meta_get32; 21081c42de6dSgd78059 aip->ahi_put32 = bscbus_meta_put32; 21091c42de6dSgd78059 aip->ahi_rep_get32 = bscbus_meta_rep_get32; 21101c42de6dSgd78059 aip->ahi_rep_put32 = bscbus_meta_rep_put32; 21111c42de6dSgd78059 21121c42de6dSgd78059 aip->ahi_get64 = bscbus_no_get64; 21131c42de6dSgd78059 aip->ahi_put64 = bscbus_no_put64; 21141c42de6dSgd78059 aip->ahi_rep_get64 = bscbus_no_rep_get64; 21151c42de6dSgd78059 aip->ahi_rep_put64 = bscbus_no_rep_put64; 21161c42de6dSgd78059 21171c42de6dSgd78059 aip->ahi_fault_check = bscbus_acc_fault_check; 21181c42de6dSgd78059 break; 21191c42de6dSgd78059 } 21201c42de6dSgd78059 hdlp->ah_addr = *addrp = vaddr; 21211c42de6dSgd78059 hdlp->ah_len = len; 21221c42de6dSgd78059 hdlp->ah_bus_private = csp; 21231c42de6dSgd78059 return (DDI_SUCCESS); 21241c42de6dSgd78059 21251c42de6dSgd78059 case DDI_MO_UNMAP: 21261c42de6dSgd78059 *addrp = NULL; 21271c42de6dSgd78059 hdlp->ah_bus_private = NULL; 21281c42de6dSgd78059 bscbus_release_channel(csp); 21291c42de6dSgd78059 return (DDI_SUCCESS); 21301c42de6dSgd78059 } 21311c42de6dSgd78059 } 21321c42de6dSgd78059 21331c42de6dSgd78059 #endif /* NDI_ACC_HDL_V2 */ 21341c42de6dSgd78059 21351c42de6dSgd78059 static int 21361c42de6dSgd78059 bscbus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 21371c42de6dSgd78059 off_t off, off_t len, caddr_t *addrp) 21381c42de6dSgd78059 { 21391c42de6dSgd78059 struct bscbus_child_info *lcip; 21401c42de6dSgd78059 struct bscbus_state *ssp; 21411c42de6dSgd78059 lombus_regspec_t *rsp; 21421c42de6dSgd78059 21431c42de6dSgd78059 if ((ssp = bscbus_getstate(dip, -1, "bscbus_map")) == NULL) 21441c42de6dSgd78059 return (DDI_FAILURE); /* this "can't happen" */ 21451c42de6dSgd78059 21461c42de6dSgd78059 /* 21471c42de6dSgd78059 * Validate mapping request ... 21481c42de6dSgd78059 */ 21491c42de6dSgd78059 21501c42de6dSgd78059 if (mp->map_flags != DDI_MF_KERNEL_MAPPING) 21511c42de6dSgd78059 return (DDI_ME_UNSUPPORTED); 21521c42de6dSgd78059 if (mp->map_handlep == NULL) 21531c42de6dSgd78059 return (DDI_ME_UNSUPPORTED); 21541c42de6dSgd78059 if (mp->map_type != DDI_MT_RNUMBER) 21551c42de6dSgd78059 return (DDI_ME_UNIMPLEMENTED); 21561c42de6dSgd78059 if ((lcip = ddi_get_parent_data(rdip)) == NULL) 21571c42de6dSgd78059 return (DDI_ME_INVAL); 21581c42de6dSgd78059 if ((rsp = lcip->rsp) == NULL) 21591c42de6dSgd78059 return (DDI_ME_INVAL); 21601c42de6dSgd78059 if (mp->map_obj.rnumber >= lcip->nregs) 21611c42de6dSgd78059 return (DDI_ME_RNUMBER_RANGE); 21621c42de6dSgd78059 rsp += mp->map_obj.rnumber; 21631c42de6dSgd78059 if (off < 0 || off >= rsp->lombus_size) 21641c42de6dSgd78059 return (DDI_ME_INVAL); 21651c42de6dSgd78059 if (len == 0) 21661c42de6dSgd78059 len = rsp->lombus_size-off; 21671c42de6dSgd78059 if (len < 0) 21681c42de6dSgd78059 return (DDI_ME_INVAL); 21691c42de6dSgd78059 if (off+len < 0 || off+len > rsp->lombus_size) 21701c42de6dSgd78059 return (DDI_ME_INVAL); 21711c42de6dSgd78059 21721c42de6dSgd78059 return (bscbus_map_handle( 2173*19397407SSherry Moore &ssp->channel[LOMBUS_SPACE_TO_CHANNEL(rsp->lombus_space)], 2174*19397407SSherry Moore mp->map_op, LOMBUS_SPACE_TO_REGSET(rsp->lombus_space), 2175*19397407SSherry Moore VREG_TO_ADDR(rsp->lombus_base+off), len, mp->map_handlep, addrp)); 21761c42de6dSgd78059 } 21771c42de6dSgd78059 21781c42de6dSgd78059 21791c42de6dSgd78059 static int 21801c42de6dSgd78059 bscbus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 21811c42de6dSgd78059 void *arg, void *result) 21821c42de6dSgd78059 { 21831c42de6dSgd78059 struct bscbus_child_info *lcip; 21841c42de6dSgd78059 lombus_regspec_t *rsp; 21851c42de6dSgd78059 dev_info_t *cdip; 21861c42de6dSgd78059 char addr[32]; 21871c42de6dSgd78059 uint_t nregs; 21881c42de6dSgd78059 uint_t rnum; 21891c42de6dSgd78059 int *regs; 21901c42de6dSgd78059 int limit; 21911c42de6dSgd78059 int err; 21921c42de6dSgd78059 int i; 21931c42de6dSgd78059 21941c42de6dSgd78059 if (bscbus_getstate(dip, -1, "bscbus_ctlops") == NULL) 21951c42de6dSgd78059 return (DDI_FAILURE); /* this "can't happen" */ 21961c42de6dSgd78059 21971c42de6dSgd78059 switch (op) { 21981c42de6dSgd78059 default: 21991c42de6dSgd78059 break; 22001c42de6dSgd78059 22011c42de6dSgd78059 case DDI_CTLOPS_INITCHILD: 22021c42de6dSgd78059 /* 22031c42de6dSgd78059 * First, look up and validate the "reg" property. 22041c42de6dSgd78059 * 22051c42de6dSgd78059 * It must be a non-empty integer array containing a set 22061c42de6dSgd78059 * of triples. Once we've verified that, we can treat it 22071c42de6dSgd78059 * as an array of type lombus_regspec_t[], which defines 22081c42de6dSgd78059 * the meaning of the elements of each triple: 22091c42de6dSgd78059 * + the first element of each triple must be a valid space 22101c42de6dSgd78059 * + the second and third elements (base, size) of each 22111c42de6dSgd78059 * triple must define a valid subrange of that space 22121c42de6dSgd78059 * If it passes all the tests, we save it away for future 22131c42de6dSgd78059 * reference in the child's parent-private-data field. 22141c42de6dSgd78059 */ 22151c42de6dSgd78059 cdip = arg; 22161c42de6dSgd78059 err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 22171c42de6dSgd78059 DDI_PROP_DONTPASS, "reg", ®s, &nregs); 22181c42de6dSgd78059 if (err != DDI_PROP_SUCCESS) 22191c42de6dSgd78059 return (DDI_FAILURE); 22201c42de6dSgd78059 22211c42de6dSgd78059 err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0); 22221c42de6dSgd78059 nregs /= LOMBUS_REGSPEC_SIZE; 22231c42de6dSgd78059 rsp = (lombus_regspec_t *)regs; 22241c42de6dSgd78059 for (i = 0; i < nregs && !err; ++i) { 22251c42de6dSgd78059 switch (LOMBUS_SPACE_TO_REGSET(rsp[i].lombus_space)) { 22261c42de6dSgd78059 default: 22271c42de6dSgd78059 limit = 0; 22281c42de6dSgd78059 err = 1; 22291c42de6dSgd78059 cmn_err(CE_WARN, 22301c42de6dSgd78059 "child(%p): unknown reg space %d", 22311c42de6dSgd78059 (void *)cdip, rsp[i].lombus_space); 22321c42de6dSgd78059 break; 22331c42de6dSgd78059 22341c42de6dSgd78059 case LOMBUS_VREG_SPACE: 22351c42de6dSgd78059 limit = LOMBUS_MAX_REG+1; 22361c42de6dSgd78059 break; 22371c42de6dSgd78059 22381c42de6dSgd78059 case LOMBUS_PAT_SPACE: 22391c42de6dSgd78059 limit = LOMBUS_PAT_REG+1; 22401c42de6dSgd78059 break; 22411c42de6dSgd78059 22421c42de6dSgd78059 case LOMBUS_EVENT_SPACE: 22431c42de6dSgd78059 limit = LOMBUS_EVENT_REG+1; 22441c42de6dSgd78059 break; 22451c42de6dSgd78059 } 22461c42de6dSgd78059 22471c42de6dSgd78059 err |= (rsp[i].lombus_base < 0); 22481c42de6dSgd78059 err |= (rsp[i].lombus_base >= limit); 22491c42de6dSgd78059 22501c42de6dSgd78059 if (rsp[i].lombus_size == 0) 22511c42de6dSgd78059 rsp[i].lombus_size = limit-rsp[i].lombus_base; 22521c42de6dSgd78059 22531c42de6dSgd78059 err |= (rsp[i].lombus_size < 0); 22541c42de6dSgd78059 err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0); 22551c42de6dSgd78059 err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit); 22561c42de6dSgd78059 22571c42de6dSgd78059 err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit); 22581c42de6dSgd78059 22591c42de6dSgd78059 } 22601c42de6dSgd78059 22611c42de6dSgd78059 if (err) { 22621c42de6dSgd78059 ddi_prop_free(regs); 22631c42de6dSgd78059 return (DDI_FAILURE); 22641c42de6dSgd78059 } 22651c42de6dSgd78059 22661c42de6dSgd78059 lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP); 22671c42de6dSgd78059 lcip->nregs = nregs; 22681c42de6dSgd78059 lcip->rsp = rsp; 22691c42de6dSgd78059 ddi_set_parent_data(cdip, lcip); 22701c42de6dSgd78059 22711c42de6dSgd78059 (void) snprintf(addr, sizeof (addr), 22721c42de6dSgd78059 "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base); 22731c42de6dSgd78059 ddi_set_name_addr(cdip, addr); 22741c42de6dSgd78059 22751c42de6dSgd78059 return (DDI_SUCCESS); 22761c42de6dSgd78059 22771c42de6dSgd78059 case DDI_CTLOPS_UNINITCHILD: 22781c42de6dSgd78059 cdip = arg; 22791c42de6dSgd78059 ddi_set_name_addr(cdip, NULL); 22801c42de6dSgd78059 lcip = ddi_get_parent_data(cdip); 22811c42de6dSgd78059 ddi_set_parent_data(cdip, NULL); 22821c42de6dSgd78059 ddi_prop_free(lcip->rsp); 22831c42de6dSgd78059 kmem_free(lcip, sizeof (*lcip)); 22841c42de6dSgd78059 return (DDI_SUCCESS); 22851c42de6dSgd78059 22861c42de6dSgd78059 case DDI_CTLOPS_REPORTDEV: 22871c42de6dSgd78059 if (rdip == NULL) 22881c42de6dSgd78059 return (DDI_FAILURE); 22891c42de6dSgd78059 22901c42de6dSgd78059 cmn_err(CE_CONT, "?BSC device: %s@%s, %s#%d\n", 22911c42de6dSgd78059 ddi_node_name(rdip), ddi_get_name_addr(rdip), 22921c42de6dSgd78059 ddi_driver_name(dip), ddi_get_instance(dip)); 22931c42de6dSgd78059 22941c42de6dSgd78059 return (DDI_SUCCESS); 22951c42de6dSgd78059 22961c42de6dSgd78059 case DDI_CTLOPS_REGSIZE: 22971c42de6dSgd78059 if ((lcip = ddi_get_parent_data(rdip)) == NULL) 22981c42de6dSgd78059 return (DDI_FAILURE); 22991c42de6dSgd78059 if ((rnum = *(uint_t *)arg) >= lcip->nregs) 23001c42de6dSgd78059 return (DDI_FAILURE); 23011c42de6dSgd78059 *(off_t *)result = lcip->rsp[rnum].lombus_size; 23021c42de6dSgd78059 return (DDI_SUCCESS); 23031c42de6dSgd78059 23041c42de6dSgd78059 case DDI_CTLOPS_NREGS: 23051c42de6dSgd78059 if ((lcip = ddi_get_parent_data(rdip)) == NULL) 23061c42de6dSgd78059 return (DDI_FAILURE); 23071c42de6dSgd78059 *(int *)result = lcip->nregs; 23081c42de6dSgd78059 return (DDI_SUCCESS); 23091c42de6dSgd78059 } 23101c42de6dSgd78059 23111c42de6dSgd78059 return (ddi_ctlops(dip, rdip, op, arg, result)); 23121c42de6dSgd78059 } 23131c42de6dSgd78059 23141c42de6dSgd78059 23151c42de6dSgd78059 /* 23161c42de6dSgd78059 * This nexus does not support passing interrupts to leaf drivers, so 23171c42de6dSgd78059 * all the intrspec-related operations just fail as cleanly as possible. 23181c42de6dSgd78059 */ 23191c42de6dSgd78059 23201c42de6dSgd78059 /*ARGSUSED*/ 23211c42de6dSgd78059 static int 23221c42de6dSgd78059 bscbus_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 23231c42de6dSgd78059 ddi_intr_handle_impl_t *hdlp, void *result) 23241c42de6dSgd78059 { 23251c42de6dSgd78059 #if defined(__sparc) 23261c42de6dSgd78059 return (i_ddi_intr_ops(dip, rdip, op, hdlp, result)); 23271c42de6dSgd78059 #else 23281c42de6dSgd78059 _NOTE(ARGUNUSED(dip, rdip, op, hdlp, result)) 23291c42de6dSgd78059 return (DDI_FAILURE); 23301c42de6dSgd78059 #endif 23311c42de6dSgd78059 } 23321c42de6dSgd78059 23331c42de6dSgd78059 /* 23341c42de6dSgd78059 * Clean up on detach or failure of attach 23351c42de6dSgd78059 */ 23361c42de6dSgd78059 static int 23371c42de6dSgd78059 bscbus_unattach(struct bscbus_state *ssp, int instance) 23381c42de6dSgd78059 { 23391c42de6dSgd78059 int chno; 23401c42de6dSgd78059 23411c42de6dSgd78059 if (ssp != NULL) { 23421c42de6dSgd78059 for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) { 23431c42de6dSgd78059 ASSERT(ssp->channel[chno].map_count == 0); 23441c42de6dSgd78059 } 23451c42de6dSgd78059 bscbus_offline(ssp); 23461c42de6dSgd78059 ddi_set_driver_private(ssp->dip, NULL); 23471c42de6dSgd78059 mutex_destroy(ssp->ch_mutex); 23481c42de6dSgd78059 } 23491c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 23501c42de6dSgd78059 if (ssp->cmd_log_size != 0) { 23511c42de6dSgd78059 kmem_free(ssp->cmd_log, 23521c42de6dSgd78059 ssp->cmd_log_size * sizeof (bsc_cmd_log_t)); 23531c42de6dSgd78059 } 23541c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 23551c42de6dSgd78059 23561c42de6dSgd78059 23571c42de6dSgd78059 ddi_soft_state_free(bscbus_statep, instance); 23581c42de6dSgd78059 return (DDI_FAILURE); 23591c42de6dSgd78059 } 23601c42de6dSgd78059 23611c42de6dSgd78059 /* 23621c42de6dSgd78059 * Autoconfiguration routines 23631c42de6dSgd78059 */ 23641c42de6dSgd78059 23651c42de6dSgd78059 static int 23661c42de6dSgd78059 bscbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 23671c42de6dSgd78059 { 23681c42de6dSgd78059 struct bscbus_state *ssp = NULL; 23691c42de6dSgd78059 int chno; 23701c42de6dSgd78059 int instance; 23711c42de6dSgd78059 int err; 23721c42de6dSgd78059 23731c42de6dSgd78059 switch (cmd) { 23741c42de6dSgd78059 default: 23751c42de6dSgd78059 return (DDI_FAILURE); 23761c42de6dSgd78059 23771c42de6dSgd78059 case DDI_ATTACH: 23781c42de6dSgd78059 break; 23791c42de6dSgd78059 } 23801c42de6dSgd78059 23811c42de6dSgd78059 /* 23821c42de6dSgd78059 * Allocate the soft-state structure 23831c42de6dSgd78059 */ 23841c42de6dSgd78059 instance = ddi_get_instance(dip); 23851c42de6dSgd78059 if (ddi_soft_state_zalloc(bscbus_statep, instance) != DDI_SUCCESS) 23861c42de6dSgd78059 return (DDI_FAILURE); 23871c42de6dSgd78059 if ((ssp = bscbus_getstate(dip, instance, "bscbus_attach")) == NULL) 23881c42de6dSgd78059 return (bscbus_unattach(ssp, instance)); 23891c42de6dSgd78059 ddi_set_driver_private(dip, ssp); 23901c42de6dSgd78059 23911c42de6dSgd78059 /* 23921c42de6dSgd78059 * Initialise devinfo-related fields 23931c42de6dSgd78059 */ 23941c42de6dSgd78059 ssp->dip = dip; 23951c42de6dSgd78059 ssp->majornum = ddi_driver_major(dip); 23961c42de6dSgd78059 ssp->instance = instance; 23971c42de6dSgd78059 23981c42de6dSgd78059 /* 23991c42de6dSgd78059 * Set various options from .conf properties 24001c42de6dSgd78059 */ 24011c42de6dSgd78059 ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 24021c42de6dSgd78059 DDI_PROP_DONTPASS, "debug", 0); 24031c42de6dSgd78059 24041c42de6dSgd78059 mutex_init(ssp->ch_mutex, NULL, MUTEX_DRIVER, NULL); 24051c42de6dSgd78059 24061c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 24071c42de6dSgd78059 ssp->cmd_log_size = bscbus_cmd_log_size; 24081c42de6dSgd78059 if (ssp->cmd_log_size != 0) { 24091c42de6dSgd78059 ssp->cmd_log_idx = 0; 2410*19397407SSherry Moore ssp->cmd_log = kmem_zalloc(ssp->cmd_log_size * 2411*19397407SSherry Moore sizeof (bsc_cmd_log_t), KM_SLEEP); 24121c42de6dSgd78059 } 24131c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 24141c42de6dSgd78059 24151c42de6dSgd78059 /* 24161c42de6dSgd78059 * Online the hardware ... 24171c42de6dSgd78059 */ 24181c42de6dSgd78059 err = bscbus_online(ssp); 24191c42de6dSgd78059 if (err != 0) 24201c42de6dSgd78059 return (bscbus_unattach(ssp, instance)); 24211c42de6dSgd78059 24221c42de6dSgd78059 for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) { 24231c42de6dSgd78059 struct bscbus_channel_state *csp = &ssp->channel[chno]; 24241c42de6dSgd78059 24251c42de6dSgd78059 /* 24261c42de6dSgd78059 * Initialise state 24271c42de6dSgd78059 * The hardware/interrupts are setup at map time to 24281c42de6dSgd78059 * avoid claiming hardware that OBP is using 24291c42de6dSgd78059 */ 24301c42de6dSgd78059 csp->ssp = ssp; 24311c42de6dSgd78059 csp->chno = chno; 24321c42de6dSgd78059 csp->map_count = 0; 24331c42de6dSgd78059 csp->map_dog = B_FALSE; 24341c42de6dSgd78059 } 24351c42de6dSgd78059 24361c42de6dSgd78059 /* 24371c42de6dSgd78059 * All done, report success 24381c42de6dSgd78059 */ 24391c42de6dSgd78059 ddi_report_dev(dip); 24401c42de6dSgd78059 return (DDI_SUCCESS); 24411c42de6dSgd78059 } 24421c42de6dSgd78059 24431c42de6dSgd78059 static int 24441c42de6dSgd78059 bscbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 24451c42de6dSgd78059 { 24461c42de6dSgd78059 struct bscbus_state *ssp; 24471c42de6dSgd78059 int instance; 24481c42de6dSgd78059 24491c42de6dSgd78059 switch (cmd) { 24501c42de6dSgd78059 default: 24511c42de6dSgd78059 return (DDI_FAILURE); 24521c42de6dSgd78059 24531c42de6dSgd78059 case DDI_DETACH: 24541c42de6dSgd78059 break; 24551c42de6dSgd78059 } 24561c42de6dSgd78059 24571c42de6dSgd78059 instance = ddi_get_instance(dip); 24581c42de6dSgd78059 if ((ssp = bscbus_getstate(dip, instance, "bscbus_detach")) == NULL) 24591c42de6dSgd78059 return (DDI_FAILURE); /* this "can't happen" */ 24601c42de6dSgd78059 24611c42de6dSgd78059 (void) bscbus_unattach(ssp, instance); 24621c42de6dSgd78059 return (DDI_SUCCESS); 24631c42de6dSgd78059 } 24641c42de6dSgd78059 24651c42de6dSgd78059 static int 24661c42de6dSgd78059 bscbus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) 24671c42de6dSgd78059 { 24681c42de6dSgd78059 struct bscbus_state *ssp; 24691c42de6dSgd78059 int chno; 24701c42de6dSgd78059 24711c42de6dSgd78059 _NOTE(ARGUNUSED(cmd)) 24721c42de6dSgd78059 24731c42de6dSgd78059 if ((ssp = bscbus_getstate(dip, -1, "bscbus_reset")) == NULL) 24741c42de6dSgd78059 return (DDI_FAILURE); 24751c42de6dSgd78059 24761c42de6dSgd78059 for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) { 24771c42de6dSgd78059 bscbus_hw_reset(&ssp->channel[chno]); 24781c42de6dSgd78059 } 24791c42de6dSgd78059 return (DDI_SUCCESS); 24801c42de6dSgd78059 } 24811c42de6dSgd78059 24821c42de6dSgd78059 24831c42de6dSgd78059 /* 24841c42de6dSgd78059 * System interface structures 24851c42de6dSgd78059 */ 24861c42de6dSgd78059 24871c42de6dSgd78059 static struct cb_ops bscbus_cb_ops = 24881c42de6dSgd78059 { 24891c42de6dSgd78059 nodev, /* b/c open */ 24901c42de6dSgd78059 nodev, /* b/c close */ 24911c42de6dSgd78059 nodev, /* b strategy */ 24921c42de6dSgd78059 nodev, /* b print */ 24931c42de6dSgd78059 nodev, /* b dump */ 24941c42de6dSgd78059 nodev, /* c read */ 24951c42de6dSgd78059 nodev, /* c write */ 24961c42de6dSgd78059 nodev, /* c ioctl */ 24971c42de6dSgd78059 nodev, /* c devmap */ 24981c42de6dSgd78059 nodev, /* c mmap */ 24991c42de6dSgd78059 nodev, /* c segmap */ 25001c42de6dSgd78059 nochpoll, /* c poll */ 25011c42de6dSgd78059 ddi_prop_op, /* b/c prop_op */ 25021c42de6dSgd78059 NULL, /* c streamtab */ 25031c42de6dSgd78059 D_MP | D_NEW /* b/c flags */ 25041c42de6dSgd78059 }; 25051c42de6dSgd78059 25061c42de6dSgd78059 static struct bus_ops bscbus_bus_ops = 25071c42de6dSgd78059 { 25081c42de6dSgd78059 BUSO_REV, /* revision */ 25091c42de6dSgd78059 bscbus_map, /* bus_map */ 25101c42de6dSgd78059 0, /* get_intrspec */ 25111c42de6dSgd78059 0, /* add_intrspec */ 25121c42de6dSgd78059 0, /* remove_intrspec */ 25131c42de6dSgd78059 i_ddi_map_fault, /* map_fault */ 25141c42de6dSgd78059 ddi_no_dma_map, /* dma_map */ 25151c42de6dSgd78059 ddi_no_dma_allochdl, /* allocate DMA handle */ 25161c42de6dSgd78059 ddi_no_dma_freehdl, /* free DMA handle */ 25171c42de6dSgd78059 ddi_no_dma_bindhdl, /* bind DMA handle */ 25181c42de6dSgd78059 ddi_no_dma_unbindhdl, /* unbind DMA handle */ 25191c42de6dSgd78059 ddi_no_dma_flush, /* flush DMA */ 25201c42de6dSgd78059 ddi_no_dma_win, /* move DMA window */ 25211c42de6dSgd78059 ddi_no_dma_mctl, /* generic DMA control */ 25221c42de6dSgd78059 bscbus_ctlops, /* generic control */ 25231c42de6dSgd78059 ddi_bus_prop_op, /* prop_op */ 25241c42de6dSgd78059 ndi_busop_get_eventcookie, /* get_eventcookie */ 25251c42de6dSgd78059 ndi_busop_add_eventcall, /* add_eventcall */ 25261c42de6dSgd78059 ndi_busop_remove_eventcall, /* remove_eventcall */ 25271c42de6dSgd78059 ndi_post_event, /* post_event */ 25281c42de6dSgd78059 0, /* interrupt control */ 25291c42de6dSgd78059 0, /* bus_config */ 25301c42de6dSgd78059 0, /* bus_unconfig */ 25311c42de6dSgd78059 0, /* bus_fm_init */ 25321c42de6dSgd78059 0, /* bus_fm_fini */ 25331c42de6dSgd78059 0, /* bus_fm_access_enter */ 25341c42de6dSgd78059 0, /* bus_fm_access_exit */ 25351c42de6dSgd78059 0, /* bus_power */ 25361c42de6dSgd78059 bscbus_intr_op /* bus_intr_op */ 25371c42de6dSgd78059 }; 25381c42de6dSgd78059 25391c42de6dSgd78059 static struct dev_ops bscbus_dev_ops = 25401c42de6dSgd78059 { 25411c42de6dSgd78059 DEVO_REV, 25421c42de6dSgd78059 0, /* refcount */ 25431c42de6dSgd78059 ddi_no_info, /* getinfo */ 25441c42de6dSgd78059 nulldev, /* identify */ 25451c42de6dSgd78059 nulldev, /* probe */ 25461c42de6dSgd78059 bscbus_attach, /* attach */ 25471c42de6dSgd78059 bscbus_detach, /* detach */ 25481c42de6dSgd78059 bscbus_reset, /* reset */ 25491c42de6dSgd78059 &bscbus_cb_ops, /* driver operations */ 2550*19397407SSherry Moore &bscbus_bus_ops, /* bus operations */ 2551*19397407SSherry Moore NULL, /* power */ 2552*19397407SSherry Moore ddi_quiesce_not_needed, /* quiesce */ 25531c42de6dSgd78059 }; 25541c42de6dSgd78059 25551c42de6dSgd78059 static struct modldrv modldrv = 25561c42de6dSgd78059 { 25571c42de6dSgd78059 &mod_driverops, 2558*19397407SSherry Moore "bscbus driver", 25591c42de6dSgd78059 &bscbus_dev_ops 25601c42de6dSgd78059 }; 25611c42de6dSgd78059 25621c42de6dSgd78059 static struct modlinkage modlinkage = 25631c42de6dSgd78059 { 25641c42de6dSgd78059 MODREV_1, 25651c42de6dSgd78059 { 25661c42de6dSgd78059 &modldrv, 25671c42de6dSgd78059 NULL 25681c42de6dSgd78059 } 25691c42de6dSgd78059 }; 25701c42de6dSgd78059 25711c42de6dSgd78059 25721c42de6dSgd78059 /* 25731c42de6dSgd78059 * Dynamic loader interface code 25741c42de6dSgd78059 */ 25751c42de6dSgd78059 25761c42de6dSgd78059 int 25771c42de6dSgd78059 _init(void) 25781c42de6dSgd78059 { 25791c42de6dSgd78059 int err; 25801c42de6dSgd78059 25811c42de6dSgd78059 err = ddi_soft_state_init(&bscbus_statep, 25821c42de6dSgd78059 sizeof (struct bscbus_state), 0); 25831c42de6dSgd78059 if (err == DDI_SUCCESS) 25841c42de6dSgd78059 if ((err = mod_install(&modlinkage)) != DDI_SUCCESS) { 25851c42de6dSgd78059 ddi_soft_state_fini(&bscbus_statep); 25861c42de6dSgd78059 } 25871c42de6dSgd78059 25881c42de6dSgd78059 return (err); 25891c42de6dSgd78059 } 25901c42de6dSgd78059 25911c42de6dSgd78059 int 25921c42de6dSgd78059 _info(struct modinfo *mip) 25931c42de6dSgd78059 { 25941c42de6dSgd78059 return (mod_info(&modlinkage, mip)); 25951c42de6dSgd78059 } 25961c42de6dSgd78059 25971c42de6dSgd78059 int 25981c42de6dSgd78059 _fini(void) 25991c42de6dSgd78059 { 26001c42de6dSgd78059 int err; 26011c42de6dSgd78059 26021c42de6dSgd78059 if ((err = mod_remove(&modlinkage)) == DDI_SUCCESS) { 26031c42de6dSgd78059 ddi_soft_state_fini(&bscbus_statep); 26041c42de6dSgd78059 bscbus_major = NOMAJOR; 26051c42de6dSgd78059 } 26061c42de6dSgd78059 26071c42de6dSgd78059 return (err); 26081c42de6dSgd78059 } 26091c42de6dSgd78059 26101c42de6dSgd78059 #ifdef BSCBUS_LOGSTATUS 26111c42de6dSgd78059 void bscbus_cmd_log(struct bscbus_channel_state *csp, bsc_cmd_stamp_t cat, 26121c42de6dSgd78059 uint8_t status, uint8_t data) 26131c42de6dSgd78059 { 26141c42de6dSgd78059 int idx; 26151c42de6dSgd78059 bsc_cmd_log_t *logp; 26161c42de6dSgd78059 struct bscbus_state *ssp; 26171c42de6dSgd78059 26181c42de6dSgd78059 if ((csp) == NULL) 26191c42de6dSgd78059 return; 26201c42de6dSgd78059 if ((ssp = (csp)->ssp) == NULL) 26211c42de6dSgd78059 return; 26221c42de6dSgd78059 if (ssp->cmd_log_size == 0) 26231c42de6dSgd78059 return; 26241c42de6dSgd78059 if ((bscbus_cmd_log_flags & (1 << cat)) == 0) 26251c42de6dSgd78059 return; 26261c42de6dSgd78059 idx = atomic_add_32_nv(&ssp->cmd_log_idx, 1); 26271c42de6dSgd78059 logp = &ssp->cmd_log[idx % ssp->cmd_log_size]; 26281c42de6dSgd78059 logp->bcl_seq = idx; 26291c42de6dSgd78059 logp->bcl_cat = cat; 26301c42de6dSgd78059 logp->bcl_now = gethrtime(); 26311c42de6dSgd78059 logp->bcl_chno = csp->chno; 26321c42de6dSgd78059 logp->bcl_cmdstate = csp->cmdstate; 26331c42de6dSgd78059 logp->bcl_status = status; 26341c42de6dSgd78059 logp->bcl_data = data; 26351c42de6dSgd78059 } 26361c42de6dSgd78059 #endif /* BSCBUS_LOGSTATUS */ 2637