xref: /onnv-gate/usr/src/uts/common/io/bscbus.c (revision 11066:cebb50cbe4f9)
14135Sgd78059 /*
24135Sgd78059  * CDDL HEADER START
34135Sgd78059  *
44135Sgd78059  * The contents of this file are subject to the terms of the
54135Sgd78059  * Common Development and Distribution License (the "License").
64135Sgd78059  * You may not use this file except in compliance with the License.
74135Sgd78059  *
84135Sgd78059  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94135Sgd78059  * or http://www.opensolaris.org/os/licensing.
104135Sgd78059  * See the License for the specific language governing permissions
114135Sgd78059  * and limitations under the License.
124135Sgd78059  *
134135Sgd78059  * When distributing Covered Code, include this CDDL HEADER in each
144135Sgd78059  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154135Sgd78059  * If applicable, add the following below this CDDL HEADER, with the
164135Sgd78059  * fields enclosed by brackets "[]" replaced with your own identifying
174135Sgd78059  * information: Portions Copyright [yyyy] [name of copyright owner]
184135Sgd78059  *
194135Sgd78059  * CDDL HEADER END
204135Sgd78059  */
214135Sgd78059 /*
22*11066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
234135Sgd78059  * Use is subject to license terms.
244135Sgd78059  *
254135Sgd78059  * The "bscbus" driver provides access to the LOMlite2 virtual registers,
264135Sgd78059  * so that its clients (children) need not be concerned with the details
274135Sgd78059  * of the access mechanism, which in this case is implemented via a
284135Sgd78059  * packet-based protocol over a Xbus (similar to ebus) parallel link to the
294135Sgd78059  * H8 host interface registers.
304135Sgd78059  *
314135Sgd78059  * On the other hand, this driver doesn't generally know what the virtual
324135Sgd78059  * registers signify - only the clients need this information.
334135Sgd78059  */
344135Sgd78059 
354135Sgd78059 
364135Sgd78059 #include <sys/note.h>
374135Sgd78059 #include <sys/types.h>
384135Sgd78059 #include <sys/conf.h>
394135Sgd78059 #include <sys/debug.h>
404135Sgd78059 #include <sys/errno.h>
414135Sgd78059 #include <sys/file.h>
424135Sgd78059 
434135Sgd78059 #if defined(__sparc)
444135Sgd78059 #include <sys/intr.h>
454135Sgd78059 #include <sys/membar.h>
464135Sgd78059 #endif
474135Sgd78059 
484135Sgd78059 #include <sys/kmem.h>
494135Sgd78059 #include <sys/modctl.h>
504135Sgd78059 #include <sys/note.h>
514135Sgd78059 #include <sys/open.h>
524135Sgd78059 #include <sys/poll.h>
534135Sgd78059 #include <sys/spl.h>
544135Sgd78059 #include <sys/stat.h>
554135Sgd78059 #include <sys/strlog.h>
564135Sgd78059 #include <sys/atomic.h>
574135Sgd78059 
584135Sgd78059 #include <sys/ddi.h>
594135Sgd78059 #include <sys/sunddi.h>
604135Sgd78059 #include <sys/sunndi.h>
614135Sgd78059 
624135Sgd78059 #include <sys/bscbus.h>
634135Sgd78059 
644135Sgd78059 #if	defined(NDI_ACC_HDL_V2)
654135Sgd78059 
664135Sgd78059 /*
674135Sgd78059  * Compiling for Solaris 10+ with access handle enhancements
684135Sgd78059  */
694135Sgd78059 #define	HANDLE_TYPE		ndi_acc_handle_t
704135Sgd78059 #define	HANDLE_ADDR(hdlp)	(hdlp->ah_addr)
714135Sgd78059 #define	HANDLE_FAULT(hdlp)	(hdlp->ah_fault)
724135Sgd78059 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ah_len)
734135Sgd78059 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ah_bus_private)
744135Sgd78059 
754135Sgd78059 #else
764135Sgd78059 
774135Sgd78059 /*
784135Sgd78059  * Compatibility definitions for backport to Solaris 8/9
794135Sgd78059  */
804135Sgd78059 #define	HANDLE_TYPE		ddi_acc_impl_t
814135Sgd78059 #define	HANDLE_ADDR(hdlp)	(hdlp->ahi_common.ah_addr)
824135Sgd78059 #define	HANDLE_FAULT(hdlp)	(hdlp->ahi_fault)
834135Sgd78059 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ahi_common.ah_len)
844135Sgd78059 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ahi_common.ah_bus_private)
854135Sgd78059 
864135Sgd78059 #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
874135Sgd78059 
884135Sgd78059 #endif	/* NDI_ACC_HDL_V2 */
894135Sgd78059 
904135Sgd78059 
914135Sgd78059 /*
924135Sgd78059  * Local definitions
934135Sgd78059  */
944135Sgd78059 #define	MYNAME			"bscbus"
954135Sgd78059 #define	NOMAJOR			(~(major_t)0)
964135Sgd78059 #define	DUMMY_VALUE		(~(int8_t)0)
974135Sgd78059 
984135Sgd78059 #define	BSCBUS_INST_TO_MINOR(i)	(i)
994135Sgd78059 #define	BSCBUS_MINOR_TO_INST(m)	(m)
1004135Sgd78059 
1014135Sgd78059 #define	BSCBUS_MAX_CHANNELS	(4)
1024135Sgd78059 
1034135Sgd78059 #define	BSCBUS_DUMMY_ADDRESS	((caddr_t)0x0CADD1ED)
1044135Sgd78059 #define	ADDR_TO_OFFSET(a, hdlp)	((caddr_t)(a) - HANDLE_ADDR(hdlp))
1054135Sgd78059 #define	ADDR_TO_VREG(a)		((caddr_t)(a) - BSCBUS_DUMMY_ADDRESS)
1064135Sgd78059 #define	VREG_TO_ADDR(v)		(BSCBUS_DUMMY_ADDRESS + (v))
1074135Sgd78059 
1084135Sgd78059 #ifdef DEBUG
1094135Sgd78059 #define	BSCBUS_LOGSTATUS
1104135Sgd78059 #endif /* DEBUG */
1114135Sgd78059 
1124135Sgd78059 #ifdef BSCBUS_LOGSTATUS
1134135Sgd78059 /*
1144135Sgd78059  * BSC command logging routines.
1154135Sgd78059  * Record the data passing to and from the BSC
1164135Sgd78059  */
1174135Sgd78059 
1184135Sgd78059 typedef enum {
1194135Sgd78059 	BSC_CMD_BUSY = 1,		/* bsc reports busy	*/
1204135Sgd78059 	BSC_CMD_CLEARING = 2,		/* clearing bsc busy	*/
1214135Sgd78059 	BSC_CMD_CLEARED = 3,		/* cleared bsc busy	*/
1224135Sgd78059 	BSC_CMD_SENDING = 4,		/* sending next byte	*/
1234135Sgd78059 	BSC_CMD_SENT = 5,		/* sending last byte	*/
1244135Sgd78059 	BSC_CMD_PENDING = 6,		/* got sent byte ack	*/
1254135Sgd78059 	BSC_CMD_REPLY = 7,		/* got reply byte	*/
1264135Sgd78059 	BSC_CMD_COMPLETE = 8,		/* command complete	*/
1274135Sgd78059 	BSC_CMD_ERROR_SEQ = 9,		/* error status		*/
1284135Sgd78059 	BSC_CMD_ERROR_STATUS = 10,	/* error status		*/
1294135Sgd78059 	BSC_CMD_ERROR_OFLOW = 11,	/* error status		*/
1304135Sgd78059 	BSC_CMD_ERROR_TOUT = 12,	/* error status		*/
1314135Sgd78059 
1324135Sgd78059 	BSC_CMD_PROCESS = 13,		/* async intr		*/
1334135Sgd78059 	BSC_CMD_V1INTR = 14,		/* v1 intr		*/
1344135Sgd78059 	BSC_CMD_V1INTRUNCL = 15,	/* v1 intr unclaim	*/
1354135Sgd78059 	BSC_CMD_DOGPAT = 17		/* watchdog pat		*/
1364135Sgd78059 } bsc_cmd_stamp_t;
1374135Sgd78059 
1384135Sgd78059 typedef struct {
1394135Sgd78059 	hrtime_t	bcl_now;
1404135Sgd78059 	int		bcl_seq;
1414135Sgd78059 	bsc_cmd_stamp_t	bcl_cat;
1424135Sgd78059 	uint8_t		bcl_chno;
1434135Sgd78059 	uint8_t		bcl_cmdstate;
1444135Sgd78059 	uint8_t		bcl_status;
1454135Sgd78059 	uint8_t		bcl_data;
1464135Sgd78059 } bsc_cmd_log_t;
1474135Sgd78059 
1484135Sgd78059 uint32_t	bscbus_cmd_log_size = 1024;
1494135Sgd78059 
1504135Sgd78059 uint32_t	bscbus_cmd_log_flags = 0xffffffff;
1514135Sgd78059 
1524135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
1534135Sgd78059 
1544135Sgd78059 /*
1554135Sgd78059  * The following definitions are taken from the Hardware Manual for
1564135Sgd78059  * the Hitachi H8S/2148 in conjunction with the hardware specification
1574135Sgd78059  * for the Stiletto blade.
1584135Sgd78059  *
1594135Sgd78059  * Each instance of the host interface has 3 registers on the H8:
1604135Sgd78059  * IDRn  - Input Data Register	- write-only for Solaris.
1614135Sgd78059  *				  writes to this can be done via two
1624135Sgd78059  *				  addresses - control and data.
1634135Sgd78059  *				  The H8 can determine which address was
1644135Sgd78059  *				  written by examining the C/D bit in
1654135Sgd78059  *				  the status register.
1664135Sgd78059  * ODRn  - Output Data Register - read-only for Solaris.
1674135Sgd78059  *				  A read has the side effect of acknowledging
1684135Sgd78059  *				  interrupts.
1694135Sgd78059  * STRn  - Status Register	- read-only for Solaris.
1704135Sgd78059  *
1714135Sgd78059  *
1724135Sgd78059  *
1734135Sgd78059  * In terms of host access to this the Input and Output data registers are
1744135Sgd78059  * mapped at the same address.
1754135Sgd78059  */
1764135Sgd78059 #define	H8_IDRD	0
1774135Sgd78059 #define	H8_IDRC	1
1784135Sgd78059 #define	H8_ODR	0
1794135Sgd78059 #define	H8_STR	1
1804135Sgd78059 
1814135Sgd78059 #define	H8_STR_OBF		0x01	/* data available in ODR */
1824135Sgd78059 #define	H8_STR_IBF		0x02	/* data for H8 in IDR */
1834135Sgd78059 #define	H8_STR_IDRC		0x08	/* last write to IDR was to IDRC */
1844135Sgd78059 					/* 0=data, 1=command */
1854135Sgd78059 #define	H8_STR_BUSY		0x04	/* H8 busy processing command */
1864135Sgd78059 #define	H8_STR_TOKENPROTOCOL	0x80	/* token-passing protocol */
1874135Sgd78059 
1884135Sgd78059 /*
1894135Sgd78059  * Packet format ...
1904135Sgd78059  */
1914135Sgd78059 #define	BSCBUS_MASK		0xc0	/* Byte-type bits		*/
1924135Sgd78059 #define	BSCBUS_PARAM		0x00	/* Parameter byte: 0b0xxxxxxx	*/
1934135Sgd78059 #define	BSCBUS_LAST		0x80	/* Last byte of packet		*/
1944135Sgd78059 #define	BSCBUS_CMD		0x80	/* Command byte:   0b10###XWV	*/
1954135Sgd78059 #define	BSCBUS_STATUS		0xc0	/* Status  byte:   0b11###AEV	*/
1964135Sgd78059 
1974135Sgd78059 #define	BSCBUS_SEQ		0x38	/* Sequence number bits		*/
1984135Sgd78059 #define	BSCBUS_SEQ_LSB		0x08	/* Sequence number LSB		*/
1994135Sgd78059 #define	BSCBUS_CMD_XADDR	0x04	/* Extended (2-byte) addressing	*/
2004135Sgd78059 #define	BSCBUS_CMD_WRITE	0x02	/* Write command		*/
2014135Sgd78059 #define	BSCBUS_CMD_WMSB		0x01	/* Set MSB on Write		*/
2024135Sgd78059 #define	BSCBUS_CMD_READ		0x01	/* Read command			*/
2034135Sgd78059 #define	BSCBUS_CMD_NOP		0x00	/* NOP command			*/
2044135Sgd78059 
2054135Sgd78059 #define	BSCBUS_STATUS_ASYNC	0x04	/* Asynchronous event pending	*/
2064135Sgd78059 #define	BSCBUS_STATUS_ERR	0x02	/* Error in command processing	*/
2074135Sgd78059 #define	BSCBUS_STATUS_MSB	0x01	/* MSB of Value read		*/
2084135Sgd78059 
2094135Sgd78059 #define	BSCBUS_VREG_LO(x)	((x) & ((1 << 7) - 1))
2104135Sgd78059 #define	BSCBUS_VREG_HI(x)	((x) >> 7)
2114135Sgd78059 
2124135Sgd78059 #define	BSCBUS_BUFSIZE		8
2134135Sgd78059 
2144135Sgd78059 #define	BSCBUS_CHANNEL_TO_OFFSET(chno)	((chno) * 2)	/* Register offset */
2154135Sgd78059 
2164135Sgd78059 /*
2174135Sgd78059  * Time periods, in nanoseconds
2184135Sgd78059  *
2194135Sgd78059  * Note that LOMBUS_ONE_SEC and some other time
2204135Sgd78059  * periods are defined in <sys/lombus.h>
2214135Sgd78059  */
2224135Sgd78059 #define	BSCBUS_CMD_POLL			(LOMBUS_ONE_SEC)
2234135Sgd78059 #define	BSCBUS_CMD_POLLNOINTS		(LOMBUS_ONE_SEC/20)
2244135Sgd78059 #define	BSCBUS_HWRESET_POLL		(LOMBUS_ONE_SEC/20)
2254135Sgd78059 #define	BSCBUS_HWRESET_TIMEOUT		(LOMBUS_ONE_SEC*2)
2264135Sgd78059 
2274135Sgd78059 #define	BSCBUS_DOG_PAT_POLL_LIMIT	(1000)
2284135Sgd78059 #define	BSCBUS_DOG_PAT_POLL		(1)
2294135Sgd78059 #define	BSCBUS_PAT_RETRY_LIMIT	5
2304135Sgd78059 
2314135Sgd78059 /*
2324135Sgd78059  * Local datatypes
2334135Sgd78059  */
2344135Sgd78059 enum bscbus_cmdstate {
2354135Sgd78059 	BSCBUS_CMDSTATE_IDLE,		/* No transaction in progress */
2364135Sgd78059 	BSCBUS_CMDSTATE_BUSY,		/* Setting up command */
2374135Sgd78059 	BSCBUS_CMDSTATE_CLEARING,	/* Clearing firmware busy status */
2384135Sgd78059 	BSCBUS_CMDSTATE_SENDING,	/* Waiting to send data to f/w */
2394135Sgd78059 	BSCBUS_CMDSTATE_PENDING,	/* Waiting for ack from f/w */
2404135Sgd78059 	BSCBUS_CMDSTATE_WAITING,	/* Waiting for status from f/w */
2414135Sgd78059 	BSCBUS_CMDSTATE_READY,		/* Status received/command done */
2424135Sgd78059 	BSCBUS_CMDSTATE_ERROR		/* Command failed with error */
2434135Sgd78059 };
2444135Sgd78059 
2454135Sgd78059 struct bscbus_channel_state {
2464135Sgd78059 	/* Changes to these are protected by the instance ch_mutex mutex */
2474135Sgd78059 	struct bscbus_state	*ssp;
2484135Sgd78059 	uint8_t			*ch_regs;
2494135Sgd78059 	ddi_acc_handle_t	ch_handle;  /* per channel access handle */
2504135Sgd78059 	unsigned int		chno;
2514135Sgd78059 	unsigned int		map_count; /* Number of mappings to channel */
2524135Sgd78059 	boolean_t		map_dog;   /* channel is mapped for watchdog */
2534135Sgd78059 
2544135Sgd78059 	/*
2554135Sgd78059 	 * Flag to indicate that we've incurred a hardware fault on
2564135Sgd78059 	 * accesses to the H8; once this is set, we fake all further
2574135Sgd78059 	 * accesses in order not to provoke additional bus errors.
2584135Sgd78059 	 */
2594135Sgd78059 	boolean_t		xio_fault;
2604135Sgd78059 
2614135Sgd78059 	/*
2624135Sgd78059 	 * Data protected by the dog_mutex: the watchdog-patting
2634135Sgd78059 	 * protocol data (since the dog can be patted from a high-level
2644135Sgd78059 	 * cyclic), and the interrupt-enabled flag.
2654135Sgd78059 	 */
2664135Sgd78059 	kmutex_t		dog_mutex[1];
2674135Sgd78059 	unsigned int		pat_retry_count;
2684135Sgd78059 	unsigned int		pat_fail_count;
2694135Sgd78059 
2704135Sgd78059 	/*
2714135Sgd78059 	 * Serial protocol state data, protected by lo_mutex
2724135Sgd78059 	 * (which is initialised using <lo_iblk>)
2734135Sgd78059 	 */
2744135Sgd78059 	kmutex_t		lo_mutex[1];
2754135Sgd78059 	ddi_iblock_cookie_t	lo_iblk;
2764135Sgd78059 	kcondvar_t		lo_cv[1];
2774135Sgd78059 	int			unclaimed_count;
2784135Sgd78059 
2794135Sgd78059 	volatile enum bscbus_cmdstate cmdstate;
2804135Sgd78059 	clock_t			deadline;
2814135Sgd78059 	clock_t			poll_hz;
2824135Sgd78059 	boolean_t		interrupt_failed;
2834135Sgd78059 	uint8_t 		cmdbuf[BSCBUS_BUFSIZE];
2844135Sgd78059 	uint8_t			*cmdp;	/* Points to last tx'd in cmdbuf */
2854135Sgd78059 	uint8_t			reply[BSCBUS_BUFSIZE];
2864135Sgd78059 	uint8_t			async;
2874135Sgd78059 	uint8_t			index;
2884135Sgd78059 	uint8_t			result;
2894135Sgd78059 	uint8_t			sequence;
2904135Sgd78059 	uint32_t		error;
2914135Sgd78059 };
2924135Sgd78059 
2934135Sgd78059 #define	BSCBUS_TX_PENDING(csp)		((csp)->cmdp > (csp)->cmdbuf)
2944135Sgd78059 
2954135Sgd78059 /*
2964135Sgd78059  * This driver's soft-state structure
2974135Sgd78059  */
2984135Sgd78059 
2994135Sgd78059 struct bscbus_state {
3004135Sgd78059 	/*
3014135Sgd78059 	 * Configuration data, set during attach
3024135Sgd78059 	 */
3034135Sgd78059 	dev_info_t		*dip;
3044135Sgd78059 	major_t			majornum;
3054135Sgd78059 	int			instance;
3064135Sgd78059 
3074135Sgd78059 	ddi_acc_handle_t	h8_handle;
3084135Sgd78059 	uint8_t			*h8_regs;
3094135Sgd78059 
3104135Sgd78059 	/*
3114135Sgd78059 	 * Parameters derived from .conf properties
3124135Sgd78059 	 */
3134135Sgd78059 	uint32_t		debug;
3144135Sgd78059 
3154135Sgd78059 	/*
3164135Sgd78059 	 * Flag to indicate that we are using per channel
3174135Sgd78059 	 * mapping of the register sets and interrupts.
3184135Sgd78059 	 * reg set 0 is chan 0
3194135Sgd78059 	 * reg set 1 is chan 1 ...
3204135Sgd78059 	 *
3214135Sgd78059 	 * Interrupts are specified in that order but later
3224135Sgd78059 	 * channels may not have interrupts.
3234135Sgd78059 	 */
3244135Sgd78059 	boolean_t		per_channel_regs;
3254135Sgd78059 
3264135Sgd78059 	/*
3274135Sgd78059 	 * channel state data, protected by ch_mutex
3284135Sgd78059 	 * channel claim/release requests are protected by this mutex.
3294135Sgd78059 	 */
3304135Sgd78059 	kmutex_t		ch_mutex[1];
3314135Sgd78059 	struct bscbus_channel_state	channel[BSCBUS_MAX_CHANNELS];
3324135Sgd78059 
3334135Sgd78059 #ifdef BSCBUS_LOGSTATUS
3344135Sgd78059 	/*
3354135Sgd78059 	 * Command logging buffer for recording transactions with the
3364135Sgd78059 	 * BSC. This is useful for debugging failed transactions and other
3374135Sgd78059 	 * such funnies.
3384135Sgd78059 	 */
3394135Sgd78059 	bsc_cmd_log_t		*cmd_log;
3404135Sgd78059 	uint32_t		cmd_log_idx;
3414135Sgd78059 	uint32_t		cmd_log_size;
3424135Sgd78059 	uint32_t		cmd_log_flags;
3434135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
3444135Sgd78059 };
3454135Sgd78059 
3464135Sgd78059 /*
3474135Sgd78059  * The auxiliary structure attached to each child
3484135Sgd78059  * (the child's parent-private-data points to this).
3494135Sgd78059  */
3504135Sgd78059 struct bscbus_child_info {
3514135Sgd78059 	lombus_regspec_t *rsp;
3524135Sgd78059 	int nregs;
3534135Sgd78059 };
3544135Sgd78059 
3554135Sgd78059 #ifdef BSCBUS_LOGSTATUS
3564135Sgd78059 void bscbus_cmd_log(struct bscbus_channel_state *, bsc_cmd_stamp_t,
3574135Sgd78059     uint8_t, uint8_t);
3584135Sgd78059 #else /* BSCBUS_LOGSTATUS */
3594135Sgd78059 #define	bscbus_cmd_log(state, stamp, status, data)
3604135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
3614135Sgd78059 
3624135Sgd78059 
3634135Sgd78059 /*
3644135Sgd78059  * Local data
3654135Sgd78059  */
3664135Sgd78059 
3674135Sgd78059 static void *bscbus_statep;
3684135Sgd78059 
3694135Sgd78059 static major_t bscbus_major = NOMAJOR;
3704135Sgd78059 
3714135Sgd78059 static ddi_device_acc_attr_t bscbus_dev_acc_attr[1] = {
3724135Sgd78059 	DDI_DEVICE_ATTR_V0,
3734135Sgd78059 	DDI_STRUCTURE_LE_ACC,
3744135Sgd78059 	DDI_STRICTORDER_ACC
3754135Sgd78059 };
3764135Sgd78059 
3774135Sgd78059 
3784135Sgd78059 /*
3794135Sgd78059  *  General utility routines ...
3804135Sgd78059  */
3814135Sgd78059 
3824135Sgd78059 #ifdef DEBUG
3834135Sgd78059 static void
bscbus_trace(struct bscbus_channel_state * csp,char code,const char * caller,const char * fmt,...)3844135Sgd78059 bscbus_trace(struct bscbus_channel_state *csp, char code, const char *caller,
3854135Sgd78059 	const char *fmt, ...)
3864135Sgd78059 {
3874135Sgd78059 	char buf[256];
3884135Sgd78059 	char *p;
3894135Sgd78059 	va_list va;
3904135Sgd78059 
3914135Sgd78059 	if (csp->ssp->debug & (1 << (code-'@'))) {
3924135Sgd78059 		p = buf;
3934135Sgd78059 		(void) snprintf(p, sizeof (buf) - (p - buf),
3947656SSherry.Moore@Sun.COM 		    "%s/%s: ", MYNAME, caller);
3954135Sgd78059 		p += strlen(p);
3964135Sgd78059 
3974135Sgd78059 		va_start(va, fmt);
3984135Sgd78059 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
3994135Sgd78059 		va_end(va);
4004135Sgd78059 
4014135Sgd78059 		buf[sizeof (buf) - 1] = '\0';
4024135Sgd78059 		(void) strlog(csp->ssp->majornum, csp->ssp->instance,
4034135Sgd78059 		    code, SL_TRACE, buf);
4044135Sgd78059 	}
4054135Sgd78059 }
4064135Sgd78059 #else /* DEBUG */
4074135Sgd78059 #define	bscbus_trace
4084135Sgd78059 #endif /* DEBUG */
4094135Sgd78059 
4104135Sgd78059 static struct bscbus_state *
bscbus_getstate(dev_info_t * dip,int instance,const char * caller)4114135Sgd78059 bscbus_getstate(dev_info_t *dip, int instance, const char *caller)
4124135Sgd78059 {
4134135Sgd78059 	struct bscbus_state *ssp = NULL;
4144135Sgd78059 	dev_info_t *sdip = NULL;
4154135Sgd78059 	major_t dmaj = NOMAJOR;
4164135Sgd78059 
4174135Sgd78059 	if (dip != NULL) {
4184135Sgd78059 		/*
4194135Sgd78059 		 * Use the instance number from the <dip>; also,
4204135Sgd78059 		 * check that it really corresponds to this driver
4214135Sgd78059 		 */
4224135Sgd78059 		instance = ddi_get_instance(dip);
4234135Sgd78059 		dmaj = ddi_driver_major(dip);
4244135Sgd78059 		if (bscbus_major == NOMAJOR && dmaj != NOMAJOR)
4254135Sgd78059 			bscbus_major = dmaj;
4264135Sgd78059 		else if (dmaj != bscbus_major) {
4274135Sgd78059 			cmn_err(CE_WARN,
4284135Sgd78059 			    "%s: major number mismatch (%d vs. %d) in %s(),"
4294135Sgd78059 			    "probably due to child misconfiguration",
4304135Sgd78059 			    MYNAME, bscbus_major, dmaj, caller);
4314135Sgd78059 			instance = -1;
4324135Sgd78059 		}
4334135Sgd78059 	}
4344135Sgd78059 
4354135Sgd78059 	if (instance >= 0)
4364135Sgd78059 		ssp = ddi_get_soft_state(bscbus_statep, instance);
4374135Sgd78059 	if (ssp != NULL) {
4384135Sgd78059 		sdip = ssp->dip;
4394135Sgd78059 		if (dip == NULL && sdip == NULL)
4404135Sgd78059 			ssp = NULL;
4414135Sgd78059 		else if (dip != NULL && sdip != NULL && sdip != dip) {
4424135Sgd78059 			cmn_err(CE_WARN,
4434135Sgd78059 			    "%s: devinfo mismatch (%p vs. %p) in %s(), "
4444135Sgd78059 			    "probably due to child misconfiguration",
4454135Sgd78059 			    MYNAME, (void *)dip, (void *)sdip, caller);
4464135Sgd78059 			ssp = NULL;
4474135Sgd78059 		}
4484135Sgd78059 	}
4494135Sgd78059 
4504135Sgd78059 	return (ssp);
4514135Sgd78059 }
4524135Sgd78059 
4534135Sgd78059 /*
4544135Sgd78059  * Lowest-level I/O register read/write
4554135Sgd78059  */
4564135Sgd78059 
4574135Sgd78059 static void
bscbus_put_reg(struct bscbus_channel_state * csp,uint_t reg,uint8_t val)4584135Sgd78059 bscbus_put_reg(struct bscbus_channel_state *csp, uint_t reg, uint8_t val)
4594135Sgd78059 {
4604135Sgd78059 	if (csp->ch_handle != NULL && !csp->xio_fault) {
4614135Sgd78059 		ddi_put8(csp->ch_handle,
4624135Sgd78059 		    csp->ch_regs + reg, val);
4634135Sgd78059 	}
4644135Sgd78059 }
4654135Sgd78059 
4664135Sgd78059 static uint8_t
bscbus_get_reg(struct bscbus_channel_state * csp,uint_t reg)4674135Sgd78059 bscbus_get_reg(struct bscbus_channel_state *csp, uint_t reg)
4684135Sgd78059 {
4694135Sgd78059 	uint8_t val;
4704135Sgd78059 
4714135Sgd78059 	if (csp->ch_handle != NULL && !csp->xio_fault)
4724135Sgd78059 		val = ddi_get8(csp->ch_handle,
4734135Sgd78059 		    csp->ch_regs + reg);
4744135Sgd78059 	else
4754135Sgd78059 		val = DUMMY_VALUE;
4764135Sgd78059 
4774135Sgd78059 	return (val);
4784135Sgd78059 }
4794135Sgd78059 
4804135Sgd78059 static void
bscbus_check_fault_status(struct bscbus_channel_state * csp)4814135Sgd78059 bscbus_check_fault_status(struct bscbus_channel_state *csp)
4824135Sgd78059 {
4834135Sgd78059 	csp->xio_fault =
4847656SSherry.Moore@Sun.COM 	    ddi_check_acc_handle(csp->ch_handle) != DDI_SUCCESS;
4854135Sgd78059 }
4864135Sgd78059 
4874135Sgd78059 static boolean_t
bscbus_faulty(struct bscbus_channel_state * csp)4884135Sgd78059 bscbus_faulty(struct bscbus_channel_state *csp)
4894135Sgd78059 {
4904135Sgd78059 	if (!csp->xio_fault)
4914135Sgd78059 		bscbus_check_fault_status(csp);
4924135Sgd78059 	return (csp->xio_fault);
4934135Sgd78059 }
4944135Sgd78059 
4954135Sgd78059 /*
4964135Sgd78059  * Write data into h8 registers
4974135Sgd78059  */
4984135Sgd78059 static void
bscbus_pat_dog(struct bscbus_channel_state * csp,uint8_t val)4994135Sgd78059 bscbus_pat_dog(struct bscbus_channel_state *csp, uint8_t val)
5004135Sgd78059 {
5014135Sgd78059 	uint8_t status;
5024135Sgd78059 	uint32_t doglimit = BSCBUS_DOG_PAT_POLL_LIMIT;
5034135Sgd78059 
5044135Sgd78059 	bscbus_trace(csp, 'W', "bscbus_pat_dog:", "");
5054135Sgd78059 
5064135Sgd78059 	bscbus_cmd_log(csp, BSC_CMD_DOGPAT, 0, val);
5074135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
5084135Sgd78059 	while (status & H8_STR_IBF) {
5094135Sgd78059 		if (csp->pat_retry_count > BSCBUS_PAT_RETRY_LIMIT) {
5104135Sgd78059 			/*
5114135Sgd78059 			 * Previous attempts to contact BSC have failed.
5124135Sgd78059 			 * Do not bother waiting for it to eat previous
5134135Sgd78059 			 * data.
5144135Sgd78059 			 * Pat anyway just in case the BSC is really alive
5154135Sgd78059 			 * and the IBF bit is lying.
5164135Sgd78059 			 */
5174135Sgd78059 			bscbus_put_reg(csp, H8_IDRC, val);
5184135Sgd78059 			bscbus_trace(csp, 'W', "bscbus_pat_dog:",
5194135Sgd78059 			    "retry count exceeded");
5204135Sgd78059 			return;
5214135Sgd78059 		}
5224135Sgd78059 		if (--doglimit == 0) {
5234135Sgd78059 			/* The BSC is not responding - give up */
5244135Sgd78059 			csp->pat_fail_count++;
5254135Sgd78059 			csp->pat_retry_count++;
5264135Sgd78059 			/* Pat anyway just in case the BSC is really alive */
5274135Sgd78059 			bscbus_put_reg(csp, H8_IDRC, val);
5284135Sgd78059 			bscbus_trace(csp, 'W', "bscbus_pat_dog:",
5294135Sgd78059 			    "poll limit exceeded");
5304135Sgd78059 			return;
5314135Sgd78059 		}
5324135Sgd78059 		drv_usecwait(BSCBUS_DOG_PAT_POLL);
5334135Sgd78059 		status = bscbus_get_reg(csp, H8_STR);
5344135Sgd78059 	}
5354135Sgd78059 	bscbus_put_reg(csp, H8_IDRC, val);
5364135Sgd78059 	csp->pat_retry_count = 0;
5374135Sgd78059 }
5384135Sgd78059 
5394135Sgd78059 /*
5404135Sgd78059  * State diagrams for how bscbus_process works.
5414135Sgd78059  *	BSCBUS_CMDSTATE_IDLE		No transaction in progress
5424135Sgd78059  *	BSCBUS_CMDSTATE_BUSY		Setting up command
5434135Sgd78059  *	BSCBUS_CMDSTATE_CLEARING	Clearing firmware busy status
5444135Sgd78059  *	BSCBUS_CMDSTATE_SENDING		Waiting to send data to f/w
5454135Sgd78059  *	BSCBUS_CMDSTATE_PENDING		Waiting for ack from f/w
5464135Sgd78059  *	BSCBUS_CMDSTATE_WAITING		Waiting for status from f/w
5474135Sgd78059  *	BSCBUS_CMDSTATE_READY		Status received/command done
5484135Sgd78059  *	BSCBUS_CMDSTATE_ERROR		Command failed with error
5494135Sgd78059  *
5504135Sgd78059  *	+----------+
5514135Sgd78059  *	|	   |
5524135Sgd78059  *	| IDLE/BUSY|
5534135Sgd78059  *	|   (0/1)  |  abnormal
5544135Sgd78059  *	+----------+  state
5554135Sgd78059  *	    |	  \   detected
5564135Sgd78059  *	    |	   \------>------+  +----<---+
5574135Sgd78059  *	bsc |			 |  |	     |
5584135Sgd78059  *	is  |			 V  V	     |
5594135Sgd78059  *     ready|		     +----------+    |
5604135Sgd78059  *	    |		     |		|    ^
5614135Sgd78059  *	    |		     | CLEARING |    |
5624135Sgd78059  *	    |		     |	 (2)	|    |
5634135Sgd78059  *	    |		     +----------+    |
5644135Sgd78059  *	    |		 cleared /  | \	     | more to clear
5654135Sgd78059  *	    |			/   |  \-->--+
5664135Sgd78059  *	    |  +-------<-------/    V
5674135Sgd78059  *	    |  |		    |
5684135Sgd78059  *	    V  V		    |timeout
5694135Sgd78059  *	+----------+ timeout	    |
5704135Sgd78059  *	|	   |------>---------+--------+
5714135Sgd78059  *	| SENDING  |			     |
5724135Sgd78059  *	|   (3)	   |------<-------+	     |
5734135Sgd78059  *	+----------+		  |	     V
5744135Sgd78059  *	sent|	 \ send		  ^ack	     |
5754135Sgd78059  *	last|	  \ next	  |received  |
5764135Sgd78059  *	    |	   \	     +----------+    |
5774135Sgd78059  *	    |	    \	     |		|    |
5784135Sgd78059  *	    |	     \------>| PENDING	|-->-+
5794135Sgd78059  *	    |		     |	 (4)	|    |
5804135Sgd78059  *	    |		     +----------+    |timeout
5814135Sgd78059  *	    |	 +---<----+		     |
5824135Sgd78059  *	    |	 |	  |		     |
5834135Sgd78059  *	    V	 V	  |		     |
5844135Sgd78059  *	+----------+	  |		     |
5854135Sgd78059  *	|	   |	  |		     |
5864135Sgd78059  *	| WAITING  |	  ^		     |
5874135Sgd78059  *	|   (5)	   |	  |		     |
5884135Sgd78059  *	+----------+	  |		     |
5894135Sgd78059  *	    |  | |more	  |		     |
5904135Sgd78059  *	    |  V |required|		     |
5914135Sgd78059  *	done|  | +--->----+		     |
5924135Sgd78059  *	    |  +--->--------------+  +---<---+
5934135Sgd78059  *	    |	error/timeout	  |  |
5944135Sgd78059  *	    V			  V  V
5954135Sgd78059  *	+----------+	      +----------+
5964135Sgd78059  *	|	   |	      |		 |
5974135Sgd78059  *	| READY	   |	      |	 ERROR	 |
5984135Sgd78059  *	|   (7)	   |	      |	  (6)	 |
5994135Sgd78059  *	+----------+	      +----------+
6004135Sgd78059  *	    |			  |
6014135Sgd78059  *	    V			  V
6024135Sgd78059  *	    |			  |
6034135Sgd78059  *	    +------>---+---<------+
6044135Sgd78059  *		       |
6054135Sgd78059  *		       |
6064135Sgd78059  *		     Back to
6074135Sgd78059  *		      Idle
6084135Sgd78059  */
6094135Sgd78059 
6104135Sgd78059 static void
bscbus_process_sending(struct bscbus_channel_state * csp,uint8_t status)6114135Sgd78059 bscbus_process_sending(struct bscbus_channel_state *csp, uint8_t status)
6124135Sgd78059 {
6134135Sgd78059 	/*
6144135Sgd78059 	 * When we get here we actually expect H8_STR_IBF to
6154135Sgd78059 	 * be clear but we check just in case of problems.
6164135Sgd78059 	 */
6174135Sgd78059 	ASSERT(BSCBUS_TX_PENDING(csp));
6184135Sgd78059 	if (!(status & H8_STR_IBF)) {
6194135Sgd78059 		bscbus_put_reg(csp, H8_IDRD, *--csp->cmdp);
6204135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_process_sending",
6214135Sgd78059 		    "state %d; val $%x",
6224135Sgd78059 		    csp->cmdstate, *csp->cmdp);
6234135Sgd78059 		if (!BSCBUS_TX_PENDING(csp)) {
6244135Sgd78059 			bscbus_cmd_log(csp, BSC_CMD_SENT,
6254135Sgd78059 			    status, *csp->cmdp);
6264135Sgd78059 			/* No more pending - move to waiting state */
6274135Sgd78059 			bscbus_trace(csp, 'P', "bscbus_process_sending",
6284135Sgd78059 			    "moving to waiting");
6294135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_WAITING;
6304135Sgd78059 			/* Extend deadline because time has moved on */
6314135Sgd78059 			csp->deadline = ddi_get_lbolt() +
6327656SSherry.Moore@Sun.COM 			    drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
6334135Sgd78059 		} else {
6344135Sgd78059 			/* Wait for ack of this byte */
6354135Sgd78059 			bscbus_cmd_log(csp, BSC_CMD_SENDING,
6364135Sgd78059 			    status, *csp->cmdp);
6374135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_PENDING;
6384135Sgd78059 			bscbus_trace(csp, 'P', "bscbus_process_sending",
6394135Sgd78059 			    "moving to pending");
6404135Sgd78059 		}
6414135Sgd78059 	}
6424135Sgd78059 }
6434135Sgd78059 
6444135Sgd78059 static void
bscbus_process_clearing(struct bscbus_channel_state * csp,uint8_t status,uint8_t data)6454135Sgd78059 bscbus_process_clearing(struct bscbus_channel_state *csp,
6464135Sgd78059     uint8_t status, uint8_t data)
6474135Sgd78059 {
6484135Sgd78059 	/*
6494135Sgd78059 	 * We only enter this state if H8_STR_BUSY was set when
6504135Sgd78059 	 * we started the transaction. We just ignore all received
6514135Sgd78059 	 * data until we see OBF set AND BUSY cleared.
6524135Sgd78059 	 * It is not good enough to see BUSY clear on its own
6534135Sgd78059 	 */
6544135Sgd78059 	if ((status & H8_STR_OBF) && !(status & H8_STR_BUSY)) {
6554135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_CLEARED, status, data);
6564135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
6574135Sgd78059 		/* Throw away any data received up until now */
6584135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_process_clearing",
6594135Sgd78059 		    "busy cleared");
6604135Sgd78059 		/*
6614135Sgd78059 		 * Send the next byte immediately.
6624135Sgd78059 		 * At this stage we should clear the OBF flag because that
6634135Sgd78059 		 * data has been used. IBF is still valid so do not clear that.
6644135Sgd78059 		 */
6654135Sgd78059 		status &= ~(H8_STR_OBF);
6664135Sgd78059 		bscbus_process_sending(csp, status);
6674135Sgd78059 	} else {
6684135Sgd78059 		if (status & H8_STR_OBF) {
6694135Sgd78059 			bscbus_cmd_log(csp, BSC_CMD_CLEARING, status, data);
6704135Sgd78059 		}
6714135Sgd78059 	}
6724135Sgd78059 }
6734135Sgd78059 
6744135Sgd78059 static void
bscbus_process_pending(struct bscbus_channel_state * csp,uint8_t status)6754135Sgd78059 bscbus_process_pending(struct bscbus_channel_state *csp, uint8_t status)
6764135Sgd78059 {
6774135Sgd78059 	/* We are waiting for an acknowledgement of a byte */
6784135Sgd78059 	if (status & H8_STR_OBF) {
6794135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_PENDING,
6804135Sgd78059 		    status, *csp->cmdp);
6814135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_process_pending",
6824135Sgd78059 		    "moving to sending");
6834135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
6844135Sgd78059 		/*
6854135Sgd78059 		 * Send the next byte immediately.
6864135Sgd78059 		 * At this stage we should clear the OBF flag because that
6874135Sgd78059 		 * data has been used. IBF is still valid so do not clear that.
6884135Sgd78059 		 */
6894135Sgd78059 		status &= ~(H8_STR_OBF);
6904135Sgd78059 		bscbus_process_sending(csp, status);
6914135Sgd78059 	}
6924135Sgd78059 }
6934135Sgd78059 
6944135Sgd78059 static boolean_t
bscbus_process_waiting(struct bscbus_channel_state * csp,uint8_t status,uint8_t data)6954135Sgd78059 bscbus_process_waiting(struct bscbus_channel_state *csp,
6964135Sgd78059     uint8_t status, uint8_t data)
6974135Sgd78059 {
6984135Sgd78059 	uint8_t rcvd = 0;
6994135Sgd78059 	boolean_t ready = B_FALSE;
7004135Sgd78059 	uint8_t tmp;
7014135Sgd78059 
7024135Sgd78059 	if (status & H8_STR_OBF) {
7034135Sgd78059 		csp->reply[rcvd = csp->index] = data;
7044135Sgd78059 		if (++rcvd < BSCBUS_BUFSIZE)
7054135Sgd78059 			csp->index = rcvd;
7064135Sgd78059 
7074135Sgd78059 		bscbus_trace(csp, 'D', "bscbus_process_waiting",
7084135Sgd78059 		    "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
7094135Sgd78059 		    rcvd,
7104135Sgd78059 		    csp->reply[0], csp->reply[1],
7114135Sgd78059 		    csp->reply[2], csp->reply[3],
7124135Sgd78059 		    csp->reply[4], csp->reply[5],
7134135Sgd78059 		    csp->reply[6], csp->reply[7]);
7144135Sgd78059 	}
7154135Sgd78059 
7164135Sgd78059 	if (rcvd == 0) {
7174135Sgd78059 		/*
7184135Sgd78059 		 * No bytes received this time through (though there
7194135Sgd78059 		 * might be a partial packet sitting in the buffer).
7204135Sgd78059 		 */
7214135Sgd78059 		/* EMPTY */
7224135Sgd78059 		;
7234135Sgd78059 	} else if (rcvd >= BSCBUS_BUFSIZE) {
7244135Sgd78059 		/*
7254135Sgd78059 		 * Buffer overflow; discard the data & treat as an error
7264135Sgd78059 		 * (even if the last byte read did claim to terminate a
7274135Sgd78059 		 * packet, it can't be a valid one 'cos it's too long!)
7284135Sgd78059 		 */
7294135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_ERROR_OFLOW, status, data);
7304135Sgd78059 		csp->index = 0;
7314135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
7324135Sgd78059 		csp->error = LOMBUS_ERR_OFLOW;
7334135Sgd78059 		ready = B_TRUE;
7344135Sgd78059 	} else if ((data & BSCBUS_LAST) == 0) {
7354135Sgd78059 		/*
7364135Sgd78059 		 * Packet not yet complete; leave the partial packet in
7374135Sgd78059 		 * the buffer for later ...
7384135Sgd78059 		 */
7394135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_REPLY, status, data);
7404135Sgd78059 	} else if ((data & BSCBUS_MASK) != BSCBUS_STATUS) {
7414135Sgd78059 		/* Invalid "status" byte - maybe an echo of the command? */
7424135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_ERROR_STATUS, status, data);
7434135Sgd78059 
7444135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
7454135Sgd78059 		csp->error = LOMBUS_ERR_BADSTATUS;
7464135Sgd78059 		ready = B_TRUE;
7474135Sgd78059 	} else if ((data & BSCBUS_SEQ) != csp->sequence) {
7484135Sgd78059 		/* Wrong sequence number!  Flag this as an error */
7494135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_ERROR_SEQ, status, data);
7504135Sgd78059 
7514135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
7524135Sgd78059 		csp->error = LOMBUS_ERR_SEQUENCE;
7534135Sgd78059 		ready = B_TRUE;
7544135Sgd78059 	} else {
7554135Sgd78059 		/*
7564135Sgd78059 		 * Finally, we know that's it's a valid reply to our
7574135Sgd78059 		 * last command.  Update the ASYNC status, derive the
7584135Sgd78059 		 * reply parameter (if any), and check the ERROR bit
7594135Sgd78059 		 * to find out what the parameter means.
7604135Sgd78059 		 *
7614135Sgd78059 		 * Note that not all the values read/assigned here
7624135Sgd78059 		 * are meaningful, but it doesn't matter; the waiting
7634135Sgd78059 		 * thread will know which one(s) it should check.
7644135Sgd78059 		 */
7654135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_COMPLETE, status, data);
7664135Sgd78059 		csp->async = (data & BSCBUS_STATUS_ASYNC) ? 1 : 0;
7674135Sgd78059 
7684135Sgd78059 		tmp = ((data & BSCBUS_STATUS_MSB) ? 0x80 : 0) | csp->reply[0];
7694135Sgd78059 		if (data & BSCBUS_STATUS_ERR) {
7704135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
7714135Sgd78059 			csp->error = tmp;
7724135Sgd78059 		} else {
7734135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_READY;
7744135Sgd78059 			csp->result = tmp;
7754135Sgd78059 		}
7764135Sgd78059 		ready = B_TRUE;
7774135Sgd78059 	}
7784135Sgd78059 	return (ready);
7794135Sgd78059 }
7804135Sgd78059 
7814135Sgd78059 /*
7824135Sgd78059  * Packet receive handler
7834135Sgd78059  *
7844135Sgd78059  * This routine should be called from the low-level softint,
7854135Sgd78059  * or bscbus_cmd() (for polled operation), with the
7864135Sgd78059  * low-level mutex already held.
7874135Sgd78059  */
7884135Sgd78059 static void
bscbus_process(struct bscbus_channel_state * csp,uint8_t status,uint8_t data)7894135Sgd78059 bscbus_process(struct bscbus_channel_state *csp,
7904135Sgd78059     uint8_t status, uint8_t data)
7914135Sgd78059 {
7924135Sgd78059 	boolean_t ready = B_FALSE;
7934135Sgd78059 
7944135Sgd78059 	ASSERT(mutex_owned(csp->lo_mutex));
7954135Sgd78059 
7964135Sgd78059 	if ((status & H8_STR_OBF) || (status & H8_STR_IBF)) {
7974135Sgd78059 		bscbus_trace(csp, 'D', "bscbus_process",
7984135Sgd78059 		    "state %d; error $%x",
7994135Sgd78059 		    csp->cmdstate, csp->error);
8004135Sgd78059 	}
8014135Sgd78059 
8024135Sgd78059 	switch (csp->cmdstate) {
8034135Sgd78059 	case BSCBUS_CMDSTATE_CLEARING:
8044135Sgd78059 		bscbus_process_clearing(csp, status, data);
8054135Sgd78059 		break;
8064135Sgd78059 	case BSCBUS_CMDSTATE_SENDING:
8074135Sgd78059 		bscbus_process_sending(csp, status);
8084135Sgd78059 		break;
8094135Sgd78059 	case BSCBUS_CMDSTATE_PENDING:
8104135Sgd78059 		bscbus_process_pending(csp, status);
8114135Sgd78059 		break;
8124135Sgd78059 	case BSCBUS_CMDSTATE_WAITING:
8134135Sgd78059 		ready = bscbus_process_waiting(csp, status, data);
8144135Sgd78059 		break;
8154135Sgd78059 	default:
8164135Sgd78059 		/* Nothing to do */
8174135Sgd78059 		break;
8184135Sgd78059 	}
8194135Sgd78059 
8204135Sgd78059 	/*
8214135Sgd78059 	 * Check for timeouts - but only if the command has not yet
8224135Sgd78059 	 * completed (ready is true when command completes in this
8234135Sgd78059 	 * call to bscbus_process OR cmdstate is READY or ERROR if
8244135Sgd78059 	 * this is a spurious call to bscbus_process i.e. a spurious
8254135Sgd78059 	 * interrupt)
8264135Sgd78059 	 */
8274135Sgd78059 	if (!ready &&
8284135Sgd78059 	    ((ddi_get_lbolt() - csp->deadline) > 0) &&
8294135Sgd78059 	    csp->cmdstate != BSCBUS_CMDSTATE_READY &&
8304135Sgd78059 	    csp->cmdstate != BSCBUS_CMDSTATE_ERROR) {
8314135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_process",
8324135Sgd78059 		    "timeout previous state %d; error $%x",
8334135Sgd78059 		    csp->cmdstate, csp->error);
8344135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_ERROR_TOUT, status, data);
8354135Sgd78059 		if (csp->cmdstate == BSCBUS_CMDSTATE_CLEARING) {
8364135Sgd78059 			/* Move onto sending because busy might be stuck */
8374135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
8384135Sgd78059 			/* Extend timeout relative to original start time */
8394135Sgd78059 			csp->deadline += drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
8404135Sgd78059 		} else if (csp->cmdstate != BSCBUS_CMDSTATE_IDLE) {
8414135Sgd78059 			csp->cmdstate = BSCBUS_CMDSTATE_ERROR;
8424135Sgd78059 			csp->error = LOMBUS_ERR_TIMEOUT;
8434135Sgd78059 		}
8444135Sgd78059 		ready = B_TRUE;
8454135Sgd78059 	}
8464135Sgd78059 
8474135Sgd78059 	if ((status & H8_STR_OBF) || (status & H8_STR_IBF) || ready) {
8484135Sgd78059 		bscbus_trace(csp, 'D', "bscbus_process",
8494135Sgd78059 		    "last $%02x; state %d; error $%x; ready %d",
8504135Sgd78059 		    data, csp->cmdstate, csp->error, ready);
8514135Sgd78059 	}
8524135Sgd78059 	if (ready)
8534135Sgd78059 		cv_broadcast(csp->lo_cv);
8544135Sgd78059 }
8554135Sgd78059 
8564135Sgd78059 static uint_t
bscbus_hwintr(caddr_t arg)8574135Sgd78059 bscbus_hwintr(caddr_t arg)
8584135Sgd78059 {
8594135Sgd78059 	struct bscbus_channel_state *csp = (void *)arg;
8604135Sgd78059 
8614135Sgd78059 	uint8_t status;
8624135Sgd78059 	uint8_t data = 0xb0 /* Dummy value */;
8634135Sgd78059 
8644135Sgd78059 	mutex_enter(csp->lo_mutex);
8654135Sgd78059 	/*
8664135Sgd78059 	 * Read the registers to ensure that the interrupt is cleared.
8674135Sgd78059 	 * Status must be read first because reading data changes the
8684135Sgd78059 	 * status.
8694135Sgd78059 	 * We always read the data because that clears the interrupt down.
8704135Sgd78059 	 * This is horrible hardware semantics but we have to do it!
8714135Sgd78059 	 */
8724135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
8734135Sgd78059 	data = bscbus_get_reg(csp, H8_ODR);
8744135Sgd78059 	if (!(status & H8_STR_OBF)) {
8754135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_V1INTRUNCL, status, data);
8764135Sgd78059 		csp->unclaimed_count++;
8774135Sgd78059 	} else {
8784135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_V1INTR, status, data);
8794135Sgd78059 	}
8804135Sgd78059 	if (status & H8_STR_TOKENPROTOCOL) {
8814135Sgd78059 		bscbus_process(csp, status, data);
8824135Sgd78059 		if (csp->interrupt_failed) {
8834135Sgd78059 			bscbus_trace(csp, 'I', "bscbus_hwintr:",
8844135Sgd78059 			    "interrupt fault cleared channel %d", csp->chno);
8854135Sgd78059 			csp->interrupt_failed = B_FALSE;
8864135Sgd78059 			csp->poll_hz = drv_usectohz(BSCBUS_CMD_POLL / 1000);
8874135Sgd78059 		}
8884135Sgd78059 	}
8894135Sgd78059 
8904135Sgd78059 	mutex_exit(csp->lo_mutex);
8914135Sgd78059 	return (DDI_INTR_CLAIMED);
8924135Sgd78059 }
8934135Sgd78059 
8944135Sgd78059 void
bscbus_poll(struct bscbus_channel_state * csp)8954135Sgd78059 bscbus_poll(struct bscbus_channel_state *csp)
8964135Sgd78059 {
8974135Sgd78059 	/*
8984135Sgd78059 	 * This routine is only called if we timeout in userland
8994135Sgd78059 	 * waiting for an interrupt. This generally means that we have
9004135Sgd78059 	 * lost interrupt capabilities or that something has gone
9014135Sgd78059 	 * wrong.  In this case we are allowed to access the hardware
9024135Sgd78059 	 * and read the data register if necessary.
9034135Sgd78059 	 * If interrupts return then recovery actions should mend us!
9044135Sgd78059 	 */
9054135Sgd78059 	uint8_t status;
9064135Sgd78059 	uint8_t data = 0xfa; /* Dummy value */
9074135Sgd78059 
9084135Sgd78059 	ASSERT(mutex_owned(csp->lo_mutex));
9094135Sgd78059 
9104135Sgd78059 	/* Should look for data to receive */
9114135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
9124135Sgd78059 	if (status & H8_STR_OBF) {
9134135Sgd78059 		/* There is data available */
9144135Sgd78059 		data = bscbus_get_reg(csp, H8_ODR);
9154135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_PROCESS, status, data);
9164135Sgd78059 	}
9174135Sgd78059 	bscbus_process(csp, status, data);
9184135Sgd78059 }
9194135Sgd78059 
9204135Sgd78059 /*
9214135Sgd78059  * Serial protocol
9224135Sgd78059  *
9234135Sgd78059  * This routine builds a command and sets it in progress.
9244135Sgd78059  */
9254135Sgd78059 static uint8_t
bscbus_cmd(HANDLE_TYPE * hdlp,ptrdiff_t vreg,uint_t val,uint_t cmd)9264135Sgd78059 bscbus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd)
9274135Sgd78059 {
9284135Sgd78059 	struct bscbus_channel_state *csp;
9294135Sgd78059 	clock_t start;
9304135Sgd78059 	uint8_t status;
9314135Sgd78059 
9324135Sgd78059 	/*
9334135Sgd78059 	 * First of all, wait for the interface to be available.
9344135Sgd78059 	 *
9354135Sgd78059 	 * NOTE: we blow through all the mutex/cv/state checking and
9364135Sgd78059 	 * preempt any command in progress if the system is panicking!
9374135Sgd78059 	 */
9384135Sgd78059 	csp = HANDLE_PRIVATE(hdlp);
9394135Sgd78059 	mutex_enter(csp->lo_mutex);
9404135Sgd78059 	while (csp->cmdstate != BSCBUS_CMDSTATE_IDLE && !ddi_in_panic())
9414135Sgd78059 		cv_wait(csp->lo_cv, csp->lo_mutex);
9424135Sgd78059 
9434135Sgd78059 	csp->cmdstate = BSCBUS_CMDSTATE_BUSY;
9444135Sgd78059 	csp->sequence = (csp->sequence + BSCBUS_SEQ_LSB) & BSCBUS_SEQ;
9454135Sgd78059 
9464135Sgd78059 	/*
9474135Sgd78059 	 * We have exclusive ownership, so assemble the command (backwards):
9484135Sgd78059 	 *
9494135Sgd78059 	 * [byte 0]	Command:	modified by XADDR and/or WMSB bits
9504135Sgd78059 	 * [Optional] Parameter: 	Value to write (low 7 bits)
9514135Sgd78059 	 * [Optional] Parameter: 	Register number (high 7 bits)
9524135Sgd78059 	 * [Optional] Parameter: 	Register number (low 7 bits)
9534135Sgd78059 	 */
9544135Sgd78059 	csp->cmdp = &csp->cmdbuf[0];
9554135Sgd78059 	*csp->cmdp++ = BSCBUS_CMD | csp->sequence | cmd;
9564135Sgd78059 	switch (cmd) {
9574135Sgd78059 	case BSCBUS_CMD_WRITE:
9584135Sgd78059 		*csp->cmdp++ = val & 0x7f;
9594135Sgd78059 		if (val >= 0x80)
9604135Sgd78059 			csp->cmdbuf[0] |= BSCBUS_CMD_WMSB;
9614135Sgd78059 		/*FALLTHRU*/
9624135Sgd78059 	case BSCBUS_CMD_READ:
9634135Sgd78059 		if (BSCBUS_VREG_HI(vreg) != 0) {
9644135Sgd78059 			*csp->cmdp++ = BSCBUS_VREG_HI(vreg);
9654135Sgd78059 			csp->cmdbuf[0] |= BSCBUS_CMD_XADDR;
9664135Sgd78059 		}
9674135Sgd78059 		*csp->cmdp++ = BSCBUS_VREG_LO(vreg);
9684135Sgd78059 		/*FALLTHRU*/
9694135Sgd78059 	case BSCBUS_CMD_NOP:
9704135Sgd78059 		break;
9714135Sgd78059 	}
9724135Sgd78059 
9734135Sgd78059 	/*
9744135Sgd78059 	 * Check and update the H8 h/w fault status before accessing
9754135Sgd78059 	 * the chip registers.  If there's a (new or previous) fault,
9764135Sgd78059 	 * we'll run through the protocol but won't really touch the
9774135Sgd78059 	 * hardware and all commands will timeout.  If a previously
9784135Sgd78059 	 * discovered fault has now gone away (!), then we can (try to)
9794135Sgd78059 	 * proceed with the new command (probably a probe).
9804135Sgd78059 	 */
9814135Sgd78059 	bscbus_check_fault_status(csp);
9824135Sgd78059 
9834135Sgd78059 	/*
9844135Sgd78059 	 * Prepare for the command (to be processed by the interrupt
9854135Sgd78059 	 * handler and/or polling loop below), and wait for a response
9864135Sgd78059 	 * or timeout.
9874135Sgd78059 	 */
9884135Sgd78059 	start = ddi_get_lbolt();
9894135Sgd78059 	csp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
9904135Sgd78059 	csp->error = 0;
9914135Sgd78059 	csp->index = 0;
9924135Sgd78059 	csp->result = DUMMY_VALUE;
9934135Sgd78059 
9944135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
9954135Sgd78059 	if (status & H8_STR_BUSY) {
9964135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0xfd);
9974135Sgd78059 		/*
9984135Sgd78059 		 * Must ensure that the busy state has cleared before
9994135Sgd78059 		 * sending the command
10004135Sgd78059 		 */
10014135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_CLEARING;
10024135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_cmd",
10034135Sgd78059 		    "h8 reporting status (%x) busy - clearing", status);
10044135Sgd78059 	} else {
10054135Sgd78059 		/* It is clear to send the command immediately */
10064135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_SENDING;
10074135Sgd78059 		bscbus_trace(csp, 'P', "bscbus_cmd",
10084135Sgd78059 		    "sending first byte of command, status %x", status);
10094135Sgd78059 		bscbus_poll(csp);
10104135Sgd78059 	}
10114135Sgd78059 
10124135Sgd78059 	csp->poll_hz = drv_usectohz(
10137656SSherry.Moore@Sun.COM 	    (csp->interrupt_failed ?
10147656SSherry.Moore@Sun.COM 	    BSCBUS_CMD_POLLNOINTS : BSCBUS_CMD_POLL) / 1000);
10154135Sgd78059 
10164135Sgd78059 	while ((csp->cmdstate != BSCBUS_CMDSTATE_READY) &&
10174135Sgd78059 	    (csp->cmdstate != BSCBUS_CMDSTATE_ERROR)) {
10184135Sgd78059 		ASSERT(csp->cmdstate != BSCBUS_CMDSTATE_IDLE);
10194135Sgd78059 
1020*11066Srafael.vanoni@sun.com 		if ((cv_reltimedwait(csp->lo_cv, csp->lo_mutex,
1021*11066Srafael.vanoni@sun.com 		    csp->poll_hz, TR_CLOCK_TICK) == -1) &&
10224135Sgd78059 		    csp->cmdstate != BSCBUS_CMDSTATE_READY &&
10234135Sgd78059 		    csp->cmdstate != BSCBUS_CMDSTATE_ERROR) {
10244135Sgd78059 			if (!csp->interrupt_failed) {
10254135Sgd78059 				bscbus_trace(csp, 'I', "bscbus_cmd:",
10264135Sgd78059 				    "interrupt_failed channel %d", csp->chno);
10274135Sgd78059 				csp->interrupt_failed = B_TRUE;
10284135Sgd78059 				csp->poll_hz = drv_usectohz(
10297656SSherry.Moore@Sun.COM 				    BSCBUS_CMD_POLLNOINTS / 1000);
10304135Sgd78059 			}
10314135Sgd78059 			bscbus_poll(csp);
10324135Sgd78059 		}
10334135Sgd78059 	}
10344135Sgd78059 
10354135Sgd78059 	/*
10364135Sgd78059 	 * The return value may not be meaningful but retrieve it anyway
10374135Sgd78059 	 */
10384135Sgd78059 	val = csp->result;
10394135Sgd78059 	if (bscbus_faulty(csp)) {
10404135Sgd78059 		val = DUMMY_VALUE;
10414135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW;
10424135Sgd78059 	} else if (csp->cmdstate != BSCBUS_CMDSTATE_READY) {
10434135Sgd78059 		/*
10444135Sgd78059 		 * Some problem here ... transfer the error code from
10454135Sgd78059 		 * the per-instance state to the per-handle fault flag.
10464135Sgd78059 		 * The error code shouldn't be zero!
10474135Sgd78059 		 */
10484135Sgd78059 		if (csp->error != 0)
10494135Sgd78059 			HANDLE_FAULT(hdlp) = csp->error;
10504135Sgd78059 		else
10514135Sgd78059 			HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE;
10524135Sgd78059 	}
10534135Sgd78059 
10544135Sgd78059 	/*
10554135Sgd78059 	 * All done now!
10564135Sgd78059 	 */
10574135Sgd78059 	csp->index = 0;
10584135Sgd78059 	csp->cmdstate = BSCBUS_CMDSTATE_IDLE;
10594135Sgd78059 	cv_broadcast(csp->lo_cv);
10604135Sgd78059 	mutex_exit(csp->lo_mutex);
10614135Sgd78059 
10624135Sgd78059 	return (val);
10634135Sgd78059 }
10644135Sgd78059 
10654135Sgd78059 /*
10664135Sgd78059  * Space 0 - LOM virtual register access
10674135Sgd78059  * Only 8-bit accesses are supported.
10684135Sgd78059  */
10694135Sgd78059 static uint8_t
bscbus_vreg_get8(HANDLE_TYPE * hdlp,uint8_t * addr)10704135Sgd78059 bscbus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
10714135Sgd78059 {
10724135Sgd78059 	ptrdiff_t offset;
10734135Sgd78059 
10744135Sgd78059 	/*
10754135Sgd78059 	 * Check the offset that the caller has added to the base address
10764135Sgd78059 	 * against the length of the mapping originally requested.
10774135Sgd78059 	 */
10784135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
10794135Sgd78059 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10804135Sgd78059 		/*
10814135Sgd78059 		 * Invalid access - flag a fault and return a dummy value
10824135Sgd78059 		 */
10834135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10844135Sgd78059 		return (DUMMY_VALUE);
10854135Sgd78059 	}
10864135Sgd78059 
10874135Sgd78059 	/*
10884135Sgd78059 	 * Derive the virtual register number and run the command
10894135Sgd78059 	 */
10904135Sgd78059 	return (bscbus_cmd(hdlp, ADDR_TO_VREG(addr), 0, BSCBUS_CMD_READ));
10914135Sgd78059 }
10924135Sgd78059 
10934135Sgd78059 static void
bscbus_vreg_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)10944135Sgd78059 bscbus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
10954135Sgd78059 {
10964135Sgd78059 	ptrdiff_t offset;
10974135Sgd78059 
10984135Sgd78059 	/*
10994135Sgd78059 	 * Check the offset that the caller has added to the base address
11004135Sgd78059 	 * against the length of the mapping originally requested.
11014135Sgd78059 	 */
11024135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
11034135Sgd78059 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11044135Sgd78059 		/*
11054135Sgd78059 		 * Invalid access - flag a fault and return
11064135Sgd78059 		 */
11074135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11084135Sgd78059 		return;
11094135Sgd78059 	}
11104135Sgd78059 
11114135Sgd78059 	/*
11124135Sgd78059 	 * Derive the virtual register number and run the command
11134135Sgd78059 	 */
11144135Sgd78059 	(void) bscbus_cmd(hdlp, ADDR_TO_VREG(addr), val, BSCBUS_CMD_WRITE);
11154135Sgd78059 }
11164135Sgd78059 
11174135Sgd78059 static void
bscbus_vreg_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11184135Sgd78059 bscbus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11194135Sgd78059 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11204135Sgd78059 {
11214135Sgd78059 	size_t inc;
11224135Sgd78059 
11234135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11244135Sgd78059 	for (; repcount--; dev_addr += inc)
11254135Sgd78059 		*host_addr++ = bscbus_vreg_get8(hdlp, dev_addr);
11264135Sgd78059 }
11274135Sgd78059 
11284135Sgd78059 static void
bscbus_vreg_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11294135Sgd78059 bscbus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11304135Sgd78059 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11314135Sgd78059 {
11324135Sgd78059 	size_t inc;
11334135Sgd78059 
11344135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11354135Sgd78059 	for (; repcount--; dev_addr += inc)
11364135Sgd78059 		bscbus_vreg_put8(hdlp, dev_addr, *host_addr++);
11374135Sgd78059 }
11384135Sgd78059 
11394135Sgd78059 
11404135Sgd78059 /*
11414135Sgd78059  * Space 1 - LOM watchdog pat register access
11424135Sgd78059  * Only 8-bit accesses are supported.
11434135Sgd78059  *
11444135Sgd78059  * Reads have no effect and return 0.
11454135Sgd78059  *
11464135Sgd78059  * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
11474135Sgd78059  * way of zeroing the destination area ;-) and still won't pat the dog.
11484135Sgd78059  *
11494135Sgd78059  * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
11504135Sgd78059  * only count as a single pat, no matter how many bytes the caller
11514135Sgd78059  * says to write, as the inter-pat time is VERY long compared with
11524135Sgd78059  * the time it will take to read the memory source area.
11534135Sgd78059  */
11544135Sgd78059 
11554135Sgd78059 static uint8_t
bscbus_pat_get8(HANDLE_TYPE * hdlp,uint8_t * addr)11564135Sgd78059 bscbus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
11574135Sgd78059 {
11584135Sgd78059 	ptrdiff_t offset;
11594135Sgd78059 
11604135Sgd78059 	/*
11614135Sgd78059 	 * Check the offset that the caller has added to the base address
11624135Sgd78059 	 * against the length of the mapping originally requested.
11634135Sgd78059 	 */
11644135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
11654135Sgd78059 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11664135Sgd78059 		/*
11674135Sgd78059 		 * Invalid access - flag a fault and return a dummy value
11684135Sgd78059 		 */
11694135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11704135Sgd78059 		return (DUMMY_VALUE);
11714135Sgd78059 	}
11724135Sgd78059 
11734135Sgd78059 	return (0);
11744135Sgd78059 }
11754135Sgd78059 
11764135Sgd78059 static void
bscbus_pat_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)11774135Sgd78059 bscbus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
11784135Sgd78059 {
11794135Sgd78059 	struct bscbus_channel_state *csp;
11804135Sgd78059 	ptrdiff_t offset;
11814135Sgd78059 
11824135Sgd78059 	/*
11834135Sgd78059 	 * Check the offset that the caller has added to the base address
11844135Sgd78059 	 * against the length of the mapping originally requested.
11854135Sgd78059 	 */
11864135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
11874135Sgd78059 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11884135Sgd78059 		/*
11894135Sgd78059 		 * Invalid access - flag a fault and return
11904135Sgd78059 		 */
11914135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11924135Sgd78059 		return;
11934135Sgd78059 	}
11944135Sgd78059 
11954135Sgd78059 	csp = HANDLE_PRIVATE(hdlp);
11964135Sgd78059 	mutex_enter(csp->dog_mutex);
11974135Sgd78059 	bscbus_pat_dog(csp, val);
11984135Sgd78059 	mutex_exit(csp->dog_mutex);
11994135Sgd78059 }
12004135Sgd78059 
12014135Sgd78059 static void
bscbus_pat_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)12024135Sgd78059 bscbus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
12034135Sgd78059 	uint8_t *dev_addr, size_t repcount, uint_t flags)
12044135Sgd78059 {
12054135Sgd78059 	size_t inc;
12064135Sgd78059 
12074135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12084135Sgd78059 	for (; repcount--; dev_addr += inc)
12094135Sgd78059 		*host_addr++ = bscbus_pat_get8(hdlp, dev_addr);
12104135Sgd78059 }
12114135Sgd78059 
12124135Sgd78059 static void
bscbus_pat_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)12134135Sgd78059 bscbus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
12144135Sgd78059 	uint8_t *dev_addr, size_t repcount, uint_t flags)
12154135Sgd78059 {
12164135Sgd78059 	size_t inc;
12174135Sgd78059 
12184135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12194135Sgd78059 	for (; repcount--; dev_addr += inc)
12204135Sgd78059 		bscbus_pat_put8(hdlp, dev_addr, *host_addr++);
12214135Sgd78059 }
12224135Sgd78059 
12234135Sgd78059 
12244135Sgd78059 /*
12254135Sgd78059  * Space 2 - LOM async event flag register access
12264135Sgd78059  * Only 16-bit accesses are supported.
12274135Sgd78059  */
12284135Sgd78059 static uint16_t
bscbus_event_get16(HANDLE_TYPE * hdlp,uint16_t * addr)12294135Sgd78059 bscbus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
12304135Sgd78059 {
12314135Sgd78059 	struct bscbus_channel_state *csp;
12324135Sgd78059 	ptrdiff_t offset;
12334135Sgd78059 
12344135Sgd78059 	/*
12354135Sgd78059 	 * Check the offset that the caller has added to the base address
12364135Sgd78059 	 * against the length of the mapping orignally requested.
12374135Sgd78059 	 */
12384135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
12394135Sgd78059 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
12404135Sgd78059 		/*
12414135Sgd78059 		 * Invalid access - flag a fault and return a dummy value
12424135Sgd78059 		 */
12434135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
12444135Sgd78059 		return (DUMMY_VALUE);
12454135Sgd78059 	}
12464135Sgd78059 
12474135Sgd78059 	/*
12484135Sgd78059 	 * Return the value of the asynchronous-event-pending flag
12494135Sgd78059 	 * as passed back by the LOM at the end of the last command.
12504135Sgd78059 	 */
12514135Sgd78059 	csp = HANDLE_PRIVATE(hdlp);
12524135Sgd78059 	return (csp->async);
12534135Sgd78059 }
12544135Sgd78059 
12554135Sgd78059 static void
bscbus_event_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)12564135Sgd78059 bscbus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
12574135Sgd78059 {
12584135Sgd78059 	ptrdiff_t offset;
12594135Sgd78059 
12604135Sgd78059 	_NOTE(ARGUNUSED(val))
12614135Sgd78059 
12624135Sgd78059 	/*
12634135Sgd78059 	 * Check the offset that the caller has added to the base address
12644135Sgd78059 	 * against the length of the mapping originally requested.
12654135Sgd78059 	 */
12664135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
12674135Sgd78059 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
12684135Sgd78059 		/*
12694135Sgd78059 		 * Invalid access - flag a fault and return
12704135Sgd78059 		 */
12714135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
12724135Sgd78059 		return;
12734135Sgd78059 	}
12744135Sgd78059 
12754135Sgd78059 	/*
12764135Sgd78059 	 * The user can't overwrite the asynchronous-event-pending flag!
12774135Sgd78059 	 */
12784135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO;
12794135Sgd78059 }
12804135Sgd78059 
12814135Sgd78059 static void
bscbus_event_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)12824135Sgd78059 bscbus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
12834135Sgd78059 	uint16_t *dev_addr, size_t repcount, uint_t flags)
12844135Sgd78059 {
12854135Sgd78059 	size_t inc;
12864135Sgd78059 
12874135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12884135Sgd78059 	for (; repcount--; dev_addr += inc)
12894135Sgd78059 		*host_addr++ = bscbus_event_get16(hdlp, dev_addr);
12904135Sgd78059 }
12914135Sgd78059 
12924135Sgd78059 static void
bscbus_event_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)12934135Sgd78059 bscbus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
12944135Sgd78059 	uint16_t *dev_addr, size_t repcount, uint_t flags)
12954135Sgd78059 {
12964135Sgd78059 	size_t inc;
12974135Sgd78059 
12984135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12994135Sgd78059 	for (; repcount--; dev_addr += inc)
13004135Sgd78059 		bscbus_event_put16(hdlp, dev_addr, *host_addr++);
13014135Sgd78059 }
13024135Sgd78059 
13034135Sgd78059 
13044135Sgd78059 /*
13054135Sgd78059  * All spaces - access handle fault information
13064135Sgd78059  * Only 32-bit accesses are supported.
13074135Sgd78059  */
13084135Sgd78059 static uint32_t
bscbus_meta_get32(HANDLE_TYPE * hdlp,uint32_t * addr)13094135Sgd78059 bscbus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr)
13104135Sgd78059 {
13114135Sgd78059 	struct bscbus_channel_state *csp;
13124135Sgd78059 	ptrdiff_t offset;
13134135Sgd78059 
13144135Sgd78059 	/*
13154135Sgd78059 	 * Derive the offset that the caller has added to the base
13164135Sgd78059 	 * address originally returned, and use it to determine
13174135Sgd78059 	 * which meta-register is to be accessed ...
13184135Sgd78059 	 */
13194135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
13204135Sgd78059 	switch (offset) {
13214135Sgd78059 	case LOMBUS_FAULT_REG:
13224135Sgd78059 		/*
13234135Sgd78059 		 * This meta-register provides a code for the most
13244135Sgd78059 		 * recent virtual register access fault, if any.
13254135Sgd78059 		 */
13264135Sgd78059 		return (HANDLE_FAULT(hdlp));
13274135Sgd78059 
13284135Sgd78059 	case LOMBUS_PROBE_REG:
13294135Sgd78059 		/*
13304135Sgd78059 		 * Reading this meta-register clears any existing fault
13314135Sgd78059 		 * (at the virtual, not the hardware access layer), then
13324135Sgd78059 		 * runs a NOP command and returns the fault code from that.
13334135Sgd78059 		 */
13344135Sgd78059 		HANDLE_FAULT(hdlp) = 0;
13354135Sgd78059 		(void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP);
13364135Sgd78059 		return (HANDLE_FAULT(hdlp));
13374135Sgd78059 
13384135Sgd78059 	case LOMBUS_ASYNC_REG:
13394135Sgd78059 		/*
13404135Sgd78059 		 * Obsolescent - but still supported for backwards
13414135Sgd78059 		 * compatibility.  This is an alias for the newer
13424135Sgd78059 		 * LOMBUS_EVENT_REG, but doesn't require a separate
13434135Sgd78059 		 * "reg" entry and ddi_regs_map_setup() call.
13444135Sgd78059 		 *
13454135Sgd78059 		 * It returns the value of the asynchronous-event-pending
13464135Sgd78059 		 * flag as passed back by the BSC at the end of the last
13474135Sgd78059 		 * completed command.
13484135Sgd78059 		 */
13494135Sgd78059 		csp = HANDLE_PRIVATE(hdlp);
13504135Sgd78059 		return (csp->async);
13514135Sgd78059 
13524135Sgd78059 	default:
13534135Sgd78059 		/*
13544135Sgd78059 		 * Invalid access - flag a fault and return a dummy value
13554135Sgd78059 		 */
13564135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13574135Sgd78059 		return (DUMMY_VALUE);
13584135Sgd78059 	}
13594135Sgd78059 }
13604135Sgd78059 
13614135Sgd78059 static void
bscbus_meta_put32(HANDLE_TYPE * hdlp,uint32_t * addr,uint32_t val)13624135Sgd78059 bscbus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val)
13634135Sgd78059 {
13644135Sgd78059 	ptrdiff_t offset;
13654135Sgd78059 
13664135Sgd78059 	/*
13674135Sgd78059 	 * Derive the offset that the caller has added to the base
13684135Sgd78059 	 * address originally returned, and use it to determine
13694135Sgd78059 	 * which meta-register is to be accessed ...
13704135Sgd78059 	 */
13714135Sgd78059 	offset = ADDR_TO_OFFSET(addr, hdlp);
13724135Sgd78059 	switch (offset) {
13734135Sgd78059 	case LOMBUS_FAULT_REG:
13744135Sgd78059 		/*
13754135Sgd78059 		 * This meta-register contains a code for the most
13764135Sgd78059 		 * recent virtual register access fault, if any.
13774135Sgd78059 		 * It can be cleared simply by writing 0 to it.
13784135Sgd78059 		 */
13794135Sgd78059 		HANDLE_FAULT(hdlp) = val;
13804135Sgd78059 		return;
13814135Sgd78059 
13824135Sgd78059 	case LOMBUS_PROBE_REG:
13834135Sgd78059 		/*
13844135Sgd78059 		 * Writing this meta-register clears any existing fault
13854135Sgd78059 		 * (at the virtual, not the hardware acess layer), then
13864135Sgd78059 		 * runs a NOP command.  The caller can check the fault
13874135Sgd78059 		 * code later if required.
13884135Sgd78059 		 */
13894135Sgd78059 		HANDLE_FAULT(hdlp) = 0;
13904135Sgd78059 		(void) bscbus_cmd(hdlp, 0, 0, BSCBUS_CMD_NOP);
13914135Sgd78059 		return;
13924135Sgd78059 
13934135Sgd78059 	default:
13944135Sgd78059 		/*
13954135Sgd78059 		 * Invalid access - flag a fault
13964135Sgd78059 		 */
13974135Sgd78059 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13984135Sgd78059 		return;
13994135Sgd78059 	}
14004135Sgd78059 }
14014135Sgd78059 
14024135Sgd78059 static void
bscbus_meta_rep_get32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)14034135Sgd78059 bscbus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
14044135Sgd78059 	uint32_t *dev_addr, size_t repcount, uint_t flags)
14054135Sgd78059 {
14064135Sgd78059 	size_t inc;
14074135Sgd78059 
14084135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
14094135Sgd78059 	for (; repcount--; dev_addr += inc)
14104135Sgd78059 		*host_addr++ = bscbus_meta_get32(hdlp, dev_addr);
14114135Sgd78059 }
14124135Sgd78059 
14134135Sgd78059 static void
bscbus_meta_rep_put32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)14144135Sgd78059 bscbus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
14154135Sgd78059 	uint32_t *dev_addr, size_t repcount, uint_t flags)
14164135Sgd78059 {
14174135Sgd78059 	size_t inc;
14184135Sgd78059 
14194135Sgd78059 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
14204135Sgd78059 	for (; repcount--; dev_addr += inc)
14214135Sgd78059 		bscbus_meta_put32(hdlp, dev_addr, *host_addr++);
14224135Sgd78059 }
14234135Sgd78059 
14244135Sgd78059 
14254135Sgd78059 /*
14264135Sgd78059  * Finally, some dummy functions for all unsupported access
14274135Sgd78059  * space/size/mode combinations ...
14284135Sgd78059  */
14294135Sgd78059 static uint8_t
bscbus_no_get8(HANDLE_TYPE * hdlp,uint8_t * addr)14304135Sgd78059 bscbus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
14314135Sgd78059 {
14324135Sgd78059 	_NOTE(ARGUNUSED(addr))
14334135Sgd78059 
14344135Sgd78059 	/*
14354135Sgd78059 	 * Invalid access - flag a fault and return a dummy value
14364135Sgd78059 	 */
14374135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14384135Sgd78059 	return (DUMMY_VALUE);
14394135Sgd78059 }
14404135Sgd78059 
14414135Sgd78059 static void
bscbus_no_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)14424135Sgd78059 bscbus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
14434135Sgd78059 {
14444135Sgd78059 	_NOTE(ARGUNUSED(addr, val))
14454135Sgd78059 
14464135Sgd78059 	/*
14474135Sgd78059 	 * Invalid access - flag a fault
14484135Sgd78059 	 */
14494135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14504135Sgd78059 }
14514135Sgd78059 
14524135Sgd78059 static void
bscbus_no_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)14534135Sgd78059 bscbus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
14544135Sgd78059 		uint8_t *dev_addr, size_t repcount, uint_t flags)
14554135Sgd78059 {
14564135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14574135Sgd78059 
14584135Sgd78059 	/*
14594135Sgd78059 	 * Invalid access - flag a fault
14604135Sgd78059 	 */
14614135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14624135Sgd78059 }
14634135Sgd78059 
14644135Sgd78059 static void
bscbus_no_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)14654135Sgd78059 bscbus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
14664135Sgd78059 	uint8_t *dev_addr, size_t repcount, uint_t flags)
14674135Sgd78059 {
14684135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14694135Sgd78059 
14704135Sgd78059 	/*
14714135Sgd78059 	 * Invalid access - flag a fault
14724135Sgd78059 	 */
14734135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14744135Sgd78059 }
14754135Sgd78059 
14764135Sgd78059 static uint16_t
bscbus_no_get16(HANDLE_TYPE * hdlp,uint16_t * addr)14774135Sgd78059 bscbus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
14784135Sgd78059 {
14794135Sgd78059 	_NOTE(ARGUNUSED(addr))
14804135Sgd78059 
14814135Sgd78059 	/*
14824135Sgd78059 	 * Invalid access - flag a fault and return a dummy value
14834135Sgd78059 	 */
14844135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14854135Sgd78059 	return (DUMMY_VALUE);
14864135Sgd78059 }
14874135Sgd78059 
14884135Sgd78059 static void
bscbus_no_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)14894135Sgd78059 bscbus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
14904135Sgd78059 {
14914135Sgd78059 	_NOTE(ARGUNUSED(addr, val))
14924135Sgd78059 
14934135Sgd78059 	/*
14944135Sgd78059 	 * Invalid access - flag a fault
14954135Sgd78059 	 */
14964135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14974135Sgd78059 }
14984135Sgd78059 
14994135Sgd78059 static void
bscbus_no_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)15004135Sgd78059 bscbus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
15014135Sgd78059 		uint16_t *dev_addr, size_t repcount, uint_t flags)
15024135Sgd78059 {
15034135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
15044135Sgd78059 
15054135Sgd78059 	/*
15064135Sgd78059 	 * Invalid access - flag a fault
15074135Sgd78059 	 */
15084135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15094135Sgd78059 }
15104135Sgd78059 
15114135Sgd78059 static void
bscbus_no_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)15124135Sgd78059 bscbus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
15134135Sgd78059 	uint16_t *dev_addr, size_t repcount, uint_t flags)
15144135Sgd78059 {
15154135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
15164135Sgd78059 
15174135Sgd78059 	/*
15184135Sgd78059 	 * Invalid access - flag a fault
15194135Sgd78059 	 */
15204135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15214135Sgd78059 }
15224135Sgd78059 
15234135Sgd78059 static uint64_t
bscbus_no_get64(HANDLE_TYPE * hdlp,uint64_t * addr)15244135Sgd78059 bscbus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr)
15254135Sgd78059 {
15264135Sgd78059 	_NOTE(ARGUNUSED(addr))
15274135Sgd78059 
15284135Sgd78059 	/*
15294135Sgd78059 	 * Invalid access - flag a fault and return a dummy value
15304135Sgd78059 	 */
15314135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15324135Sgd78059 	return (DUMMY_VALUE);
15334135Sgd78059 }
15344135Sgd78059 
15354135Sgd78059 static void
bscbus_no_put64(HANDLE_TYPE * hdlp,uint64_t * addr,uint64_t val)15364135Sgd78059 bscbus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val)
15374135Sgd78059 {
15384135Sgd78059 	_NOTE(ARGUNUSED(addr, val))
15394135Sgd78059 
15404135Sgd78059 	/*
15414135Sgd78059 	 * Invalid access - flag a fault
15424135Sgd78059 	 */
15434135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15444135Sgd78059 }
15454135Sgd78059 
15464135Sgd78059 static void
bscbus_no_rep_get64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)15474135Sgd78059 bscbus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
15484135Sgd78059 	uint64_t *dev_addr, size_t repcount, uint_t flags)
15494135Sgd78059 {
15504135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
15514135Sgd78059 
15524135Sgd78059 	/*
15534135Sgd78059 	 * Invalid access - flag a fault
15544135Sgd78059 	 */
15554135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15564135Sgd78059 }
15574135Sgd78059 
15584135Sgd78059 static void
bscbus_no_rep_put64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)15594135Sgd78059 bscbus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
15604135Sgd78059 	uint64_t *dev_addr, size_t repcount, uint_t flags)
15614135Sgd78059 {
15624135Sgd78059 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
15634135Sgd78059 
15644135Sgd78059 	/*
15654135Sgd78059 	 * Invalid access - flag a fault
15664135Sgd78059 	 */
15674135Sgd78059 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
15684135Sgd78059 }
15694135Sgd78059 
15704135Sgd78059 static int
bscbus_acc_fault_check(HANDLE_TYPE * hdlp)15714135Sgd78059 bscbus_acc_fault_check(HANDLE_TYPE *hdlp)
15724135Sgd78059 {
15734135Sgd78059 	return (HANDLE_FAULT(hdlp) != 0);
15744135Sgd78059 }
15754135Sgd78059 
15764135Sgd78059 /*
15774135Sgd78059  * Hardware setup - ensure that there are no pending transactions and
15784135Sgd78059  * hence no pending interrupts. We do this be ensuring that the BSC is
15794135Sgd78059  * not reporting a busy condition and that it does not have any data
15804135Sgd78059  * pending in its output buffer.
15814135Sgd78059  * This is important because if we have pending interrupts at attach
15824135Sgd78059  * time Solaris will hang due to bugs in ddi_get_iblock_cookie.
15834135Sgd78059  */
15844135Sgd78059 static void
bscbus_hw_reset(struct bscbus_channel_state * csp)15854135Sgd78059 bscbus_hw_reset(struct bscbus_channel_state *csp)
15864135Sgd78059 {
15874135Sgd78059 	int64_t timeout;
15884135Sgd78059 	uint8_t status;
15894135Sgd78059 
15904135Sgd78059 	if (csp->map_count == 0) {
15914135Sgd78059 		/* No-one using this instance - no need to reset hardware */
15924135Sgd78059 		return;
15934135Sgd78059 	}
15944135Sgd78059 
15954135Sgd78059 	bscbus_trace(csp, 'R', "bscbus_hw_reset",
15964135Sgd78059 	    "resetting channel %d", csp->chno);
15974135Sgd78059 
15984135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
15994135Sgd78059 	if (status & H8_STR_BUSY) {
16004135Sgd78059 		/*
16014135Sgd78059 		 * Give the h8 time to complete a reply.
16024135Sgd78059 		 * In practice we should never worry about this
16034135Sgd78059 		 * because whenever we get here it will have been
16044135Sgd78059 		 * long enough for the h8 to complete a reply
16054135Sgd78059 		 */
16064135Sgd78059 		bscbus_cmd_log(csp, BSC_CMD_BUSY, status, 0);
16074135Sgd78059 		bscbus_trace(csp, 'R', "bscbus_hw_reset",
16084135Sgd78059 		    "h8 reporting status (%x) busy - waiting", status);
16094135Sgd78059 		if (ddi_in_panic()) {
16104135Sgd78059 			drv_usecwait(BSCBUS_HWRESET_POLL/1000);
16114135Sgd78059 		} else {
16124135Sgd78059 			delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000));
16134135Sgd78059 		}
16144135Sgd78059 	}
16154135Sgd78059 	/* Reply should be completed by now. Try to clear busy status */
16164135Sgd78059 	status = bscbus_get_reg(csp, H8_STR);
16174135Sgd78059 	if (status & (H8_STR_BUSY | H8_STR_OBF)) {
16184135Sgd78059 		bscbus_trace(csp, 'R', "bscbus_hw_reset",
16194135Sgd78059 		    "clearing busy status for channel %d", csp->chno);
16204135Sgd78059 
16214135Sgd78059 		for (timeout = BSCBUS_HWRESET_TIMEOUT;
16224135Sgd78059 		    (timeout > 0);
16234135Sgd78059 		    timeout -= BSCBUS_HWRESET_POLL) {
16244135Sgd78059 			if (status & H8_STR_OBF) {
16254135Sgd78059 				(void) bscbus_get_reg(csp, H8_ODR);
16264135Sgd78059 				if (!(status & H8_STR_BUSY)) {
16274135Sgd78059 					/* We are done */
16284135Sgd78059 					break;
16294135Sgd78059 				}
16304135Sgd78059 			}
16314135Sgd78059 			if (ddi_in_panic()) {
16324135Sgd78059 				drv_usecwait(BSCBUS_HWRESET_POLL/1000);
16334135Sgd78059 			} else {
16344135Sgd78059 				delay(drv_usectohz(BSCBUS_HWRESET_POLL/1000));
16354135Sgd78059 			}
16364135Sgd78059 			status = bscbus_get_reg(csp, H8_STR);
16374135Sgd78059 		}
16384135Sgd78059 		if (timeout <= 0) {
16394135Sgd78059 			cmn_err(CE_WARN, "bscbus_hw_reset: timed out "
16404135Sgd78059 			    "clearing busy status");
16414135Sgd78059 		}
16424135Sgd78059 	}
16434135Sgd78059 	/*
16444135Sgd78059 	 * We read ODR just in case there is a pending interrupt with
16454135Sgd78059 	 * no data. This is potentially dangerous because we could get
16464135Sgd78059 	 * out of sync due to race conditions BUT at this point the
16474135Sgd78059 	 * channel should be idle so it is safe.
16484135Sgd78059 	 */
16494135Sgd78059 	(void) bscbus_get_reg(csp, H8_ODR);
16504135Sgd78059 }
16514135Sgd78059 
16524135Sgd78059 /*
16534135Sgd78059  * Higher-level setup & teardown
16544135Sgd78059  */
16554135Sgd78059 
16564135Sgd78059 static void
bscbus_offline(struct bscbus_state * ssp)16574135Sgd78059 bscbus_offline(struct bscbus_state *ssp)
16584135Sgd78059 {
16594135Sgd78059 	if (ssp->h8_handle != NULL)
16604135Sgd78059 		ddi_regs_map_free(&ssp->h8_handle);
16614135Sgd78059 	ssp->h8_handle = NULL;
16624135Sgd78059 	ssp->h8_regs = NULL;
16634135Sgd78059 }
16644135Sgd78059 
16654135Sgd78059 static int
bscbus_online(struct bscbus_state * ssp)16664135Sgd78059 bscbus_online(struct bscbus_state *ssp)
16674135Sgd78059 {
16684135Sgd78059 	ddi_acc_handle_t h;
16694135Sgd78059 	caddr_t p;
16704135Sgd78059 	int nregs;
16714135Sgd78059 	int err;
16724135Sgd78059 
16734135Sgd78059 	ssp->h8_handle = NULL;
16744135Sgd78059 	ssp->h8_regs = (void *)NULL;
16754135Sgd78059 	ssp->per_channel_regs = B_FALSE;
16764135Sgd78059 
16774135Sgd78059 	if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS)
16784135Sgd78059 		nregs = 0;
16794135Sgd78059 
16804135Sgd78059 	switch (nregs) {
16814135Sgd78059 	case 1:
16824135Sgd78059 		/*
16834135Sgd78059 		 *  regset 0 represents the H8 interface registers
16844135Sgd78059 		 */
16854135Sgd78059 		err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0,
16864135Sgd78059 		    bscbus_dev_acc_attr, &h);
16874135Sgd78059 		if (err != DDI_SUCCESS)
16884135Sgd78059 			return (EIO);
16894135Sgd78059 
16904135Sgd78059 		ssp->h8_handle = h;
16914135Sgd78059 		ssp->h8_regs = (void *)p;
16924135Sgd78059 		break;
16934135Sgd78059 
16944135Sgd78059 	case 0:
16954135Sgd78059 		/*
16964135Sgd78059 		 *  If no registers are defined, succeed vacuously;
16974135Sgd78059 		 *  commands will be accepted, but we fake the accesses.
16984135Sgd78059 		 */
16994135Sgd78059 		break;
17004135Sgd78059 
17014135Sgd78059 	default:
17024135Sgd78059 		/*
17034135Sgd78059 		 * Remember that we are using the new register scheme.
17044135Sgd78059 		 * reg set 0 is chan 0
17054135Sgd78059 		 * reg set 1 is chan 1 ...
17064135Sgd78059 		 * Interrupts are specified in that order but later
17074135Sgd78059 		 * channels may not have interrupts.
17084135Sgd78059 		 * We map the regs later on a per channel basis.
17094135Sgd78059 		 */
17104135Sgd78059 		ssp->per_channel_regs = B_TRUE;
17114135Sgd78059 		break;
17124135Sgd78059 	}
17134135Sgd78059 	return (0);
17144135Sgd78059 }
17154135Sgd78059 
17164135Sgd78059 static int
bscbus_claim_channel(struct bscbus_channel_state * csp,boolean_t map_dog)17174135Sgd78059 bscbus_claim_channel(struct bscbus_channel_state *csp, boolean_t map_dog)
17184135Sgd78059 {
17194135Sgd78059 	int err;
17204135Sgd78059 
17214135Sgd78059 	mutex_enter(csp->ssp->ch_mutex);
17224135Sgd78059 	csp->map_count++;
17234135Sgd78059 	bscbus_trace(csp, 'C', "bscbus_claim_channel",
17244135Sgd78059 	    "claim channel for channel %d, count %d",
17254135Sgd78059 	    csp->chno, csp->map_count);
17264135Sgd78059 
17274135Sgd78059 	if (csp->map_count == 1) {
17284135Sgd78059 		/* No-one is using this channel - initialise it */
17294135Sgd78059 		bscbus_trace(csp, 'C', "bscbus_claim_channel",
17304135Sgd78059 		    "initialise channel %d, count %d",
17314135Sgd78059 		    csp->chno, csp->map_count);
17324135Sgd78059 
17334135Sgd78059 		mutex_init(csp->dog_mutex, NULL, MUTEX_DRIVER,
17344135Sgd78059 		    (void *)(uintptr_t)__ipltospl(SPL7 - 1));
17354135Sgd78059 		csp->map_dog = map_dog;
17364135Sgd78059 		csp->interrupt_failed = B_FALSE;
17374135Sgd78059 		csp->cmdstate = BSCBUS_CMDSTATE_IDLE;
17384135Sgd78059 		csp->pat_retry_count = 0;
17394135Sgd78059 		csp->pat_fail_count = 0;
17404135Sgd78059 
17414135Sgd78059 		/* Map appropriate register set for this channel */
17424135Sgd78059 		if (csp->ssp->per_channel_regs == B_TRUE) {
17434135Sgd78059 			ddi_acc_handle_t h;
17444135Sgd78059 			caddr_t p;
17454135Sgd78059 
17464135Sgd78059 			err = ddi_regs_map_setup(csp->ssp->dip, csp->chno,
17474135Sgd78059 			    &p, 0, 0, bscbus_dev_acc_attr, &h);
17484135Sgd78059 
17494135Sgd78059 			if (err != DDI_SUCCESS) {
17504135Sgd78059 				goto failed1;
17514135Sgd78059 			}
17524135Sgd78059 
17534135Sgd78059 			csp->ch_handle = h;
17544135Sgd78059 			csp->ch_regs = (void *)p;
17554135Sgd78059 
17564135Sgd78059 			bscbus_trace(csp, 'C', "bscbus_claim_channel",
17574135Sgd78059 			    "mapped chno=%d ch_handle=%d ch_regs=%p",
17584135Sgd78059 			    csp->chno, h, p);
17594135Sgd78059 		} else {
17604135Sgd78059 			/*
17614135Sgd78059 			 * if using the old reg property scheme use the
17624135Sgd78059 			 * common mapping.
17634135Sgd78059 			 */
17644135Sgd78059 			csp->ch_handle = csp->ssp->h8_handle;
17654135Sgd78059 			csp->ch_regs =
17664135Sgd78059 			    csp->ssp->h8_regs +
17674135Sgd78059 			    BSCBUS_CHANNEL_TO_OFFSET(csp->chno);
17684135Sgd78059 		}
17694135Sgd78059 
17704135Sgd78059 		/* Ensure no interrupts pending prior to getting iblk cookie */
17714135Sgd78059 		bscbus_hw_reset(csp);
17724135Sgd78059 
17734135Sgd78059 		if (csp->map_dog == 1) {
17744135Sgd78059 			/*
17754135Sgd78059 			 * we don't want lo_mutex to be initialised
17764135Sgd78059 			 * with an iblock cookie if we are the wdog,
17774135Sgd78059 			 * because we don't use interrupts.
17784135Sgd78059 			 */
17794135Sgd78059 			mutex_init(csp->lo_mutex, NULL,
17804135Sgd78059 			    MUTEX_DRIVER, NULL);
17814135Sgd78059 			cv_init(csp->lo_cv, NULL,
17824135Sgd78059 			    CV_DRIVER, NULL);
17834135Sgd78059 			csp->unclaimed_count = 0;
17844135Sgd78059 		} else {
17854135Sgd78059 			int ninterrupts;
17864135Sgd78059 
17874135Sgd78059 			/*
17884135Sgd78059 			 * check that there is an interrupt for this
17894135Sgd78059 			 * this channel. If we fail to setup interrupts we
17904135Sgd78059 			 * must unmap the registers and fail.
17914135Sgd78059 			 */
17924135Sgd78059 			err = ddi_dev_nintrs(csp->ssp->dip, &ninterrupts);
17934135Sgd78059 
17944135Sgd78059 			if (err != DDI_SUCCESS) {
17954135Sgd78059 				ninterrupts = 0;
17964135Sgd78059 			}
17974135Sgd78059 
17984135Sgd78059 			if (ninterrupts <= csp->chno) {
17994135Sgd78059 				cmn_err(CE_WARN,
18004135Sgd78059 				    "no interrupt available for "
18014135Sgd78059 				    "bscbus channel %d", csp->chno);
18024135Sgd78059 				goto failed2;
18034135Sgd78059 			}
18044135Sgd78059 
18054135Sgd78059 			if (ddi_intr_hilevel(csp->ssp->dip, csp->chno) != 0) {
18064135Sgd78059 				cmn_err(CE_WARN,
18074135Sgd78059 				    "bscbus interrupts are high "
18084135Sgd78059 				    "level - channel not usable.");
18094135Sgd78059 				goto failed2;
18104135Sgd78059 			} else {
18114135Sgd78059 				err = ddi_get_iblock_cookie(csp->ssp->dip,
18124135Sgd78059 				    csp->chno, &csp->lo_iblk);
18134135Sgd78059 				if (err != DDI_SUCCESS) {
18144135Sgd78059 					goto failed2;
18154135Sgd78059 				}
18164135Sgd78059 
18174135Sgd78059 				mutex_init(csp->lo_mutex, NULL,
18184135Sgd78059 				    MUTEX_DRIVER, csp->lo_iblk);
18194135Sgd78059 				cv_init(csp->lo_cv, NULL,
18204135Sgd78059 				    CV_DRIVER, NULL);
18214135Sgd78059 				csp->unclaimed_count = 0;
18224135Sgd78059 
18234135Sgd78059 				err = ddi_add_intr(csp->ssp->dip, csp->chno,
18244135Sgd78059 				    &csp->lo_iblk, NULL,
18254135Sgd78059 				    bscbus_hwintr, (caddr_t)csp);
18264135Sgd78059 				if (err != DDI_SUCCESS) {
18274135Sgd78059 					cv_destroy(csp->lo_cv);
18284135Sgd78059 					mutex_destroy(csp->lo_mutex);
18294135Sgd78059 					goto failed2;
18304135Sgd78059 				}
18314135Sgd78059 			}
18324135Sgd78059 		}
18334135Sgd78059 		/*
18344135Sgd78059 		 * The channel is now live and may
18354135Sgd78059 		 * receive interrupts
18364135Sgd78059 		 */
18374135Sgd78059 	} else if (csp->map_dog != map_dog) {
18384135Sgd78059 		bscbus_trace(csp, 'C', "bscbus_claim_channel",
18394135Sgd78059 		    "request conflicts with previous mapping. old %x, new %x.",
18404135Sgd78059 		    csp->map_dog, map_dog);
18414135Sgd78059 		goto failed1;
18424135Sgd78059 	}
18434135Sgd78059 	mutex_exit(csp->ssp->ch_mutex);
18444135Sgd78059 	return (1);
18454135Sgd78059 
18464135Sgd78059 failed2:
18474135Sgd78059 	/* unmap regs for failed channel */
18484135Sgd78059 	if (csp->ssp->per_channel_regs == B_TRUE) {
18494135Sgd78059 		ddi_regs_map_free(&csp->ch_handle);
18504135Sgd78059 	}
18514135Sgd78059 	csp->ch_handle = NULL;
18524135Sgd78059 	csp->ch_regs = (void *)NULL;
18534135Sgd78059 failed1:
18544135Sgd78059 	csp->map_count--;
18554135Sgd78059 	mutex_exit(csp->ssp->ch_mutex);
18564135Sgd78059 	return (0);
18574135Sgd78059 }
18584135Sgd78059 
18594135Sgd78059 static void
bscbus_release_channel(struct bscbus_channel_state * csp)18604135Sgd78059 bscbus_release_channel(struct bscbus_channel_state *csp)
18614135Sgd78059 {
18624135Sgd78059 	mutex_enter(csp->ssp->ch_mutex);
18634135Sgd78059 	if (csp->map_count == 1) {
18644135Sgd78059 		/* No-one is now using this channel - shutdown channel */
18654135Sgd78059 		bscbus_trace(csp, 'C', "bscbus_release_channel",
18664135Sgd78059 		    "shutdown channel %d, count %d",
18674135Sgd78059 		    csp->chno, csp->map_count);
18684135Sgd78059 
18694135Sgd78059 		if (csp->map_dog == 0) {
18707656SSherry.Moore@Sun.COM 			ASSERT(!ddi_intr_hilevel(csp->ssp->dip, csp->chno));
18717656SSherry.Moore@Sun.COM 			ddi_remove_intr(csp->ssp->dip, csp->chno, csp->lo_iblk);
18724135Sgd78059 		}
18734135Sgd78059 		cv_destroy(csp->lo_cv);
18744135Sgd78059 		mutex_destroy(csp->lo_mutex);
18754135Sgd78059 		mutex_destroy(csp->dog_mutex);
18764135Sgd78059 		bscbus_hw_reset(csp);
18774135Sgd78059 
18784135Sgd78059 		/* unmap registers if using the new register scheme */
18794135Sgd78059 		if (csp->ssp->per_channel_regs == B_TRUE) {
18804135Sgd78059 			ddi_regs_map_free(&csp->ch_handle);
18814135Sgd78059 		}
18824135Sgd78059 		csp->ch_handle = NULL;
18834135Sgd78059 		csp->ch_regs = (void *)NULL;
18844135Sgd78059 	}
18854135Sgd78059 	csp->map_count--;
18864135Sgd78059 	bscbus_trace(csp, 'C', "bscbus_release_channel",
18874135Sgd78059 	    "release channel %d, count %d",
18884135Sgd78059 	    csp->chno, csp->map_count);
18894135Sgd78059 	mutex_exit(csp->ssp->ch_mutex);
18904135Sgd78059 }
18914135Sgd78059 
18924135Sgd78059 
18934135Sgd78059 /*
18944135Sgd78059  *  Nexus routines
18954135Sgd78059  */
18964135Sgd78059 
18974135Sgd78059 #if	defined(NDI_ACC_HDL_V2)
18984135Sgd78059 
18994135Sgd78059 static const ndi_acc_fns_t bscbus_vreg_acc_fns = {
19004135Sgd78059 	NDI_ACC_FNS_CURRENT,
19014135Sgd78059 	NDI_ACC_FNS_V1,
19024135Sgd78059 
19034135Sgd78059 	bscbus_vreg_get8,
19044135Sgd78059 	bscbus_vreg_put8,
19054135Sgd78059 	bscbus_vreg_rep_get8,
19064135Sgd78059 	bscbus_vreg_rep_put8,
19074135Sgd78059 
19084135Sgd78059 	bscbus_no_get16,
19094135Sgd78059 	bscbus_no_put16,
19104135Sgd78059 	bscbus_no_rep_get16,
19114135Sgd78059 	bscbus_no_rep_put16,
19124135Sgd78059 
19134135Sgd78059 	bscbus_meta_get32,
19144135Sgd78059 	bscbus_meta_put32,
19154135Sgd78059 	bscbus_meta_rep_get32,
19164135Sgd78059 	bscbus_meta_rep_put32,
19174135Sgd78059 
19184135Sgd78059 	bscbus_no_get64,
19194135Sgd78059 	bscbus_no_put64,
19204135Sgd78059 	bscbus_no_rep_get64,
19214135Sgd78059 	bscbus_no_rep_put64,
19224135Sgd78059 
19234135Sgd78059 	bscbus_acc_fault_check
19244135Sgd78059 };
19254135Sgd78059 
19264135Sgd78059 static const ndi_acc_fns_t bscbus_pat_acc_fns = {
19274135Sgd78059 	NDI_ACC_FNS_CURRENT,
19284135Sgd78059 	NDI_ACC_FNS_V1,
19294135Sgd78059 
19304135Sgd78059 	bscbus_pat_get8,
19314135Sgd78059 	bscbus_pat_put8,
19324135Sgd78059 	bscbus_pat_rep_get8,
19334135Sgd78059 	bscbus_pat_rep_put8,
19344135Sgd78059 
19354135Sgd78059 	bscbus_no_get16,
19364135Sgd78059 	bscbus_no_put16,
19374135Sgd78059 	bscbus_no_rep_get16,
19384135Sgd78059 	bscbus_no_rep_put16,
19394135Sgd78059 
19404135Sgd78059 	bscbus_meta_get32,
19414135Sgd78059 	bscbus_meta_put32,
19424135Sgd78059 	bscbus_meta_rep_get32,
19434135Sgd78059 	bscbus_meta_rep_put32,
19444135Sgd78059 
19454135Sgd78059 	bscbus_no_get64,
19464135Sgd78059 	bscbus_no_put64,
19474135Sgd78059 	bscbus_no_rep_get64,
19484135Sgd78059 	bscbus_no_rep_put64,
19494135Sgd78059 
19504135Sgd78059 	bscbus_acc_fault_check
19514135Sgd78059 };
19524135Sgd78059 
19534135Sgd78059 static const ndi_acc_fns_t bscbus_event_acc_fns = {
19544135Sgd78059 	NDI_ACC_FNS_CURRENT,
19554135Sgd78059 	NDI_ACC_FNS_V1,
19564135Sgd78059 
19574135Sgd78059 	bscbus_no_get8,
19584135Sgd78059 	bscbus_no_put8,
19594135Sgd78059 	bscbus_no_rep_get8,
19604135Sgd78059 	bscbus_no_rep_put8,
19614135Sgd78059 
19624135Sgd78059 	bscbus_event_get16,
19634135Sgd78059 	bscbus_event_put16,
19644135Sgd78059 	bscbus_event_rep_get16,
19654135Sgd78059 	bscbus_event_rep_put16,
19664135Sgd78059 
19674135Sgd78059 	bscbus_meta_get32,
19684135Sgd78059 	bscbus_meta_put32,
19694135Sgd78059 	bscbus_meta_rep_get32,
19704135Sgd78059 	bscbus_meta_rep_put32,
19714135Sgd78059 
19724135Sgd78059 	bscbus_no_get64,
19734135Sgd78059 	bscbus_no_put64,
19744135Sgd78059 	bscbus_no_rep_get64,
19754135Sgd78059 	bscbus_no_rep_put64,
19764135Sgd78059 
19774135Sgd78059 	bscbus_acc_fault_check
19784135Sgd78059 };
19794135Sgd78059 
19804135Sgd78059 static int
bscbus_map_handle(struct bscbus_channel_state * csp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ndi_acc_handle_t * hdlp,caddr_t * addrp)19814135Sgd78059 bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op,
19824135Sgd78059 	int space, caddr_t vaddr, off_t len,
19834135Sgd78059 	ndi_acc_handle_t *hdlp, caddr_t *addrp)
19844135Sgd78059 {
19854135Sgd78059 	switch (op) {
19864135Sgd78059 	default:
19874135Sgd78059 		return (DDI_ME_UNIMPLEMENTED);
19884135Sgd78059 
19894135Sgd78059 	case DDI_MO_MAP_LOCKED:
19904135Sgd78059 		if (bscbus_claim_channel(csp,
19917656SSherry.Moore@Sun.COM 		    (space == LOMBUS_PAT_SPACE)) == 0) {
19924135Sgd78059 			return (DDI_ME_GENERIC);
19934135Sgd78059 		}
19944135Sgd78059 
19954135Sgd78059 		switch (space) {
19964135Sgd78059 		default:
19974135Sgd78059 			return (DDI_ME_REGSPEC_RANGE);
19984135Sgd78059 
19994135Sgd78059 		case LOMBUS_VREG_SPACE:
20004135Sgd78059 			ndi_set_acc_fns(hdlp, &bscbus_vreg_acc_fns);
20014135Sgd78059 			break;
20024135Sgd78059 
20034135Sgd78059 		case LOMBUS_PAT_SPACE:
20044135Sgd78059 			ndi_set_acc_fns(hdlp, &bscbus_pat_acc_fns);
20054135Sgd78059 			break;
20064135Sgd78059 
20074135Sgd78059 		case LOMBUS_EVENT_SPACE:
20084135Sgd78059 			ndi_set_acc_fns(hdlp, &bscbus_event_acc_fns);
20094135Sgd78059 			break;
20104135Sgd78059 		}
20114135Sgd78059 		hdlp->ah_addr = *addrp = vaddr;
20124135Sgd78059 		hdlp->ah_len = len;
20134135Sgd78059 		hdlp->ah_bus_private = csp;
20144135Sgd78059 		return (DDI_SUCCESS);
20154135Sgd78059 
20164135Sgd78059 	case DDI_MO_UNMAP:
20174135Sgd78059 		*addrp = NULL;
20184135Sgd78059 		hdlp->ah_bus_private = NULL;
20194135Sgd78059 		bscbus_release_channel(csp);
20204135Sgd78059 		return (DDI_SUCCESS);
20214135Sgd78059 	}
20224135Sgd78059 }
20234135Sgd78059 
20244135Sgd78059 #else
20254135Sgd78059 
20264135Sgd78059 static int
bscbus_map_handle(struct bscbus_channel_state * csp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ddi_acc_hdl_t * hdlp,caddr_t * addrp)20274135Sgd78059 bscbus_map_handle(struct bscbus_channel_state *csp, ddi_map_op_t op,
20284135Sgd78059 	int space, caddr_t vaddr, off_t len,
20294135Sgd78059 	ddi_acc_hdl_t *hdlp, caddr_t *addrp)
20304135Sgd78059 {
20314135Sgd78059 	ddi_acc_impl_t *aip = hdlp->ah_platform_private;
20324135Sgd78059 
20334135Sgd78059 	switch (op) {
20344135Sgd78059 	default:
20354135Sgd78059 		return (DDI_ME_UNIMPLEMENTED);
20364135Sgd78059 
20374135Sgd78059 	case DDI_MO_MAP_LOCKED:
20384135Sgd78059 		if (bscbus_claim_channel(csp,
20397656SSherry.Moore@Sun.COM 		    (space == LOMBUS_PAT_SPACE)) == 0) {
20404135Sgd78059 			return (DDI_ME_GENERIC);
20414135Sgd78059 		}
20424135Sgd78059 
20434135Sgd78059 		switch (space) {
20444135Sgd78059 		default:
20454135Sgd78059 			return (DDI_ME_REGSPEC_RANGE);
20464135Sgd78059 
20474135Sgd78059 		case LOMBUS_VREG_SPACE:
20484135Sgd78059 			aip->ahi_get8 = bscbus_vreg_get8;
20494135Sgd78059 			aip->ahi_put8 = bscbus_vreg_put8;
20504135Sgd78059 			aip->ahi_rep_get8 = bscbus_vreg_rep_get8;
20514135Sgd78059 			aip->ahi_rep_put8 = bscbus_vreg_rep_put8;
20524135Sgd78059 
20534135Sgd78059 			aip->ahi_get16 = bscbus_no_get16;
20544135Sgd78059 			aip->ahi_put16 = bscbus_no_put16;
20554135Sgd78059 			aip->ahi_rep_get16 = bscbus_no_rep_get16;
20564135Sgd78059 			aip->ahi_rep_put16 = bscbus_no_rep_put16;
20574135Sgd78059 
20584135Sgd78059 			aip->ahi_get32 = bscbus_meta_get32;
20594135Sgd78059 			aip->ahi_put32 = bscbus_meta_put32;
20604135Sgd78059 			aip->ahi_rep_get32 = bscbus_meta_rep_get32;
20614135Sgd78059 			aip->ahi_rep_put32 = bscbus_meta_rep_put32;
20624135Sgd78059 
20634135Sgd78059 			aip->ahi_get64 = bscbus_no_get64;
20644135Sgd78059 			aip->ahi_put64 = bscbus_no_put64;
20654135Sgd78059 			aip->ahi_rep_get64 = bscbus_no_rep_get64;
20664135Sgd78059 			aip->ahi_rep_put64 = bscbus_no_rep_put64;
20674135Sgd78059 
20684135Sgd78059 			aip->ahi_fault_check = bscbus_acc_fault_check;
20694135Sgd78059 			break;
20704135Sgd78059 
20714135Sgd78059 		case LOMBUS_PAT_SPACE:
20724135Sgd78059 			aip->ahi_get8 = bscbus_pat_get8;
20734135Sgd78059 			aip->ahi_put8 = bscbus_pat_put8;
20744135Sgd78059 			aip->ahi_rep_get8 = bscbus_pat_rep_get8;
20754135Sgd78059 			aip->ahi_rep_put8 = bscbus_pat_rep_put8;
20764135Sgd78059 
20774135Sgd78059 			aip->ahi_get16 = bscbus_no_get16;
20784135Sgd78059 			aip->ahi_put16 = bscbus_no_put16;
20794135Sgd78059 			aip->ahi_rep_get16 = bscbus_no_rep_get16;
20804135Sgd78059 			aip->ahi_rep_put16 = bscbus_no_rep_put16;
20814135Sgd78059 
20824135Sgd78059 			aip->ahi_get32 = bscbus_meta_get32;
20834135Sgd78059 			aip->ahi_put32 = bscbus_meta_put32;
20844135Sgd78059 			aip->ahi_rep_get32 = bscbus_meta_rep_get32;
20854135Sgd78059 			aip->ahi_rep_put32 = bscbus_meta_rep_put32;
20864135Sgd78059 
20874135Sgd78059 			aip->ahi_get64 = bscbus_no_get64;
20884135Sgd78059 			aip->ahi_put64 = bscbus_no_put64;
20894135Sgd78059 			aip->ahi_rep_get64 = bscbus_no_rep_get64;
20904135Sgd78059 			aip->ahi_rep_put64 = bscbus_no_rep_put64;
20914135Sgd78059 
20924135Sgd78059 			aip->ahi_fault_check = bscbus_acc_fault_check;
20934135Sgd78059 			break;
20944135Sgd78059 
20954135Sgd78059 		case LOMBUS_EVENT_SPACE:
20964135Sgd78059 			aip->ahi_get8 = bscbus_no_get8;
20974135Sgd78059 			aip->ahi_put8 = bscbus_no_put8;
20984135Sgd78059 			aip->ahi_rep_get8 = bscbus_no_rep_get8;
20994135Sgd78059 			aip->ahi_rep_put8 = bscbus_no_rep_put8;
21004135Sgd78059 
21014135Sgd78059 			aip->ahi_get16 = bscbus_event_get16;
21024135Sgd78059 			aip->ahi_put16 = bscbus_event_put16;
21034135Sgd78059 			aip->ahi_rep_get16 = bscbus_event_rep_get16;
21044135Sgd78059 			aip->ahi_rep_put16 = bscbus_event_rep_put16;
21054135Sgd78059 
21064135Sgd78059 			aip->ahi_get32 = bscbus_meta_get32;
21074135Sgd78059 			aip->ahi_put32 = bscbus_meta_put32;
21084135Sgd78059 			aip->ahi_rep_get32 = bscbus_meta_rep_get32;
21094135Sgd78059 			aip->ahi_rep_put32 = bscbus_meta_rep_put32;
21104135Sgd78059 
21114135Sgd78059 			aip->ahi_get64 = bscbus_no_get64;
21124135Sgd78059 			aip->ahi_put64 = bscbus_no_put64;
21134135Sgd78059 			aip->ahi_rep_get64 = bscbus_no_rep_get64;
21144135Sgd78059 			aip->ahi_rep_put64 = bscbus_no_rep_put64;
21154135Sgd78059 
21164135Sgd78059 			aip->ahi_fault_check = bscbus_acc_fault_check;
21174135Sgd78059 			break;
21184135Sgd78059 		}
21194135Sgd78059 		hdlp->ah_addr = *addrp = vaddr;
21204135Sgd78059 		hdlp->ah_len = len;
21214135Sgd78059 		hdlp->ah_bus_private = csp;
21224135Sgd78059 		return (DDI_SUCCESS);
21234135Sgd78059 
21244135Sgd78059 	case DDI_MO_UNMAP:
21254135Sgd78059 		*addrp = NULL;
21264135Sgd78059 		hdlp->ah_bus_private = NULL;
21274135Sgd78059 		bscbus_release_channel(csp);
21284135Sgd78059 		return (DDI_SUCCESS);
21294135Sgd78059 	}
21304135Sgd78059 }
21314135Sgd78059 
21324135Sgd78059 #endif	/* NDI_ACC_HDL_V2 */
21334135Sgd78059 
21344135Sgd78059 static int
bscbus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)21354135Sgd78059 bscbus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
21364135Sgd78059 	off_t off, off_t len, caddr_t *addrp)
21374135Sgd78059 {
21384135Sgd78059 	struct bscbus_child_info *lcip;
21394135Sgd78059 	struct bscbus_state *ssp;
21404135Sgd78059 	lombus_regspec_t *rsp;
21414135Sgd78059 
21424135Sgd78059 	if ((ssp = bscbus_getstate(dip, -1, "bscbus_map")) == NULL)
21434135Sgd78059 		return (DDI_FAILURE);	/* this "can't happen" */
21444135Sgd78059 
21454135Sgd78059 	/*
21464135Sgd78059 	 * Validate mapping request ...
21474135Sgd78059 	 */
21484135Sgd78059 
21494135Sgd78059 	if (mp->map_flags != DDI_MF_KERNEL_MAPPING)
21504135Sgd78059 		return (DDI_ME_UNSUPPORTED);
21514135Sgd78059 	if (mp->map_handlep == NULL)
21524135Sgd78059 		return (DDI_ME_UNSUPPORTED);
21534135Sgd78059 	if (mp->map_type != DDI_MT_RNUMBER)
21544135Sgd78059 		return (DDI_ME_UNIMPLEMENTED);
21554135Sgd78059 	if ((lcip = ddi_get_parent_data(rdip)) == NULL)
21564135Sgd78059 		return (DDI_ME_INVAL);
21574135Sgd78059 	if ((rsp = lcip->rsp) == NULL)
21584135Sgd78059 		return (DDI_ME_INVAL);
21594135Sgd78059 	if (mp->map_obj.rnumber >= lcip->nregs)
21604135Sgd78059 		return (DDI_ME_RNUMBER_RANGE);
21614135Sgd78059 	rsp += mp->map_obj.rnumber;
21624135Sgd78059 	if (off < 0 || off >= rsp->lombus_size)
21634135Sgd78059 		return (DDI_ME_INVAL);
21644135Sgd78059 	if (len == 0)
21654135Sgd78059 		len = rsp->lombus_size-off;
21664135Sgd78059 	if (len < 0)
21674135Sgd78059 		return (DDI_ME_INVAL);
21684135Sgd78059 	if (off+len < 0 || off+len > rsp->lombus_size)
21694135Sgd78059 		return (DDI_ME_INVAL);
21704135Sgd78059 
21714135Sgd78059 	return (bscbus_map_handle(
21727656SSherry.Moore@Sun.COM 	    &ssp->channel[LOMBUS_SPACE_TO_CHANNEL(rsp->lombus_space)],
21737656SSherry.Moore@Sun.COM 	    mp->map_op, LOMBUS_SPACE_TO_REGSET(rsp->lombus_space),
21747656SSherry.Moore@Sun.COM 	    VREG_TO_ADDR(rsp->lombus_base+off), len, mp->map_handlep, addrp));
21754135Sgd78059 }
21764135Sgd78059 
21774135Sgd78059 
21784135Sgd78059 static int
bscbus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)21794135Sgd78059 bscbus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
21804135Sgd78059 	void *arg, void *result)
21814135Sgd78059 {
21824135Sgd78059 	struct bscbus_child_info *lcip;
21834135Sgd78059 	lombus_regspec_t *rsp;
21844135Sgd78059 	dev_info_t *cdip;
21854135Sgd78059 	char addr[32];
21864135Sgd78059 	uint_t nregs;
21874135Sgd78059 	uint_t rnum;
21884135Sgd78059 	int *regs;
21894135Sgd78059 	int limit;
21904135Sgd78059 	int err;
21914135Sgd78059 	int i;
21924135Sgd78059 
21934135Sgd78059 	if (bscbus_getstate(dip, -1, "bscbus_ctlops") == NULL)
21944135Sgd78059 		return (DDI_FAILURE);	/* this "can't happen" */
21954135Sgd78059 
21964135Sgd78059 	switch (op) {
21974135Sgd78059 	default:
21984135Sgd78059 		break;
21994135Sgd78059 
22004135Sgd78059 	case DDI_CTLOPS_INITCHILD:
22014135Sgd78059 		/*
22024135Sgd78059 		 * First, look up and validate the "reg" property.
22034135Sgd78059 		 *
22044135Sgd78059 		 * It must be a non-empty integer array containing a set
22054135Sgd78059 		 * of triples.  Once we've verified that, we can treat it
22064135Sgd78059 		 * as an array of type lombus_regspec_t[], which defines
22074135Sgd78059 		 * the meaning of the elements of each triple:
22084135Sgd78059 		 * +  the first element of each triple must be a valid space
22094135Sgd78059 		 * +  the second and third elements (base, size) of each
22104135Sgd78059 		 *	triple must define a valid subrange of that space
22114135Sgd78059 		 * If it passes all the tests, we save it away for future
22124135Sgd78059 		 * reference in the child's parent-private-data field.
22134135Sgd78059 		 */
22144135Sgd78059 		cdip = arg;
22154135Sgd78059 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
22167656SSherry.Moore@Sun.COM 		    DDI_PROP_DONTPASS, "reg", &regs, &nregs);
22174135Sgd78059 		if (err != DDI_PROP_SUCCESS)
22184135Sgd78059 			return (DDI_FAILURE);
22194135Sgd78059 
22204135Sgd78059 		err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0);
22214135Sgd78059 		nregs /= LOMBUS_REGSPEC_SIZE;
22224135Sgd78059 		rsp = (lombus_regspec_t *)regs;
22234135Sgd78059 		for (i = 0; i < nregs && !err; ++i) {
22244135Sgd78059 			switch (LOMBUS_SPACE_TO_REGSET(rsp[i].lombus_space)) {
22254135Sgd78059 			default:
22264135Sgd78059 				limit = 0;
22274135Sgd78059 				err = 1;
22284135Sgd78059 				cmn_err(CE_WARN,
22294135Sgd78059 				    "child(%p): unknown reg space %d",
22304135Sgd78059 				    (void *)cdip, rsp[i].lombus_space);
22314135Sgd78059 				break;
22324135Sgd78059 
22334135Sgd78059 			case LOMBUS_VREG_SPACE:
22344135Sgd78059 				limit = LOMBUS_MAX_REG+1;
22354135Sgd78059 				break;
22364135Sgd78059 
22374135Sgd78059 			case LOMBUS_PAT_SPACE:
22384135Sgd78059 				limit = LOMBUS_PAT_REG+1;
22394135Sgd78059 				break;
22404135Sgd78059 
22414135Sgd78059 			case LOMBUS_EVENT_SPACE:
22424135Sgd78059 				limit = LOMBUS_EVENT_REG+1;
22434135Sgd78059 				break;
22444135Sgd78059 			}
22454135Sgd78059 
22464135Sgd78059 			err |= (rsp[i].lombus_base < 0);
22474135Sgd78059 			err |= (rsp[i].lombus_base >= limit);
22484135Sgd78059 
22494135Sgd78059 			if (rsp[i].lombus_size == 0)
22504135Sgd78059 				rsp[i].lombus_size = limit-rsp[i].lombus_base;
22514135Sgd78059 
22524135Sgd78059 			err |= (rsp[i].lombus_size < 0);
22534135Sgd78059 			err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0);
22544135Sgd78059 			err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
22554135Sgd78059 
22564135Sgd78059 			err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
22574135Sgd78059 
22584135Sgd78059 		}
22594135Sgd78059 
22604135Sgd78059 		if (err) {
22614135Sgd78059 			ddi_prop_free(regs);
22624135Sgd78059 			return (DDI_FAILURE);
22634135Sgd78059 		}
22644135Sgd78059 
22654135Sgd78059 		lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP);
22664135Sgd78059 		lcip->nregs = nregs;
22674135Sgd78059 		lcip->rsp = rsp;
22684135Sgd78059 		ddi_set_parent_data(cdip, lcip);
22694135Sgd78059 
22704135Sgd78059 		(void) snprintf(addr, sizeof (addr),
22717656SSherry.Moore@Sun.COM 		    "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base);
22724135Sgd78059 		ddi_set_name_addr(cdip, addr);
22734135Sgd78059 
22744135Sgd78059 		return (DDI_SUCCESS);
22754135Sgd78059 
22764135Sgd78059 	case DDI_CTLOPS_UNINITCHILD:
22774135Sgd78059 		cdip = arg;
22784135Sgd78059 		ddi_set_name_addr(cdip, NULL);
22794135Sgd78059 		lcip = ddi_get_parent_data(cdip);
22804135Sgd78059 		ddi_set_parent_data(cdip, NULL);
22814135Sgd78059 		ddi_prop_free(lcip->rsp);
22824135Sgd78059 		kmem_free(lcip, sizeof (*lcip));
22834135Sgd78059 		return (DDI_SUCCESS);
22844135Sgd78059 
22854135Sgd78059 	case DDI_CTLOPS_REPORTDEV:
22864135Sgd78059 		if (rdip == NULL)
22874135Sgd78059 			return (DDI_FAILURE);
22884135Sgd78059 
22894135Sgd78059 		cmn_err(CE_CONT, "?BSC device: %s@%s, %s#%d\n",
22907656SSherry.Moore@Sun.COM 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
22917656SSherry.Moore@Sun.COM 		    ddi_driver_name(dip), ddi_get_instance(dip));
22924135Sgd78059 
22934135Sgd78059 		return (DDI_SUCCESS);
22944135Sgd78059 
22954135Sgd78059 	case DDI_CTLOPS_REGSIZE:
22964135Sgd78059 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
22974135Sgd78059 			return (DDI_FAILURE);
22984135Sgd78059 		if ((rnum = *(uint_t *)arg) >= lcip->nregs)
22994135Sgd78059 			return (DDI_FAILURE);
23004135Sgd78059 		*(off_t *)result = lcip->rsp[rnum].lombus_size;
23014135Sgd78059 		return (DDI_SUCCESS);
23024135Sgd78059 
23034135Sgd78059 	case DDI_CTLOPS_NREGS:
23044135Sgd78059 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
23054135Sgd78059 			return (DDI_FAILURE);
23064135Sgd78059 		*(int *)result = lcip->nregs;
23074135Sgd78059 		return (DDI_SUCCESS);
23084135Sgd78059 	}
23094135Sgd78059 
23104135Sgd78059 	return (ddi_ctlops(dip, rdip, op, arg, result));
23114135Sgd78059 }
23124135Sgd78059 
23134135Sgd78059 
23144135Sgd78059 /*
23154135Sgd78059  * This nexus does not support passing interrupts to leaf drivers, so
23164135Sgd78059  * all the intrspec-related operations just fail as cleanly as possible.
23174135Sgd78059  */
23184135Sgd78059 
23194135Sgd78059 /*ARGSUSED*/
23204135Sgd78059 static int
bscbus_intr_op(dev_info_t * dip,dev_info_t * rdip,ddi_intr_op_t op,ddi_intr_handle_impl_t * hdlp,void * result)23214135Sgd78059 bscbus_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op,
23224135Sgd78059     ddi_intr_handle_impl_t *hdlp, void *result)
23234135Sgd78059 {
23244135Sgd78059 #if defined(__sparc)
23254135Sgd78059 	return (i_ddi_intr_ops(dip, rdip, op, hdlp, result));
23264135Sgd78059 #else
23274135Sgd78059 	_NOTE(ARGUNUSED(dip, rdip, op, hdlp, result))
23284135Sgd78059 	return (DDI_FAILURE);
23294135Sgd78059 #endif
23304135Sgd78059 }
23314135Sgd78059 
23324135Sgd78059 /*
23334135Sgd78059  *  Clean up on detach or failure of attach
23344135Sgd78059  */
23354135Sgd78059 static int
bscbus_unattach(struct bscbus_state * ssp,int instance)23364135Sgd78059 bscbus_unattach(struct bscbus_state *ssp, int instance)
23374135Sgd78059 {
23384135Sgd78059 	int chno;
23394135Sgd78059 
23404135Sgd78059 	if (ssp != NULL) {
23414135Sgd78059 		for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
23424135Sgd78059 			ASSERT(ssp->channel[chno].map_count == 0);
23434135Sgd78059 		}
23444135Sgd78059 		bscbus_offline(ssp);
23454135Sgd78059 		ddi_set_driver_private(ssp->dip, NULL);
23464135Sgd78059 		mutex_destroy(ssp->ch_mutex);
23474135Sgd78059 	}
23484135Sgd78059 #ifdef BSCBUS_LOGSTATUS
23494135Sgd78059 	if (ssp->cmd_log_size != 0) {
23504135Sgd78059 		kmem_free(ssp->cmd_log,
23514135Sgd78059 		    ssp->cmd_log_size * sizeof (bsc_cmd_log_t));
23524135Sgd78059 	}
23534135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
23544135Sgd78059 
23554135Sgd78059 
23564135Sgd78059 	ddi_soft_state_free(bscbus_statep, instance);
23574135Sgd78059 	return (DDI_FAILURE);
23584135Sgd78059 }
23594135Sgd78059 
23604135Sgd78059 /*
23614135Sgd78059  *  Autoconfiguration routines
23624135Sgd78059  */
23634135Sgd78059 
23644135Sgd78059 static int
bscbus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)23654135Sgd78059 bscbus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
23664135Sgd78059 {
23674135Sgd78059 	struct bscbus_state *ssp = NULL;
23684135Sgd78059 	int chno;
23694135Sgd78059 	int instance;
23704135Sgd78059 	int err;
23714135Sgd78059 
23724135Sgd78059 	switch (cmd) {
23734135Sgd78059 	default:
23744135Sgd78059 		return (DDI_FAILURE);
23754135Sgd78059 
23764135Sgd78059 	case DDI_ATTACH:
23774135Sgd78059 		break;
23784135Sgd78059 	}
23794135Sgd78059 
23804135Sgd78059 	/*
23814135Sgd78059 	 *  Allocate the soft-state structure
23824135Sgd78059 	 */
23834135Sgd78059 	instance = ddi_get_instance(dip);
23844135Sgd78059 	if (ddi_soft_state_zalloc(bscbus_statep, instance) != DDI_SUCCESS)
23854135Sgd78059 		return (DDI_FAILURE);
23864135Sgd78059 	if ((ssp = bscbus_getstate(dip, instance, "bscbus_attach")) == NULL)
23874135Sgd78059 		return (bscbus_unattach(ssp, instance));
23884135Sgd78059 	ddi_set_driver_private(dip, ssp);
23894135Sgd78059 
23904135Sgd78059 	/*
23914135Sgd78059 	 *  Initialise devinfo-related fields
23924135Sgd78059 	 */
23934135Sgd78059 	ssp->dip = dip;
23944135Sgd78059 	ssp->majornum = ddi_driver_major(dip);
23954135Sgd78059 	ssp->instance = instance;
23964135Sgd78059 
23974135Sgd78059 	/*
23984135Sgd78059 	 *  Set various options from .conf properties
23994135Sgd78059 	 */
24004135Sgd78059 	ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
24017656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "debug", 0);
24024135Sgd78059 
24034135Sgd78059 	mutex_init(ssp->ch_mutex, NULL, MUTEX_DRIVER, NULL);
24044135Sgd78059 
24054135Sgd78059 #ifdef BSCBUS_LOGSTATUS
24064135Sgd78059 	ssp->cmd_log_size = bscbus_cmd_log_size;
24074135Sgd78059 	if (ssp->cmd_log_size != 0) {
24084135Sgd78059 		ssp->cmd_log_idx = 0;
24097656SSherry.Moore@Sun.COM 		ssp->cmd_log = kmem_zalloc(ssp->cmd_log_size *
24107656SSherry.Moore@Sun.COM 		    sizeof (bsc_cmd_log_t), KM_SLEEP);
24114135Sgd78059 	}
24124135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
24134135Sgd78059 
24144135Sgd78059 	/*
24154135Sgd78059 	 *  Online the hardware ...
24164135Sgd78059 	 */
24174135Sgd78059 	err = bscbus_online(ssp);
24184135Sgd78059 	if (err != 0)
24194135Sgd78059 		return (bscbus_unattach(ssp, instance));
24204135Sgd78059 
24214135Sgd78059 	for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
24224135Sgd78059 		struct bscbus_channel_state *csp = &ssp->channel[chno];
24234135Sgd78059 
24244135Sgd78059 		/*
24254135Sgd78059 		 * Initialise state
24264135Sgd78059 		 * The hardware/interrupts are setup at map time to
24274135Sgd78059 		 * avoid claiming hardware that OBP is using
24284135Sgd78059 		 */
24294135Sgd78059 		csp->ssp = ssp;
24304135Sgd78059 		csp->chno = chno;
24314135Sgd78059 		csp->map_count = 0;
24324135Sgd78059 		csp->map_dog = B_FALSE;
24334135Sgd78059 	}
24344135Sgd78059 
24354135Sgd78059 	/*
24364135Sgd78059 	 *  All done, report success
24374135Sgd78059 	 */
24384135Sgd78059 	ddi_report_dev(dip);
24394135Sgd78059 	return (DDI_SUCCESS);
24404135Sgd78059 }
24414135Sgd78059 
24424135Sgd78059 static int
bscbus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)24434135Sgd78059 bscbus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
24444135Sgd78059 {
24454135Sgd78059 	struct bscbus_state *ssp;
24464135Sgd78059 	int instance;
24474135Sgd78059 
24484135Sgd78059 	switch (cmd) {
24494135Sgd78059 	default:
24504135Sgd78059 		return (DDI_FAILURE);
24514135Sgd78059 
24524135Sgd78059 	case DDI_DETACH:
24534135Sgd78059 		break;
24544135Sgd78059 	}
24554135Sgd78059 
24564135Sgd78059 	instance = ddi_get_instance(dip);
24574135Sgd78059 	if ((ssp = bscbus_getstate(dip, instance, "bscbus_detach")) == NULL)
24584135Sgd78059 		return (DDI_FAILURE);	/* this "can't happen" */
24594135Sgd78059 
24604135Sgd78059 	(void) bscbus_unattach(ssp, instance);
24614135Sgd78059 	return (DDI_SUCCESS);
24624135Sgd78059 }
24634135Sgd78059 
24644135Sgd78059 static int
bscbus_reset(dev_info_t * dip,ddi_reset_cmd_t cmd)24654135Sgd78059 bscbus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
24664135Sgd78059 {
24674135Sgd78059 	struct bscbus_state *ssp;
24684135Sgd78059 	int chno;
24694135Sgd78059 
24704135Sgd78059 	_NOTE(ARGUNUSED(cmd))
24714135Sgd78059 
24724135Sgd78059 	if ((ssp = bscbus_getstate(dip, -1, "bscbus_reset")) == NULL)
24734135Sgd78059 		return (DDI_FAILURE);
24744135Sgd78059 
24754135Sgd78059 	for (chno = 0; chno < BSCBUS_MAX_CHANNELS; chno++) {
24764135Sgd78059 		bscbus_hw_reset(&ssp->channel[chno]);
24774135Sgd78059 	}
24784135Sgd78059 	return (DDI_SUCCESS);
24794135Sgd78059 }
24804135Sgd78059 
24814135Sgd78059 
24824135Sgd78059 /*
24834135Sgd78059  * System interface structures
24844135Sgd78059  */
24854135Sgd78059 
24864135Sgd78059 static struct cb_ops bscbus_cb_ops =
24874135Sgd78059 {
24884135Sgd78059 	nodev,			/* b/c open	*/
24894135Sgd78059 	nodev,			/* b/c close	*/
24904135Sgd78059 	nodev,			/* b   strategy	*/
24914135Sgd78059 	nodev,			/* b   print	*/
24924135Sgd78059 	nodev,			/* b   dump 	*/
24934135Sgd78059 	nodev,			/* c   read	*/
24944135Sgd78059 	nodev,			/* c   write	*/
24954135Sgd78059 	nodev,			/* c   ioctl	*/
24964135Sgd78059 	nodev,			/* c   devmap	*/
24974135Sgd78059 	nodev,			/* c   mmap	*/
24984135Sgd78059 	nodev,			/* c   segmap	*/
24994135Sgd78059 	nochpoll,		/* c   poll	*/
25004135Sgd78059 	ddi_prop_op,		/* b/c prop_op	*/
25014135Sgd78059 	NULL,			/* c   streamtab */
25024135Sgd78059 	D_MP | D_NEW		/* b/c flags	*/
25034135Sgd78059 };
25044135Sgd78059 
25054135Sgd78059 static struct bus_ops bscbus_bus_ops =
25064135Sgd78059 {
25074135Sgd78059 	BUSO_REV,			/* revision		*/
25084135Sgd78059 	bscbus_map,			/* bus_map		*/
25094135Sgd78059 	0,				/* get_intrspec		*/
25104135Sgd78059 	0,				/* add_intrspec		*/
25114135Sgd78059 	0,				/* remove_intrspec	*/
25124135Sgd78059 	i_ddi_map_fault,		/* map_fault		*/
25134135Sgd78059 	ddi_no_dma_map,			/* dma_map		*/
25144135Sgd78059 	ddi_no_dma_allochdl,		/* allocate DMA handle	*/
25154135Sgd78059 	ddi_no_dma_freehdl,		/* free DMA handle	*/
25164135Sgd78059 	ddi_no_dma_bindhdl,		/* bind DMA handle	*/
25174135Sgd78059 	ddi_no_dma_unbindhdl,		/* unbind DMA handle	*/
25184135Sgd78059 	ddi_no_dma_flush,		/* flush DMA		*/
25194135Sgd78059 	ddi_no_dma_win,			/* move DMA window	*/
25204135Sgd78059 	ddi_no_dma_mctl,		/* generic DMA control	*/
25214135Sgd78059 	bscbus_ctlops,			/* generic control	*/
25224135Sgd78059 	ddi_bus_prop_op,		/* prop_op		*/
25234135Sgd78059 	ndi_busop_get_eventcookie,	/* get_eventcookie	*/
25244135Sgd78059 	ndi_busop_add_eventcall,	/* add_eventcall	*/
25254135Sgd78059 	ndi_busop_remove_eventcall,	/* remove_eventcall	*/
25264135Sgd78059 	ndi_post_event,			/* post_event		*/
25274135Sgd78059 	0,				/* interrupt control	*/
25284135Sgd78059 	0,				/* bus_config		*/
25294135Sgd78059 	0,				/* bus_unconfig		*/
25304135Sgd78059 	0,				/* bus_fm_init		*/
25314135Sgd78059 	0,				/* bus_fm_fini		*/
25324135Sgd78059 	0,				/* bus_fm_access_enter	*/
25334135Sgd78059 	0,				/* bus_fm_access_exit	*/
25344135Sgd78059 	0,				/* bus_power		*/
25354135Sgd78059 	bscbus_intr_op			/* bus_intr_op		*/
25364135Sgd78059 };
25374135Sgd78059 
25384135Sgd78059 static struct dev_ops bscbus_dev_ops =
25394135Sgd78059 {
25404135Sgd78059 	DEVO_REV,
25414135Sgd78059 	0,				/* refcount		*/
25424135Sgd78059 	ddi_no_info,			/* getinfo		*/
25434135Sgd78059 	nulldev,			/* identify		*/
25444135Sgd78059 	nulldev,			/* probe		*/
25454135Sgd78059 	bscbus_attach,			/* attach		*/
25464135Sgd78059 	bscbus_detach,			/* detach		*/
25474135Sgd78059 	bscbus_reset,			/* reset		*/
25484135Sgd78059 	&bscbus_cb_ops,			/* driver operations	*/
25497656SSherry.Moore@Sun.COM 	&bscbus_bus_ops,		/* bus operations	*/
25507656SSherry.Moore@Sun.COM 	NULL,				/* power		*/
25517656SSherry.Moore@Sun.COM 	ddi_quiesce_not_needed,			/* quiesce		*/
25524135Sgd78059 };
25534135Sgd78059 
25544135Sgd78059 static struct modldrv modldrv =
25554135Sgd78059 {
25564135Sgd78059 	&mod_driverops,
25577656SSherry.Moore@Sun.COM 	"bscbus driver",
25584135Sgd78059 	&bscbus_dev_ops
25594135Sgd78059 };
25604135Sgd78059 
25614135Sgd78059 static struct modlinkage modlinkage =
25624135Sgd78059 {
25634135Sgd78059 	MODREV_1,
25644135Sgd78059 	{
25654135Sgd78059 		&modldrv,
25664135Sgd78059 		NULL
25674135Sgd78059 	}
25684135Sgd78059 };
25694135Sgd78059 
25704135Sgd78059 
25714135Sgd78059 /*
25724135Sgd78059  *  Dynamic loader interface code
25734135Sgd78059  */
25744135Sgd78059 
25754135Sgd78059 int
_init(void)25764135Sgd78059 _init(void)
25774135Sgd78059 {
25784135Sgd78059 	int err;
25794135Sgd78059 
25804135Sgd78059 	err = ddi_soft_state_init(&bscbus_statep,
25817656SSherry.Moore@Sun.COM 	    sizeof (struct bscbus_state), 0);
25824135Sgd78059 	if (err == DDI_SUCCESS)
25834135Sgd78059 		if ((err = mod_install(&modlinkage)) != DDI_SUCCESS) {
25844135Sgd78059 			ddi_soft_state_fini(&bscbus_statep);
25854135Sgd78059 		}
25864135Sgd78059 
25874135Sgd78059 	return (err);
25884135Sgd78059 }
25894135Sgd78059 
25904135Sgd78059 int
_info(struct modinfo * mip)25914135Sgd78059 _info(struct modinfo *mip)
25924135Sgd78059 {
25934135Sgd78059 	return (mod_info(&modlinkage, mip));
25944135Sgd78059 }
25954135Sgd78059 
25964135Sgd78059 int
_fini(void)25974135Sgd78059 _fini(void)
25984135Sgd78059 {
25994135Sgd78059 	int err;
26004135Sgd78059 
26014135Sgd78059 	if ((err = mod_remove(&modlinkage)) == DDI_SUCCESS) {
26024135Sgd78059 		ddi_soft_state_fini(&bscbus_statep);
26034135Sgd78059 		bscbus_major = NOMAJOR;
26044135Sgd78059 	}
26054135Sgd78059 
26064135Sgd78059 	return (err);
26074135Sgd78059 }
26084135Sgd78059 
26094135Sgd78059 #ifdef BSCBUS_LOGSTATUS
bscbus_cmd_log(struct bscbus_channel_state * csp,bsc_cmd_stamp_t cat,uint8_t status,uint8_t data)26104135Sgd78059 void bscbus_cmd_log(struct bscbus_channel_state *csp, bsc_cmd_stamp_t cat,
26114135Sgd78059     uint8_t status, uint8_t data)
26124135Sgd78059 {
26134135Sgd78059 	int idx;
26144135Sgd78059 	bsc_cmd_log_t *logp;
26154135Sgd78059 	struct bscbus_state *ssp;
26164135Sgd78059 
26174135Sgd78059 	if ((csp) == NULL)
26184135Sgd78059 		return;
26194135Sgd78059 	if ((ssp = (csp)->ssp) == NULL)
26204135Sgd78059 		return;
26214135Sgd78059 	if (ssp->cmd_log_size == 0)
26224135Sgd78059 		return;
26234135Sgd78059 	if ((bscbus_cmd_log_flags & (1 << cat)) == 0)
26244135Sgd78059 		return;
26254135Sgd78059 	idx = atomic_add_32_nv(&ssp->cmd_log_idx, 1);
26264135Sgd78059 	logp = &ssp->cmd_log[idx % ssp->cmd_log_size];
26274135Sgd78059 	logp->bcl_seq = idx;
26284135Sgd78059 	logp->bcl_cat = cat;
26294135Sgd78059 	logp->bcl_now = gethrtime();
26304135Sgd78059 	logp->bcl_chno = csp->chno;
26314135Sgd78059 	logp->bcl_cmdstate = csp->cmdstate;
26324135Sgd78059 	logp->bcl_status = status;
26334135Sgd78059 	logp->bcl_data = data;
26344135Sgd78059 }
26354135Sgd78059 #endif /* BSCBUS_LOGSTATUS */
2636