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