xref: /onnv-gate/usr/src/uts/sun4u/lw2plus/io/lombus.c (revision 11311:639e7bc0b42f)
12305Sstevel /*
22305Sstevel  * CDDL HEADER START
32305Sstevel  *
42305Sstevel  * The contents of this file are subject to the terms of the
55107Seota  * Common Development and Distribution License (the "License").
65107Seota  * You may not use this file except in compliance with the License.
72305Sstevel  *
82305Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92305Sstevel  * or http://www.opensolaris.org/os/licensing.
102305Sstevel  * See the License for the specific language governing permissions
112305Sstevel  * and limitations under the License.
122305Sstevel  *
132305Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
142305Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152305Sstevel  * If applicable, add the following below this CDDL HEADER, with the
162305Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
172305Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
182305Sstevel  *
192305Sstevel  * CDDL HEADER END
202305Sstevel  */
212305Sstevel /*
2211066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
232305Sstevel  * Use is subject to license terms.
242305Sstevel  *
252305Sstevel  * The "lombus" driver provides access to the LOMlite2 virtual registers,
262305Sstevel  * so that its clients (children) need not be concerned with the details
272305Sstevel  * of the access mechanism, which in this case is implemented via a
282305Sstevel  * packet-based protocol over a serial link connected to one of the serial
292305Sstevel  * ports of the SuperIO (SIO) chip.
302305Sstevel  *
312305Sstevel  * On the other hand, this driver doesn't generally know what the virtual
322305Sstevel  * registers signify - only the clients need this information.
332305Sstevel  */
342305Sstevel 
352305Sstevel 
362305Sstevel /*
372305Sstevel  *  Header files
382305Sstevel  */
392305Sstevel 
402305Sstevel #include <sys/types.h>
412305Sstevel #include <sys/conf.h>
422305Sstevel #include <sys/debug.h>
432305Sstevel #include <sys/errno.h>
442305Sstevel #include <sys/file.h>
452305Sstevel #include <sys/intr.h>
462305Sstevel #include <sys/kmem.h>
472305Sstevel #include <sys/membar.h>
482305Sstevel #include <sys/modctl.h>
492305Sstevel #include <sys/note.h>
502305Sstevel #include <sys/open.h>
512305Sstevel #include <sys/poll.h>
522305Sstevel #include <sys/spl.h>
532305Sstevel #include <sys/stat.h>
542305Sstevel #include <sys/strlog.h>
552305Sstevel 
562305Sstevel #include <sys/ddi.h>
572305Sstevel #include <sys/sunddi.h>
582305Sstevel #include <sys/sunndi.h>
592305Sstevel 
602305Sstevel #include <sys/lombus.h>
612305Sstevel 
622305Sstevel 
632305Sstevel #if	defined(NDI_ACC_HDL_V2)
642305Sstevel 
652305Sstevel /*
662305Sstevel  * Compiling for Solaris 9+ with access handle enhancements
672305Sstevel  */
682305Sstevel #define	HANDLE_TYPE		ndi_acc_handle_t
692305Sstevel #define	HANDLE_ADDR(hdlp)	(hdlp->ah_addr)
702305Sstevel #define	HANDLE_FAULT(hdlp)	(hdlp->ah_fault)
712305Sstevel #define	HANDLE_MAPLEN(hdlp)	(hdlp->ah_len)
722305Sstevel #define	HANDLE_PRIVATE(hdlp)	(hdlp->ah_bus_private)
732305Sstevel 
742305Sstevel #else
752305Sstevel 
762305Sstevel /*
772305Sstevel  * Compatibility definitions for backport to Solaris 8
782305Sstevel  */
792305Sstevel #define	HANDLE_TYPE		ddi_acc_impl_t
802305Sstevel #define	HANDLE_ADDR(hdlp)	(hdlp->ahi_common.ah_addr)
812305Sstevel #define	HANDLE_FAULT(hdlp)	(hdlp->ahi_fault)
822305Sstevel #define	HANDLE_MAPLEN(hdlp)	(hdlp->ahi_common.ah_len)
832305Sstevel #define	HANDLE_PRIVATE(hdlp)	(hdlp->ahi_common.ah_bus_private)
842305Sstevel 
852305Sstevel #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
862305Sstevel 
872305Sstevel #endif	/* NDI_ACC_HDL_V2 */
882305Sstevel 
892305Sstevel 
902305Sstevel /*
912305Sstevel  * Local definitions
922305Sstevel  */
932305Sstevel #define	MYNAME			"lombus"
942305Sstevel #define	NOMAJOR			(~(major_t)0)
952305Sstevel #define	DUMMY_VALUE		(~(int8_t)0)
962305Sstevel 
972305Sstevel #define	LOMBUS_INST_TO_MINOR(i)	(i)
982305Sstevel #define	LOMBUS_MINOR_TO_INST(m)	(m)
992305Sstevel 
1002305Sstevel #define	LOMBUS_DUMMY_ADDRESS	((caddr_t)0x0CADD1ED)
1012305Sstevel #define	ADDR_TO_OFFSET(a, hdlp)	((caddr_t)(a) - HANDLE_ADDR(hdlp))
1022305Sstevel #define	ADDR_TO_VREG(a)		((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS)
1032305Sstevel #define	VREG_TO_ADDR(v)		(LOMBUS_DUMMY_ADDRESS + (v))
1042305Sstevel 
1052305Sstevel 
1062305Sstevel /*
1072305Sstevel  * The following definitions are taken from the datasheet
1082305Sstevel  * for the National Semiconductor PC87317 (SuperIO) chip.
1092305Sstevel  *
1102305Sstevel  * This chip implements UART functionality as logical device 6.
1112305Sstevel  * It provides all sorts of wierd modes and extensions, but we
1122305Sstevel  * have chosen to use only the 16550-compatible features
1132305Sstevel  * ("non-extended mode").
1142305Sstevel  *
1152305Sstevel  * Hardware: serial chip register numbers
1162305Sstevel  */
1172305Sstevel #define	SIO_RXD			0	/* read		*/
1182305Sstevel #define	SIO_TXD			0	/* write	*/
1192305Sstevel #define	SIO_IER			1
1202305Sstevel #define	SIO_EIR			2	/* read		*/
1212305Sstevel #define	SIO_FCR			2	/* write	*/
1222305Sstevel #define	SIO_LCR			3
1232305Sstevel #define	SIO_BSR			3	/* wierd	*/
1242305Sstevel #define	SIO_MCR			4
1252305Sstevel #define	SIO_LSR			5
1262305Sstevel #define	SIO_MSR			6
1272305Sstevel #define	SIO_SCR			7
1282305Sstevel 
1292305Sstevel #define	SIO_LBGDL		0	/* bank 1	*/
1302305Sstevel #define	SIO_LBGDH		1	/* bank 1	*/
1312305Sstevel 
1322305Sstevel /*
1332305Sstevel  * Hardware: serial chip register bits
1342305Sstevel  */
1352305Sstevel #define	SIO_IER_RXHDL_IE	0x01
1362305Sstevel #define	SIO_IER_STD		0x00
1372305Sstevel 
1382305Sstevel #define	SIO_EIR_IPF		0x01
1392305Sstevel #define	SIO_EIR_IPR0		0x02
1402305Sstevel #define	SIO_EIR_IPR1		0x04
1412305Sstevel #define	SIO_EIR_RXFT		0x08
1422305Sstevel #define	SIO_EIR_FEN0		0x40
1432305Sstevel #define	SIO_EIR_FEN1		0x80
1442305Sstevel 
1452305Sstevel #define	SIO_FCR_FIFO_EN		0x01
1462305Sstevel #define	SIO_FCR_RXSR		0x02
1472305Sstevel #define	SIO_FCR_TXSR		0x04
1482305Sstevel #define	SIO_FCR_RXFTH0		0x40
1492305Sstevel #define	SIO_FCR_RXFTH1		0x80
1502305Sstevel #define	SIO_FCR_STD		(SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN)
1512305Sstevel 
1522305Sstevel #define	SIO_LCR_WLS0		0x01
1532305Sstevel #define	SIO_LCR_WLS1		0x02
1542305Sstevel #define	SIO_LCR_STB		0x04
1552305Sstevel #define	SIO_LCR_PEN		0x08
1562305Sstevel #define	SIO_LCR_EPS		0x10
1572305Sstevel #define	SIO_LCR_STKP		0x20
1582305Sstevel #define	SIO_LCR_SBRK		0x40
1592305Sstevel #define	SIO_LCR_BKSE		0x80
1602305Sstevel #define	SIO_LCR_8BIT		(SIO_LCR_WLS0|SIO_LCR_WLS1)
1612305Sstevel #define	SIO_LCR_EPAR		(SIO_LCR_PEN|SIO_LCR_EPS)
1622305Sstevel #define	SIO_LCR_STD		(SIO_LCR_8BIT|SIO_LCR_EPAR)
1632305Sstevel 
1642305Sstevel #define	SIO_BSR_BANK0		(SIO_LCR_STD)
1652305Sstevel #define	SIO_BSR_BANK1		(SIO_LCR_BKSE|SIO_LCR_STD)
1662305Sstevel 
1672305Sstevel #define	SIO_MCR_DTR		0x01
1682305Sstevel #define	SIO_MCR_RTS		0x02
1692305Sstevel #define	SIO_MCR_ISEN		0x08
1702305Sstevel #define	SIO_MCR_STD		(SIO_MCR_ISEN)
1712305Sstevel 
1722305Sstevel #define	SIO_LSR_RXDA		0x01
1732305Sstevel #define	SIO_LSR_OE		0x02
1742305Sstevel #define	SIO_LSR_PE		0x04
1752305Sstevel #define	SIO_LSR_FE		0x08
1762305Sstevel #define	SIO_LSR_BRKE		0x10
1772305Sstevel #define	SIO_LSR_TXRDY		0x20
1782305Sstevel #define	SIO_LSR_TXEMP		0x40
1792305Sstevel #define	SIO_LSR_ER_INF		0x80
1802305Sstevel 
1812305Sstevel #define	SIO_MSR_DCTS		0x01
1822305Sstevel #define	SIO_MSR_DDSR		0x02
1832305Sstevel #define	SIO_MSR_TERI		0x04
1842305Sstevel #define	SIO_MSR_DDCD		0x08
1852305Sstevel #define	SIO_MSR_CTS		0x10
1862305Sstevel #define	SIO_MSR_DSR		0x20
1872305Sstevel #define	SIO_MSR_RI		0x40
1882305Sstevel #define	SIO_MSR_DCD		0x80
1892305Sstevel 
1902305Sstevel /*
1912305Sstevel  * Min/max/default baud rates, and a macro to convert from a baud
1922305Sstevel  * rate to the number (divisor) to put in the baud rate registers
1932305Sstevel  */
1942305Sstevel #define	SIO_BAUD_MIN		50
1952305Sstevel #define	SIO_BAUD_MAX		115200
1962305Sstevel #define	SIO_BAUD_DEFAULT	38400
1972305Sstevel #define	SIO_BAUD_TO_DIVISOR(b)	(115200 / (b))
1982305Sstevel 
1992305Sstevel 
2002305Sstevel /*
2012305Sstevel  * Packet format ...
2022305Sstevel  */
2032305Sstevel #define	LOMBUS_MASK		0xc0	/* Byte-type bits		*/
2042305Sstevel #define	LOMBUS_PARAM		0x00	/* Parameter byte: 0b0xxxxxxx	*/
2052305Sstevel #define	LOMBUS_LAST		0x80	/* Last byte of packet		*/
2062305Sstevel #define	LOMBUS_CMD		0x80	/* Command byte:   0b10###XWV	*/
2072305Sstevel #define	LOMBUS_STATUS		0xc0	/* Status  byte:   0b11###AEV	*/
2082305Sstevel 
2092305Sstevel #define	LOMBUS_SEQ		0x38	/* Sequence number bits		*/
2102305Sstevel #define	LOMBUS_SEQ_LSB		0x08	/* Sequence number LSB		*/
2112305Sstevel #define	LOMBUS_CMD_XADDR	0x04	/* Extended (2-byte) addressing	*/
2122305Sstevel #define	LOMBUS_CMD_WRITE	0x02	/* Write command		*/
2132305Sstevel #define	LOMBUS_CMD_WMSB		0x01	/* Set MSB on Write		*/
2142305Sstevel #define	LOMBUS_CMD_READ		0x01	/* Read command			*/
2152305Sstevel #define	LOMBUS_CMD_NOP		0x00	/* NOP command			*/
2162305Sstevel 
2172305Sstevel #define	LOMBUS_STATUS_ASYNC	0x04	/* Asynchronous event pending	*/
2182305Sstevel #define	LOMBUS_STATUS_ERR	0x02	/* Error in command processing	*/
2192305Sstevel #define	LOMBUS_STATUS_MSB	0x01	/* MSB of Value read		*/
2202305Sstevel 
2212305Sstevel #define	LOMBUS_VREG_LO(x)	((x) & ((1 << 7) - 1))
2222305Sstevel #define	LOMBUS_VREG_HI(x)	((x) >> 7)
2232305Sstevel 
2242305Sstevel #define	LOMBUS_BUFSIZE		8
2252305Sstevel 
2262305Sstevel 
2272305Sstevel /*
2282305Sstevel  * Time periods, in nanoseconds
2292305Sstevel  *
2302305Sstevel  * Note that LOMBUS_ONE_SEC and some other time
2312305Sstevel  * periods are defined in <sys/lombus.h>
2322305Sstevel  */
2332305Sstevel #define	LOMBUS_CMD_POLL		(LOMBUS_ONE_SEC/20)
2342305Sstevel #define	LOMBUS_CTS_POLL		(LOMBUS_ONE_SEC/20)
2352305Sstevel #define	LOMBUS_CTS_TIMEOUT	(LOMBUS_ONE_SEC*2)
2362305Sstevel 
2372305Sstevel 
2382305Sstevel /*
2392305Sstevel  * Local datatypes
2402305Sstevel  */
2412305Sstevel enum lombus_cmdstate {
2422305Sstevel 	LOMBUS_CMDSTATE_IDLE,
2432305Sstevel 	LOMBUS_CMDSTATE_BUSY,
2442305Sstevel 	LOMBUS_CMDSTATE_WAITING,
2452305Sstevel 	LOMBUS_CMDSTATE_READY,
2462305Sstevel 	LOMBUS_CMDSTATE_ERROR
2472305Sstevel };
2482305Sstevel 
2492305Sstevel 
2502305Sstevel /*
2512305Sstevel  * This driver's soft-state structure
2522305Sstevel  */
2532305Sstevel 
2542305Sstevel struct lombus_state {
2552305Sstevel 	/*
2562305Sstevel 	 * Configuration data, set during attach
2572305Sstevel 	 */
2582305Sstevel 	dev_info_t *dip;
2592305Sstevel 	major_t majornum;
2602305Sstevel 	int instance;
2612305Sstevel 
2622305Sstevel 	ddi_acc_handle_t sio_handle;
2632305Sstevel 	uint8_t *sio_regs;
2642305Sstevel 	ddi_softintr_t softid;
2655107Seota 	ddi_periodic_t cycid; /* periodical callback */
2662305Sstevel 
2672305Sstevel 	/*
2682305Sstevel 	 * Parameters derived from .conf properties
2692305Sstevel 	 */
2702305Sstevel 	boolean_t allow_echo;
2712305Sstevel 	int baud;
2722305Sstevel 	uint32_t debug;
2732305Sstevel 	boolean_t fake_cts;
2742305Sstevel 
2752305Sstevel 	/*
2762305Sstevel 	 * Hardware mutex (initialised using <hw_iblk>),
2772305Sstevel 	 * used to prevent retriggering the softint while
2782305Sstevel 	 * it's still fetching data out of the chip FIFO.
2792305Sstevel 	 */
2802305Sstevel 	kmutex_t hw_mutex[1];
2812305Sstevel 	ddi_iblock_cookie_t hw_iblk;
2822305Sstevel 
2832305Sstevel 	/*
2842305Sstevel 	 * Data protected by the hardware mutex: the watchdog-patting
2852305Sstevel 	 * protocol data (since the dog can be patted from a high-level
2862305Sstevel 	 * cyclic), and the interrupt-enabled flag.
2872305Sstevel 	 */
2882305Sstevel 	hrtime_t hw_last_pat;
2892305Sstevel 	boolean_t hw_int_enabled;
2902305Sstevel 
2912305Sstevel 	/*
2922305Sstevel 	 * Flag to indicate that we've incurred a hardware fault on
2932305Sstevel 	 * accesses to the SIO; once this is set, we fake all further
2942305Sstevel 	 * accesses in order not to provoke additional bus errors.
2952305Sstevel 	 */
2962305Sstevel 	boolean_t sio_fault;
2972305Sstevel 
2982305Sstevel 	/*
2992305Sstevel 	 * Serial protocol state data, protected by lo_mutex
3002305Sstevel 	 * (which is initialised using <lo_iblk>)
3012305Sstevel 	 */
3022305Sstevel 	kmutex_t lo_mutex[1];
3032305Sstevel 	ddi_iblock_cookie_t lo_iblk;
3042305Sstevel 	kcondvar_t lo_cv[1];
3052305Sstevel 
3062305Sstevel 	volatile enum lombus_cmdstate cmdstate;
3072305Sstevel 	clock_t deadline;
3082305Sstevel 	uint8_t cmdbuf[LOMBUS_BUFSIZE];
3092305Sstevel 	uint8_t reply[LOMBUS_BUFSIZE];
3102305Sstevel 	uint8_t async;
3112305Sstevel 	uint8_t index;
3122305Sstevel 	uint8_t result;
3132305Sstevel 	uint8_t sequence;
3142305Sstevel 	uint32_t error;
3152305Sstevel };
3162305Sstevel 
3172305Sstevel /*
3182305Sstevel  * The auxiliary structure attached to each child
3192305Sstevel  * (the child's parent-private-data points to this).
3202305Sstevel  */
3212305Sstevel struct lombus_child_info {
3222305Sstevel 	lombus_regspec_t *rsp;
3232305Sstevel 	int nregs;
3242305Sstevel };
3252305Sstevel 
3262305Sstevel 
3272305Sstevel /*
3282305Sstevel  * Local data
3292305Sstevel  */
3302305Sstevel 
3312305Sstevel static void *lombus_statep;
3322305Sstevel 
3332305Sstevel static major_t lombus_major = NOMAJOR;
3342305Sstevel 
3352305Sstevel static ddi_device_acc_attr_t lombus_dev_acc_attr[1] =
3362305Sstevel {
3372305Sstevel 	DDI_DEVICE_ATTR_V0,
3382305Sstevel 	DDI_STRUCTURE_LE_ACC,
3392305Sstevel 	DDI_STRICTORDER_ACC
3402305Sstevel };
3412305Sstevel 
3422305Sstevel 
3432305Sstevel /*
3442305Sstevel  *  General utility routines ...
3452305Sstevel  */
3462305Sstevel 
3472305Sstevel static void
lombus_trace(struct lombus_state * ssp,char code,const char * caller,const char * fmt,...)3482305Sstevel lombus_trace(struct lombus_state *ssp, char code, const char *caller,
3492305Sstevel 	const char *fmt, ...)
3502305Sstevel {
3512305Sstevel 	char buf[256];
3522305Sstevel 	char *p;
3532305Sstevel 	va_list va;
3542305Sstevel 
3552305Sstevel 	if (ssp->debug & (1 << (code-'@'))) {
3562305Sstevel 		p = buf;
357*11311SSurya.Prakki@Sun.COM 		(void) snprintf(p, sizeof (buf) - (p - buf),
3585107Seota 		    "%s/%s: ", MYNAME, caller);
3592305Sstevel 		p += strlen(p);
3602305Sstevel 
3612305Sstevel 		va_start(va, fmt);
362*11311SSurya.Prakki@Sun.COM 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
3632305Sstevel 		va_end(va);
3642305Sstevel 
3652305Sstevel 		buf[sizeof (buf) - 1] = '\0';
366*11311SSurya.Prakki@Sun.COM 		(void) strlog(ssp->majornum, ssp->instance, code, SL_TRACE,
367*11311SSurya.Prakki@Sun.COM 		    buf);
3682305Sstevel 	}
3692305Sstevel }
3702305Sstevel 
3712305Sstevel static struct lombus_state *
lombus_getstate(dev_info_t * dip,int instance,const char * caller)3722305Sstevel lombus_getstate(dev_info_t *dip, int instance, const char *caller)
3732305Sstevel {
3742305Sstevel 	struct lombus_state *ssp = NULL;
3752305Sstevel 	dev_info_t *sdip = NULL;
3762305Sstevel 	major_t dmaj = NOMAJOR;
3772305Sstevel 
3782305Sstevel 	if (dip != NULL) {
3792305Sstevel 		/*
3802305Sstevel 		 * Use the instance number from the <dip>; also,
3812305Sstevel 		 * check that it really corresponds to this driver
3822305Sstevel 		 */
3832305Sstevel 		instance = ddi_get_instance(dip);
3842305Sstevel 		dmaj = ddi_driver_major(dip);
3852305Sstevel 		if (lombus_major == NOMAJOR && dmaj != NOMAJOR)
3862305Sstevel 			lombus_major = dmaj;
3872305Sstevel 		else if (dmaj != lombus_major) {
3882305Sstevel 			cmn_err(CE_WARN,
3892305Sstevel 			    "%s: major number mismatch (%d vs. %d) in %s(),"
3902305Sstevel 			    "probably due to child misconfiguration",
3912305Sstevel 			    MYNAME, lombus_major, dmaj, caller);
3922305Sstevel 			instance = -1;
3932305Sstevel 		}
3942305Sstevel 	}
3952305Sstevel 
3962305Sstevel 	if (instance >= 0)
3972305Sstevel 		ssp = ddi_get_soft_state(lombus_statep, instance);
3982305Sstevel 	if (ssp != NULL) {
3992305Sstevel 		sdip = ssp->dip;
4002305Sstevel 		if (dip == NULL && sdip == NULL)
4012305Sstevel 			ssp = NULL;
4022305Sstevel 		else if (dip != NULL && sdip != NULL && sdip != dip) {
4032305Sstevel 			cmn_err(CE_WARN,
4042305Sstevel 			    "%s: devinfo mismatch (%p vs. %p) in %s(), "
4052305Sstevel 			    "probably due to child misconfiguration",
4062305Sstevel 			    MYNAME, (void *)dip, (void *)sdip, caller);
4072305Sstevel 			ssp = NULL;
4082305Sstevel 		}
4092305Sstevel 	}
4102305Sstevel 
4112305Sstevel 	return (ssp);
4122305Sstevel }
4132305Sstevel 
4142305Sstevel /*
4152305Sstevel  * Lowest-level serial I/O chip register read/write
4162305Sstevel  */
4172305Sstevel 
4182305Sstevel static void
sio_put_reg(struct lombus_state * ssp,uint_t reg,uint8_t val)4192305Sstevel sio_put_reg(struct lombus_state *ssp, uint_t reg, uint8_t val)
4202305Sstevel {
4212305Sstevel 	lombus_trace(ssp, 'P', "sio_put_reg", "REG[%d] <- $%02x", reg, val);
4222305Sstevel 
4232305Sstevel 	if (ssp->sio_handle != NULL && !ssp->sio_fault) {
4242305Sstevel 		/*
4252305Sstevel 		 * The chip is mapped as "I/O" (e.g. with the side-effect
4262305Sstevel 		 * bit on SPARC), therefore accesses are required to be
4272305Sstevel 		 * in-order, with no value cacheing.  However, there can
4282305Sstevel 		 * still be write-behind buffering, so it is not guaranteed
4292305Sstevel 		 * that a write actually reaches the chip in a given time.
4302305Sstevel 		 *
4312305Sstevel 		 * To force the access right through to the chip, we follow
4322305Sstevel 		 * the write with another write (to the SCRATCH register)
4332305Sstevel 		 * and a read (of the value just written to the SCRATCH
4342305Sstevel 		 * register).  The SCRATCH register is specifically provided
4352305Sstevel 		 * for temporary data and has no effect on the SIO's own
4362305Sstevel 		 * operation, making it ideal as a synchronising mechanism.
4372305Sstevel 		 *
4382305Sstevel 		 * If we didn't do this, it would be possible that the new
4392305Sstevel 		 * value wouldn't reach the chip (and have the *intended*
4402305Sstevel 		 * side-effects, such as disabling interrupts), for such a
4412305Sstevel 		 * long time that the processor could execute a *lot* of
4422305Sstevel 		 * instructions - including exiting the interrupt service
4432305Sstevel 		 * routine and re-enabling interrupts.  This effect was
4442305Sstevel 		 * observed to lead to spurious (unclaimed) interrupts in
4452305Sstevel 		 * some circumstances.
4462305Sstevel 		 *
4472305Sstevel 		 * This will no longer be needed once "synchronous" access
4482305Sstevel 		 * handles are available (see PSARC/2000/269 and 2000/531).
4492305Sstevel 		 */
4502305Sstevel 		ddi_put8(ssp->sio_handle, ssp->sio_regs + reg, val);
4512305Sstevel 		ddi_put8(ssp->sio_handle, ssp->sio_regs + SIO_SCR, val);
4522305Sstevel 		membar_sync();
4532305Sstevel 		(void) ddi_get8(ssp->sio_handle, ssp->sio_regs + SIO_SCR);
4542305Sstevel 	}
4552305Sstevel }
4562305Sstevel 
4572305Sstevel static uint8_t
sio_get_reg(struct lombus_state * ssp,uint_t reg)4582305Sstevel sio_get_reg(struct lombus_state *ssp, uint_t reg)
4592305Sstevel {
4602305Sstevel 	uint8_t val;
4612305Sstevel 
4622305Sstevel 	if (ssp->sio_handle && !ssp->sio_fault)
4632305Sstevel 		val = ddi_get8(ssp->sio_handle, ssp->sio_regs + reg);
4642305Sstevel 	else
4652305Sstevel 		val = DUMMY_VALUE;
4662305Sstevel 
4672305Sstevel 	lombus_trace(ssp, 'G', "sio_get_reg", "$%02x <- REG[%d]", val, reg);
4682305Sstevel 
4692305Sstevel 	return (val);
4702305Sstevel }
4712305Sstevel 
4722305Sstevel static void
sio_check_fault_status(struct lombus_state * ssp)4732305Sstevel sio_check_fault_status(struct lombus_state *ssp)
4742305Sstevel {
4752305Sstevel 	ssp->sio_fault = ddi_check_acc_handle(ssp->sio_handle) != DDI_SUCCESS;
4762305Sstevel }
4772305Sstevel 
4782305Sstevel static boolean_t
sio_faulty(struct lombus_state * ssp)4792305Sstevel sio_faulty(struct lombus_state *ssp)
4802305Sstevel {
4812305Sstevel 	if (!ssp->sio_fault)
4822305Sstevel 		sio_check_fault_status(ssp);
4832305Sstevel 	return (ssp->sio_fault);
4842305Sstevel }
4852305Sstevel 
4862305Sstevel 
4872305Sstevel /*
4882305Sstevel  * Check for data ready.
4892305Sstevel  */
4902305Sstevel static boolean_t
sio_data_ready(struct lombus_state * ssp)4912305Sstevel sio_data_ready(struct lombus_state *ssp)
4922305Sstevel {
4932305Sstevel 	uint8_t status;
4942305Sstevel 
4952305Sstevel 	/*
4962305Sstevel 	 * Data is available if the RXDA bit in the LSR is nonzero
4972305Sstevel 	 * (if reading it didn't incur a fault).
4982305Sstevel 	 */
4992305Sstevel 	status = sio_get_reg(ssp, SIO_LSR);
5002305Sstevel 	return ((status & SIO_LSR_RXDA) != 0 && !sio_faulty(ssp));
5012305Sstevel }
5022305Sstevel 
5032305Sstevel /*
5042305Sstevel  * Check for LOM ready
5052305Sstevel  */
5062305Sstevel static boolean_t
sio_lom_ready(struct lombus_state * ssp)5072305Sstevel sio_lom_ready(struct lombus_state *ssp)
5082305Sstevel {
5092305Sstevel 	uint8_t status;
5102305Sstevel 	boolean_t rslt;
5112305Sstevel 
5122305Sstevel 	/*
5132305Sstevel 	 * The LOM is ready if the CTS bit in the MSR is 1, meaning
5142305Sstevel 	 * that the /CTS signal is being asserted (driven LOW) -
5152305Sstevel 	 * unless we incurred a fault in trying to read the MSR!
5162305Sstevel 	 *
5172305Sstevel 	 * For debugging, we force the result to TRUE if the FAKE flag is set
5182305Sstevel 	 */
5192305Sstevel 	status = sio_get_reg(ssp, SIO_MSR);
5202305Sstevel 	rslt = (status & SIO_MSR_CTS) != 0 && !sio_faulty(ssp);
5212305Sstevel 
5222305Sstevel 	lombus_trace(ssp, 'R', "sio_lom_ready", "S $%02x R %d F %d",
5235107Seota 	    status, rslt, ssp->fake_cts);
5242305Sstevel 
5252305Sstevel 	return (rslt || ssp->fake_cts);
5262305Sstevel }
5272305Sstevel 
5282305Sstevel #if	0
5292305Sstevel /*
5302305Sstevel  * Check for interrupt pending
5312305Sstevel  */
5322305Sstevel static boolean_t
5332305Sstevel sio_irq_pending(struct lombus_state *ssp)
5342305Sstevel {
5352305Sstevel 	uint8_t status;
5362305Sstevel 	boolean_t rslt;
5372305Sstevel 
5382305Sstevel 	/*
5392305Sstevel 	 * An interrupt is pending if the IPF bit in the EIR is 0,
5402305Sstevel 	 * assuming we didn't incur a fault in trying to ready it.
5412305Sstevel 	 *
5422305Sstevel 	 * Note: we expect that every time we read this register
5432305Sstevel 	 * (which is only done from the interrupt service routine),
5442305Sstevel 	 * we will see $11001100 (RX FIFO timeout interrupt pending).
5452305Sstevel 	 */
5462305Sstevel 	status = sio_get_reg(ssp, SIO_EIR);
5472305Sstevel 
5482305Sstevel 	rslt = (status & SIO_EIR_IPF) == 0 && !sio_faulty(ssp);
5492305Sstevel 	lombus_trace(ssp, 'I', "sio_irq_pending", "S $%02x R %d",
5505107Seota 	    status, rslt);
5512305Sstevel 
5522305Sstevel 	/*
5532305Sstevel 	 * To investigate whether we're getting any abnormal interrupts
5542305Sstevel 	 * this code checks that the status value is as expected, and that
5552305Sstevel 	 * chip-level interrupts are supposed to be enabled at this time.
5562305Sstevel 	 * This will cause a PANIC (on a driver compiled with DEBUG) if
5572305Sstevel 	 * all is not as expected ...
5582305Sstevel 	 */
5592305Sstevel 	ASSERT(status == 0xCC);
5602305Sstevel 	ASSERT(ssp->hw_int_enabled);
5612305Sstevel 
5622305Sstevel 	return (rslt);
5632305Sstevel }
5642305Sstevel #endif	/* 0 */
5652305Sstevel 
5662305Sstevel /*
5672305Sstevel  * Enable/disable interrupts
5682305Sstevel  */
5692305Sstevel static void
lombus_set_irq(struct lombus_state * ssp,boolean_t newstate)5702305Sstevel lombus_set_irq(struct lombus_state *ssp, boolean_t newstate)
5712305Sstevel {
5722305Sstevel 	uint8_t val;
5732305Sstevel 
5742305Sstevel 	val = newstate ? SIO_IER_RXHDL_IE : 0;
5752305Sstevel 	sio_put_reg(ssp, SIO_IER, SIO_IER_STD | val);
5762305Sstevel 	ssp->hw_int_enabled = newstate;
5772305Sstevel }
5782305Sstevel 
5792305Sstevel /*
5802305Sstevel  * Assert/deassert RTS
5812305Sstevel  */
5822305Sstevel static void
lombus_toggle_rts(struct lombus_state * ssp)5832305Sstevel lombus_toggle_rts(struct lombus_state *ssp)
5842305Sstevel {
5852305Sstevel 	uint8_t val;
5862305Sstevel 
5872305Sstevel 	val = sio_get_reg(ssp, SIO_MCR);
5882305Sstevel 	val &= SIO_MCR_RTS;
5892305Sstevel 	val ^= SIO_MCR_RTS;
5902305Sstevel 	val |= SIO_MCR_STD;
5912305Sstevel 	sio_put_reg(ssp, SIO_MCR, val);
5922305Sstevel }
5932305Sstevel 
5942305Sstevel 
5952305Sstevel /*
5962305Sstevel  * High-level interrupt handler:
5972305Sstevel  *	Checks whether initialisation is complete (to avoid a race
5982305Sstevel  *	with mutex_init()), and whether chip interrupts are enabled.
5992305Sstevel  *	If not, the interrupt's not for us, so just return UNCLAIMED.
6002305Sstevel  *	Otherwise, disable the interrupt, trigger a softint, and return
6012305Sstevel  *	CLAIMED.  The softint handler will then do all the real work.
6022305Sstevel  *
6032305Sstevel  *	NOTE: the chip interrupt capability is only re-enabled once the
6042305Sstevel  *	receive code has run, but that can be called from a poll loop
6052305Sstevel  *	or cyclic callback as well as from the softint.  So it's *not*
6062305Sstevel  *	guaranteed that there really is a chip interrupt pending here,
6072305Sstevel  *	'cos the work may already have been done and the reason for the
6082305Sstevel  *	interrupt gone away before we get here.
6092305Sstevel  *
6102305Sstevel  *	OTOH, if we come through here twice without the receive code
6112305Sstevel  *	having run in between, that's definitely wrong.  In such an
6122305Sstevel  *	event, we would notice that chip interrupts haven't yet been
6132305Sstevel  *	re-enabled and return UNCLAIMED, allowing the system's jabber
6142305Sstevel  *	protect code (if any) to do its job.
6152305Sstevel  */
6162305Sstevel static uint_t
lombus_hi_intr(caddr_t arg)6172305Sstevel lombus_hi_intr(caddr_t arg)
6182305Sstevel {
6192305Sstevel 	struct lombus_state *ssp = (void *)arg;
6202305Sstevel 	uint_t claim;
6212305Sstevel 
6222305Sstevel 	claim = DDI_INTR_UNCLAIMED;
6235107Seota 	if (ssp->cycid != NULL) {
6242305Sstevel 		mutex_enter(ssp->hw_mutex);
6252305Sstevel 		if (ssp->hw_int_enabled) {
6262305Sstevel 			lombus_set_irq(ssp, B_FALSE);
6272305Sstevel 			ddi_trigger_softintr(ssp->softid);
6282305Sstevel 			claim = DDI_INTR_CLAIMED;
6292305Sstevel 		}
6302305Sstevel 		mutex_exit(ssp->hw_mutex);
6312305Sstevel 	}
6322305Sstevel 
6332305Sstevel 	return (claim);
6342305Sstevel }
6352305Sstevel 
6362305Sstevel /*
6372305Sstevel  * Packet receive handler
6382305Sstevel  *
6392305Sstevel  * This routine should be called from the low-level softint, or the
6402305Sstevel  * cyclic callback, or lombus_cmd() (for polled operation), with the
6412305Sstevel  * low-level mutex already held.
6422305Sstevel  */
6432305Sstevel static void
lombus_receive(struct lombus_state * ssp)6442305Sstevel lombus_receive(struct lombus_state *ssp)
6452305Sstevel {
6462305Sstevel 	boolean_t ready = B_FALSE;
6472305Sstevel 	uint8_t data = 0;
6482305Sstevel 	uint8_t rcvd = 0;
6492305Sstevel 	uint8_t tmp;
6502305Sstevel 
6512305Sstevel 	lombus_trace(ssp, 'S', "lombus_receive",
6525107Seota 	    "state %d; error $%x",
6535107Seota 	    ssp->cmdstate, ssp->error);
6542305Sstevel 
6552305Sstevel 	/*
6562305Sstevel 	 * Check for access faults before starting the receive
6572305Sstevel 	 * loop (we don't want to cause bus errors or suchlike
6582305Sstevel 	 * unpleasantness in the event that the SIO has died).
6592305Sstevel 	 */
6602305Sstevel 	if (!sio_faulty(ssp)) {
6612305Sstevel 		/*
6622305Sstevel 		 * Read bytes from the FIFO until they're all gone,
6632305Sstevel 		 * or we find the 'END OF PACKET' set on one, or
6642305Sstevel 		 * our buffer overflows (which must be an error)
6652305Sstevel 		 */
6662305Sstevel 		mutex_enter(ssp->hw_mutex);
6672305Sstevel 		while (sio_data_ready(ssp)) {
6682305Sstevel 			data = sio_get_reg(ssp, SIO_RXD);
6692305Sstevel 			ssp->reply[rcvd = ssp->index] = data;
6702305Sstevel 			if (++rcvd >= LOMBUS_BUFSIZE)
6712305Sstevel 				break;
6722305Sstevel 			ssp->index = rcvd;
6732305Sstevel 			if (data & LOMBUS_LAST)
6742305Sstevel 				break;
6752305Sstevel 		}
6762305Sstevel 		lombus_set_irq(ssp, B_TRUE);
6772305Sstevel 		mutex_exit(ssp->hw_mutex);
6782305Sstevel 	}
6792305Sstevel 
6802305Sstevel 	lombus_trace(ssp, 'S', "lombus_receive",
6815107Seota 	    "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
6825107Seota 	    rcvd,
6835107Seota 	    ssp->reply[0], ssp->reply[1],
6845107Seota 	    ssp->reply[2], ssp->reply[3],
6855107Seota 	    ssp->reply[4], ssp->reply[5],
6865107Seota 	    ssp->reply[6], ssp->reply[7]);
6872305Sstevel 
6882305Sstevel 	if (ssp->cmdstate != LOMBUS_CMDSTATE_WAITING) {
6892305Sstevel 		/*
6902305Sstevel 		 * We're not expecting any data in this state, so if
6912305Sstevel 		 * we DID receive any data, we just throw it away by
6922305Sstevel 		 * resetting the buffer index to 0.
6932305Sstevel 		 */
6942305Sstevel 		ssp->index = 0;
6952305Sstevel 	} else if (rcvd == 0) {
6962305Sstevel 		/*
6972305Sstevel 		 * No bytes received this time through (though there
6982305Sstevel 		 * might be a partial packet sitting in the buffer).
6992305Sstevel 		 * If it seems the LOM is taking too long to respond,
7002305Sstevel 		 * we'll assume it's died and return an error.
7012305Sstevel 		 */
7022305Sstevel 		if (ddi_get_lbolt() > ssp->deadline) {
7032305Sstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7042305Sstevel 			ssp->error = LOMBUS_ERR_TIMEOUT;
7052305Sstevel 			ready = B_TRUE;
7062305Sstevel 		}
7072305Sstevel 	} else if (rcvd >= LOMBUS_BUFSIZE) {
7082305Sstevel 		/*
7092305Sstevel 		 * Buffer overflow; discard the data & treat as an error
7102305Sstevel 		 * (even if the last byte read did claim to terminate a
7112305Sstevel 		 * packet, it can't be a valid one 'cos it's too long!)
7122305Sstevel 		 */
7132305Sstevel 		ssp->index = 0;
7142305Sstevel 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7152305Sstevel 		ssp->error = LOMBUS_ERR_OFLOW;
7162305Sstevel 		ready = B_TRUE;
7172305Sstevel 	} else if ((data & LOMBUS_LAST) == 0) {
7182305Sstevel 		/*
7192305Sstevel 		 * Packet not yet complete; leave the partial packet in
7202305Sstevel 		 * the buffer for later ...
7212305Sstevel 		 */
7222305Sstevel 		_NOTE(EMPTY)
7232305Sstevel 		;
7242305Sstevel 	} else if ((data & LOMBUS_MASK) != LOMBUS_STATUS) {
7252305Sstevel 		/*
7262305Sstevel 		 * Invalid "status" byte - maybe an echo of the command?
7272305Sstevel 		 *
7282305Sstevel 		 * As a debugging feature, we allow for this, assuming
7292305Sstevel 		 * that if the LOM has echoed the command byte, it has
7302305Sstevel 		 * also echoed all the parameter bytes before starting
7312305Sstevel 		 * command processing.  So, we dump out the buffer and
7322305Sstevel 		 * then clear it, so we can go back to looking for the
7332305Sstevel 		 * real reply.
7342305Sstevel 		 *
7352305Sstevel 		 * Otherwise, we just drop the data & flag an error.
7362305Sstevel 		 */
7372305Sstevel 		if (ssp->allow_echo) {
7382305Sstevel 			lombus_trace(ssp, 'E', "lombus_receive",
7395107Seota 			    "echo $%02x $%02x $%02x $%02x "
7405107Seota 			    "$%02x $%02x $%02x $%02x",
7415107Seota 			    ssp->reply[0], ssp->reply[1],
7425107Seota 			    ssp->reply[2], ssp->reply[3],
7435107Seota 			    ssp->reply[4], ssp->reply[5],
7445107Seota 			    ssp->reply[6], ssp->reply[7]);
7452305Sstevel 			ssp->index = 0;
7462305Sstevel 		} else {
7472305Sstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7482305Sstevel 			ssp->error = LOMBUS_ERR_BADSTATUS;
7492305Sstevel 			ready = B_TRUE;
7502305Sstevel 		}
7512305Sstevel 	} else if ((data & LOMBUS_SEQ) != ssp->sequence) {
7522305Sstevel 		/*
7532305Sstevel 		 * Wrong sequence number!  Flag this as an error
7542305Sstevel 		 */
7552305Sstevel 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7562305Sstevel 		ssp->error = LOMBUS_ERR_SEQUENCE;
7572305Sstevel 		ready = B_TRUE;
7582305Sstevel 	} else {
7592305Sstevel 		/*
7602305Sstevel 		 * Finally, we know that's it's a valid reply to our
7612305Sstevel 		 * last command.  Update the ASYNC status, derive the
7622305Sstevel 		 * reply parameter (if any), and check the ERROR bit
7632305Sstevel 		 * to find out what the parameter means.
7642305Sstevel 		 *
7652305Sstevel 		 * Note that not all the values read/assigned here
7662305Sstevel 		 * are meaningful, but it doesn't matter; the waiting
7672305Sstevel 		 * thread will know which one(s) it should check.
7682305Sstevel 		 */
7692305Sstevel 		ssp->async = (data & LOMBUS_STATUS_ASYNC) ? 1 : 0;
7702305Sstevel 		tmp = ((data & LOMBUS_STATUS_MSB) ? 0x80 : 0) | ssp->reply[0];
7712305Sstevel 		if (data & LOMBUS_STATUS_ERR) {
7722305Sstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7732305Sstevel 			ssp->error = tmp;
7742305Sstevel 		} else {
7752305Sstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_READY;
7762305Sstevel 			ssp->result = tmp;
7772305Sstevel 		}
7782305Sstevel 		ready = B_TRUE;
7792305Sstevel 	}
7802305Sstevel 
7812305Sstevel 	lombus_trace(ssp, 'T', "lombus_receive",
7825107Seota 	    "rcvd %d; last $%02x; state %d; error $%x; ready %d",
7835107Seota 	    rcvd, data, ssp->cmdstate, ssp->error, ready);
7842305Sstevel 
7852305Sstevel 	if (ready)
7862305Sstevel 		cv_broadcast(ssp->lo_cv);
7872305Sstevel }
7882305Sstevel 
7892305Sstevel /*
7902305Sstevel  * Low-level softint handler
7912305Sstevel  *
7922305Sstevel  * This routine should be triggered whenever there's a byte to be read
7932305Sstevel  */
7942305Sstevel static uint_t
lombus_softint(caddr_t arg)7952305Sstevel lombus_softint(caddr_t arg)
7962305Sstevel {
7972305Sstevel 	struct lombus_state *ssp = (void *)arg;
7982305Sstevel 
7992305Sstevel 	mutex_enter(ssp->lo_mutex);
8002305Sstevel 	lombus_receive(ssp);
8012305Sstevel 	mutex_exit(ssp->lo_mutex);
8022305Sstevel 
8032305Sstevel 	return (DDI_INTR_CLAIMED);
8042305Sstevel }
8052305Sstevel 
8062305Sstevel /*
8072305Sstevel  * Cyclic handler: just calls the receive routine, in case interrupts
8082305Sstevel  * are not being delivered and in order to handle command timeout
8092305Sstevel  */
8102305Sstevel static void
lombus_cyclic(void * arg)8112305Sstevel lombus_cyclic(void *arg)
8122305Sstevel {
8132305Sstevel 	struct lombus_state *ssp = (void *)arg;
8142305Sstevel 
8152305Sstevel 	mutex_enter(ssp->lo_mutex);
8162305Sstevel 	lombus_receive(ssp);
8172305Sstevel 	mutex_exit(ssp->lo_mutex);
8182305Sstevel }
8192305Sstevel 
8202305Sstevel 
8212305Sstevel /*
8222305Sstevel  * Serial protocol
8232305Sstevel  *
8242305Sstevel  * This routine builds a command and sets it in progress.
8252305Sstevel  */
8262305Sstevel static uint8_t
lombus_cmd(HANDLE_TYPE * hdlp,ptrdiff_t vreg,uint_t val,uint_t cmd)8272305Sstevel lombus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd)
8282305Sstevel {
8292305Sstevel 	struct lombus_state *ssp;
8302305Sstevel 	clock_t start;
8312305Sstevel 	uint8_t *p;
8322305Sstevel 
8332305Sstevel 	/*
8342305Sstevel 	 * First of all, wait for the interface to be available.
8352305Sstevel 	 *
8362305Sstevel 	 * NOTE: we blow through all the mutex/cv/state checking and
8372305Sstevel 	 * preempt any command in progress if the system is panicking!
8382305Sstevel 	 */
8392305Sstevel 	ssp = HANDLE_PRIVATE(hdlp);
8402305Sstevel 	mutex_enter(ssp->lo_mutex);
8412305Sstevel 	while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr)
8422305Sstevel 		cv_wait(ssp->lo_cv, ssp->lo_mutex);
8432305Sstevel 
8442305Sstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_BUSY;
8452305Sstevel 	ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ;
8462305Sstevel 
8472305Sstevel 	/*
8482305Sstevel 	 * We have exclusive ownership, so assemble the command (backwards):
8492305Sstevel 	 *
8502305Sstevel 	 * [byte 0]	Command:	modified by XADDR and/or WMSB bits
8512305Sstevel 	 * [Optional] Parameter: 	Value to write (low 7 bits)
8522305Sstevel 	 * [Optional] Parameter: 	Register number (high 7 bits)
8532305Sstevel 	 * [Optional] Parameter: 	Register number (low 7 bits)
8542305Sstevel 	 */
8552305Sstevel 	p = &ssp->cmdbuf[0];
8562305Sstevel 	*p++ = LOMBUS_CMD | ssp->sequence | cmd;
8572305Sstevel 	switch (cmd) {
8582305Sstevel 	case LOMBUS_CMD_WRITE:
8592305Sstevel 		*p++ = val & 0x7f;
8602305Sstevel 		if (val >= 0x80)
8612305Sstevel 			ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB;
8622305Sstevel 		/*FALLTHRU*/
8632305Sstevel 	case LOMBUS_CMD_READ:
8642305Sstevel 		if (LOMBUS_VREG_HI(vreg) != 0) {
8652305Sstevel 			*p++ = LOMBUS_VREG_HI(vreg);
8662305Sstevel 			ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR;
8672305Sstevel 		}
8682305Sstevel 		*p++ = LOMBUS_VREG_LO(vreg);
8692305Sstevel 		/*FALLTHRU*/
8702305Sstevel 	case LOMBUS_CMD_NOP:
8712305Sstevel 		break;
8722305Sstevel 	}
8732305Sstevel 
8742305Sstevel 	/*
8752305Sstevel 	 * Check and update the SIO h/w fault status before accessing
8762305Sstevel 	 * the chip registers.  If there's a (new or previous) fault,
8772305Sstevel 	 * we'll run through the protocol but won't really touch the
8782305Sstevel 	 * hardware and all commands will timeout.  If a previously
8792305Sstevel 	 * discovered fault has now gone away (!), then we can (try to)
8802305Sstevel 	 * proceed with the new command (probably a probe).
8812305Sstevel 	 */
8822305Sstevel 	sio_check_fault_status(ssp);
8832305Sstevel 
8842305Sstevel 	/*
8852305Sstevel 	 * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
8862305Sstevel 	 * us that it's ready for the next command.  If it doesn't, though,
8872305Sstevel 	 * we'll send it anyway, on the basis that the CTS signal might be
8882305Sstevel 	 * open- or short-circuited (or the LOM firmware forgot to set it,
8892305Sstevel 	 * or the LOM just got reset, or whatever ...)
8902305Sstevel 	 */
8912305Sstevel 	start = ddi_get_lbolt();
8922305Sstevel 	ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000);
8932305Sstevel 	while (!sio_lom_ready(ssp)) {
89411066Srafael.vanoni@sun.com 		if (ddi_get_lbolt() > ssp->deadline)
8952305Sstevel 			break;
89611066Srafael.vanoni@sun.com 
897*11311SSurya.Prakki@Sun.COM 		(void) cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
89811066Srafael.vanoni@sun.com 		    drv_usectohz(LOMBUS_CTS_POLL/1000), TR_CLOCK_TICK);
8992305Sstevel 	}
9002305Sstevel 
9012305Sstevel 	/*
9022305Sstevel 	 * Either the LOM is ready, or we timed out waiting for CTS.
9032305Sstevel 	 * In either case, we're going to send the command now by
9042305Sstevel 	 * stuffing the packet into the Tx FIFO, reversing it as we go.
9052305Sstevel 	 * We call lombus_receive() first to ensure there isn't any
9062305Sstevel 	 * garbage left in the Rx FIFO from an earlier command that
9072305Sstevel 	 * timed out (or was pre-empted by a PANIC!).  This also makes
9082305Sstevel 	 * sure that SIO interrupts are enabled so we'll see the reply
9092305Sstevel 	 * more quickly (the poll loop below will still work even if
9102305Sstevel 	 * interrupts aren't enabled, but it will take longer).
9112305Sstevel 	 */
9122305Sstevel 	lombus_receive(ssp);
9132305Sstevel 	mutex_enter(ssp->hw_mutex);
9142305Sstevel 	while (p > ssp->cmdbuf)
9152305Sstevel 		sio_put_reg(ssp, SIO_TXD, *--p);
9162305Sstevel 	mutex_exit(ssp->hw_mutex);
9172305Sstevel 
9182305Sstevel 	/*
9192305Sstevel 	 * Prepare for the reply (to be processed by the interrupt/cyclic
9202305Sstevel 	 * handler and/or polling loop below), then wait for a response
9212305Sstevel 	 * or timeout.
9222305Sstevel 	 */
9232305Sstevel 	start = ddi_get_lbolt();
9242305Sstevel 	ssp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
9252305Sstevel 	ssp->error = 0;
9262305Sstevel 	ssp->index = 0;
9272305Sstevel 	ssp->result = DUMMY_VALUE;
9282305Sstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_WAITING;
9292305Sstevel 	while (ssp->cmdstate == LOMBUS_CMDSTATE_WAITING) {
93011066Srafael.vanoni@sun.com 		if (cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
93111066Srafael.vanoni@sun.com 		    drv_usectohz(LOMBUS_CMD_POLL/1000), TR_CLOCK_TICK) == -1)
9322305Sstevel 			lombus_receive(ssp);
9332305Sstevel 	}
9342305Sstevel 
9352305Sstevel 	/*
9362305Sstevel 	 * The return value may not be meaningful but retrieve it anyway
9372305Sstevel 	 */
9382305Sstevel 	val = ssp->result;
9392305Sstevel 	if (sio_faulty(ssp)) {
9402305Sstevel 		val = DUMMY_VALUE;
9412305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW;
9422305Sstevel 	} else if (ssp->cmdstate != LOMBUS_CMDSTATE_READY) {
9432305Sstevel 		/*
9442305Sstevel 		 * Some problem here ... transfer the error code from
9452305Sstevel 		 * the per-instance state to the per-handle fault flag.
9462305Sstevel 		 * The error code shouldn't be zero!
9472305Sstevel 		 */
9482305Sstevel 		if (ssp->error != 0)
9492305Sstevel 			HANDLE_FAULT(hdlp) = ssp->error;
9502305Sstevel 		else
9512305Sstevel 			HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE;
9522305Sstevel 	}
9532305Sstevel 
9542305Sstevel 	/*
9552305Sstevel 	 * All done now!
9562305Sstevel 	 */
9572305Sstevel 	ssp->index = 0;
9582305Sstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
9592305Sstevel 	cv_broadcast(ssp->lo_cv);
9602305Sstevel 	mutex_exit(ssp->lo_mutex);
9612305Sstevel 
9622305Sstevel 	return (val);
9632305Sstevel }
9642305Sstevel 
9652305Sstevel 
9662305Sstevel /*
9672305Sstevel  * Space 0 - LOM virtual register access
9682305Sstevel  * Only 8-bit accesses are supported.
9692305Sstevel  */
9702305Sstevel static uint8_t
lombus_vreg_get8(HANDLE_TYPE * hdlp,uint8_t * addr)9712305Sstevel lombus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
9722305Sstevel {
9732305Sstevel 	ptrdiff_t offset;
9742305Sstevel 
9752305Sstevel 	/*
9762305Sstevel 	 * Check the offset that the caller has added to the base address
9772305Sstevel 	 * against the length of the mapping originally requested.
9782305Sstevel 	 */
9792305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
9802305Sstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
9812305Sstevel 		/*
9822305Sstevel 		 * Invalid access - flag a fault and return a dummy value
9832305Sstevel 		 */
9842305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
9852305Sstevel 		return (DUMMY_VALUE);
9862305Sstevel 	}
9872305Sstevel 
9882305Sstevel 	/*
9892305Sstevel 	 * Derive the virtual register number and run the command
9902305Sstevel 	 */
9912305Sstevel 	return (lombus_cmd(hdlp, ADDR_TO_VREG(addr), 0, LOMBUS_CMD_READ));
9922305Sstevel }
9932305Sstevel 
9942305Sstevel static void
lombus_vreg_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)9952305Sstevel lombus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
9962305Sstevel {
9972305Sstevel 	ptrdiff_t offset;
9982305Sstevel 
9992305Sstevel 	/*
10002305Sstevel 	 * Check the offset that the caller has added to the base address
10012305Sstevel 	 * against the length of the mapping originally requested.
10022305Sstevel 	 */
10032305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10042305Sstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10052305Sstevel 		/*
10062305Sstevel 		 * Invalid access - flag a fault and return
10072305Sstevel 		 */
10082305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10092305Sstevel 		return;
10102305Sstevel 	}
10112305Sstevel 
10122305Sstevel 	/*
10132305Sstevel 	 * Derive the virtual register number and run the command
10142305Sstevel 	 */
10152305Sstevel 	(void) lombus_cmd(hdlp, ADDR_TO_VREG(addr), val, LOMBUS_CMD_WRITE);
10162305Sstevel }
10172305Sstevel 
10182305Sstevel static void
lombus_vreg_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)10192305Sstevel lombus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
10202305Sstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
10212305Sstevel {
10222305Sstevel 	size_t inc;
10232305Sstevel 
10242305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
10252305Sstevel 	for (; repcount--; dev_addr += inc)
10262305Sstevel 		*host_addr++ = lombus_vreg_get8(hdlp, dev_addr);
10272305Sstevel }
10282305Sstevel 
10292305Sstevel static void
lombus_vreg_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)10302305Sstevel lombus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
10312305Sstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
10322305Sstevel {
10332305Sstevel 	size_t inc;
10342305Sstevel 
10352305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
10362305Sstevel 	for (; repcount--; dev_addr += inc)
10372305Sstevel 		lombus_vreg_put8(hdlp, dev_addr, *host_addr++);
10382305Sstevel }
10392305Sstevel 
10402305Sstevel 
10412305Sstevel /*
10422305Sstevel  * Space 1 - LOM watchdog pat register access
10432305Sstevel  * Only 8-bit accesses are supported.
10442305Sstevel  *
10452305Sstevel  * Reads have no effect and return 0.
10462305Sstevel  *
10472305Sstevel  * Writes pat the dog by toggling the RTS line iff enough time has
10482305Sstevel  * elapsed since last time we toggled it.
10492305Sstevel  *
10502305Sstevel  * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
10512305Sstevel  * way of zeroing the destination area ;-) and still won't pat the dog.
10522305Sstevel  *
10532305Sstevel  * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
10542305Sstevel  * only count as a single pat, no matter how many bytes the caller
10552305Sstevel  * says to write, as the inter-pat time is VERY long compared with
10562305Sstevel  * the time it will take to read the memory source area.
10572305Sstevel  */
10582305Sstevel 
10592305Sstevel static uint8_t
lombus_pat_get8(HANDLE_TYPE * hdlp,uint8_t * addr)10602305Sstevel lombus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
10612305Sstevel {
10622305Sstevel 	ptrdiff_t offset;
10632305Sstevel 
10642305Sstevel 	/*
10652305Sstevel 	 * Check the offset that the caller has added to the base address
10662305Sstevel 	 * against the length of the mapping originally requested.
10672305Sstevel 	 */
10682305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10692305Sstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10702305Sstevel 		/*
10712305Sstevel 		 * Invalid access - flag a fault and return a dummy value
10722305Sstevel 		 */
10732305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10742305Sstevel 		return (DUMMY_VALUE);
10752305Sstevel 	}
10762305Sstevel 
10772305Sstevel 	return (0);
10782305Sstevel }
10792305Sstevel 
10802305Sstevel static void
lombus_pat_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)10812305Sstevel lombus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
10822305Sstevel {
10832305Sstevel 	struct lombus_state *ssp;
10842305Sstevel 	ptrdiff_t offset;
10852305Sstevel 	hrtime_t now;
10862305Sstevel 
10872305Sstevel 	_NOTE(ARGUNUSED(val))
10882305Sstevel 
10892305Sstevel 	/*
10902305Sstevel 	 * Check the offset that the caller has added to the base address
10912305Sstevel 	 * against the length of the mapping originally requested.
10922305Sstevel 	 */
10932305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10942305Sstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10952305Sstevel 		/*
10962305Sstevel 		 * Invalid access - flag a fault and return
10972305Sstevel 		 */
10982305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10992305Sstevel 		return;
11002305Sstevel 	}
11012305Sstevel 
11022305Sstevel 	ssp = HANDLE_PRIVATE(hdlp);
11032305Sstevel 	mutex_enter(ssp->hw_mutex);
11042305Sstevel 	now = gethrtime();
11052305Sstevel 	if ((now - ssp->hw_last_pat) >= LOMBUS_MIN_PAT) {
11062305Sstevel 		lombus_toggle_rts(ssp);
11072305Sstevel 		ssp->hw_last_pat = now;
11082305Sstevel 	}
11092305Sstevel 	mutex_exit(ssp->hw_mutex);
11102305Sstevel }
11112305Sstevel 
11122305Sstevel static void
lombus_pat_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11132305Sstevel lombus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11142305Sstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11152305Sstevel {
11162305Sstevel 	size_t inc;
11172305Sstevel 
11182305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11192305Sstevel 	for (; repcount--; dev_addr += inc)
11202305Sstevel 		*host_addr++ = lombus_pat_get8(hdlp, dev_addr);
11212305Sstevel }
11222305Sstevel 
11232305Sstevel static void
lombus_pat_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11242305Sstevel lombus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11252305Sstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11262305Sstevel {
11272305Sstevel 	size_t inc;
11282305Sstevel 
11292305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11302305Sstevel 	for (; repcount--; dev_addr += inc)
11312305Sstevel 		lombus_pat_put8(hdlp, dev_addr, *host_addr++);
11322305Sstevel }
11332305Sstevel 
11342305Sstevel 
11352305Sstevel /*
11362305Sstevel  * Space 2 - LOM async event flag register access
11372305Sstevel  * Only 16-bit accesses are supported.
11382305Sstevel  */
11392305Sstevel static uint16_t
lombus_event_get16(HANDLE_TYPE * hdlp,uint16_t * addr)11402305Sstevel lombus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
11412305Sstevel {
11422305Sstevel 	struct lombus_state *ssp;
11432305Sstevel 	ptrdiff_t offset;
11442305Sstevel 
11452305Sstevel 	/*
11462305Sstevel 	 * Check the offset that the caller has added to the base address
11472305Sstevel 	 * against the length of the mapping orignally requested.
11482305Sstevel 	 */
11492305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
11502305Sstevel 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11512305Sstevel 		/*
11522305Sstevel 		 * Invalid access - flag a fault and return a dummy value
11532305Sstevel 		 */
11542305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11552305Sstevel 		return (DUMMY_VALUE);
11562305Sstevel 	}
11572305Sstevel 
11582305Sstevel 	/*
11592305Sstevel 	 * Return the value of the asynchronous-event-pending flag
11602305Sstevel 	 * as passed back by the LOM at the end of the last command.
11612305Sstevel 	 */
11622305Sstevel 	ssp = HANDLE_PRIVATE(hdlp);
11632305Sstevel 	return (ssp->async);
11642305Sstevel }
11652305Sstevel 
11662305Sstevel static void
lombus_event_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)11672305Sstevel lombus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
11682305Sstevel {
11692305Sstevel 	ptrdiff_t offset;
11702305Sstevel 
11712305Sstevel 	_NOTE(ARGUNUSED(val))
11722305Sstevel 
11732305Sstevel 	/*
11742305Sstevel 	 * Check the offset that the caller has added to the base address
11752305Sstevel 	 * against the length of the mapping originally requested.
11762305Sstevel 	 */
11772305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
11782305Sstevel 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11792305Sstevel 		/*
11802305Sstevel 		 * Invalid access - flag a fault and return
11812305Sstevel 		 */
11822305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11832305Sstevel 		return;
11842305Sstevel 	}
11852305Sstevel 
11862305Sstevel 	/*
11872305Sstevel 	 * The user can't overwrite the asynchronous-event-pending flag!
11882305Sstevel 	 */
11892305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO;
11902305Sstevel }
11912305Sstevel 
11922305Sstevel static void
lombus_event_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)11932305Sstevel lombus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
11942305Sstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
11952305Sstevel {
11962305Sstevel 	size_t inc;
11972305Sstevel 
11982305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11992305Sstevel 	for (; repcount--; dev_addr += inc)
12002305Sstevel 		*host_addr++ = lombus_event_get16(hdlp, dev_addr);
12012305Sstevel }
12022305Sstevel 
12032305Sstevel static void
lombus_event_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)12042305Sstevel lombus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
12052305Sstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
12062305Sstevel {
12072305Sstevel 	size_t inc;
12082305Sstevel 
12092305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12102305Sstevel 	for (; repcount--; dev_addr += inc)
12112305Sstevel 		lombus_event_put16(hdlp, dev_addr, *host_addr++);
12122305Sstevel }
12132305Sstevel 
12142305Sstevel 
12152305Sstevel /*
12162305Sstevel  * All spaces - access handle fault information
12172305Sstevel  * Only 32-bit accesses are supported.
12182305Sstevel  */
12192305Sstevel static uint32_t
lombus_meta_get32(HANDLE_TYPE * hdlp,uint32_t * addr)12202305Sstevel lombus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr)
12212305Sstevel {
12222305Sstevel 	struct lombus_state *ssp;
12232305Sstevel 	ptrdiff_t offset;
12242305Sstevel 
12252305Sstevel 	/*
12262305Sstevel 	 * Derive the offset that the caller has added to the base
12272305Sstevel 	 * address originally returned, and use it to determine
12282305Sstevel 	 * which meta-register is to be accessed ...
12292305Sstevel 	 */
12302305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
12312305Sstevel 	switch (offset) {
12322305Sstevel 	case LOMBUS_FAULT_REG:
12332305Sstevel 		/*
12342305Sstevel 		 * This meta-register provides a code for the most
12352305Sstevel 		 * recent virtual register access fault, if any.
12362305Sstevel 		 */
12372305Sstevel 		return (HANDLE_FAULT(hdlp));
12382305Sstevel 
12392305Sstevel 	case LOMBUS_PROBE_REG:
12402305Sstevel 		/*
12412305Sstevel 		 * Reading this meta-register clears any existing fault
12422305Sstevel 		 * (at the virtual, not the hardware access layer), then
12432305Sstevel 		 * runs a NOP command and returns the fault code from that.
12442305Sstevel 		 */
12452305Sstevel 		HANDLE_FAULT(hdlp) = 0;
1246*11311SSurya.Prakki@Sun.COM 		(void) lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
12472305Sstevel 		return (HANDLE_FAULT(hdlp));
12482305Sstevel 
12492305Sstevel 	case LOMBUS_ASYNC_REG:
12502305Sstevel 		/*
12512305Sstevel 		 * Obsolescent - but still supported for backwards
12522305Sstevel 		 * compatibility.  This is an alias for the newer
12532305Sstevel 		 * LOMBUS_EVENT_REG, but doesn't require a separate
12542305Sstevel 		 * "reg" entry and ddi_regs_map_setup() call.
12552305Sstevel 		 *
12562305Sstevel 		 * It returns the value of the asynchronous-event-pending
12572305Sstevel 		 * flag as passed back by the LOM at the end of the last
12582305Sstevel 		 * completed command.
12592305Sstevel 		 */
12602305Sstevel 		ssp = HANDLE_PRIVATE(hdlp);
12612305Sstevel 		return (ssp->async);
12622305Sstevel 
12632305Sstevel 	default:
12642305Sstevel 		/*
12652305Sstevel 		 * Invalid access - flag a fault and return a dummy value
12662305Sstevel 		 */
12672305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
12682305Sstevel 		return (DUMMY_VALUE);
12692305Sstevel 	}
12702305Sstevel }
12712305Sstevel 
12722305Sstevel static void
lombus_meta_put32(HANDLE_TYPE * hdlp,uint32_t * addr,uint32_t val)12732305Sstevel lombus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val)
12742305Sstevel {
12752305Sstevel 	ptrdiff_t offset;
12762305Sstevel 
12772305Sstevel 	/*
12782305Sstevel 	 * Derive the offset that the caller has added to the base
12792305Sstevel 	 * address originally returned, and use it to determine
12802305Sstevel 	 * which meta-register is to be accessed ...
12812305Sstevel 	 */
12822305Sstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
12832305Sstevel 	switch (offset) {
12842305Sstevel 	case LOMBUS_FAULT_REG:
12852305Sstevel 		/*
12862305Sstevel 		 * This meta-register contains a code for the most
12872305Sstevel 		 * recent virtual register access fault, if any.
12882305Sstevel 		 * It can be cleared simply by writing 0 to it.
12892305Sstevel 		 */
12902305Sstevel 		HANDLE_FAULT(hdlp) = val;
12912305Sstevel 		return;
12922305Sstevel 
12932305Sstevel 	case LOMBUS_PROBE_REG:
12942305Sstevel 		/*
12952305Sstevel 		 * Writing this meta-register clears any existing fault
12962305Sstevel 		 * (at the virtual, not the hardware acess layer), then
12972305Sstevel 		 * runs a NOP command.  The caller can check the fault
12982305Sstevel 		 * code later if required.
12992305Sstevel 		 */
13002305Sstevel 		HANDLE_FAULT(hdlp) = 0;
1301*11311SSurya.Prakki@Sun.COM 		(void) lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
13022305Sstevel 		return;
13032305Sstevel 
13042305Sstevel 	default:
13052305Sstevel 		/*
13062305Sstevel 		 * Invalid access - flag a fault
13072305Sstevel 		 */
13082305Sstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13092305Sstevel 		return;
13102305Sstevel 	}
13112305Sstevel }
13122305Sstevel 
13132305Sstevel static void
lombus_meta_rep_get32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)13142305Sstevel lombus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
13152305Sstevel 	uint32_t *dev_addr, size_t repcount, uint_t flags)
13162305Sstevel {
13172305Sstevel 	size_t inc;
13182305Sstevel 
13192305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
13202305Sstevel 	for (; repcount--; dev_addr += inc)
13212305Sstevel 		*host_addr++ = lombus_meta_get32(hdlp, dev_addr);
13222305Sstevel }
13232305Sstevel 
13242305Sstevel static void
lombus_meta_rep_put32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)13252305Sstevel lombus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
13262305Sstevel 	uint32_t *dev_addr, size_t repcount, uint_t flags)
13272305Sstevel {
13282305Sstevel 	size_t inc;
13292305Sstevel 
13302305Sstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
13312305Sstevel 	for (; repcount--; dev_addr += inc)
13322305Sstevel 		lombus_meta_put32(hdlp, dev_addr, *host_addr++);
13332305Sstevel }
13342305Sstevel 
13352305Sstevel 
13362305Sstevel /*
13372305Sstevel  * Finally, some dummy functions for all unsupported access
13382305Sstevel  * space/size/mode combinations ...
13392305Sstevel  */
13402305Sstevel static uint8_t
lombus_no_get8(HANDLE_TYPE * hdlp,uint8_t * addr)13412305Sstevel lombus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
13422305Sstevel {
13432305Sstevel 	_NOTE(ARGUNUSED(addr))
13442305Sstevel 
13452305Sstevel 	/*
13462305Sstevel 	 * Invalid access - flag a fault and return a dummy value
13472305Sstevel 	 */
13482305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13492305Sstevel 	return (DUMMY_VALUE);
13502305Sstevel }
13512305Sstevel 
13522305Sstevel static void
lombus_no_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)13532305Sstevel lombus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
13542305Sstevel {
13552305Sstevel 	_NOTE(ARGUNUSED(addr, val))
13562305Sstevel 
13572305Sstevel 	/*
13582305Sstevel 	 * Invalid access - flag a fault
13592305Sstevel 	 */
13602305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13612305Sstevel }
13622305Sstevel 
13632305Sstevel static void
lombus_no_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)13642305Sstevel lombus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
13652305Sstevel 		uint8_t *dev_addr, size_t repcount, uint_t flags)
13662305Sstevel {
13672305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
13682305Sstevel 
13692305Sstevel 	/*
13702305Sstevel 	 * Invalid access - flag a fault
13712305Sstevel 	 */
13722305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13732305Sstevel }
13742305Sstevel 
13752305Sstevel static void
lombus_no_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)13762305Sstevel lombus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
13772305Sstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
13782305Sstevel {
13792305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
13802305Sstevel 
13812305Sstevel 	/*
13822305Sstevel 	 * Invalid access - flag a fault
13832305Sstevel 	 */
13842305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13852305Sstevel }
13862305Sstevel 
13872305Sstevel static uint16_t
lombus_no_get16(HANDLE_TYPE * hdlp,uint16_t * addr)13882305Sstevel lombus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
13892305Sstevel {
13902305Sstevel 	_NOTE(ARGUNUSED(addr))
13912305Sstevel 
13922305Sstevel 	/*
13932305Sstevel 	 * Invalid access - flag a fault and return a dummy value
13942305Sstevel 	 */
13952305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13962305Sstevel 	return (DUMMY_VALUE);
13972305Sstevel }
13982305Sstevel 
13992305Sstevel static void
lombus_no_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)14002305Sstevel lombus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
14012305Sstevel {
14022305Sstevel 	_NOTE(ARGUNUSED(addr, val))
14032305Sstevel 
14042305Sstevel 	/*
14052305Sstevel 	 * Invalid access - flag a fault
14062305Sstevel 	 */
14072305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14082305Sstevel }
14092305Sstevel 
14102305Sstevel static void
lombus_no_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)14112305Sstevel lombus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
14122305Sstevel 		uint16_t *dev_addr, size_t repcount, uint_t flags)
14132305Sstevel {
14142305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14152305Sstevel 
14162305Sstevel 	/*
14172305Sstevel 	 * Invalid access - flag a fault
14182305Sstevel 	 */
14192305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14202305Sstevel }
14212305Sstevel 
14222305Sstevel static void
lombus_no_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)14232305Sstevel lombus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
14242305Sstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
14252305Sstevel {
14262305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14272305Sstevel 
14282305Sstevel 	/*
14292305Sstevel 	 * Invalid access - flag a fault
14302305Sstevel 	 */
14312305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14322305Sstevel }
14332305Sstevel 
14342305Sstevel static uint64_t
lombus_no_get64(HANDLE_TYPE * hdlp,uint64_t * addr)14352305Sstevel lombus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr)
14362305Sstevel {
14372305Sstevel 	_NOTE(ARGUNUSED(addr))
14382305Sstevel 
14392305Sstevel 	/*
14402305Sstevel 	 * Invalid access - flag a fault and return a dummy value
14412305Sstevel 	 */
14422305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14432305Sstevel 	return (DUMMY_VALUE);
14442305Sstevel }
14452305Sstevel 
14462305Sstevel static void
lombus_no_put64(HANDLE_TYPE * hdlp,uint64_t * addr,uint64_t val)14472305Sstevel lombus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val)
14482305Sstevel {
14492305Sstevel 	_NOTE(ARGUNUSED(addr, val))
14502305Sstevel 
14512305Sstevel 	/*
14522305Sstevel 	 * Invalid access - flag a fault
14532305Sstevel 	 */
14542305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14552305Sstevel }
14562305Sstevel 
14572305Sstevel static void
lombus_no_rep_get64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)14582305Sstevel lombus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
14592305Sstevel 	uint64_t *dev_addr, size_t repcount, uint_t flags)
14602305Sstevel {
14612305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14622305Sstevel 
14632305Sstevel 	/*
14642305Sstevel 	 * Invalid access - flag a fault
14652305Sstevel 	 */
14662305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14672305Sstevel }
14682305Sstevel 
14692305Sstevel static void
lombus_no_rep_put64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)14702305Sstevel lombus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
14712305Sstevel 	uint64_t *dev_addr, size_t repcount, uint_t flags)
14722305Sstevel {
14732305Sstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14742305Sstevel 
14752305Sstevel 	/*
14762305Sstevel 	 * Invalid access - flag a fault
14772305Sstevel 	 */
14782305Sstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14792305Sstevel }
14802305Sstevel 
14812305Sstevel static int
lombus_acc_fault_check(HANDLE_TYPE * hdlp)14822305Sstevel lombus_acc_fault_check(HANDLE_TYPE *hdlp)
14832305Sstevel {
14842305Sstevel 	return (HANDLE_FAULT(hdlp) != 0);
14852305Sstevel }
14862305Sstevel 
14872305Sstevel 
14882305Sstevel /*
14892305Sstevel  * Hardware setup - put the SIO chip in the required operational
14902305Sstevel  * state,  with all our favourite parameters programmed correctly.
14912305Sstevel  * This routine leaves all SIO interrupts disabled.
14922305Sstevel  */
14932305Sstevel 
14942305Sstevel static void
lombus_hw_reset(struct lombus_state * ssp)14952305Sstevel lombus_hw_reset(struct lombus_state *ssp)
14962305Sstevel {
14972305Sstevel 	uint16_t divisor;
14982305Sstevel 
14992305Sstevel 	/*
15002305Sstevel 	 * Disable interrupts, soft reset Tx and Rx circuitry,
15012305Sstevel 	 * reselect standard modes (bits/char, parity, etc).
15022305Sstevel 	 */
15032305Sstevel 	lombus_set_irq(ssp, B_FALSE);
15042305Sstevel 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR);
15052305Sstevel 	sio_put_reg(ssp, SIO_LCR, SIO_LCR_STD);
15062305Sstevel 
15072305Sstevel 	/*
15082305Sstevel 	 * Select the proper baud rate; if the value is invalid
15092305Sstevel 	 * (presumably 0, i.e. not specified, but also if the
15102305Sstevel 	 * "baud" property is set to some silly value), we assume
15112305Sstevel 	 * the default.
15122305Sstevel 	 */
15132305Sstevel 	if (ssp->baud < SIO_BAUD_MIN || ssp->baud > SIO_BAUD_MAX)
15142305Sstevel 		divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT);
15152305Sstevel 	else
15162305Sstevel 		divisor = SIO_BAUD_TO_DIVISOR(ssp->baud);
15172305Sstevel 
15182305Sstevel 	/*
15192305Sstevel 	 * According to the datasheet, it is forbidden for the divisor
15202305Sstevel 	 * register to be zero.  So when loading the register in two
15212305Sstevel 	 * steps, we have to make sure that the temporary value formed
15222305Sstevel 	 * between loads is nonzero.  However, we can't rely on either
15232305Sstevel 	 * half already having a nonzero value, as the datasheet also
15242305Sstevel 	 * says that these registers are indeterminate after a reset!
15252305Sstevel 	 * So, we explicitly set the low byte to a non-zero value first;
15262305Sstevel 	 * then we can safely load the high byte, and then the correct
15272305Sstevel 	 * value for the low byte, without the result ever being zero.
15282305Sstevel 	 */
15292305Sstevel 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK1);
15302305Sstevel 	sio_put_reg(ssp, SIO_LBGDL, 0xff);
15312305Sstevel 	sio_put_reg(ssp, SIO_LBGDH, divisor >> 8);
15322305Sstevel 	sio_put_reg(ssp, SIO_LBGDL, divisor & 0xff);
15332305Sstevel 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK0);
15342305Sstevel 
15352305Sstevel 	/*
15362305Sstevel 	 * Program the remaining device registers as required
15372305Sstevel 	 */
15382305Sstevel 	sio_put_reg(ssp, SIO_MCR, SIO_MCR_STD);
15392305Sstevel 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_STD);
15402305Sstevel }
15412305Sstevel 
15422305Sstevel 
15432305Sstevel /*
15442305Sstevel  * Higher-level setup & teardown
15452305Sstevel  */
15462305Sstevel 
15472305Sstevel static void
lombus_offline(struct lombus_state * ssp)15482305Sstevel lombus_offline(struct lombus_state *ssp)
15492305Sstevel {
15502305Sstevel 	if (ssp->sio_handle != NULL)
15512305Sstevel 		ddi_regs_map_free(&ssp->sio_handle);
15522305Sstevel 	ssp->sio_handle = NULL;
15532305Sstevel 	ssp->sio_regs = NULL;
15542305Sstevel }
15552305Sstevel 
15562305Sstevel static int
lombus_online(struct lombus_state * ssp)15572305Sstevel lombus_online(struct lombus_state *ssp)
15582305Sstevel {
15592305Sstevel 	ddi_acc_handle_t h;
15602305Sstevel 	caddr_t p;
15612305Sstevel 	int nregs;
15622305Sstevel 	int err;
15632305Sstevel 
15642305Sstevel 	if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS)
15652305Sstevel 		nregs = 0;
15662305Sstevel 
15672305Sstevel 	switch (nregs) {
15682305Sstevel 	default:
15692305Sstevel 	case 1:
15702305Sstevel 		/*
15712305Sstevel 		 *  regset 0 represents the SIO operating registers
15722305Sstevel 		 */
15732305Sstevel 		err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0,
15742305Sstevel 		    lombus_dev_acc_attr, &h);
15752305Sstevel 		lombus_trace(ssp, 'O', "online",
15762305Sstevel 		    "regmap 0 status %d addr $%p", err, p);
15772305Sstevel 		if (err != DDI_SUCCESS)
15782305Sstevel 			return (EIO);
15792305Sstevel 
15802305Sstevel 		ssp->sio_handle = h;
15812305Sstevel 		ssp->sio_regs = (void *)p;
15822305Sstevel 		break;
15832305Sstevel 
15842305Sstevel 	case 0:
15852305Sstevel 		/*
15862305Sstevel 		 *  If no registers are defined, succeed vacuously;
15872305Sstevel 		 *  commands will be accepted, but we fake the accesses.
15882305Sstevel 		 */
15892305Sstevel 		break;
15902305Sstevel 	}
15912305Sstevel 
15922305Sstevel 	/*
15932305Sstevel 	 * Now that the registers are mapped, we can initialise the SIO h/w
15942305Sstevel 	 */
15952305Sstevel 	lombus_hw_reset(ssp);
15962305Sstevel 	return (0);
15972305Sstevel }
15982305Sstevel 
15992305Sstevel 
16002305Sstevel /*
16012305Sstevel  *  Nexus routines
16022305Sstevel  */
16032305Sstevel 
16042305Sstevel #if	defined(NDI_ACC_HDL_V2)
16052305Sstevel 
16062305Sstevel static const ndi_acc_fns_t lombus_vreg_acc_fns = {
16072305Sstevel 	NDI_ACC_FNS_CURRENT,
16082305Sstevel 	NDI_ACC_FNS_V1,
16092305Sstevel 
16102305Sstevel 	lombus_vreg_get8,
16112305Sstevel 	lombus_vreg_put8,
16122305Sstevel 	lombus_vreg_rep_get8,
16132305Sstevel 	lombus_vreg_rep_put8,
16142305Sstevel 
16152305Sstevel 	lombus_no_get16,
16162305Sstevel 	lombus_no_put16,
16172305Sstevel 	lombus_no_rep_get16,
16182305Sstevel 	lombus_no_rep_put16,
16192305Sstevel 
16202305Sstevel 	lombus_meta_get32,
16212305Sstevel 	lombus_meta_put32,
16222305Sstevel 	lombus_meta_rep_get32,
16232305Sstevel 	lombus_meta_rep_put32,
16242305Sstevel 
16252305Sstevel 	lombus_no_get64,
16262305Sstevel 	lombus_no_put64,
16272305Sstevel 	lombus_no_rep_get64,
16282305Sstevel 	lombus_no_rep_put64,
16292305Sstevel 
16302305Sstevel 	lombus_acc_fault_check
16312305Sstevel };
16322305Sstevel 
16332305Sstevel static const ndi_acc_fns_t lombus_pat_acc_fns = {
16342305Sstevel 	NDI_ACC_FNS_CURRENT,
16352305Sstevel 	NDI_ACC_FNS_V1,
16362305Sstevel 
16372305Sstevel 	lombus_pat_get8,
16382305Sstevel 	lombus_pat_put8,
16392305Sstevel 	lombus_pat_rep_get8,
16402305Sstevel 	lombus_pat_rep_put8,
16412305Sstevel 
16422305Sstevel 	lombus_no_get16,
16432305Sstevel 	lombus_no_put16,
16442305Sstevel 	lombus_no_rep_get16,
16452305Sstevel 	lombus_no_rep_put16,
16462305Sstevel 
16472305Sstevel 	lombus_meta_get32,
16482305Sstevel 	lombus_meta_put32,
16492305Sstevel 	lombus_meta_rep_get32,
16502305Sstevel 	lombus_meta_rep_put32,
16512305Sstevel 
16522305Sstevel 	lombus_no_get64,
16532305Sstevel 	lombus_no_put64,
16542305Sstevel 	lombus_no_rep_get64,
16552305Sstevel 	lombus_no_rep_put64,
16562305Sstevel 
16572305Sstevel 	lombus_acc_fault_check
16582305Sstevel };
16592305Sstevel 
16602305Sstevel static const ndi_acc_fns_t lombus_event_acc_fns = {
16612305Sstevel 	NDI_ACC_FNS_CURRENT,
16622305Sstevel 	NDI_ACC_FNS_V1,
16632305Sstevel 
16642305Sstevel 	lombus_no_get8,
16652305Sstevel 	lombus_no_put8,
16662305Sstevel 	lombus_no_rep_get8,
16672305Sstevel 	lombus_no_rep_put8,
16682305Sstevel 
16692305Sstevel 	lombus_event_get16,
16702305Sstevel 	lombus_event_put16,
16712305Sstevel 	lombus_event_rep_get16,
16722305Sstevel 	lombus_event_rep_put16,
16732305Sstevel 
16742305Sstevel 	lombus_meta_get32,
16752305Sstevel 	lombus_meta_put32,
16762305Sstevel 	lombus_meta_rep_get32,
16772305Sstevel 	lombus_meta_rep_put32,
16782305Sstevel 
16792305Sstevel 	lombus_no_get64,
16802305Sstevel 	lombus_no_put64,
16812305Sstevel 	lombus_no_rep_get64,
16822305Sstevel 	lombus_no_rep_put64,
16832305Sstevel 
16842305Sstevel 	lombus_acc_fault_check
16852305Sstevel };
16862305Sstevel 
16872305Sstevel static int
lombus_map_handle(struct lombus_state * ssp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ndi_acc_handle_t * hdlp,caddr_t * addrp)16882305Sstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
16892305Sstevel 	int space, caddr_t vaddr, off_t len,
16902305Sstevel 	ndi_acc_handle_t *hdlp, caddr_t *addrp)
16912305Sstevel {
16922305Sstevel 	switch (op) {
16932305Sstevel 	default:
16942305Sstevel 		return (DDI_ME_UNIMPLEMENTED);
16952305Sstevel 
16962305Sstevel 	case DDI_MO_MAP_LOCKED:
16972305Sstevel 		switch (space) {
16982305Sstevel 		default:
16992305Sstevel 			return (DDI_ME_REGSPEC_RANGE);
17002305Sstevel 
17012305Sstevel 		case LOMBUS_VREG_SPACE:
17022305Sstevel 			ndi_set_acc_fns(hdlp, &lombus_vreg_acc_fns);
17032305Sstevel 			break;
17042305Sstevel 
17052305Sstevel 		case LOMBUS_PAT_SPACE:
17062305Sstevel 			ndi_set_acc_fns(hdlp, &lombus_pat_acc_fns);
17072305Sstevel 			break;
17082305Sstevel 
17092305Sstevel 		case LOMBUS_EVENT_SPACE:
17102305Sstevel 			ndi_set_acc_fns(hdlp, &lombus_event_acc_fns);
17112305Sstevel 			break;
17122305Sstevel 		}
17132305Sstevel 		hdlp->ah_addr = *addrp = vaddr;
17142305Sstevel 		hdlp->ah_len = len;
17152305Sstevel 		hdlp->ah_bus_private = ssp;
17162305Sstevel 		return (DDI_SUCCESS);
17172305Sstevel 
17182305Sstevel 	case DDI_MO_UNMAP:
17192305Sstevel 		*addrp = NULL;
17202305Sstevel 		hdlp->ah_bus_private = NULL;
17212305Sstevel 		return (DDI_SUCCESS);
17222305Sstevel 	}
17232305Sstevel }
17242305Sstevel 
17252305Sstevel #else
17262305Sstevel 
17272305Sstevel static int
lombus_map_handle(struct lombus_state * ssp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ddi_acc_hdl_t * hdlp,caddr_t * addrp)17282305Sstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
17292305Sstevel 	int space, caddr_t vaddr, off_t len,
17302305Sstevel 	ddi_acc_hdl_t *hdlp, caddr_t *addrp)
17312305Sstevel {
17322305Sstevel 	ddi_acc_impl_t *aip = hdlp->ah_platform_private;
17332305Sstevel 
17342305Sstevel 	switch (op) {
17352305Sstevel 	default:
17362305Sstevel 		return (DDI_ME_UNIMPLEMENTED);
17372305Sstevel 
17382305Sstevel 	case DDI_MO_MAP_LOCKED:
17392305Sstevel 		switch (space) {
17402305Sstevel 		default:
17412305Sstevel 			return (DDI_ME_REGSPEC_RANGE);
17422305Sstevel 
17432305Sstevel 		case LOMBUS_VREG_SPACE:
17442305Sstevel 			aip->ahi_get8 = lombus_vreg_get8;
17452305Sstevel 			aip->ahi_put8 = lombus_vreg_put8;
17462305Sstevel 			aip->ahi_rep_get8 = lombus_vreg_rep_get8;
17472305Sstevel 			aip->ahi_rep_put8 = lombus_vreg_rep_put8;
17482305Sstevel 
17492305Sstevel 			aip->ahi_get16 = lombus_no_get16;
17502305Sstevel 			aip->ahi_put16 = lombus_no_put16;
17512305Sstevel 			aip->ahi_rep_get16 = lombus_no_rep_get16;
17522305Sstevel 			aip->ahi_rep_put16 = lombus_no_rep_put16;
17532305Sstevel 
17542305Sstevel 			aip->ahi_get32 = lombus_meta_get32;
17552305Sstevel 			aip->ahi_put32 = lombus_meta_put32;
17562305Sstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
17572305Sstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
17582305Sstevel 
17592305Sstevel 			aip->ahi_get64 = lombus_no_get64;
17602305Sstevel 			aip->ahi_put64 = lombus_no_put64;
17612305Sstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
17622305Sstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
17632305Sstevel 
17642305Sstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
17652305Sstevel 			break;
17662305Sstevel 
17672305Sstevel 		case LOMBUS_PAT_SPACE:
17682305Sstevel 			aip->ahi_get8 = lombus_pat_get8;
17692305Sstevel 			aip->ahi_put8 = lombus_pat_put8;
17702305Sstevel 			aip->ahi_rep_get8 = lombus_pat_rep_get8;
17712305Sstevel 			aip->ahi_rep_put8 = lombus_pat_rep_put8;
17722305Sstevel 
17732305Sstevel 			aip->ahi_get16 = lombus_no_get16;
17742305Sstevel 			aip->ahi_put16 = lombus_no_put16;
17752305Sstevel 			aip->ahi_rep_get16 = lombus_no_rep_get16;
17762305Sstevel 			aip->ahi_rep_put16 = lombus_no_rep_put16;
17772305Sstevel 
17782305Sstevel 			aip->ahi_get32 = lombus_meta_get32;
17792305Sstevel 			aip->ahi_put32 = lombus_meta_put32;
17802305Sstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
17812305Sstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
17822305Sstevel 
17832305Sstevel 			aip->ahi_get64 = lombus_no_get64;
17842305Sstevel 			aip->ahi_put64 = lombus_no_put64;
17852305Sstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
17862305Sstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
17872305Sstevel 
17882305Sstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
17892305Sstevel 			break;
17902305Sstevel 
17912305Sstevel 		case LOMBUS_EVENT_SPACE:
17922305Sstevel 			aip->ahi_get8 = lombus_no_get8;
17932305Sstevel 			aip->ahi_put8 = lombus_no_put8;
17942305Sstevel 			aip->ahi_rep_get8 = lombus_no_rep_get8;
17952305Sstevel 			aip->ahi_rep_put8 = lombus_no_rep_put8;
17962305Sstevel 
17972305Sstevel 			aip->ahi_get16 = lombus_event_get16;
17982305Sstevel 			aip->ahi_put16 = lombus_event_put16;
17992305Sstevel 			aip->ahi_rep_get16 = lombus_event_rep_get16;
18002305Sstevel 			aip->ahi_rep_put16 = lombus_event_rep_put16;
18012305Sstevel 
18022305Sstevel 			aip->ahi_get32 = lombus_meta_get32;
18032305Sstevel 			aip->ahi_put32 = lombus_meta_put32;
18042305Sstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
18052305Sstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
18062305Sstevel 
18072305Sstevel 			aip->ahi_get64 = lombus_no_get64;
18082305Sstevel 			aip->ahi_put64 = lombus_no_put64;
18092305Sstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
18102305Sstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
18112305Sstevel 
18122305Sstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
18132305Sstevel 			break;
18142305Sstevel 		}
18152305Sstevel 		hdlp->ah_addr = *addrp = vaddr;
18162305Sstevel 		hdlp->ah_len = len;
18172305Sstevel 		hdlp->ah_bus_private = ssp;
18182305Sstevel 		return (DDI_SUCCESS);
18192305Sstevel 
18202305Sstevel 	case DDI_MO_UNMAP:
18212305Sstevel 		*addrp = NULL;
18222305Sstevel 		hdlp->ah_bus_private = NULL;
18232305Sstevel 		return (DDI_SUCCESS);
18242305Sstevel 	}
18252305Sstevel }
18262305Sstevel 
18272305Sstevel #endif	/* NDI_ACC_HDL_V2 */
18282305Sstevel 
18292305Sstevel static int
lombus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)18302305Sstevel lombus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
18312305Sstevel 	off_t off, off_t len, caddr_t *addrp)
18322305Sstevel {
18332305Sstevel 	struct lombus_child_info *lcip;
18342305Sstevel 	struct lombus_state *ssp;
18352305Sstevel 	lombus_regspec_t *rsp;
18362305Sstevel 
18372305Sstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_map")) == NULL)
18382305Sstevel 		return (DDI_FAILURE);	/* this "can't happen" */
18392305Sstevel 
18402305Sstevel 	/*
18412305Sstevel 	 * Validate mapping request ...
18422305Sstevel 	 */
18432305Sstevel 
18442305Sstevel 	if (mp->map_flags != DDI_MF_KERNEL_MAPPING)
18452305Sstevel 		return (DDI_ME_UNSUPPORTED);
18462305Sstevel 	if (mp->map_handlep == NULL)
18472305Sstevel 		return (DDI_ME_UNSUPPORTED);
18482305Sstevel 	if (mp->map_type != DDI_MT_RNUMBER)
18492305Sstevel 		return (DDI_ME_UNIMPLEMENTED);
18502305Sstevel 	if ((lcip = ddi_get_parent_data(rdip)) == NULL)
18512305Sstevel 		return (DDI_ME_INVAL);
18522305Sstevel 	if ((rsp = lcip->rsp) == NULL)
18532305Sstevel 		return (DDI_ME_INVAL);
18542305Sstevel 	if (mp->map_obj.rnumber >= lcip->nregs)
18552305Sstevel 		return (DDI_ME_RNUMBER_RANGE);
18562305Sstevel 	rsp += mp->map_obj.rnumber;
18572305Sstevel 	if (off < 0 || off >= rsp->lombus_size)
18582305Sstevel 		return (DDI_ME_INVAL);
18592305Sstevel 	if (len == 0)
18602305Sstevel 		len = rsp->lombus_size-off;
18612305Sstevel 	if (len < 0)
18622305Sstevel 		return (DDI_ME_INVAL);
18632305Sstevel 	if (off+len < 0 || off+len > rsp->lombus_size)
18642305Sstevel 		return (DDI_ME_INVAL);
18652305Sstevel 
18662305Sstevel 	return (lombus_map_handle(ssp, mp->map_op,
18675107Seota 	    rsp->lombus_space, VREG_TO_ADDR(rsp->lombus_base+off), len,
18685107Seota 	    mp->map_handlep, addrp));
18692305Sstevel }
18702305Sstevel 
18712305Sstevel static int
lombus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)18722305Sstevel lombus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
18732305Sstevel 	void *arg, void *result)
18742305Sstevel {
18752305Sstevel 	struct lombus_child_info *lcip;
18762305Sstevel 	struct lombus_state *ssp;
18772305Sstevel 	lombus_regspec_t *rsp;
18782305Sstevel 	dev_info_t *cdip;
18792305Sstevel 	char addr[32];
18802305Sstevel 	uint_t nregs;
18812305Sstevel 	uint_t rnum;
18822305Sstevel 	int *regs;
18832305Sstevel 	int limit;
18842305Sstevel 	int err;
18852305Sstevel 	int i;
18862305Sstevel 
18872305Sstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_ctlops")) == NULL)
18882305Sstevel 		return (DDI_FAILURE);	/* this "can't happen" */
18892305Sstevel 
18902305Sstevel 	switch (op) {
18912305Sstevel 	default:
18922305Sstevel 		break;
18932305Sstevel 
18942305Sstevel 	case DDI_CTLOPS_INITCHILD:
18952305Sstevel 		/*
18962305Sstevel 		 * First, look up and validate the "reg" property.
18972305Sstevel 		 *
18982305Sstevel 		 * It must be a non-empty integer array containing a set
18992305Sstevel 		 * of triples.  Once we've verified that, we can treat it
19002305Sstevel 		 * as an array of type lombus_regspec_t[], which defines
19012305Sstevel 		 * the meaning of the elements of each triple:
19022305Sstevel 		 * +  the first element of each triple must be a valid space
19032305Sstevel 		 * +  the second and third elements (base, size) of each
19042305Sstevel 		 *	triple must define a valid subrange of that space
19052305Sstevel 		 * If it passes all the tests, we save it away for future
19062305Sstevel 		 * reference in the child's parent-private-data field.
19072305Sstevel 		 */
19082305Sstevel 		cdip = arg;
19092305Sstevel 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
19105107Seota 		    DDI_PROP_DONTPASS, "reg", &regs, &nregs);
19112305Sstevel 		lombus_trace(ssp, 'C', "initchild",
19122305Sstevel 		    "prop status %d size %d", err, nregs);
19132305Sstevel 		if (err != DDI_PROP_SUCCESS)
19142305Sstevel 			return (DDI_FAILURE);
19152305Sstevel 
19162305Sstevel 		err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0);
19172305Sstevel 		nregs /= LOMBUS_REGSPEC_SIZE;
19182305Sstevel 		rsp = (lombus_regspec_t *)regs;
19192305Sstevel 		for (i = 0; i < nregs && !err; ++i) {
19202305Sstevel 			switch (rsp[i].lombus_space) {
19212305Sstevel 			default:
19222305Sstevel 				limit = 0;
19232305Sstevel 				err = 1;
19242305Sstevel 				break;
19252305Sstevel 
19262305Sstevel 			case LOMBUS_VREG_SPACE:
19272305Sstevel 				limit = LOMBUS_MAX_REG+1;
19282305Sstevel 				break;
19292305Sstevel 
19302305Sstevel 			case LOMBUS_PAT_SPACE:
19312305Sstevel 				limit = LOMBUS_PAT_REG+1;
19322305Sstevel 				break;
19332305Sstevel 
19342305Sstevel 			case LOMBUS_EVENT_SPACE:
19352305Sstevel 				limit = LOMBUS_EVENT_REG+1;
19362305Sstevel 				break;
19372305Sstevel 			}
19382305Sstevel 
19392305Sstevel 			err |= (rsp[i].lombus_base < 0);
19402305Sstevel 			err |= (rsp[i].lombus_base >= limit);
19412305Sstevel 
19422305Sstevel 			if (rsp[i].lombus_size == 0)
19432305Sstevel 				rsp[i].lombus_size = limit-rsp[i].lombus_base;
19442305Sstevel 			err |= (rsp[i].lombus_size < 0);
19452305Sstevel 
19462305Sstevel 			err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0);
19472305Sstevel 			err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
19482305Sstevel 		}
19492305Sstevel 
19502305Sstevel 		if (err) {
19512305Sstevel 			ddi_prop_free(regs);
19522305Sstevel 			return (DDI_FAILURE);
19532305Sstevel 		}
19542305Sstevel 
19552305Sstevel 		lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP);
19562305Sstevel 		lcip->nregs = nregs;
19572305Sstevel 		lcip->rsp = rsp;
19582305Sstevel 		ddi_set_parent_data(cdip, lcip);
19592305Sstevel 
19602305Sstevel 		(void) snprintf(addr, sizeof (addr),
19615107Seota 		    "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base);
19622305Sstevel 		ddi_set_name_addr(cdip, addr);
19632305Sstevel 
19642305Sstevel 		return (DDI_SUCCESS);
19652305Sstevel 
19662305Sstevel 	case DDI_CTLOPS_UNINITCHILD:
19672305Sstevel 		cdip = arg;
19682305Sstevel 		ddi_set_name_addr(cdip, NULL);
19692305Sstevel 		lcip = ddi_get_parent_data(cdip);
19702305Sstevel 		ddi_set_parent_data(cdip, NULL);
19712305Sstevel 		ddi_prop_free(lcip->rsp);
19722305Sstevel 		kmem_free(lcip, sizeof (*lcip));
19732305Sstevel 		return (DDI_SUCCESS);
19742305Sstevel 
19752305Sstevel 	case DDI_CTLOPS_REPORTDEV:
19762305Sstevel 		if (rdip == NULL)
19772305Sstevel 			return (DDI_FAILURE);
19782305Sstevel 
19792305Sstevel 		cmn_err(CE_CONT, "?LOM device: %s@%s, %s#%d\n",
19805107Seota 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
19815107Seota 		    ddi_driver_name(dip), ddi_get_instance(dip));
19822305Sstevel 
19832305Sstevel 		return (DDI_SUCCESS);
19842305Sstevel 
19852305Sstevel 	case DDI_CTLOPS_REGSIZE:
19862305Sstevel 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
19872305Sstevel 			return (DDI_FAILURE);
19882305Sstevel 		if ((rnum = *(uint_t *)arg) >= lcip->nregs)
19892305Sstevel 			return (DDI_FAILURE);
19902305Sstevel 		*(off_t *)result = lcip->rsp[rnum].lombus_size;
19912305Sstevel 		return (DDI_SUCCESS);
19922305Sstevel 
19932305Sstevel 	case DDI_CTLOPS_NREGS:
19942305Sstevel 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
19952305Sstevel 			return (DDI_FAILURE);
19962305Sstevel 		*(int *)result = lcip->nregs;
19972305Sstevel 		return (DDI_SUCCESS);
19982305Sstevel 	}
19992305Sstevel 
20002305Sstevel 	return (ddi_ctlops(dip, rdip, op, arg, result));
20012305Sstevel }
20022305Sstevel 
20032305Sstevel 
20042305Sstevel /*
20052305Sstevel  *  Clean up on detach or failure of attach
20062305Sstevel  */
20072305Sstevel static int
lombus_unattach(struct lombus_state * ssp,int instance)20082305Sstevel lombus_unattach(struct lombus_state *ssp, int instance)
20092305Sstevel {
20102305Sstevel 	if (ssp != NULL) {
20112305Sstevel 		lombus_hw_reset(ssp);
20125107Seota 		if (ssp->cycid != NULL) {
20135107Seota 			ddi_periodic_delete(ssp->cycid);
20145107Seota 			ssp->cycid = NULL;
20152305Sstevel 			if (ssp->sio_handle != NULL)
20162305Sstevel 				ddi_remove_intr(ssp->dip, 0, ssp->hw_iblk);
20172305Sstevel 			ddi_remove_softintr(ssp->softid);
20182305Sstevel 			cv_destroy(ssp->lo_cv);
20192305Sstevel 			mutex_destroy(ssp->lo_mutex);
20202305Sstevel 			mutex_destroy(ssp->hw_mutex);
20212305Sstevel 		}
20222305Sstevel 		lombus_offline(ssp);
20232305Sstevel 		ddi_set_driver_private(ssp->dip, NULL);
20242305Sstevel 	}
20252305Sstevel 
20262305Sstevel 	ddi_soft_state_free(lombus_statep, instance);
20272305Sstevel 	return (DDI_FAILURE);
20282305Sstevel }
20292305Sstevel 
20302305Sstevel /*
20312305Sstevel  *  Autoconfiguration routines
20322305Sstevel  */
20332305Sstevel 
20342305Sstevel static int
lombus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)20352305Sstevel lombus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
20362305Sstevel {
20372305Sstevel 	struct lombus_state *ssp = NULL;
20382305Sstevel 	int instance;
20392305Sstevel 	int err;
20402305Sstevel 
20412305Sstevel 	switch (cmd) {
20422305Sstevel 	default:
20432305Sstevel 		return (DDI_FAILURE);
20442305Sstevel 
20452305Sstevel 	case DDI_ATTACH:
20462305Sstevel 		break;
20472305Sstevel 	}
20482305Sstevel 
20492305Sstevel 	/*
20502305Sstevel 	 *  Allocate the soft-state structure
20512305Sstevel 	 */
20522305Sstevel 	instance = ddi_get_instance(dip);
20532305Sstevel 	if (ddi_soft_state_zalloc(lombus_statep, instance) != DDI_SUCCESS)
20542305Sstevel 		return (DDI_FAILURE);
20552305Sstevel 	if ((ssp = lombus_getstate(dip, instance, "lombus_attach")) == NULL)
20562305Sstevel 		return (lombus_unattach(ssp, instance));
20572305Sstevel 	ddi_set_driver_private(dip, ssp);
20582305Sstevel 
20592305Sstevel 	/*
20602305Sstevel 	 *  Initialise devinfo-related fields
20612305Sstevel 	 */
20622305Sstevel 	ssp->dip = dip;
20632305Sstevel 	ssp->majornum = ddi_driver_major(dip);
20642305Sstevel 	ssp->instance = instance;
20652305Sstevel 
20662305Sstevel 	/*
20672305Sstevel 	 *  Set various options from .conf properties
20682305Sstevel 	 */
20692305Sstevel 	ssp->allow_echo = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20705107Seota 	    DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0;
20712305Sstevel 	ssp->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20725107Seota 	    DDI_PROP_DONTPASS, "baud-rate", 0);
20732305Sstevel 	ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20745107Seota 	    DDI_PROP_DONTPASS, "debug", 0);
20752305Sstevel 	ssp->fake_cts = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20765107Seota 	    DDI_PROP_DONTPASS, "fake-cts", 0) != 0;
20772305Sstevel 
20782305Sstevel 	/*
20792305Sstevel 	 * Initialise current state & time
20802305Sstevel 	 */
20812305Sstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
20822305Sstevel 	ssp->hw_last_pat = gethrtime();
20835107Seota 	ssp->cycid = NULL;
20842305Sstevel 
20852305Sstevel 	/*
20862305Sstevel 	 *  Online the hardware ...
20872305Sstevel 	 */
20882305Sstevel 	err = lombus_online(ssp);
20892305Sstevel 	if (err != 0)
20902305Sstevel 		return (lombus_unattach(ssp, instance));
20912305Sstevel 
20922305Sstevel 	/*
20932305Sstevel 	 * Install soft and hard interrupt handler(s)
20942305Sstevel 	 * Initialise mutexes and cv
20952305Sstevel 	 * Start cyclic callbacks
20962305Sstevel 	 * Enable interrupts
20972305Sstevel 	 */
20982305Sstevel 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ssp->softid,
20995107Seota 	    &ssp->lo_iblk, NULL, lombus_softint, (caddr_t)ssp);
21002305Sstevel 	if (err != DDI_SUCCESS)
21012305Sstevel 		return (lombus_unattach(ssp, instance));
21022305Sstevel 
21032305Sstevel 	if (ssp->sio_handle != NULL)
21042305Sstevel 		err = ddi_add_intr(dip, 0, &ssp->hw_iblk, NULL,
21055107Seota 		    lombus_hi_intr, (caddr_t)ssp);
21062305Sstevel 
21072305Sstevel 	mutex_init(ssp->hw_mutex, NULL, MUTEX_DRIVER, ssp->hw_iblk);
21082305Sstevel 	mutex_init(ssp->lo_mutex, NULL, MUTEX_DRIVER, ssp->lo_iblk);
21092305Sstevel 	cv_init(ssp->lo_cv, NULL, CV_DRIVER, NULL);
21102305Sstevel 
21115107Seota 	/*
21125107Seota 	 * Register a periodical handler.
21135107Seota 	 */
21145107Seota 	ssp->cycid = ddi_periodic_add(lombus_cyclic, ssp, LOMBUS_ONE_SEC,
21155107Seota 	    DDI_IPL_1);
21162305Sstevel 
21172305Sstevel 	/*
21182305Sstevel 	 * Final check before enabling h/w interrupts - did
21192305Sstevel 	 * we successfully install the h/w interrupt handler?
21202305Sstevel 	 */
21212305Sstevel 	if (err != DDI_SUCCESS)
21222305Sstevel 		return (lombus_unattach(ssp, instance));
21232305Sstevel 
21242305Sstevel 	lombus_set_irq(ssp, B_TRUE);
21252305Sstevel 
21262305Sstevel 	/*
21272305Sstevel 	 *  All done, report success
21282305Sstevel 	 */
21292305Sstevel 	ddi_report_dev(dip);
21302305Sstevel 	return (DDI_SUCCESS);
21312305Sstevel }
21322305Sstevel 
21332305Sstevel 
21342305Sstevel static int
lombus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)21352305Sstevel lombus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
21362305Sstevel {
21372305Sstevel 	struct lombus_state *ssp;
21382305Sstevel 	int instance;
21392305Sstevel 
21402305Sstevel 	switch (cmd) {
21412305Sstevel 	default:
21422305Sstevel 		return (DDI_FAILURE);
21432305Sstevel 
21442305Sstevel 	case DDI_DETACH:
21452305Sstevel 		break;
21462305Sstevel 	}
21472305Sstevel 
21482305Sstevel 	instance = ddi_get_instance(dip);
21492305Sstevel 	if ((ssp = lombus_getstate(dip, instance, "lombus_detach")) == NULL)
21502305Sstevel 		return (DDI_FAILURE);	/* this "can't happen" */
21512305Sstevel 
21522305Sstevel 	(void) lombus_unattach(ssp, instance);
21532305Sstevel 	return (DDI_SUCCESS);
21542305Sstevel }
21552305Sstevel 
21562305Sstevel static int
lombus_reset(dev_info_t * dip,ddi_reset_cmd_t cmd)21572305Sstevel lombus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
21582305Sstevel {
21592305Sstevel 	struct lombus_state *ssp;
21602305Sstevel 
21612305Sstevel 	_NOTE(ARGUNUSED(cmd))
21622305Sstevel 
21632305Sstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_reset")) == NULL)
21642305Sstevel 		return (DDI_FAILURE);
21652305Sstevel 
21662305Sstevel 	lombus_hw_reset(ssp);
21672305Sstevel 	return (DDI_SUCCESS);
21682305Sstevel }
21692305Sstevel 
21702305Sstevel 
21712305Sstevel /*
21722305Sstevel  * System interface structures
21732305Sstevel  */
21742305Sstevel 
21752305Sstevel static struct cb_ops lombus_cb_ops =
21762305Sstevel {
21772305Sstevel 	nodev,			/* b/c open	*/
21782305Sstevel 	nodev,			/* b/c close	*/
21792305Sstevel 	nodev,			/* b   strategy	*/
21802305Sstevel 	nodev,			/* b   print	*/
21812305Sstevel 	nodev,			/* b   dump 	*/
21822305Sstevel 	nodev,			/* c   read	*/
21832305Sstevel 	nodev,			/* c   write	*/
21842305Sstevel 	nodev,			/* c   ioctl	*/
21852305Sstevel 	nodev,			/* c   devmap	*/
21862305Sstevel 	nodev,			/* c   mmap	*/
21872305Sstevel 	nodev,			/* c   segmap	*/
21882305Sstevel 	nochpoll,		/* c   poll	*/
21892305Sstevel 	ddi_prop_op,		/* b/c prop_op	*/
21902305Sstevel 	NULL,			/* c   streamtab */
21912305Sstevel 	D_MP | D_NEW		/* b/c flags	*/
21922305Sstevel };
21932305Sstevel 
21942305Sstevel static struct bus_ops lombus_bus_ops =
21952305Sstevel {
21962305Sstevel 	BUSO_REV,			/* revision		*/
21972305Sstevel 	lombus_map,			/* bus_map		*/
21982305Sstevel 	0,				/* get_intrspec		*/
21992305Sstevel 	0,				/* add_intrspec		*/
22002305Sstevel 	0,				/* remove_intrspec	*/
22012305Sstevel 	i_ddi_map_fault,		/* map_fault		*/
22022305Sstevel 	ddi_no_dma_map,			/* dma_map		*/
22032305Sstevel 	ddi_no_dma_allochdl,		/* allocate DMA handle	*/
22042305Sstevel 	ddi_no_dma_freehdl,		/* free DMA handle	*/
22052305Sstevel 	ddi_no_dma_bindhdl,		/* bind DMA handle	*/
22062305Sstevel 	ddi_no_dma_unbindhdl,		/* unbind DMA handle	*/
22072305Sstevel 	ddi_no_dma_flush,		/* flush DMA		*/
22082305Sstevel 	ddi_no_dma_win,			/* move DMA window	*/
22092305Sstevel 	ddi_no_dma_mctl,		/* generic DMA control	*/
22102305Sstevel 	lombus_ctlops,			/* generic control	*/
22112305Sstevel 	ddi_bus_prop_op,		/* prop_op		*/
22122305Sstevel 	ndi_busop_get_eventcookie,	/* get_eventcookie	*/
22132305Sstevel 	ndi_busop_add_eventcall,	/* add_eventcall	*/
22142305Sstevel 	ndi_busop_remove_eventcall,	/* remove_eventcall	*/
22152305Sstevel 	ndi_post_event,			/* post_event		*/
22162305Sstevel 	0,				/* interrupt control	*/
22172305Sstevel 	0,				/* bus_config		*/
22182305Sstevel 	0,				/* bus_unconfig		*/
22192305Sstevel 	0,				/* bus_fm_init		*/
22202305Sstevel 	0,				/* bus_fm_fini		*/
22212305Sstevel 	0,				/* bus_fm_access_enter	*/
22222305Sstevel 	0,				/* bus_fm_access_exit	*/
22232305Sstevel 	0,				/* bus_power		*/
22242305Sstevel 	i_ddi_intr_ops			/* bus_intr_op		*/
22252305Sstevel };
22262305Sstevel 
22272305Sstevel static struct dev_ops lombus_dev_ops =
22282305Sstevel {
22292305Sstevel 	DEVO_REV,
22302305Sstevel 	0,				/* refcount		*/
22312305Sstevel 	ddi_no_info,			/* getinfo		*/
22322305Sstevel 	nulldev,			/* identify		*/
22332305Sstevel 	nulldev,			/* probe		*/
22342305Sstevel 	lombus_attach,			/* attach		*/
22352305Sstevel 	lombus_detach,			/* detach		*/
22362305Sstevel 	lombus_reset,			/* reset		*/
22372305Sstevel 	&lombus_cb_ops,			/* driver operations	*/
22387656SSherry.Moore@Sun.COM 	&lombus_bus_ops,		/* bus operations	*/
22397656SSherry.Moore@Sun.COM 	NULL,				/* power		*/
22407656SSherry.Moore@Sun.COM 	ddi_quiesce_not_supported,	/* devo_quiesce		*/
22412305Sstevel };
22422305Sstevel 
22432305Sstevel static struct modldrv modldrv =
22442305Sstevel {
22452305Sstevel 	&mod_driverops,
22467656SSherry.Moore@Sun.COM 	"lombus driver",
22472305Sstevel 	&lombus_dev_ops
22482305Sstevel };
22492305Sstevel 
22502305Sstevel static struct modlinkage modlinkage =
22512305Sstevel {
22522305Sstevel 	MODREV_1,
22532305Sstevel 	{
22542305Sstevel 		&modldrv,
22552305Sstevel 		NULL
22562305Sstevel 	}
22572305Sstevel };
22582305Sstevel 
22592305Sstevel 
22602305Sstevel /*
22612305Sstevel  *  Dynamic loader interface code
22622305Sstevel  */
22632305Sstevel 
22642305Sstevel int
_init(void)22652305Sstevel _init(void)
22662305Sstevel {
22672305Sstevel 	int err;
22682305Sstevel 
22692305Sstevel 	err = ddi_soft_state_init(&lombus_statep,
22705107Seota 	    sizeof (struct lombus_state), 0);
22712305Sstevel 	if (err == DDI_SUCCESS)
22722305Sstevel 		if ((err = mod_install(&modlinkage)) != 0) {
22732305Sstevel 			ddi_soft_state_fini(&lombus_statep);
22742305Sstevel 		}
22752305Sstevel 
22762305Sstevel 	return (err);
22772305Sstevel }
22782305Sstevel 
22792305Sstevel int
_info(struct modinfo * mip)22802305Sstevel _info(struct modinfo *mip)
22812305Sstevel {
22822305Sstevel 	return (mod_info(&modlinkage, mip));
22832305Sstevel }
22842305Sstevel 
22852305Sstevel int
_fini(void)22862305Sstevel _fini(void)
22872305Sstevel {
22882305Sstevel 	int err;
22892305Sstevel 
22902305Sstevel 	if ((err = mod_remove(&modlinkage)) == 0) {
22912305Sstevel 		ddi_soft_state_fini(&lombus_statep);
22922305Sstevel 		lombus_major = NOMAJOR;
22932305Sstevel 	}
22942305Sstevel 
22952305Sstevel 	return (err);
22962305Sstevel }
2297