xref: /titanic_44/usr/src/uts/sun4u/lw2plus/io/lombus.c (revision 07d06da50d310a325b457d6330165aebab1e0064)
13db86aabSstevel /*
23db86aabSstevel  * CDDL HEADER START
33db86aabSstevel  *
43db86aabSstevel  * The contents of this file are subject to the terms of the
5dd4eeefdSeota  * Common Development and Distribution License (the "License").
6dd4eeefdSeota  * You may not use this file except in compliance with the License.
73db86aabSstevel  *
83db86aabSstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
93db86aabSstevel  * or http://www.opensolaris.org/os/licensing.
103db86aabSstevel  * See the License for the specific language governing permissions
113db86aabSstevel  * and limitations under the License.
123db86aabSstevel  *
133db86aabSstevel  * When distributing Covered Code, include this CDDL HEADER in each
143db86aabSstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
153db86aabSstevel  * If applicable, add the following below this CDDL HEADER, with the
163db86aabSstevel  * fields enclosed by brackets "[]" replaced with your own identifying
173db86aabSstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
183db86aabSstevel  *
193db86aabSstevel  * CDDL HEADER END
203db86aabSstevel  */
213db86aabSstevel /*
22d3d50737SRafael Vanoni  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
233db86aabSstevel  * Use is subject to license terms.
243db86aabSstevel  *
253db86aabSstevel  * The "lombus" driver provides access to the LOMlite2 virtual registers,
263db86aabSstevel  * so that its clients (children) need not be concerned with the details
273db86aabSstevel  * of the access mechanism, which in this case is implemented via a
283db86aabSstevel  * packet-based protocol over a serial link connected to one of the serial
293db86aabSstevel  * ports of the SuperIO (SIO) chip.
303db86aabSstevel  *
313db86aabSstevel  * On the other hand, this driver doesn't generally know what the virtual
323db86aabSstevel  * registers signify - only the clients need this information.
333db86aabSstevel  */
343db86aabSstevel 
353db86aabSstevel 
363db86aabSstevel /*
373db86aabSstevel  *  Header files
383db86aabSstevel  */
393db86aabSstevel 
403db86aabSstevel #include <sys/types.h>
413db86aabSstevel #include <sys/conf.h>
423db86aabSstevel #include <sys/debug.h>
433db86aabSstevel #include <sys/errno.h>
443db86aabSstevel #include <sys/file.h>
453db86aabSstevel #include <sys/intr.h>
463db86aabSstevel #include <sys/kmem.h>
473db86aabSstevel #include <sys/membar.h>
483db86aabSstevel #include <sys/modctl.h>
493db86aabSstevel #include <sys/note.h>
503db86aabSstevel #include <sys/open.h>
513db86aabSstevel #include <sys/poll.h>
523db86aabSstevel #include <sys/spl.h>
533db86aabSstevel #include <sys/stat.h>
543db86aabSstevel #include <sys/strlog.h>
553db86aabSstevel 
563db86aabSstevel #include <sys/ddi.h>
573db86aabSstevel #include <sys/sunddi.h>
583db86aabSstevel #include <sys/sunndi.h>
593db86aabSstevel 
603db86aabSstevel #include <sys/lombus.h>
613db86aabSstevel 
623db86aabSstevel 
633db86aabSstevel #if	defined(NDI_ACC_HDL_V2)
643db86aabSstevel 
653db86aabSstevel /*
663db86aabSstevel  * Compiling for Solaris 9+ with access handle enhancements
673db86aabSstevel  */
683db86aabSstevel #define	HANDLE_TYPE		ndi_acc_handle_t
693db86aabSstevel #define	HANDLE_ADDR(hdlp)	(hdlp->ah_addr)
703db86aabSstevel #define	HANDLE_FAULT(hdlp)	(hdlp->ah_fault)
713db86aabSstevel #define	HANDLE_MAPLEN(hdlp)	(hdlp->ah_len)
723db86aabSstevel #define	HANDLE_PRIVATE(hdlp)	(hdlp->ah_bus_private)
733db86aabSstevel 
743db86aabSstevel #else
753db86aabSstevel 
763db86aabSstevel /*
773db86aabSstevel  * Compatibility definitions for backport to Solaris 8
783db86aabSstevel  */
793db86aabSstevel #define	HANDLE_TYPE		ddi_acc_impl_t
803db86aabSstevel #define	HANDLE_ADDR(hdlp)	(hdlp->ahi_common.ah_addr)
813db86aabSstevel #define	HANDLE_FAULT(hdlp)	(hdlp->ahi_fault)
823db86aabSstevel #define	HANDLE_MAPLEN(hdlp)	(hdlp->ahi_common.ah_len)
833db86aabSstevel #define	HANDLE_PRIVATE(hdlp)	(hdlp->ahi_common.ah_bus_private)
843db86aabSstevel 
853db86aabSstevel #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
863db86aabSstevel 
873db86aabSstevel #endif	/* NDI_ACC_HDL_V2 */
883db86aabSstevel 
893db86aabSstevel 
903db86aabSstevel /*
913db86aabSstevel  * Local definitions
923db86aabSstevel  */
933db86aabSstevel #define	MYNAME			"lombus"
943db86aabSstevel #define	NOMAJOR			(~(major_t)0)
953db86aabSstevel #define	DUMMY_VALUE		(~(int8_t)0)
963db86aabSstevel 
973db86aabSstevel #define	LOMBUS_INST_TO_MINOR(i)	(i)
983db86aabSstevel #define	LOMBUS_MINOR_TO_INST(m)	(m)
993db86aabSstevel 
1003db86aabSstevel #define	LOMBUS_DUMMY_ADDRESS	((caddr_t)0x0CADD1ED)
1013db86aabSstevel #define	ADDR_TO_OFFSET(a, hdlp)	((caddr_t)(a) - HANDLE_ADDR(hdlp))
1023db86aabSstevel #define	ADDR_TO_VREG(a)		((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS)
1033db86aabSstevel #define	VREG_TO_ADDR(v)		(LOMBUS_DUMMY_ADDRESS + (v))
1043db86aabSstevel 
1053db86aabSstevel 
1063db86aabSstevel /*
1073db86aabSstevel  * The following definitions are taken from the datasheet
1083db86aabSstevel  * for the National Semiconductor PC87317 (SuperIO) chip.
1093db86aabSstevel  *
1103db86aabSstevel  * This chip implements UART functionality as logical device 6.
1113db86aabSstevel  * It provides all sorts of wierd modes and extensions, but we
1123db86aabSstevel  * have chosen to use only the 16550-compatible features
1133db86aabSstevel  * ("non-extended mode").
1143db86aabSstevel  *
1153db86aabSstevel  * Hardware: serial chip register numbers
1163db86aabSstevel  */
1173db86aabSstevel #define	SIO_RXD			0	/* read		*/
1183db86aabSstevel #define	SIO_TXD			0	/* write	*/
1193db86aabSstevel #define	SIO_IER			1
1203db86aabSstevel #define	SIO_EIR			2	/* read		*/
1213db86aabSstevel #define	SIO_FCR			2	/* write	*/
1223db86aabSstevel #define	SIO_LCR			3
1233db86aabSstevel #define	SIO_BSR			3	/* wierd	*/
1243db86aabSstevel #define	SIO_MCR			4
1253db86aabSstevel #define	SIO_LSR			5
1263db86aabSstevel #define	SIO_MSR			6
1273db86aabSstevel #define	SIO_SCR			7
1283db86aabSstevel 
1293db86aabSstevel #define	SIO_LBGDL		0	/* bank 1	*/
1303db86aabSstevel #define	SIO_LBGDH		1	/* bank 1	*/
1313db86aabSstevel 
1323db86aabSstevel /*
1333db86aabSstevel  * Hardware: serial chip register bits
1343db86aabSstevel  */
1353db86aabSstevel #define	SIO_IER_RXHDL_IE	0x01
1363db86aabSstevel #define	SIO_IER_STD		0x00
1373db86aabSstevel 
1383db86aabSstevel #define	SIO_EIR_IPF		0x01
1393db86aabSstevel #define	SIO_EIR_IPR0		0x02
1403db86aabSstevel #define	SIO_EIR_IPR1		0x04
1413db86aabSstevel #define	SIO_EIR_RXFT		0x08
1423db86aabSstevel #define	SIO_EIR_FEN0		0x40
1433db86aabSstevel #define	SIO_EIR_FEN1		0x80
1443db86aabSstevel 
1453db86aabSstevel #define	SIO_FCR_FIFO_EN		0x01
1463db86aabSstevel #define	SIO_FCR_RXSR		0x02
1473db86aabSstevel #define	SIO_FCR_TXSR		0x04
1483db86aabSstevel #define	SIO_FCR_RXFTH0		0x40
1493db86aabSstevel #define	SIO_FCR_RXFTH1		0x80
1503db86aabSstevel #define	SIO_FCR_STD		(SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN)
1513db86aabSstevel 
1523db86aabSstevel #define	SIO_LCR_WLS0		0x01
1533db86aabSstevel #define	SIO_LCR_WLS1		0x02
1543db86aabSstevel #define	SIO_LCR_STB		0x04
1553db86aabSstevel #define	SIO_LCR_PEN		0x08
1563db86aabSstevel #define	SIO_LCR_EPS		0x10
1573db86aabSstevel #define	SIO_LCR_STKP		0x20
1583db86aabSstevel #define	SIO_LCR_SBRK		0x40
1593db86aabSstevel #define	SIO_LCR_BKSE		0x80
1603db86aabSstevel #define	SIO_LCR_8BIT		(SIO_LCR_WLS0|SIO_LCR_WLS1)
1613db86aabSstevel #define	SIO_LCR_EPAR		(SIO_LCR_PEN|SIO_LCR_EPS)
1623db86aabSstevel #define	SIO_LCR_STD		(SIO_LCR_8BIT|SIO_LCR_EPAR)
1633db86aabSstevel 
1643db86aabSstevel #define	SIO_BSR_BANK0		(SIO_LCR_STD)
1653db86aabSstevel #define	SIO_BSR_BANK1		(SIO_LCR_BKSE|SIO_LCR_STD)
1663db86aabSstevel 
1673db86aabSstevel #define	SIO_MCR_DTR		0x01
1683db86aabSstevel #define	SIO_MCR_RTS		0x02
1693db86aabSstevel #define	SIO_MCR_ISEN		0x08
1703db86aabSstevel #define	SIO_MCR_STD		(SIO_MCR_ISEN)
1713db86aabSstevel 
1723db86aabSstevel #define	SIO_LSR_RXDA		0x01
1733db86aabSstevel #define	SIO_LSR_OE		0x02
1743db86aabSstevel #define	SIO_LSR_PE		0x04
1753db86aabSstevel #define	SIO_LSR_FE		0x08
1763db86aabSstevel #define	SIO_LSR_BRKE		0x10
1773db86aabSstevel #define	SIO_LSR_TXRDY		0x20
1783db86aabSstevel #define	SIO_LSR_TXEMP		0x40
1793db86aabSstevel #define	SIO_LSR_ER_INF		0x80
1803db86aabSstevel 
1813db86aabSstevel #define	SIO_MSR_DCTS		0x01
1823db86aabSstevel #define	SIO_MSR_DDSR		0x02
1833db86aabSstevel #define	SIO_MSR_TERI		0x04
1843db86aabSstevel #define	SIO_MSR_DDCD		0x08
1853db86aabSstevel #define	SIO_MSR_CTS		0x10
1863db86aabSstevel #define	SIO_MSR_DSR		0x20
1873db86aabSstevel #define	SIO_MSR_RI		0x40
1883db86aabSstevel #define	SIO_MSR_DCD		0x80
1893db86aabSstevel 
1903db86aabSstevel /*
1913db86aabSstevel  * Min/max/default baud rates, and a macro to convert from a baud
1923db86aabSstevel  * rate to the number (divisor) to put in the baud rate registers
1933db86aabSstevel  */
1943db86aabSstevel #define	SIO_BAUD_MIN		50
1953db86aabSstevel #define	SIO_BAUD_MAX		115200
1963db86aabSstevel #define	SIO_BAUD_DEFAULT	38400
1973db86aabSstevel #define	SIO_BAUD_TO_DIVISOR(b)	(115200 / (b))
1983db86aabSstevel 
1993db86aabSstevel 
2003db86aabSstevel /*
2013db86aabSstevel  * Packet format ...
2023db86aabSstevel  */
2033db86aabSstevel #define	LOMBUS_MASK		0xc0	/* Byte-type bits		*/
2043db86aabSstevel #define	LOMBUS_PARAM		0x00	/* Parameter byte: 0b0xxxxxxx	*/
2053db86aabSstevel #define	LOMBUS_LAST		0x80	/* Last byte of packet		*/
2063db86aabSstevel #define	LOMBUS_CMD		0x80	/* Command byte:   0b10###XWV	*/
2073db86aabSstevel #define	LOMBUS_STATUS		0xc0	/* Status  byte:   0b11###AEV	*/
2083db86aabSstevel 
2093db86aabSstevel #define	LOMBUS_SEQ		0x38	/* Sequence number bits		*/
2103db86aabSstevel #define	LOMBUS_SEQ_LSB		0x08	/* Sequence number LSB		*/
2113db86aabSstevel #define	LOMBUS_CMD_XADDR	0x04	/* Extended (2-byte) addressing	*/
2123db86aabSstevel #define	LOMBUS_CMD_WRITE	0x02	/* Write command		*/
2133db86aabSstevel #define	LOMBUS_CMD_WMSB		0x01	/* Set MSB on Write		*/
2143db86aabSstevel #define	LOMBUS_CMD_READ		0x01	/* Read command			*/
2153db86aabSstevel #define	LOMBUS_CMD_NOP		0x00	/* NOP command			*/
2163db86aabSstevel 
2173db86aabSstevel #define	LOMBUS_STATUS_ASYNC	0x04	/* Asynchronous event pending	*/
2183db86aabSstevel #define	LOMBUS_STATUS_ERR	0x02	/* Error in command processing	*/
2193db86aabSstevel #define	LOMBUS_STATUS_MSB	0x01	/* MSB of Value read		*/
2203db86aabSstevel 
2213db86aabSstevel #define	LOMBUS_VREG_LO(x)	((x) & ((1 << 7) - 1))
2223db86aabSstevel #define	LOMBUS_VREG_HI(x)	((x) >> 7)
2233db86aabSstevel 
2243db86aabSstevel #define	LOMBUS_BUFSIZE		8
2253db86aabSstevel 
2263db86aabSstevel 
2273db86aabSstevel /*
2283db86aabSstevel  * Time periods, in nanoseconds
2293db86aabSstevel  *
2303db86aabSstevel  * Note that LOMBUS_ONE_SEC and some other time
2313db86aabSstevel  * periods are defined in <sys/lombus.h>
2323db86aabSstevel  */
2333db86aabSstevel #define	LOMBUS_CMD_POLL		(LOMBUS_ONE_SEC/20)
2343db86aabSstevel #define	LOMBUS_CTS_POLL		(LOMBUS_ONE_SEC/20)
2353db86aabSstevel #define	LOMBUS_CTS_TIMEOUT	(LOMBUS_ONE_SEC*2)
2363db86aabSstevel 
2373db86aabSstevel 
2383db86aabSstevel /*
2393db86aabSstevel  * Local datatypes
2403db86aabSstevel  */
2413db86aabSstevel enum lombus_cmdstate {
2423db86aabSstevel 	LOMBUS_CMDSTATE_IDLE,
2433db86aabSstevel 	LOMBUS_CMDSTATE_BUSY,
2443db86aabSstevel 	LOMBUS_CMDSTATE_WAITING,
2453db86aabSstevel 	LOMBUS_CMDSTATE_READY,
2463db86aabSstevel 	LOMBUS_CMDSTATE_ERROR
2473db86aabSstevel };
2483db86aabSstevel 
2493db86aabSstevel 
2503db86aabSstevel /*
2513db86aabSstevel  * This driver's soft-state structure
2523db86aabSstevel  */
2533db86aabSstevel 
2543db86aabSstevel struct lombus_state {
2553db86aabSstevel 	/*
2563db86aabSstevel 	 * Configuration data, set during attach
2573db86aabSstevel 	 */
2583db86aabSstevel 	dev_info_t *dip;
2593db86aabSstevel 	major_t majornum;
2603db86aabSstevel 	int instance;
2613db86aabSstevel 
2623db86aabSstevel 	ddi_acc_handle_t sio_handle;
2633db86aabSstevel 	uint8_t *sio_regs;
2643db86aabSstevel 	ddi_softintr_t softid;
265dd4eeefdSeota 	ddi_periodic_t cycid; /* periodical callback */
2663db86aabSstevel 
2673db86aabSstevel 	/*
2683db86aabSstevel 	 * Parameters derived from .conf properties
2693db86aabSstevel 	 */
2703db86aabSstevel 	boolean_t allow_echo;
2713db86aabSstevel 	int baud;
2723db86aabSstevel 	uint32_t debug;
2733db86aabSstevel 	boolean_t fake_cts;
2743db86aabSstevel 
2753db86aabSstevel 	/*
2763db86aabSstevel 	 * Hardware mutex (initialised using <hw_iblk>),
2773db86aabSstevel 	 * used to prevent retriggering the softint while
2783db86aabSstevel 	 * it's still fetching data out of the chip FIFO.
2793db86aabSstevel 	 */
2803db86aabSstevel 	kmutex_t hw_mutex[1];
2813db86aabSstevel 	ddi_iblock_cookie_t hw_iblk;
2823db86aabSstevel 
2833db86aabSstevel 	/*
2843db86aabSstevel 	 * Data protected by the hardware mutex: the watchdog-patting
2853db86aabSstevel 	 * protocol data (since the dog can be patted from a high-level
2863db86aabSstevel 	 * cyclic), and the interrupt-enabled flag.
2873db86aabSstevel 	 */
2883db86aabSstevel 	hrtime_t hw_last_pat;
2893db86aabSstevel 	boolean_t hw_int_enabled;
2903db86aabSstevel 
2913db86aabSstevel 	/*
2923db86aabSstevel 	 * Flag to indicate that we've incurred a hardware fault on
2933db86aabSstevel 	 * accesses to the SIO; once this is set, we fake all further
2943db86aabSstevel 	 * accesses in order not to provoke additional bus errors.
2953db86aabSstevel 	 */
2963db86aabSstevel 	boolean_t sio_fault;
2973db86aabSstevel 
2983db86aabSstevel 	/*
2993db86aabSstevel 	 * Serial protocol state data, protected by lo_mutex
3003db86aabSstevel 	 * (which is initialised using <lo_iblk>)
3013db86aabSstevel 	 */
3023db86aabSstevel 	kmutex_t lo_mutex[1];
3033db86aabSstevel 	ddi_iblock_cookie_t lo_iblk;
3043db86aabSstevel 	kcondvar_t lo_cv[1];
3053db86aabSstevel 
3063db86aabSstevel 	volatile enum lombus_cmdstate cmdstate;
3073db86aabSstevel 	clock_t deadline;
3083db86aabSstevel 	uint8_t cmdbuf[LOMBUS_BUFSIZE];
3093db86aabSstevel 	uint8_t reply[LOMBUS_BUFSIZE];
3103db86aabSstevel 	uint8_t async;
3113db86aabSstevel 	uint8_t index;
3123db86aabSstevel 	uint8_t result;
3133db86aabSstevel 	uint8_t sequence;
3143db86aabSstevel 	uint32_t error;
3153db86aabSstevel };
3163db86aabSstevel 
3173db86aabSstevel /*
3183db86aabSstevel  * The auxiliary structure attached to each child
3193db86aabSstevel  * (the child's parent-private-data points to this).
3203db86aabSstevel  */
3213db86aabSstevel struct lombus_child_info {
3223db86aabSstevel 	lombus_regspec_t *rsp;
3233db86aabSstevel 	int nregs;
3243db86aabSstevel };
3253db86aabSstevel 
3263db86aabSstevel 
3273db86aabSstevel /*
3283db86aabSstevel  * Local data
3293db86aabSstevel  */
3303db86aabSstevel 
3313db86aabSstevel static void *lombus_statep;
3323db86aabSstevel 
3333db86aabSstevel static major_t lombus_major = NOMAJOR;
3343db86aabSstevel 
3353db86aabSstevel static ddi_device_acc_attr_t lombus_dev_acc_attr[1] =
3363db86aabSstevel {
3373db86aabSstevel 	DDI_DEVICE_ATTR_V0,
3383db86aabSstevel 	DDI_STRUCTURE_LE_ACC,
3393db86aabSstevel 	DDI_STRICTORDER_ACC
3403db86aabSstevel };
3413db86aabSstevel 
3423db86aabSstevel 
3433db86aabSstevel /*
3443db86aabSstevel  *  General utility routines ...
3453db86aabSstevel  */
3463db86aabSstevel 
3473db86aabSstevel static void
lombus_trace(struct lombus_state * ssp,char code,const char * caller,const char * fmt,...)3483db86aabSstevel lombus_trace(struct lombus_state *ssp, char code, const char *caller,
3493db86aabSstevel 	const char *fmt, ...)
3503db86aabSstevel {
3513db86aabSstevel 	char buf[256];
3523db86aabSstevel 	char *p;
3533db86aabSstevel 	va_list va;
3543db86aabSstevel 
3553db86aabSstevel 	if (ssp->debug & (1 << (code-'@'))) {
3563db86aabSstevel 		p = buf;
357*07d06da5SSurya Prakki 		(void) snprintf(p, sizeof (buf) - (p - buf),
3583db86aabSstevel 		    "%s/%s: ", MYNAME, caller);
3593db86aabSstevel 		p += strlen(p);
3603db86aabSstevel 
3613db86aabSstevel 		va_start(va, fmt);
362*07d06da5SSurya Prakki 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
3633db86aabSstevel 		va_end(va);
3643db86aabSstevel 
3653db86aabSstevel 		buf[sizeof (buf) - 1] = '\0';
366*07d06da5SSurya Prakki 		(void) strlog(ssp->majornum, ssp->instance, code, SL_TRACE,
367*07d06da5SSurya Prakki 		    buf);
3683db86aabSstevel 	}
3693db86aabSstevel }
3703db86aabSstevel 
3713db86aabSstevel static struct lombus_state *
lombus_getstate(dev_info_t * dip,int instance,const char * caller)3723db86aabSstevel lombus_getstate(dev_info_t *dip, int instance, const char *caller)
3733db86aabSstevel {
3743db86aabSstevel 	struct lombus_state *ssp = NULL;
3753db86aabSstevel 	dev_info_t *sdip = NULL;
3763db86aabSstevel 	major_t dmaj = NOMAJOR;
3773db86aabSstevel 
3783db86aabSstevel 	if (dip != NULL) {
3793db86aabSstevel 		/*
3803db86aabSstevel 		 * Use the instance number from the <dip>; also,
3813db86aabSstevel 		 * check that it really corresponds to this driver
3823db86aabSstevel 		 */
3833db86aabSstevel 		instance = ddi_get_instance(dip);
3843db86aabSstevel 		dmaj = ddi_driver_major(dip);
3853db86aabSstevel 		if (lombus_major == NOMAJOR && dmaj != NOMAJOR)
3863db86aabSstevel 			lombus_major = dmaj;
3873db86aabSstevel 		else if (dmaj != lombus_major) {
3883db86aabSstevel 			cmn_err(CE_WARN,
3893db86aabSstevel 			    "%s: major number mismatch (%d vs. %d) in %s(),"
3903db86aabSstevel 			    "probably due to child misconfiguration",
3913db86aabSstevel 			    MYNAME, lombus_major, dmaj, caller);
3923db86aabSstevel 			instance = -1;
3933db86aabSstevel 		}
3943db86aabSstevel 	}
3953db86aabSstevel 
3963db86aabSstevel 	if (instance >= 0)
3973db86aabSstevel 		ssp = ddi_get_soft_state(lombus_statep, instance);
3983db86aabSstevel 	if (ssp != NULL) {
3993db86aabSstevel 		sdip = ssp->dip;
4003db86aabSstevel 		if (dip == NULL && sdip == NULL)
4013db86aabSstevel 			ssp = NULL;
4023db86aabSstevel 		else if (dip != NULL && sdip != NULL && sdip != dip) {
4033db86aabSstevel 			cmn_err(CE_WARN,
4043db86aabSstevel 			    "%s: devinfo mismatch (%p vs. %p) in %s(), "
4053db86aabSstevel 			    "probably due to child misconfiguration",
4063db86aabSstevel 			    MYNAME, (void *)dip, (void *)sdip, caller);
4073db86aabSstevel 			ssp = NULL;
4083db86aabSstevel 		}
4093db86aabSstevel 	}
4103db86aabSstevel 
4113db86aabSstevel 	return (ssp);
4123db86aabSstevel }
4133db86aabSstevel 
4143db86aabSstevel /*
4153db86aabSstevel  * Lowest-level serial I/O chip register read/write
4163db86aabSstevel  */
4173db86aabSstevel 
4183db86aabSstevel static void
sio_put_reg(struct lombus_state * ssp,uint_t reg,uint8_t val)4193db86aabSstevel sio_put_reg(struct lombus_state *ssp, uint_t reg, uint8_t val)
4203db86aabSstevel {
4213db86aabSstevel 	lombus_trace(ssp, 'P', "sio_put_reg", "REG[%d] <- $%02x", reg, val);
4223db86aabSstevel 
4233db86aabSstevel 	if (ssp->sio_handle != NULL && !ssp->sio_fault) {
4243db86aabSstevel 		/*
4253db86aabSstevel 		 * The chip is mapped as "I/O" (e.g. with the side-effect
4263db86aabSstevel 		 * bit on SPARC), therefore accesses are required to be
4273db86aabSstevel 		 * in-order, with no value cacheing.  However, there can
4283db86aabSstevel 		 * still be write-behind buffering, so it is not guaranteed
4293db86aabSstevel 		 * that a write actually reaches the chip in a given time.
4303db86aabSstevel 		 *
4313db86aabSstevel 		 * To force the access right through to the chip, we follow
4323db86aabSstevel 		 * the write with another write (to the SCRATCH register)
4333db86aabSstevel 		 * and a read (of the value just written to the SCRATCH
4343db86aabSstevel 		 * register).  The SCRATCH register is specifically provided
4353db86aabSstevel 		 * for temporary data and has no effect on the SIO's own
4363db86aabSstevel 		 * operation, making it ideal as a synchronising mechanism.
4373db86aabSstevel 		 *
4383db86aabSstevel 		 * If we didn't do this, it would be possible that the new
4393db86aabSstevel 		 * value wouldn't reach the chip (and have the *intended*
4403db86aabSstevel 		 * side-effects, such as disabling interrupts), for such a
4413db86aabSstevel 		 * long time that the processor could execute a *lot* of
4423db86aabSstevel 		 * instructions - including exiting the interrupt service
4433db86aabSstevel 		 * routine and re-enabling interrupts.  This effect was
4443db86aabSstevel 		 * observed to lead to spurious (unclaimed) interrupts in
4453db86aabSstevel 		 * some circumstances.
4463db86aabSstevel 		 *
4473db86aabSstevel 		 * This will no longer be needed once "synchronous" access
4483db86aabSstevel 		 * handles are available (see PSARC/2000/269 and 2000/531).
4493db86aabSstevel 		 */
4503db86aabSstevel 		ddi_put8(ssp->sio_handle, ssp->sio_regs + reg, val);
4513db86aabSstevel 		ddi_put8(ssp->sio_handle, ssp->sio_regs + SIO_SCR, val);
4523db86aabSstevel 		membar_sync();
4533db86aabSstevel 		(void) ddi_get8(ssp->sio_handle, ssp->sio_regs + SIO_SCR);
4543db86aabSstevel 	}
4553db86aabSstevel }
4563db86aabSstevel 
4573db86aabSstevel static uint8_t
sio_get_reg(struct lombus_state * ssp,uint_t reg)4583db86aabSstevel sio_get_reg(struct lombus_state *ssp, uint_t reg)
4593db86aabSstevel {
4603db86aabSstevel 	uint8_t val;
4613db86aabSstevel 
4623db86aabSstevel 	if (ssp->sio_handle && !ssp->sio_fault)
4633db86aabSstevel 		val = ddi_get8(ssp->sio_handle, ssp->sio_regs + reg);
4643db86aabSstevel 	else
4653db86aabSstevel 		val = DUMMY_VALUE;
4663db86aabSstevel 
4673db86aabSstevel 	lombus_trace(ssp, 'G', "sio_get_reg", "$%02x <- REG[%d]", val, reg);
4683db86aabSstevel 
4693db86aabSstevel 	return (val);
4703db86aabSstevel }
4713db86aabSstevel 
4723db86aabSstevel static void
sio_check_fault_status(struct lombus_state * ssp)4733db86aabSstevel sio_check_fault_status(struct lombus_state *ssp)
4743db86aabSstevel {
4753db86aabSstevel 	ssp->sio_fault = ddi_check_acc_handle(ssp->sio_handle) != DDI_SUCCESS;
4763db86aabSstevel }
4773db86aabSstevel 
4783db86aabSstevel static boolean_t
sio_faulty(struct lombus_state * ssp)4793db86aabSstevel sio_faulty(struct lombus_state *ssp)
4803db86aabSstevel {
4813db86aabSstevel 	if (!ssp->sio_fault)
4823db86aabSstevel 		sio_check_fault_status(ssp);
4833db86aabSstevel 	return (ssp->sio_fault);
4843db86aabSstevel }
4853db86aabSstevel 
4863db86aabSstevel 
4873db86aabSstevel /*
4883db86aabSstevel  * Check for data ready.
4893db86aabSstevel  */
4903db86aabSstevel static boolean_t
sio_data_ready(struct lombus_state * ssp)4913db86aabSstevel sio_data_ready(struct lombus_state *ssp)
4923db86aabSstevel {
4933db86aabSstevel 	uint8_t status;
4943db86aabSstevel 
4953db86aabSstevel 	/*
4963db86aabSstevel 	 * Data is available if the RXDA bit in the LSR is nonzero
4973db86aabSstevel 	 * (if reading it didn't incur a fault).
4983db86aabSstevel 	 */
4993db86aabSstevel 	status = sio_get_reg(ssp, SIO_LSR);
5003db86aabSstevel 	return ((status & SIO_LSR_RXDA) != 0 && !sio_faulty(ssp));
5013db86aabSstevel }
5023db86aabSstevel 
5033db86aabSstevel /*
5043db86aabSstevel  * Check for LOM ready
5053db86aabSstevel  */
5063db86aabSstevel static boolean_t
sio_lom_ready(struct lombus_state * ssp)5073db86aabSstevel sio_lom_ready(struct lombus_state *ssp)
5083db86aabSstevel {
5093db86aabSstevel 	uint8_t status;
5103db86aabSstevel 	boolean_t rslt;
5113db86aabSstevel 
5123db86aabSstevel 	/*
5133db86aabSstevel 	 * The LOM is ready if the CTS bit in the MSR is 1, meaning
5143db86aabSstevel 	 * that the /CTS signal is being asserted (driven LOW) -
5153db86aabSstevel 	 * unless we incurred a fault in trying to read the MSR!
5163db86aabSstevel 	 *
5173db86aabSstevel 	 * For debugging, we force the result to TRUE if the FAKE flag is set
5183db86aabSstevel 	 */
5193db86aabSstevel 	status = sio_get_reg(ssp, SIO_MSR);
5203db86aabSstevel 	rslt = (status & SIO_MSR_CTS) != 0 && !sio_faulty(ssp);
5213db86aabSstevel 
5223db86aabSstevel 	lombus_trace(ssp, 'R', "sio_lom_ready", "S $%02x R %d F %d",
5233db86aabSstevel 	    status, rslt, ssp->fake_cts);
5243db86aabSstevel 
5253db86aabSstevel 	return (rslt || ssp->fake_cts);
5263db86aabSstevel }
5273db86aabSstevel 
5283db86aabSstevel #if	0
5293db86aabSstevel /*
5303db86aabSstevel  * Check for interrupt pending
5313db86aabSstevel  */
5323db86aabSstevel static boolean_t
5333db86aabSstevel sio_irq_pending(struct lombus_state *ssp)
5343db86aabSstevel {
5353db86aabSstevel 	uint8_t status;
5363db86aabSstevel 	boolean_t rslt;
5373db86aabSstevel 
5383db86aabSstevel 	/*
5393db86aabSstevel 	 * An interrupt is pending if the IPF bit in the EIR is 0,
5403db86aabSstevel 	 * assuming we didn't incur a fault in trying to ready it.
5413db86aabSstevel 	 *
5423db86aabSstevel 	 * Note: we expect that every time we read this register
5433db86aabSstevel 	 * (which is only done from the interrupt service routine),
5443db86aabSstevel 	 * we will see $11001100 (RX FIFO timeout interrupt pending).
5453db86aabSstevel 	 */
5463db86aabSstevel 	status = sio_get_reg(ssp, SIO_EIR);
5473db86aabSstevel 
5483db86aabSstevel 	rslt = (status & SIO_EIR_IPF) == 0 && !sio_faulty(ssp);
5493db86aabSstevel 	lombus_trace(ssp, 'I', "sio_irq_pending", "S $%02x R %d",
5503db86aabSstevel 	    status, rslt);
5513db86aabSstevel 
5523db86aabSstevel 	/*
5533db86aabSstevel 	 * To investigate whether we're getting any abnormal interrupts
5543db86aabSstevel 	 * this code checks that the status value is as expected, and that
5553db86aabSstevel 	 * chip-level interrupts are supposed to be enabled at this time.
5563db86aabSstevel 	 * This will cause a PANIC (on a driver compiled with DEBUG) if
5573db86aabSstevel 	 * all is not as expected ...
5583db86aabSstevel 	 */
5593db86aabSstevel 	ASSERT(status == 0xCC);
5603db86aabSstevel 	ASSERT(ssp->hw_int_enabled);
5613db86aabSstevel 
5623db86aabSstevel 	return (rslt);
5633db86aabSstevel }
5643db86aabSstevel #endif	/* 0 */
5653db86aabSstevel 
5663db86aabSstevel /*
5673db86aabSstevel  * Enable/disable interrupts
5683db86aabSstevel  */
5693db86aabSstevel static void
lombus_set_irq(struct lombus_state * ssp,boolean_t newstate)5703db86aabSstevel lombus_set_irq(struct lombus_state *ssp, boolean_t newstate)
5713db86aabSstevel {
5723db86aabSstevel 	uint8_t val;
5733db86aabSstevel 
5743db86aabSstevel 	val = newstate ? SIO_IER_RXHDL_IE : 0;
5753db86aabSstevel 	sio_put_reg(ssp, SIO_IER, SIO_IER_STD | val);
5763db86aabSstevel 	ssp->hw_int_enabled = newstate;
5773db86aabSstevel }
5783db86aabSstevel 
5793db86aabSstevel /*
5803db86aabSstevel  * Assert/deassert RTS
5813db86aabSstevel  */
5823db86aabSstevel static void
lombus_toggle_rts(struct lombus_state * ssp)5833db86aabSstevel lombus_toggle_rts(struct lombus_state *ssp)
5843db86aabSstevel {
5853db86aabSstevel 	uint8_t val;
5863db86aabSstevel 
5873db86aabSstevel 	val = sio_get_reg(ssp, SIO_MCR);
5883db86aabSstevel 	val &= SIO_MCR_RTS;
5893db86aabSstevel 	val ^= SIO_MCR_RTS;
5903db86aabSstevel 	val |= SIO_MCR_STD;
5913db86aabSstevel 	sio_put_reg(ssp, SIO_MCR, val);
5923db86aabSstevel }
5933db86aabSstevel 
5943db86aabSstevel 
5953db86aabSstevel /*
5963db86aabSstevel  * High-level interrupt handler:
5973db86aabSstevel  *	Checks whether initialisation is complete (to avoid a race
5983db86aabSstevel  *	with mutex_init()), and whether chip interrupts are enabled.
5993db86aabSstevel  *	If not, the interrupt's not for us, so just return UNCLAIMED.
6003db86aabSstevel  *	Otherwise, disable the interrupt, trigger a softint, and return
6013db86aabSstevel  *	CLAIMED.  The softint handler will then do all the real work.
6023db86aabSstevel  *
6033db86aabSstevel  *	NOTE: the chip interrupt capability is only re-enabled once the
6043db86aabSstevel  *	receive code has run, but that can be called from a poll loop
6053db86aabSstevel  *	or cyclic callback as well as from the softint.  So it's *not*
6063db86aabSstevel  *	guaranteed that there really is a chip interrupt pending here,
6073db86aabSstevel  *	'cos the work may already have been done and the reason for the
6083db86aabSstevel  *	interrupt gone away before we get here.
6093db86aabSstevel  *
6103db86aabSstevel  *	OTOH, if we come through here twice without the receive code
6113db86aabSstevel  *	having run in between, that's definitely wrong.  In such an
6123db86aabSstevel  *	event, we would notice that chip interrupts haven't yet been
6133db86aabSstevel  *	re-enabled and return UNCLAIMED, allowing the system's jabber
6143db86aabSstevel  *	protect code (if any) to do its job.
6153db86aabSstevel  */
6163db86aabSstevel static uint_t
lombus_hi_intr(caddr_t arg)6173db86aabSstevel lombus_hi_intr(caddr_t arg)
6183db86aabSstevel {
6193db86aabSstevel 	struct lombus_state *ssp = (void *)arg;
6203db86aabSstevel 	uint_t claim;
6213db86aabSstevel 
6223db86aabSstevel 	claim = DDI_INTR_UNCLAIMED;
623dd4eeefdSeota 	if (ssp->cycid != NULL) {
6243db86aabSstevel 		mutex_enter(ssp->hw_mutex);
6253db86aabSstevel 		if (ssp->hw_int_enabled) {
6263db86aabSstevel 			lombus_set_irq(ssp, B_FALSE);
6273db86aabSstevel 			ddi_trigger_softintr(ssp->softid);
6283db86aabSstevel 			claim = DDI_INTR_CLAIMED;
6293db86aabSstevel 		}
6303db86aabSstevel 		mutex_exit(ssp->hw_mutex);
6313db86aabSstevel 	}
6323db86aabSstevel 
6333db86aabSstevel 	return (claim);
6343db86aabSstevel }
6353db86aabSstevel 
6363db86aabSstevel /*
6373db86aabSstevel  * Packet receive handler
6383db86aabSstevel  *
6393db86aabSstevel  * This routine should be called from the low-level softint, or the
6403db86aabSstevel  * cyclic callback, or lombus_cmd() (for polled operation), with the
6413db86aabSstevel  * low-level mutex already held.
6423db86aabSstevel  */
6433db86aabSstevel static void
lombus_receive(struct lombus_state * ssp)6443db86aabSstevel lombus_receive(struct lombus_state *ssp)
6453db86aabSstevel {
6463db86aabSstevel 	boolean_t ready = B_FALSE;
6473db86aabSstevel 	uint8_t data = 0;
6483db86aabSstevel 	uint8_t rcvd = 0;
6493db86aabSstevel 	uint8_t tmp;
6503db86aabSstevel 
6513db86aabSstevel 	lombus_trace(ssp, 'S', "lombus_receive",
6523db86aabSstevel 	    "state %d; error $%x",
6533db86aabSstevel 	    ssp->cmdstate, ssp->error);
6543db86aabSstevel 
6553db86aabSstevel 	/*
6563db86aabSstevel 	 * Check for access faults before starting the receive
6573db86aabSstevel 	 * loop (we don't want to cause bus errors or suchlike
6583db86aabSstevel 	 * unpleasantness in the event that the SIO has died).
6593db86aabSstevel 	 */
6603db86aabSstevel 	if (!sio_faulty(ssp)) {
6613db86aabSstevel 		/*
6623db86aabSstevel 		 * Read bytes from the FIFO until they're all gone,
6633db86aabSstevel 		 * or we find the 'END OF PACKET' set on one, or
6643db86aabSstevel 		 * our buffer overflows (which must be an error)
6653db86aabSstevel 		 */
6663db86aabSstevel 		mutex_enter(ssp->hw_mutex);
6673db86aabSstevel 		while (sio_data_ready(ssp)) {
6683db86aabSstevel 			data = sio_get_reg(ssp, SIO_RXD);
6693db86aabSstevel 			ssp->reply[rcvd = ssp->index] = data;
6703db86aabSstevel 			if (++rcvd >= LOMBUS_BUFSIZE)
6713db86aabSstevel 				break;
6723db86aabSstevel 			ssp->index = rcvd;
6733db86aabSstevel 			if (data & LOMBUS_LAST)
6743db86aabSstevel 				break;
6753db86aabSstevel 		}
6763db86aabSstevel 		lombus_set_irq(ssp, B_TRUE);
6773db86aabSstevel 		mutex_exit(ssp->hw_mutex);
6783db86aabSstevel 	}
6793db86aabSstevel 
6803db86aabSstevel 	lombus_trace(ssp, 'S', "lombus_receive",
6813db86aabSstevel 	    "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
6823db86aabSstevel 	    rcvd,
6833db86aabSstevel 	    ssp->reply[0], ssp->reply[1],
6843db86aabSstevel 	    ssp->reply[2], ssp->reply[3],
6853db86aabSstevel 	    ssp->reply[4], ssp->reply[5],
6863db86aabSstevel 	    ssp->reply[6], ssp->reply[7]);
6873db86aabSstevel 
6883db86aabSstevel 	if (ssp->cmdstate != LOMBUS_CMDSTATE_WAITING) {
6893db86aabSstevel 		/*
6903db86aabSstevel 		 * We're not expecting any data in this state, so if
6913db86aabSstevel 		 * we DID receive any data, we just throw it away by
6923db86aabSstevel 		 * resetting the buffer index to 0.
6933db86aabSstevel 		 */
6943db86aabSstevel 		ssp->index = 0;
6953db86aabSstevel 	} else if (rcvd == 0) {
6963db86aabSstevel 		/*
6973db86aabSstevel 		 * No bytes received this time through (though there
6983db86aabSstevel 		 * might be a partial packet sitting in the buffer).
6993db86aabSstevel 		 * If it seems the LOM is taking too long to respond,
7003db86aabSstevel 		 * we'll assume it's died and return an error.
7013db86aabSstevel 		 */
7023db86aabSstevel 		if (ddi_get_lbolt() > ssp->deadline) {
7033db86aabSstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7043db86aabSstevel 			ssp->error = LOMBUS_ERR_TIMEOUT;
7053db86aabSstevel 			ready = B_TRUE;
7063db86aabSstevel 		}
7073db86aabSstevel 	} else if (rcvd >= LOMBUS_BUFSIZE) {
7083db86aabSstevel 		/*
7093db86aabSstevel 		 * Buffer overflow; discard the data & treat as an error
7103db86aabSstevel 		 * (even if the last byte read did claim to terminate a
7113db86aabSstevel 		 * packet, it can't be a valid one 'cos it's too long!)
7123db86aabSstevel 		 */
7133db86aabSstevel 		ssp->index = 0;
7143db86aabSstevel 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7153db86aabSstevel 		ssp->error = LOMBUS_ERR_OFLOW;
7163db86aabSstevel 		ready = B_TRUE;
7173db86aabSstevel 	} else if ((data & LOMBUS_LAST) == 0) {
7183db86aabSstevel 		/*
7193db86aabSstevel 		 * Packet not yet complete; leave the partial packet in
7203db86aabSstevel 		 * the buffer for later ...
7213db86aabSstevel 		 */
7223db86aabSstevel 		_NOTE(EMPTY)
7233db86aabSstevel 		;
7243db86aabSstevel 	} else if ((data & LOMBUS_MASK) != LOMBUS_STATUS) {
7253db86aabSstevel 		/*
7263db86aabSstevel 		 * Invalid "status" byte - maybe an echo of the command?
7273db86aabSstevel 		 *
7283db86aabSstevel 		 * As a debugging feature, we allow for this, assuming
7293db86aabSstevel 		 * that if the LOM has echoed the command byte, it has
7303db86aabSstevel 		 * also echoed all the parameter bytes before starting
7313db86aabSstevel 		 * command processing.  So, we dump out the buffer and
7323db86aabSstevel 		 * then clear it, so we can go back to looking for the
7333db86aabSstevel 		 * real reply.
7343db86aabSstevel 		 *
7353db86aabSstevel 		 * Otherwise, we just drop the data & flag an error.
7363db86aabSstevel 		 */
7373db86aabSstevel 		if (ssp->allow_echo) {
7383db86aabSstevel 			lombus_trace(ssp, 'E', "lombus_receive",
7393db86aabSstevel 			    "echo $%02x $%02x $%02x $%02x "
7403db86aabSstevel 			    "$%02x $%02x $%02x $%02x",
7413db86aabSstevel 			    ssp->reply[0], ssp->reply[1],
7423db86aabSstevel 			    ssp->reply[2], ssp->reply[3],
7433db86aabSstevel 			    ssp->reply[4], ssp->reply[5],
7443db86aabSstevel 			    ssp->reply[6], ssp->reply[7]);
7453db86aabSstevel 			ssp->index = 0;
7463db86aabSstevel 		} else {
7473db86aabSstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7483db86aabSstevel 			ssp->error = LOMBUS_ERR_BADSTATUS;
7493db86aabSstevel 			ready = B_TRUE;
7503db86aabSstevel 		}
7513db86aabSstevel 	} else if ((data & LOMBUS_SEQ) != ssp->sequence) {
7523db86aabSstevel 		/*
7533db86aabSstevel 		 * Wrong sequence number!  Flag this as an error
7543db86aabSstevel 		 */
7553db86aabSstevel 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7563db86aabSstevel 		ssp->error = LOMBUS_ERR_SEQUENCE;
7573db86aabSstevel 		ready = B_TRUE;
7583db86aabSstevel 	} else {
7593db86aabSstevel 		/*
7603db86aabSstevel 		 * Finally, we know that's it's a valid reply to our
7613db86aabSstevel 		 * last command.  Update the ASYNC status, derive the
7623db86aabSstevel 		 * reply parameter (if any), and check the ERROR bit
7633db86aabSstevel 		 * to find out what the parameter means.
7643db86aabSstevel 		 *
7653db86aabSstevel 		 * Note that not all the values read/assigned here
7663db86aabSstevel 		 * are meaningful, but it doesn't matter; the waiting
7673db86aabSstevel 		 * thread will know which one(s) it should check.
7683db86aabSstevel 		 */
7693db86aabSstevel 		ssp->async = (data & LOMBUS_STATUS_ASYNC) ? 1 : 0;
7703db86aabSstevel 		tmp = ((data & LOMBUS_STATUS_MSB) ? 0x80 : 0) | ssp->reply[0];
7713db86aabSstevel 		if (data & LOMBUS_STATUS_ERR) {
7723db86aabSstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
7733db86aabSstevel 			ssp->error = tmp;
7743db86aabSstevel 		} else {
7753db86aabSstevel 			ssp->cmdstate = LOMBUS_CMDSTATE_READY;
7763db86aabSstevel 			ssp->result = tmp;
7773db86aabSstevel 		}
7783db86aabSstevel 		ready = B_TRUE;
7793db86aabSstevel 	}
7803db86aabSstevel 
7813db86aabSstevel 	lombus_trace(ssp, 'T', "lombus_receive",
7823db86aabSstevel 	    "rcvd %d; last $%02x; state %d; error $%x; ready %d",
7833db86aabSstevel 	    rcvd, data, ssp->cmdstate, ssp->error, ready);
7843db86aabSstevel 
7853db86aabSstevel 	if (ready)
7863db86aabSstevel 		cv_broadcast(ssp->lo_cv);
7873db86aabSstevel }
7883db86aabSstevel 
7893db86aabSstevel /*
7903db86aabSstevel  * Low-level softint handler
7913db86aabSstevel  *
7923db86aabSstevel  * This routine should be triggered whenever there's a byte to be read
7933db86aabSstevel  */
7943db86aabSstevel static uint_t
lombus_softint(caddr_t arg)7953db86aabSstevel lombus_softint(caddr_t arg)
7963db86aabSstevel {
7973db86aabSstevel 	struct lombus_state *ssp = (void *)arg;
7983db86aabSstevel 
7993db86aabSstevel 	mutex_enter(ssp->lo_mutex);
8003db86aabSstevel 	lombus_receive(ssp);
8013db86aabSstevel 	mutex_exit(ssp->lo_mutex);
8023db86aabSstevel 
8033db86aabSstevel 	return (DDI_INTR_CLAIMED);
8043db86aabSstevel }
8053db86aabSstevel 
8063db86aabSstevel /*
8073db86aabSstevel  * Cyclic handler: just calls the receive routine, in case interrupts
8083db86aabSstevel  * are not being delivered and in order to handle command timeout
8093db86aabSstevel  */
8103db86aabSstevel static void
lombus_cyclic(void * arg)8113db86aabSstevel lombus_cyclic(void *arg)
8123db86aabSstevel {
8133db86aabSstevel 	struct lombus_state *ssp = (void *)arg;
8143db86aabSstevel 
8153db86aabSstevel 	mutex_enter(ssp->lo_mutex);
8163db86aabSstevel 	lombus_receive(ssp);
8173db86aabSstevel 	mutex_exit(ssp->lo_mutex);
8183db86aabSstevel }
8193db86aabSstevel 
8203db86aabSstevel 
8213db86aabSstevel /*
8223db86aabSstevel  * Serial protocol
8233db86aabSstevel  *
8243db86aabSstevel  * This routine builds a command and sets it in progress.
8253db86aabSstevel  */
8263db86aabSstevel static uint8_t
lombus_cmd(HANDLE_TYPE * hdlp,ptrdiff_t vreg,uint_t val,uint_t cmd)8273db86aabSstevel lombus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd)
8283db86aabSstevel {
8293db86aabSstevel 	struct lombus_state *ssp;
8303db86aabSstevel 	clock_t start;
8313db86aabSstevel 	uint8_t *p;
8323db86aabSstevel 
8333db86aabSstevel 	/*
8343db86aabSstevel 	 * First of all, wait for the interface to be available.
8353db86aabSstevel 	 *
8363db86aabSstevel 	 * NOTE: we blow through all the mutex/cv/state checking and
8373db86aabSstevel 	 * preempt any command in progress if the system is panicking!
8383db86aabSstevel 	 */
8393db86aabSstevel 	ssp = HANDLE_PRIVATE(hdlp);
8403db86aabSstevel 	mutex_enter(ssp->lo_mutex);
8413db86aabSstevel 	while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr)
8423db86aabSstevel 		cv_wait(ssp->lo_cv, ssp->lo_mutex);
8433db86aabSstevel 
8443db86aabSstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_BUSY;
8453db86aabSstevel 	ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ;
8463db86aabSstevel 
8473db86aabSstevel 	/*
8483db86aabSstevel 	 * We have exclusive ownership, so assemble the command (backwards):
8493db86aabSstevel 	 *
8503db86aabSstevel 	 * [byte 0]	Command:	modified by XADDR and/or WMSB bits
8513db86aabSstevel 	 * [Optional] Parameter: 	Value to write (low 7 bits)
8523db86aabSstevel 	 * [Optional] Parameter: 	Register number (high 7 bits)
8533db86aabSstevel 	 * [Optional] Parameter: 	Register number (low 7 bits)
8543db86aabSstevel 	 */
8553db86aabSstevel 	p = &ssp->cmdbuf[0];
8563db86aabSstevel 	*p++ = LOMBUS_CMD | ssp->sequence | cmd;
8573db86aabSstevel 	switch (cmd) {
8583db86aabSstevel 	case LOMBUS_CMD_WRITE:
8593db86aabSstevel 		*p++ = val & 0x7f;
8603db86aabSstevel 		if (val >= 0x80)
8613db86aabSstevel 			ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB;
8623db86aabSstevel 		/*FALLTHRU*/
8633db86aabSstevel 	case LOMBUS_CMD_READ:
8643db86aabSstevel 		if (LOMBUS_VREG_HI(vreg) != 0) {
8653db86aabSstevel 			*p++ = LOMBUS_VREG_HI(vreg);
8663db86aabSstevel 			ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR;
8673db86aabSstevel 		}
8683db86aabSstevel 		*p++ = LOMBUS_VREG_LO(vreg);
8693db86aabSstevel 		/*FALLTHRU*/
8703db86aabSstevel 	case LOMBUS_CMD_NOP:
8713db86aabSstevel 		break;
8723db86aabSstevel 	}
8733db86aabSstevel 
8743db86aabSstevel 	/*
8753db86aabSstevel 	 * Check and update the SIO h/w fault status before accessing
8763db86aabSstevel 	 * the chip registers.  If there's a (new or previous) fault,
8773db86aabSstevel 	 * we'll run through the protocol but won't really touch the
8783db86aabSstevel 	 * hardware and all commands will timeout.  If a previously
8793db86aabSstevel 	 * discovered fault has now gone away (!), then we can (try to)
8803db86aabSstevel 	 * proceed with the new command (probably a probe).
8813db86aabSstevel 	 */
8823db86aabSstevel 	sio_check_fault_status(ssp);
8833db86aabSstevel 
8843db86aabSstevel 	/*
8853db86aabSstevel 	 * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
8863db86aabSstevel 	 * us that it's ready for the next command.  If it doesn't, though,
8873db86aabSstevel 	 * we'll send it anyway, on the basis that the CTS signal might be
8883db86aabSstevel 	 * open- or short-circuited (or the LOM firmware forgot to set it,
8893db86aabSstevel 	 * or the LOM just got reset, or whatever ...)
8903db86aabSstevel 	 */
8913db86aabSstevel 	start = ddi_get_lbolt();
8923db86aabSstevel 	ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000);
8933db86aabSstevel 	while (!sio_lom_ready(ssp)) {
894d3d50737SRafael Vanoni 		if (ddi_get_lbolt() > ssp->deadline)
8953db86aabSstevel 			break;
896d3d50737SRafael Vanoni 
897*07d06da5SSurya Prakki 		(void) cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
898d3d50737SRafael Vanoni 		    drv_usectohz(LOMBUS_CTS_POLL/1000), TR_CLOCK_TICK);
8993db86aabSstevel 	}
9003db86aabSstevel 
9013db86aabSstevel 	/*
9023db86aabSstevel 	 * Either the LOM is ready, or we timed out waiting for CTS.
9033db86aabSstevel 	 * In either case, we're going to send the command now by
9043db86aabSstevel 	 * stuffing the packet into the Tx FIFO, reversing it as we go.
9053db86aabSstevel 	 * We call lombus_receive() first to ensure there isn't any
9063db86aabSstevel 	 * garbage left in the Rx FIFO from an earlier command that
9073db86aabSstevel 	 * timed out (or was pre-empted by a PANIC!).  This also makes
9083db86aabSstevel 	 * sure that SIO interrupts are enabled so we'll see the reply
9093db86aabSstevel 	 * more quickly (the poll loop below will still work even if
9103db86aabSstevel 	 * interrupts aren't enabled, but it will take longer).
9113db86aabSstevel 	 */
9123db86aabSstevel 	lombus_receive(ssp);
9133db86aabSstevel 	mutex_enter(ssp->hw_mutex);
9143db86aabSstevel 	while (p > ssp->cmdbuf)
9153db86aabSstevel 		sio_put_reg(ssp, SIO_TXD, *--p);
9163db86aabSstevel 	mutex_exit(ssp->hw_mutex);
9173db86aabSstevel 
9183db86aabSstevel 	/*
9193db86aabSstevel 	 * Prepare for the reply (to be processed by the interrupt/cyclic
9203db86aabSstevel 	 * handler and/or polling loop below), then wait for a response
9213db86aabSstevel 	 * or timeout.
9223db86aabSstevel 	 */
9233db86aabSstevel 	start = ddi_get_lbolt();
9243db86aabSstevel 	ssp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
9253db86aabSstevel 	ssp->error = 0;
9263db86aabSstevel 	ssp->index = 0;
9273db86aabSstevel 	ssp->result = DUMMY_VALUE;
9283db86aabSstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_WAITING;
9293db86aabSstevel 	while (ssp->cmdstate == LOMBUS_CMDSTATE_WAITING) {
930d3d50737SRafael Vanoni 		if (cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
931d3d50737SRafael Vanoni 		    drv_usectohz(LOMBUS_CMD_POLL/1000), TR_CLOCK_TICK) == -1)
9323db86aabSstevel 			lombus_receive(ssp);
9333db86aabSstevel 	}
9343db86aabSstevel 
9353db86aabSstevel 	/*
9363db86aabSstevel 	 * The return value may not be meaningful but retrieve it anyway
9373db86aabSstevel 	 */
9383db86aabSstevel 	val = ssp->result;
9393db86aabSstevel 	if (sio_faulty(ssp)) {
9403db86aabSstevel 		val = DUMMY_VALUE;
9413db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW;
9423db86aabSstevel 	} else if (ssp->cmdstate != LOMBUS_CMDSTATE_READY) {
9433db86aabSstevel 		/*
9443db86aabSstevel 		 * Some problem here ... transfer the error code from
9453db86aabSstevel 		 * the per-instance state to the per-handle fault flag.
9463db86aabSstevel 		 * The error code shouldn't be zero!
9473db86aabSstevel 		 */
9483db86aabSstevel 		if (ssp->error != 0)
9493db86aabSstevel 			HANDLE_FAULT(hdlp) = ssp->error;
9503db86aabSstevel 		else
9513db86aabSstevel 			HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE;
9523db86aabSstevel 	}
9533db86aabSstevel 
9543db86aabSstevel 	/*
9553db86aabSstevel 	 * All done now!
9563db86aabSstevel 	 */
9573db86aabSstevel 	ssp->index = 0;
9583db86aabSstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
9593db86aabSstevel 	cv_broadcast(ssp->lo_cv);
9603db86aabSstevel 	mutex_exit(ssp->lo_mutex);
9613db86aabSstevel 
9623db86aabSstevel 	return (val);
9633db86aabSstevel }
9643db86aabSstevel 
9653db86aabSstevel 
9663db86aabSstevel /*
9673db86aabSstevel  * Space 0 - LOM virtual register access
9683db86aabSstevel  * Only 8-bit accesses are supported.
9693db86aabSstevel  */
9703db86aabSstevel static uint8_t
lombus_vreg_get8(HANDLE_TYPE * hdlp,uint8_t * addr)9713db86aabSstevel lombus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
9723db86aabSstevel {
9733db86aabSstevel 	ptrdiff_t offset;
9743db86aabSstevel 
9753db86aabSstevel 	/*
9763db86aabSstevel 	 * Check the offset that the caller has added to the base address
9773db86aabSstevel 	 * against the length of the mapping originally requested.
9783db86aabSstevel 	 */
9793db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
9803db86aabSstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
9813db86aabSstevel 		/*
9823db86aabSstevel 		 * Invalid access - flag a fault and return a dummy value
9833db86aabSstevel 		 */
9843db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
9853db86aabSstevel 		return (DUMMY_VALUE);
9863db86aabSstevel 	}
9873db86aabSstevel 
9883db86aabSstevel 	/*
9893db86aabSstevel 	 * Derive the virtual register number and run the command
9903db86aabSstevel 	 */
9913db86aabSstevel 	return (lombus_cmd(hdlp, ADDR_TO_VREG(addr), 0, LOMBUS_CMD_READ));
9923db86aabSstevel }
9933db86aabSstevel 
9943db86aabSstevel static void
lombus_vreg_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)9953db86aabSstevel lombus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
9963db86aabSstevel {
9973db86aabSstevel 	ptrdiff_t offset;
9983db86aabSstevel 
9993db86aabSstevel 	/*
10003db86aabSstevel 	 * Check the offset that the caller has added to the base address
10013db86aabSstevel 	 * against the length of the mapping originally requested.
10023db86aabSstevel 	 */
10033db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10043db86aabSstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10053db86aabSstevel 		/*
10063db86aabSstevel 		 * Invalid access - flag a fault and return
10073db86aabSstevel 		 */
10083db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10093db86aabSstevel 		return;
10103db86aabSstevel 	}
10113db86aabSstevel 
10123db86aabSstevel 	/*
10133db86aabSstevel 	 * Derive the virtual register number and run the command
10143db86aabSstevel 	 */
10153db86aabSstevel 	(void) lombus_cmd(hdlp, ADDR_TO_VREG(addr), val, LOMBUS_CMD_WRITE);
10163db86aabSstevel }
10173db86aabSstevel 
10183db86aabSstevel static void
lombus_vreg_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)10193db86aabSstevel lombus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
10203db86aabSstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
10213db86aabSstevel {
10223db86aabSstevel 	size_t inc;
10233db86aabSstevel 
10243db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
10253db86aabSstevel 	for (; repcount--; dev_addr += inc)
10263db86aabSstevel 		*host_addr++ = lombus_vreg_get8(hdlp, dev_addr);
10273db86aabSstevel }
10283db86aabSstevel 
10293db86aabSstevel static void
lombus_vreg_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)10303db86aabSstevel lombus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
10313db86aabSstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
10323db86aabSstevel {
10333db86aabSstevel 	size_t inc;
10343db86aabSstevel 
10353db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
10363db86aabSstevel 	for (; repcount--; dev_addr += inc)
10373db86aabSstevel 		lombus_vreg_put8(hdlp, dev_addr, *host_addr++);
10383db86aabSstevel }
10393db86aabSstevel 
10403db86aabSstevel 
10413db86aabSstevel /*
10423db86aabSstevel  * Space 1 - LOM watchdog pat register access
10433db86aabSstevel  * Only 8-bit accesses are supported.
10443db86aabSstevel  *
10453db86aabSstevel  * Reads have no effect and return 0.
10463db86aabSstevel  *
10473db86aabSstevel  * Writes pat the dog by toggling the RTS line iff enough time has
10483db86aabSstevel  * elapsed since last time we toggled it.
10493db86aabSstevel  *
10503db86aabSstevel  * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
10513db86aabSstevel  * way of zeroing the destination area ;-) and still won't pat the dog.
10523db86aabSstevel  *
10533db86aabSstevel  * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
10543db86aabSstevel  * only count as a single pat, no matter how many bytes the caller
10553db86aabSstevel  * says to write, as the inter-pat time is VERY long compared with
10563db86aabSstevel  * the time it will take to read the memory source area.
10573db86aabSstevel  */
10583db86aabSstevel 
10593db86aabSstevel static uint8_t
lombus_pat_get8(HANDLE_TYPE * hdlp,uint8_t * addr)10603db86aabSstevel lombus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
10613db86aabSstevel {
10623db86aabSstevel 	ptrdiff_t offset;
10633db86aabSstevel 
10643db86aabSstevel 	/*
10653db86aabSstevel 	 * Check the offset that the caller has added to the base address
10663db86aabSstevel 	 * against the length of the mapping originally requested.
10673db86aabSstevel 	 */
10683db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10693db86aabSstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10703db86aabSstevel 		/*
10713db86aabSstevel 		 * Invalid access - flag a fault and return a dummy value
10723db86aabSstevel 		 */
10733db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10743db86aabSstevel 		return (DUMMY_VALUE);
10753db86aabSstevel 	}
10763db86aabSstevel 
10773db86aabSstevel 	return (0);
10783db86aabSstevel }
10793db86aabSstevel 
10803db86aabSstevel static void
lombus_pat_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)10813db86aabSstevel lombus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
10823db86aabSstevel {
10833db86aabSstevel 	struct lombus_state *ssp;
10843db86aabSstevel 	ptrdiff_t offset;
10853db86aabSstevel 	hrtime_t now;
10863db86aabSstevel 
10873db86aabSstevel 	_NOTE(ARGUNUSED(val))
10883db86aabSstevel 
10893db86aabSstevel 	/*
10903db86aabSstevel 	 * Check the offset that the caller has added to the base address
10913db86aabSstevel 	 * against the length of the mapping originally requested.
10923db86aabSstevel 	 */
10933db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
10943db86aabSstevel 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
10953db86aabSstevel 		/*
10963db86aabSstevel 		 * Invalid access - flag a fault and return
10973db86aabSstevel 		 */
10983db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
10993db86aabSstevel 		return;
11003db86aabSstevel 	}
11013db86aabSstevel 
11023db86aabSstevel 	ssp = HANDLE_PRIVATE(hdlp);
11033db86aabSstevel 	mutex_enter(ssp->hw_mutex);
11043db86aabSstevel 	now = gethrtime();
11053db86aabSstevel 	if ((now - ssp->hw_last_pat) >= LOMBUS_MIN_PAT) {
11063db86aabSstevel 		lombus_toggle_rts(ssp);
11073db86aabSstevel 		ssp->hw_last_pat = now;
11083db86aabSstevel 	}
11093db86aabSstevel 	mutex_exit(ssp->hw_mutex);
11103db86aabSstevel }
11113db86aabSstevel 
11123db86aabSstevel static void
lombus_pat_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11133db86aabSstevel lombus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11143db86aabSstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11153db86aabSstevel {
11163db86aabSstevel 	size_t inc;
11173db86aabSstevel 
11183db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11193db86aabSstevel 	for (; repcount--; dev_addr += inc)
11203db86aabSstevel 		*host_addr++ = lombus_pat_get8(hdlp, dev_addr);
11213db86aabSstevel }
11223db86aabSstevel 
11233db86aabSstevel static void
lombus_pat_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)11243db86aabSstevel lombus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
11253db86aabSstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
11263db86aabSstevel {
11273db86aabSstevel 	size_t inc;
11283db86aabSstevel 
11293db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11303db86aabSstevel 	for (; repcount--; dev_addr += inc)
11313db86aabSstevel 		lombus_pat_put8(hdlp, dev_addr, *host_addr++);
11323db86aabSstevel }
11333db86aabSstevel 
11343db86aabSstevel 
11353db86aabSstevel /*
11363db86aabSstevel  * Space 2 - LOM async event flag register access
11373db86aabSstevel  * Only 16-bit accesses are supported.
11383db86aabSstevel  */
11393db86aabSstevel static uint16_t
lombus_event_get16(HANDLE_TYPE * hdlp,uint16_t * addr)11403db86aabSstevel lombus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
11413db86aabSstevel {
11423db86aabSstevel 	struct lombus_state *ssp;
11433db86aabSstevel 	ptrdiff_t offset;
11443db86aabSstevel 
11453db86aabSstevel 	/*
11463db86aabSstevel 	 * Check the offset that the caller has added to the base address
11473db86aabSstevel 	 * against the length of the mapping orignally requested.
11483db86aabSstevel 	 */
11493db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
11503db86aabSstevel 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11513db86aabSstevel 		/*
11523db86aabSstevel 		 * Invalid access - flag a fault and return a dummy value
11533db86aabSstevel 		 */
11543db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11553db86aabSstevel 		return (DUMMY_VALUE);
11563db86aabSstevel 	}
11573db86aabSstevel 
11583db86aabSstevel 	/*
11593db86aabSstevel 	 * Return the value of the asynchronous-event-pending flag
11603db86aabSstevel 	 * as passed back by the LOM at the end of the last command.
11613db86aabSstevel 	 */
11623db86aabSstevel 	ssp = HANDLE_PRIVATE(hdlp);
11633db86aabSstevel 	return (ssp->async);
11643db86aabSstevel }
11653db86aabSstevel 
11663db86aabSstevel static void
lombus_event_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)11673db86aabSstevel lombus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
11683db86aabSstevel {
11693db86aabSstevel 	ptrdiff_t offset;
11703db86aabSstevel 
11713db86aabSstevel 	_NOTE(ARGUNUSED(val))
11723db86aabSstevel 
11733db86aabSstevel 	/*
11743db86aabSstevel 	 * Check the offset that the caller has added to the base address
11753db86aabSstevel 	 * against the length of the mapping originally requested.
11763db86aabSstevel 	 */
11773db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
11783db86aabSstevel 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
11793db86aabSstevel 		/*
11803db86aabSstevel 		 * Invalid access - flag a fault and return
11813db86aabSstevel 		 */
11823db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
11833db86aabSstevel 		return;
11843db86aabSstevel 	}
11853db86aabSstevel 
11863db86aabSstevel 	/*
11873db86aabSstevel 	 * The user can't overwrite the asynchronous-event-pending flag!
11883db86aabSstevel 	 */
11893db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO;
11903db86aabSstevel }
11913db86aabSstevel 
11923db86aabSstevel static void
lombus_event_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)11933db86aabSstevel lombus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
11943db86aabSstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
11953db86aabSstevel {
11963db86aabSstevel 	size_t inc;
11973db86aabSstevel 
11983db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
11993db86aabSstevel 	for (; repcount--; dev_addr += inc)
12003db86aabSstevel 		*host_addr++ = lombus_event_get16(hdlp, dev_addr);
12013db86aabSstevel }
12023db86aabSstevel 
12033db86aabSstevel static void
lombus_event_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)12043db86aabSstevel lombus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
12053db86aabSstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
12063db86aabSstevel {
12073db86aabSstevel 	size_t inc;
12083db86aabSstevel 
12093db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
12103db86aabSstevel 	for (; repcount--; dev_addr += inc)
12113db86aabSstevel 		lombus_event_put16(hdlp, dev_addr, *host_addr++);
12123db86aabSstevel }
12133db86aabSstevel 
12143db86aabSstevel 
12153db86aabSstevel /*
12163db86aabSstevel  * All spaces - access handle fault information
12173db86aabSstevel  * Only 32-bit accesses are supported.
12183db86aabSstevel  */
12193db86aabSstevel static uint32_t
lombus_meta_get32(HANDLE_TYPE * hdlp,uint32_t * addr)12203db86aabSstevel lombus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr)
12213db86aabSstevel {
12223db86aabSstevel 	struct lombus_state *ssp;
12233db86aabSstevel 	ptrdiff_t offset;
12243db86aabSstevel 
12253db86aabSstevel 	/*
12263db86aabSstevel 	 * Derive the offset that the caller has added to the base
12273db86aabSstevel 	 * address originally returned, and use it to determine
12283db86aabSstevel 	 * which meta-register is to be accessed ...
12293db86aabSstevel 	 */
12303db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
12313db86aabSstevel 	switch (offset) {
12323db86aabSstevel 	case LOMBUS_FAULT_REG:
12333db86aabSstevel 		/*
12343db86aabSstevel 		 * This meta-register provides a code for the most
12353db86aabSstevel 		 * recent virtual register access fault, if any.
12363db86aabSstevel 		 */
12373db86aabSstevel 		return (HANDLE_FAULT(hdlp));
12383db86aabSstevel 
12393db86aabSstevel 	case LOMBUS_PROBE_REG:
12403db86aabSstevel 		/*
12413db86aabSstevel 		 * Reading this meta-register clears any existing fault
12423db86aabSstevel 		 * (at the virtual, not the hardware access layer), then
12433db86aabSstevel 		 * runs a NOP command and returns the fault code from that.
12443db86aabSstevel 		 */
12453db86aabSstevel 		HANDLE_FAULT(hdlp) = 0;
1246*07d06da5SSurya Prakki 		(void) lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
12473db86aabSstevel 		return (HANDLE_FAULT(hdlp));
12483db86aabSstevel 
12493db86aabSstevel 	case LOMBUS_ASYNC_REG:
12503db86aabSstevel 		/*
12513db86aabSstevel 		 * Obsolescent - but still supported for backwards
12523db86aabSstevel 		 * compatibility.  This is an alias for the newer
12533db86aabSstevel 		 * LOMBUS_EVENT_REG, but doesn't require a separate
12543db86aabSstevel 		 * "reg" entry and ddi_regs_map_setup() call.
12553db86aabSstevel 		 *
12563db86aabSstevel 		 * It returns the value of the asynchronous-event-pending
12573db86aabSstevel 		 * flag as passed back by the LOM at the end of the last
12583db86aabSstevel 		 * completed command.
12593db86aabSstevel 		 */
12603db86aabSstevel 		ssp = HANDLE_PRIVATE(hdlp);
12613db86aabSstevel 		return (ssp->async);
12623db86aabSstevel 
12633db86aabSstevel 	default:
12643db86aabSstevel 		/*
12653db86aabSstevel 		 * Invalid access - flag a fault and return a dummy value
12663db86aabSstevel 		 */
12673db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
12683db86aabSstevel 		return (DUMMY_VALUE);
12693db86aabSstevel 	}
12703db86aabSstevel }
12713db86aabSstevel 
12723db86aabSstevel static void
lombus_meta_put32(HANDLE_TYPE * hdlp,uint32_t * addr,uint32_t val)12733db86aabSstevel lombus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val)
12743db86aabSstevel {
12753db86aabSstevel 	ptrdiff_t offset;
12763db86aabSstevel 
12773db86aabSstevel 	/*
12783db86aabSstevel 	 * Derive the offset that the caller has added to the base
12793db86aabSstevel 	 * address originally returned, and use it to determine
12803db86aabSstevel 	 * which meta-register is to be accessed ...
12813db86aabSstevel 	 */
12823db86aabSstevel 	offset = ADDR_TO_OFFSET(addr, hdlp);
12833db86aabSstevel 	switch (offset) {
12843db86aabSstevel 	case LOMBUS_FAULT_REG:
12853db86aabSstevel 		/*
12863db86aabSstevel 		 * This meta-register contains a code for the most
12873db86aabSstevel 		 * recent virtual register access fault, if any.
12883db86aabSstevel 		 * It can be cleared simply by writing 0 to it.
12893db86aabSstevel 		 */
12903db86aabSstevel 		HANDLE_FAULT(hdlp) = val;
12913db86aabSstevel 		return;
12923db86aabSstevel 
12933db86aabSstevel 	case LOMBUS_PROBE_REG:
12943db86aabSstevel 		/*
12953db86aabSstevel 		 * Writing this meta-register clears any existing fault
12963db86aabSstevel 		 * (at the virtual, not the hardware acess layer), then
12973db86aabSstevel 		 * runs a NOP command.  The caller can check the fault
12983db86aabSstevel 		 * code later if required.
12993db86aabSstevel 		 */
13003db86aabSstevel 		HANDLE_FAULT(hdlp) = 0;
1301*07d06da5SSurya Prakki 		(void) lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
13023db86aabSstevel 		return;
13033db86aabSstevel 
13043db86aabSstevel 	default:
13053db86aabSstevel 		/*
13063db86aabSstevel 		 * Invalid access - flag a fault
13073db86aabSstevel 		 */
13083db86aabSstevel 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13093db86aabSstevel 		return;
13103db86aabSstevel 	}
13113db86aabSstevel }
13123db86aabSstevel 
13133db86aabSstevel static void
lombus_meta_rep_get32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)13143db86aabSstevel lombus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
13153db86aabSstevel 	uint32_t *dev_addr, size_t repcount, uint_t flags)
13163db86aabSstevel {
13173db86aabSstevel 	size_t inc;
13183db86aabSstevel 
13193db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
13203db86aabSstevel 	for (; repcount--; dev_addr += inc)
13213db86aabSstevel 		*host_addr++ = lombus_meta_get32(hdlp, dev_addr);
13223db86aabSstevel }
13233db86aabSstevel 
13243db86aabSstevel static void
lombus_meta_rep_put32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)13253db86aabSstevel lombus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
13263db86aabSstevel 	uint32_t *dev_addr, size_t repcount, uint_t flags)
13273db86aabSstevel {
13283db86aabSstevel 	size_t inc;
13293db86aabSstevel 
13303db86aabSstevel 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
13313db86aabSstevel 	for (; repcount--; dev_addr += inc)
13323db86aabSstevel 		lombus_meta_put32(hdlp, dev_addr, *host_addr++);
13333db86aabSstevel }
13343db86aabSstevel 
13353db86aabSstevel 
13363db86aabSstevel /*
13373db86aabSstevel  * Finally, some dummy functions for all unsupported access
13383db86aabSstevel  * space/size/mode combinations ...
13393db86aabSstevel  */
13403db86aabSstevel static uint8_t
lombus_no_get8(HANDLE_TYPE * hdlp,uint8_t * addr)13413db86aabSstevel lombus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
13423db86aabSstevel {
13433db86aabSstevel 	_NOTE(ARGUNUSED(addr))
13443db86aabSstevel 
13453db86aabSstevel 	/*
13463db86aabSstevel 	 * Invalid access - flag a fault and return a dummy value
13473db86aabSstevel 	 */
13483db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13493db86aabSstevel 	return (DUMMY_VALUE);
13503db86aabSstevel }
13513db86aabSstevel 
13523db86aabSstevel static void
lombus_no_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)13533db86aabSstevel lombus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
13543db86aabSstevel {
13553db86aabSstevel 	_NOTE(ARGUNUSED(addr, val))
13563db86aabSstevel 
13573db86aabSstevel 	/*
13583db86aabSstevel 	 * Invalid access - flag a fault
13593db86aabSstevel 	 */
13603db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13613db86aabSstevel }
13623db86aabSstevel 
13633db86aabSstevel static void
lombus_no_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)13643db86aabSstevel lombus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
13653db86aabSstevel 		uint8_t *dev_addr, size_t repcount, uint_t flags)
13663db86aabSstevel {
13673db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
13683db86aabSstevel 
13693db86aabSstevel 	/*
13703db86aabSstevel 	 * Invalid access - flag a fault
13713db86aabSstevel 	 */
13723db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13733db86aabSstevel }
13743db86aabSstevel 
13753db86aabSstevel static void
lombus_no_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)13763db86aabSstevel lombus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
13773db86aabSstevel 	uint8_t *dev_addr, size_t repcount, uint_t flags)
13783db86aabSstevel {
13793db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
13803db86aabSstevel 
13813db86aabSstevel 	/*
13823db86aabSstevel 	 * Invalid access - flag a fault
13833db86aabSstevel 	 */
13843db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13853db86aabSstevel }
13863db86aabSstevel 
13873db86aabSstevel static uint16_t
lombus_no_get16(HANDLE_TYPE * hdlp,uint16_t * addr)13883db86aabSstevel lombus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
13893db86aabSstevel {
13903db86aabSstevel 	_NOTE(ARGUNUSED(addr))
13913db86aabSstevel 
13923db86aabSstevel 	/*
13933db86aabSstevel 	 * Invalid access - flag a fault and return a dummy value
13943db86aabSstevel 	 */
13953db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
13963db86aabSstevel 	return (DUMMY_VALUE);
13973db86aabSstevel }
13983db86aabSstevel 
13993db86aabSstevel static void
lombus_no_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)14003db86aabSstevel lombus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
14013db86aabSstevel {
14023db86aabSstevel 	_NOTE(ARGUNUSED(addr, val))
14033db86aabSstevel 
14043db86aabSstevel 	/*
14053db86aabSstevel 	 * Invalid access - flag a fault
14063db86aabSstevel 	 */
14073db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14083db86aabSstevel }
14093db86aabSstevel 
14103db86aabSstevel static void
lombus_no_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)14113db86aabSstevel lombus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
14123db86aabSstevel 		uint16_t *dev_addr, size_t repcount, uint_t flags)
14133db86aabSstevel {
14143db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14153db86aabSstevel 
14163db86aabSstevel 	/*
14173db86aabSstevel 	 * Invalid access - flag a fault
14183db86aabSstevel 	 */
14193db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14203db86aabSstevel }
14213db86aabSstevel 
14223db86aabSstevel static void
lombus_no_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)14233db86aabSstevel lombus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
14243db86aabSstevel 	uint16_t *dev_addr, size_t repcount, uint_t flags)
14253db86aabSstevel {
14263db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14273db86aabSstevel 
14283db86aabSstevel 	/*
14293db86aabSstevel 	 * Invalid access - flag a fault
14303db86aabSstevel 	 */
14313db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14323db86aabSstevel }
14333db86aabSstevel 
14343db86aabSstevel static uint64_t
lombus_no_get64(HANDLE_TYPE * hdlp,uint64_t * addr)14353db86aabSstevel lombus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr)
14363db86aabSstevel {
14373db86aabSstevel 	_NOTE(ARGUNUSED(addr))
14383db86aabSstevel 
14393db86aabSstevel 	/*
14403db86aabSstevel 	 * Invalid access - flag a fault and return a dummy value
14413db86aabSstevel 	 */
14423db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14433db86aabSstevel 	return (DUMMY_VALUE);
14443db86aabSstevel }
14453db86aabSstevel 
14463db86aabSstevel static void
lombus_no_put64(HANDLE_TYPE * hdlp,uint64_t * addr,uint64_t val)14473db86aabSstevel lombus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val)
14483db86aabSstevel {
14493db86aabSstevel 	_NOTE(ARGUNUSED(addr, val))
14503db86aabSstevel 
14513db86aabSstevel 	/*
14523db86aabSstevel 	 * Invalid access - flag a fault
14533db86aabSstevel 	 */
14543db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14553db86aabSstevel }
14563db86aabSstevel 
14573db86aabSstevel static void
lombus_no_rep_get64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)14583db86aabSstevel lombus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
14593db86aabSstevel 	uint64_t *dev_addr, size_t repcount, uint_t flags)
14603db86aabSstevel {
14613db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14623db86aabSstevel 
14633db86aabSstevel 	/*
14643db86aabSstevel 	 * Invalid access - flag a fault
14653db86aabSstevel 	 */
14663db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14673db86aabSstevel }
14683db86aabSstevel 
14693db86aabSstevel static void
lombus_no_rep_put64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)14703db86aabSstevel lombus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
14713db86aabSstevel 	uint64_t *dev_addr, size_t repcount, uint_t flags)
14723db86aabSstevel {
14733db86aabSstevel 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
14743db86aabSstevel 
14753db86aabSstevel 	/*
14763db86aabSstevel 	 * Invalid access - flag a fault
14773db86aabSstevel 	 */
14783db86aabSstevel 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
14793db86aabSstevel }
14803db86aabSstevel 
14813db86aabSstevel static int
lombus_acc_fault_check(HANDLE_TYPE * hdlp)14823db86aabSstevel lombus_acc_fault_check(HANDLE_TYPE *hdlp)
14833db86aabSstevel {
14843db86aabSstevel 	return (HANDLE_FAULT(hdlp) != 0);
14853db86aabSstevel }
14863db86aabSstevel 
14873db86aabSstevel 
14883db86aabSstevel /*
14893db86aabSstevel  * Hardware setup - put the SIO chip in the required operational
14903db86aabSstevel  * state,  with all our favourite parameters programmed correctly.
14913db86aabSstevel  * This routine leaves all SIO interrupts disabled.
14923db86aabSstevel  */
14933db86aabSstevel 
14943db86aabSstevel static void
lombus_hw_reset(struct lombus_state * ssp)14953db86aabSstevel lombus_hw_reset(struct lombus_state *ssp)
14963db86aabSstevel {
14973db86aabSstevel 	uint16_t divisor;
14983db86aabSstevel 
14993db86aabSstevel 	/*
15003db86aabSstevel 	 * Disable interrupts, soft reset Tx and Rx circuitry,
15013db86aabSstevel 	 * reselect standard modes (bits/char, parity, etc).
15023db86aabSstevel 	 */
15033db86aabSstevel 	lombus_set_irq(ssp, B_FALSE);
15043db86aabSstevel 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR);
15053db86aabSstevel 	sio_put_reg(ssp, SIO_LCR, SIO_LCR_STD);
15063db86aabSstevel 
15073db86aabSstevel 	/*
15083db86aabSstevel 	 * Select the proper baud rate; if the value is invalid
15093db86aabSstevel 	 * (presumably 0, i.e. not specified, but also if the
15103db86aabSstevel 	 * "baud" property is set to some silly value), we assume
15113db86aabSstevel 	 * the default.
15123db86aabSstevel 	 */
15133db86aabSstevel 	if (ssp->baud < SIO_BAUD_MIN || ssp->baud > SIO_BAUD_MAX)
15143db86aabSstevel 		divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT);
15153db86aabSstevel 	else
15163db86aabSstevel 		divisor = SIO_BAUD_TO_DIVISOR(ssp->baud);
15173db86aabSstevel 
15183db86aabSstevel 	/*
15193db86aabSstevel 	 * According to the datasheet, it is forbidden for the divisor
15203db86aabSstevel 	 * register to be zero.  So when loading the register in two
15213db86aabSstevel 	 * steps, we have to make sure that the temporary value formed
15223db86aabSstevel 	 * between loads is nonzero.  However, we can't rely on either
15233db86aabSstevel 	 * half already having a nonzero value, as the datasheet also
15243db86aabSstevel 	 * says that these registers are indeterminate after a reset!
15253db86aabSstevel 	 * So, we explicitly set the low byte to a non-zero value first;
15263db86aabSstevel 	 * then we can safely load the high byte, and then the correct
15273db86aabSstevel 	 * value for the low byte, without the result ever being zero.
15283db86aabSstevel 	 */
15293db86aabSstevel 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK1);
15303db86aabSstevel 	sio_put_reg(ssp, SIO_LBGDL, 0xff);
15313db86aabSstevel 	sio_put_reg(ssp, SIO_LBGDH, divisor >> 8);
15323db86aabSstevel 	sio_put_reg(ssp, SIO_LBGDL, divisor & 0xff);
15333db86aabSstevel 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK0);
15343db86aabSstevel 
15353db86aabSstevel 	/*
15363db86aabSstevel 	 * Program the remaining device registers as required
15373db86aabSstevel 	 */
15383db86aabSstevel 	sio_put_reg(ssp, SIO_MCR, SIO_MCR_STD);
15393db86aabSstevel 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_STD);
15403db86aabSstevel }
15413db86aabSstevel 
15423db86aabSstevel 
15433db86aabSstevel /*
15443db86aabSstevel  * Higher-level setup & teardown
15453db86aabSstevel  */
15463db86aabSstevel 
15473db86aabSstevel static void
lombus_offline(struct lombus_state * ssp)15483db86aabSstevel lombus_offline(struct lombus_state *ssp)
15493db86aabSstevel {
15503db86aabSstevel 	if (ssp->sio_handle != NULL)
15513db86aabSstevel 		ddi_regs_map_free(&ssp->sio_handle);
15523db86aabSstevel 	ssp->sio_handle = NULL;
15533db86aabSstevel 	ssp->sio_regs = NULL;
15543db86aabSstevel }
15553db86aabSstevel 
15563db86aabSstevel static int
lombus_online(struct lombus_state * ssp)15573db86aabSstevel lombus_online(struct lombus_state *ssp)
15583db86aabSstevel {
15593db86aabSstevel 	ddi_acc_handle_t h;
15603db86aabSstevel 	caddr_t p;
15613db86aabSstevel 	int nregs;
15623db86aabSstevel 	int err;
15633db86aabSstevel 
15643db86aabSstevel 	if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS)
15653db86aabSstevel 		nregs = 0;
15663db86aabSstevel 
15673db86aabSstevel 	switch (nregs) {
15683db86aabSstevel 	default:
15693db86aabSstevel 	case 1:
15703db86aabSstevel 		/*
15713db86aabSstevel 		 *  regset 0 represents the SIO operating registers
15723db86aabSstevel 		 */
15733db86aabSstevel 		err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0,
15743db86aabSstevel 		    lombus_dev_acc_attr, &h);
15753db86aabSstevel 		lombus_trace(ssp, 'O', "online",
15763db86aabSstevel 		    "regmap 0 status %d addr $%p", err, p);
15773db86aabSstevel 		if (err != DDI_SUCCESS)
15783db86aabSstevel 			return (EIO);
15793db86aabSstevel 
15803db86aabSstevel 		ssp->sio_handle = h;
15813db86aabSstevel 		ssp->sio_regs = (void *)p;
15823db86aabSstevel 		break;
15833db86aabSstevel 
15843db86aabSstevel 	case 0:
15853db86aabSstevel 		/*
15863db86aabSstevel 		 *  If no registers are defined, succeed vacuously;
15873db86aabSstevel 		 *  commands will be accepted, but we fake the accesses.
15883db86aabSstevel 		 */
15893db86aabSstevel 		break;
15903db86aabSstevel 	}
15913db86aabSstevel 
15923db86aabSstevel 	/*
15933db86aabSstevel 	 * Now that the registers are mapped, we can initialise the SIO h/w
15943db86aabSstevel 	 */
15953db86aabSstevel 	lombus_hw_reset(ssp);
15963db86aabSstevel 	return (0);
15973db86aabSstevel }
15983db86aabSstevel 
15993db86aabSstevel 
16003db86aabSstevel /*
16013db86aabSstevel  *  Nexus routines
16023db86aabSstevel  */
16033db86aabSstevel 
16043db86aabSstevel #if	defined(NDI_ACC_HDL_V2)
16053db86aabSstevel 
16063db86aabSstevel static const ndi_acc_fns_t lombus_vreg_acc_fns = {
16073db86aabSstevel 	NDI_ACC_FNS_CURRENT,
16083db86aabSstevel 	NDI_ACC_FNS_V1,
16093db86aabSstevel 
16103db86aabSstevel 	lombus_vreg_get8,
16113db86aabSstevel 	lombus_vreg_put8,
16123db86aabSstevel 	lombus_vreg_rep_get8,
16133db86aabSstevel 	lombus_vreg_rep_put8,
16143db86aabSstevel 
16153db86aabSstevel 	lombus_no_get16,
16163db86aabSstevel 	lombus_no_put16,
16173db86aabSstevel 	lombus_no_rep_get16,
16183db86aabSstevel 	lombus_no_rep_put16,
16193db86aabSstevel 
16203db86aabSstevel 	lombus_meta_get32,
16213db86aabSstevel 	lombus_meta_put32,
16223db86aabSstevel 	lombus_meta_rep_get32,
16233db86aabSstevel 	lombus_meta_rep_put32,
16243db86aabSstevel 
16253db86aabSstevel 	lombus_no_get64,
16263db86aabSstevel 	lombus_no_put64,
16273db86aabSstevel 	lombus_no_rep_get64,
16283db86aabSstevel 	lombus_no_rep_put64,
16293db86aabSstevel 
16303db86aabSstevel 	lombus_acc_fault_check
16313db86aabSstevel };
16323db86aabSstevel 
16333db86aabSstevel static const ndi_acc_fns_t lombus_pat_acc_fns = {
16343db86aabSstevel 	NDI_ACC_FNS_CURRENT,
16353db86aabSstevel 	NDI_ACC_FNS_V1,
16363db86aabSstevel 
16373db86aabSstevel 	lombus_pat_get8,
16383db86aabSstevel 	lombus_pat_put8,
16393db86aabSstevel 	lombus_pat_rep_get8,
16403db86aabSstevel 	lombus_pat_rep_put8,
16413db86aabSstevel 
16423db86aabSstevel 	lombus_no_get16,
16433db86aabSstevel 	lombus_no_put16,
16443db86aabSstevel 	lombus_no_rep_get16,
16453db86aabSstevel 	lombus_no_rep_put16,
16463db86aabSstevel 
16473db86aabSstevel 	lombus_meta_get32,
16483db86aabSstevel 	lombus_meta_put32,
16493db86aabSstevel 	lombus_meta_rep_get32,
16503db86aabSstevel 	lombus_meta_rep_put32,
16513db86aabSstevel 
16523db86aabSstevel 	lombus_no_get64,
16533db86aabSstevel 	lombus_no_put64,
16543db86aabSstevel 	lombus_no_rep_get64,
16553db86aabSstevel 	lombus_no_rep_put64,
16563db86aabSstevel 
16573db86aabSstevel 	lombus_acc_fault_check
16583db86aabSstevel };
16593db86aabSstevel 
16603db86aabSstevel static const ndi_acc_fns_t lombus_event_acc_fns = {
16613db86aabSstevel 	NDI_ACC_FNS_CURRENT,
16623db86aabSstevel 	NDI_ACC_FNS_V1,
16633db86aabSstevel 
16643db86aabSstevel 	lombus_no_get8,
16653db86aabSstevel 	lombus_no_put8,
16663db86aabSstevel 	lombus_no_rep_get8,
16673db86aabSstevel 	lombus_no_rep_put8,
16683db86aabSstevel 
16693db86aabSstevel 	lombus_event_get16,
16703db86aabSstevel 	lombus_event_put16,
16713db86aabSstevel 	lombus_event_rep_get16,
16723db86aabSstevel 	lombus_event_rep_put16,
16733db86aabSstevel 
16743db86aabSstevel 	lombus_meta_get32,
16753db86aabSstevel 	lombus_meta_put32,
16763db86aabSstevel 	lombus_meta_rep_get32,
16773db86aabSstevel 	lombus_meta_rep_put32,
16783db86aabSstevel 
16793db86aabSstevel 	lombus_no_get64,
16803db86aabSstevel 	lombus_no_put64,
16813db86aabSstevel 	lombus_no_rep_get64,
16823db86aabSstevel 	lombus_no_rep_put64,
16833db86aabSstevel 
16843db86aabSstevel 	lombus_acc_fault_check
16853db86aabSstevel };
16863db86aabSstevel 
16873db86aabSstevel 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)16883db86aabSstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
16893db86aabSstevel 	int space, caddr_t vaddr, off_t len,
16903db86aabSstevel 	ndi_acc_handle_t *hdlp, caddr_t *addrp)
16913db86aabSstevel {
16923db86aabSstevel 	switch (op) {
16933db86aabSstevel 	default:
16943db86aabSstevel 		return (DDI_ME_UNIMPLEMENTED);
16953db86aabSstevel 
16963db86aabSstevel 	case DDI_MO_MAP_LOCKED:
16973db86aabSstevel 		switch (space) {
16983db86aabSstevel 		default:
16993db86aabSstevel 			return (DDI_ME_REGSPEC_RANGE);
17003db86aabSstevel 
17013db86aabSstevel 		case LOMBUS_VREG_SPACE:
17023db86aabSstevel 			ndi_set_acc_fns(hdlp, &lombus_vreg_acc_fns);
17033db86aabSstevel 			break;
17043db86aabSstevel 
17053db86aabSstevel 		case LOMBUS_PAT_SPACE:
17063db86aabSstevel 			ndi_set_acc_fns(hdlp, &lombus_pat_acc_fns);
17073db86aabSstevel 			break;
17083db86aabSstevel 
17093db86aabSstevel 		case LOMBUS_EVENT_SPACE:
17103db86aabSstevel 			ndi_set_acc_fns(hdlp, &lombus_event_acc_fns);
17113db86aabSstevel 			break;
17123db86aabSstevel 		}
17133db86aabSstevel 		hdlp->ah_addr = *addrp = vaddr;
17143db86aabSstevel 		hdlp->ah_len = len;
17153db86aabSstevel 		hdlp->ah_bus_private = ssp;
17163db86aabSstevel 		return (DDI_SUCCESS);
17173db86aabSstevel 
17183db86aabSstevel 	case DDI_MO_UNMAP:
17193db86aabSstevel 		*addrp = NULL;
17203db86aabSstevel 		hdlp->ah_bus_private = NULL;
17213db86aabSstevel 		return (DDI_SUCCESS);
17223db86aabSstevel 	}
17233db86aabSstevel }
17243db86aabSstevel 
17253db86aabSstevel #else
17263db86aabSstevel 
17273db86aabSstevel 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)17283db86aabSstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
17293db86aabSstevel 	int space, caddr_t vaddr, off_t len,
17303db86aabSstevel 	ddi_acc_hdl_t *hdlp, caddr_t *addrp)
17313db86aabSstevel {
17323db86aabSstevel 	ddi_acc_impl_t *aip = hdlp->ah_platform_private;
17333db86aabSstevel 
17343db86aabSstevel 	switch (op) {
17353db86aabSstevel 	default:
17363db86aabSstevel 		return (DDI_ME_UNIMPLEMENTED);
17373db86aabSstevel 
17383db86aabSstevel 	case DDI_MO_MAP_LOCKED:
17393db86aabSstevel 		switch (space) {
17403db86aabSstevel 		default:
17413db86aabSstevel 			return (DDI_ME_REGSPEC_RANGE);
17423db86aabSstevel 
17433db86aabSstevel 		case LOMBUS_VREG_SPACE:
17443db86aabSstevel 			aip->ahi_get8 = lombus_vreg_get8;
17453db86aabSstevel 			aip->ahi_put8 = lombus_vreg_put8;
17463db86aabSstevel 			aip->ahi_rep_get8 = lombus_vreg_rep_get8;
17473db86aabSstevel 			aip->ahi_rep_put8 = lombus_vreg_rep_put8;
17483db86aabSstevel 
17493db86aabSstevel 			aip->ahi_get16 = lombus_no_get16;
17503db86aabSstevel 			aip->ahi_put16 = lombus_no_put16;
17513db86aabSstevel 			aip->ahi_rep_get16 = lombus_no_rep_get16;
17523db86aabSstevel 			aip->ahi_rep_put16 = lombus_no_rep_put16;
17533db86aabSstevel 
17543db86aabSstevel 			aip->ahi_get32 = lombus_meta_get32;
17553db86aabSstevel 			aip->ahi_put32 = lombus_meta_put32;
17563db86aabSstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
17573db86aabSstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
17583db86aabSstevel 
17593db86aabSstevel 			aip->ahi_get64 = lombus_no_get64;
17603db86aabSstevel 			aip->ahi_put64 = lombus_no_put64;
17613db86aabSstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
17623db86aabSstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
17633db86aabSstevel 
17643db86aabSstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
17653db86aabSstevel 			break;
17663db86aabSstevel 
17673db86aabSstevel 		case LOMBUS_PAT_SPACE:
17683db86aabSstevel 			aip->ahi_get8 = lombus_pat_get8;
17693db86aabSstevel 			aip->ahi_put8 = lombus_pat_put8;
17703db86aabSstevel 			aip->ahi_rep_get8 = lombus_pat_rep_get8;
17713db86aabSstevel 			aip->ahi_rep_put8 = lombus_pat_rep_put8;
17723db86aabSstevel 
17733db86aabSstevel 			aip->ahi_get16 = lombus_no_get16;
17743db86aabSstevel 			aip->ahi_put16 = lombus_no_put16;
17753db86aabSstevel 			aip->ahi_rep_get16 = lombus_no_rep_get16;
17763db86aabSstevel 			aip->ahi_rep_put16 = lombus_no_rep_put16;
17773db86aabSstevel 
17783db86aabSstevel 			aip->ahi_get32 = lombus_meta_get32;
17793db86aabSstevel 			aip->ahi_put32 = lombus_meta_put32;
17803db86aabSstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
17813db86aabSstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
17823db86aabSstevel 
17833db86aabSstevel 			aip->ahi_get64 = lombus_no_get64;
17843db86aabSstevel 			aip->ahi_put64 = lombus_no_put64;
17853db86aabSstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
17863db86aabSstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
17873db86aabSstevel 
17883db86aabSstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
17893db86aabSstevel 			break;
17903db86aabSstevel 
17913db86aabSstevel 		case LOMBUS_EVENT_SPACE:
17923db86aabSstevel 			aip->ahi_get8 = lombus_no_get8;
17933db86aabSstevel 			aip->ahi_put8 = lombus_no_put8;
17943db86aabSstevel 			aip->ahi_rep_get8 = lombus_no_rep_get8;
17953db86aabSstevel 			aip->ahi_rep_put8 = lombus_no_rep_put8;
17963db86aabSstevel 
17973db86aabSstevel 			aip->ahi_get16 = lombus_event_get16;
17983db86aabSstevel 			aip->ahi_put16 = lombus_event_put16;
17993db86aabSstevel 			aip->ahi_rep_get16 = lombus_event_rep_get16;
18003db86aabSstevel 			aip->ahi_rep_put16 = lombus_event_rep_put16;
18013db86aabSstevel 
18023db86aabSstevel 			aip->ahi_get32 = lombus_meta_get32;
18033db86aabSstevel 			aip->ahi_put32 = lombus_meta_put32;
18043db86aabSstevel 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
18053db86aabSstevel 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
18063db86aabSstevel 
18073db86aabSstevel 			aip->ahi_get64 = lombus_no_get64;
18083db86aabSstevel 			aip->ahi_put64 = lombus_no_put64;
18093db86aabSstevel 			aip->ahi_rep_get64 = lombus_no_rep_get64;
18103db86aabSstevel 			aip->ahi_rep_put64 = lombus_no_rep_put64;
18113db86aabSstevel 
18123db86aabSstevel 			aip->ahi_fault_check = lombus_acc_fault_check;
18133db86aabSstevel 			break;
18143db86aabSstevel 		}
18153db86aabSstevel 		hdlp->ah_addr = *addrp = vaddr;
18163db86aabSstevel 		hdlp->ah_len = len;
18173db86aabSstevel 		hdlp->ah_bus_private = ssp;
18183db86aabSstevel 		return (DDI_SUCCESS);
18193db86aabSstevel 
18203db86aabSstevel 	case DDI_MO_UNMAP:
18213db86aabSstevel 		*addrp = NULL;
18223db86aabSstevel 		hdlp->ah_bus_private = NULL;
18233db86aabSstevel 		return (DDI_SUCCESS);
18243db86aabSstevel 	}
18253db86aabSstevel }
18263db86aabSstevel 
18273db86aabSstevel #endif	/* NDI_ACC_HDL_V2 */
18283db86aabSstevel 
18293db86aabSstevel 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)18303db86aabSstevel lombus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
18313db86aabSstevel 	off_t off, off_t len, caddr_t *addrp)
18323db86aabSstevel {
18333db86aabSstevel 	struct lombus_child_info *lcip;
18343db86aabSstevel 	struct lombus_state *ssp;
18353db86aabSstevel 	lombus_regspec_t *rsp;
18363db86aabSstevel 
18373db86aabSstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_map")) == NULL)
18383db86aabSstevel 		return (DDI_FAILURE);	/* this "can't happen" */
18393db86aabSstevel 
18403db86aabSstevel 	/*
18413db86aabSstevel 	 * Validate mapping request ...
18423db86aabSstevel 	 */
18433db86aabSstevel 
18443db86aabSstevel 	if (mp->map_flags != DDI_MF_KERNEL_MAPPING)
18453db86aabSstevel 		return (DDI_ME_UNSUPPORTED);
18463db86aabSstevel 	if (mp->map_handlep == NULL)
18473db86aabSstevel 		return (DDI_ME_UNSUPPORTED);
18483db86aabSstevel 	if (mp->map_type != DDI_MT_RNUMBER)
18493db86aabSstevel 		return (DDI_ME_UNIMPLEMENTED);
18503db86aabSstevel 	if ((lcip = ddi_get_parent_data(rdip)) == NULL)
18513db86aabSstevel 		return (DDI_ME_INVAL);
18523db86aabSstevel 	if ((rsp = lcip->rsp) == NULL)
18533db86aabSstevel 		return (DDI_ME_INVAL);
18543db86aabSstevel 	if (mp->map_obj.rnumber >= lcip->nregs)
18553db86aabSstevel 		return (DDI_ME_RNUMBER_RANGE);
18563db86aabSstevel 	rsp += mp->map_obj.rnumber;
18573db86aabSstevel 	if (off < 0 || off >= rsp->lombus_size)
18583db86aabSstevel 		return (DDI_ME_INVAL);
18593db86aabSstevel 	if (len == 0)
18603db86aabSstevel 		len = rsp->lombus_size-off;
18613db86aabSstevel 	if (len < 0)
18623db86aabSstevel 		return (DDI_ME_INVAL);
18633db86aabSstevel 	if (off+len < 0 || off+len > rsp->lombus_size)
18643db86aabSstevel 		return (DDI_ME_INVAL);
18653db86aabSstevel 
18663db86aabSstevel 	return (lombus_map_handle(ssp, mp->map_op,
18673db86aabSstevel 	    rsp->lombus_space, VREG_TO_ADDR(rsp->lombus_base+off), len,
18683db86aabSstevel 	    mp->map_handlep, addrp));
18693db86aabSstevel }
18703db86aabSstevel 
18713db86aabSstevel static int
lombus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)18723db86aabSstevel lombus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
18733db86aabSstevel 	void *arg, void *result)
18743db86aabSstevel {
18753db86aabSstevel 	struct lombus_child_info *lcip;
18763db86aabSstevel 	struct lombus_state *ssp;
18773db86aabSstevel 	lombus_regspec_t *rsp;
18783db86aabSstevel 	dev_info_t *cdip;
18793db86aabSstevel 	char addr[32];
18803db86aabSstevel 	uint_t nregs;
18813db86aabSstevel 	uint_t rnum;
18823db86aabSstevel 	int *regs;
18833db86aabSstevel 	int limit;
18843db86aabSstevel 	int err;
18853db86aabSstevel 	int i;
18863db86aabSstevel 
18873db86aabSstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_ctlops")) == NULL)
18883db86aabSstevel 		return (DDI_FAILURE);	/* this "can't happen" */
18893db86aabSstevel 
18903db86aabSstevel 	switch (op) {
18913db86aabSstevel 	default:
18923db86aabSstevel 		break;
18933db86aabSstevel 
18943db86aabSstevel 	case DDI_CTLOPS_INITCHILD:
18953db86aabSstevel 		/*
18963db86aabSstevel 		 * First, look up and validate the "reg" property.
18973db86aabSstevel 		 *
18983db86aabSstevel 		 * It must be a non-empty integer array containing a set
18993db86aabSstevel 		 * of triples.  Once we've verified that, we can treat it
19003db86aabSstevel 		 * as an array of type lombus_regspec_t[], which defines
19013db86aabSstevel 		 * the meaning of the elements of each triple:
19023db86aabSstevel 		 * +  the first element of each triple must be a valid space
19033db86aabSstevel 		 * +  the second and third elements (base, size) of each
19043db86aabSstevel 		 *	triple must define a valid subrange of that space
19053db86aabSstevel 		 * If it passes all the tests, we save it away for future
19063db86aabSstevel 		 * reference in the child's parent-private-data field.
19073db86aabSstevel 		 */
19083db86aabSstevel 		cdip = arg;
19093db86aabSstevel 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
19103db86aabSstevel 		    DDI_PROP_DONTPASS, "reg", &regs, &nregs);
19113db86aabSstevel 		lombus_trace(ssp, 'C', "initchild",
19123db86aabSstevel 		    "prop status %d size %d", err, nregs);
19133db86aabSstevel 		if (err != DDI_PROP_SUCCESS)
19143db86aabSstevel 			return (DDI_FAILURE);
19153db86aabSstevel 
19163db86aabSstevel 		err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0);
19173db86aabSstevel 		nregs /= LOMBUS_REGSPEC_SIZE;
19183db86aabSstevel 		rsp = (lombus_regspec_t *)regs;
19193db86aabSstevel 		for (i = 0; i < nregs && !err; ++i) {
19203db86aabSstevel 			switch (rsp[i].lombus_space) {
19213db86aabSstevel 			default:
19223db86aabSstevel 				limit = 0;
19233db86aabSstevel 				err = 1;
19243db86aabSstevel 				break;
19253db86aabSstevel 
19263db86aabSstevel 			case LOMBUS_VREG_SPACE:
19273db86aabSstevel 				limit = LOMBUS_MAX_REG+1;
19283db86aabSstevel 				break;
19293db86aabSstevel 
19303db86aabSstevel 			case LOMBUS_PAT_SPACE:
19313db86aabSstevel 				limit = LOMBUS_PAT_REG+1;
19323db86aabSstevel 				break;
19333db86aabSstevel 
19343db86aabSstevel 			case LOMBUS_EVENT_SPACE:
19353db86aabSstevel 				limit = LOMBUS_EVENT_REG+1;
19363db86aabSstevel 				break;
19373db86aabSstevel 			}
19383db86aabSstevel 
19393db86aabSstevel 			err |= (rsp[i].lombus_base < 0);
19403db86aabSstevel 			err |= (rsp[i].lombus_base >= limit);
19413db86aabSstevel 
19423db86aabSstevel 			if (rsp[i].lombus_size == 0)
19433db86aabSstevel 				rsp[i].lombus_size = limit-rsp[i].lombus_base;
19443db86aabSstevel 			err |= (rsp[i].lombus_size < 0);
19453db86aabSstevel 
19463db86aabSstevel 			err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0);
19473db86aabSstevel 			err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
19483db86aabSstevel 		}
19493db86aabSstevel 
19503db86aabSstevel 		if (err) {
19513db86aabSstevel 			ddi_prop_free(regs);
19523db86aabSstevel 			return (DDI_FAILURE);
19533db86aabSstevel 		}
19543db86aabSstevel 
19553db86aabSstevel 		lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP);
19563db86aabSstevel 		lcip->nregs = nregs;
19573db86aabSstevel 		lcip->rsp = rsp;
19583db86aabSstevel 		ddi_set_parent_data(cdip, lcip);
19593db86aabSstevel 
19603db86aabSstevel 		(void) snprintf(addr, sizeof (addr),
19613db86aabSstevel 		    "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base);
19623db86aabSstevel 		ddi_set_name_addr(cdip, addr);
19633db86aabSstevel 
19643db86aabSstevel 		return (DDI_SUCCESS);
19653db86aabSstevel 
19663db86aabSstevel 	case DDI_CTLOPS_UNINITCHILD:
19673db86aabSstevel 		cdip = arg;
19683db86aabSstevel 		ddi_set_name_addr(cdip, NULL);
19693db86aabSstevel 		lcip = ddi_get_parent_data(cdip);
19703db86aabSstevel 		ddi_set_parent_data(cdip, NULL);
19713db86aabSstevel 		ddi_prop_free(lcip->rsp);
19723db86aabSstevel 		kmem_free(lcip, sizeof (*lcip));
19733db86aabSstevel 		return (DDI_SUCCESS);
19743db86aabSstevel 
19753db86aabSstevel 	case DDI_CTLOPS_REPORTDEV:
19763db86aabSstevel 		if (rdip == NULL)
19773db86aabSstevel 			return (DDI_FAILURE);
19783db86aabSstevel 
19793db86aabSstevel 		cmn_err(CE_CONT, "?LOM device: %s@%s, %s#%d\n",
19803db86aabSstevel 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
19813db86aabSstevel 		    ddi_driver_name(dip), ddi_get_instance(dip));
19823db86aabSstevel 
19833db86aabSstevel 		return (DDI_SUCCESS);
19843db86aabSstevel 
19853db86aabSstevel 	case DDI_CTLOPS_REGSIZE:
19863db86aabSstevel 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
19873db86aabSstevel 			return (DDI_FAILURE);
19883db86aabSstevel 		if ((rnum = *(uint_t *)arg) >= lcip->nregs)
19893db86aabSstevel 			return (DDI_FAILURE);
19903db86aabSstevel 		*(off_t *)result = lcip->rsp[rnum].lombus_size;
19913db86aabSstevel 		return (DDI_SUCCESS);
19923db86aabSstevel 
19933db86aabSstevel 	case DDI_CTLOPS_NREGS:
19943db86aabSstevel 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
19953db86aabSstevel 			return (DDI_FAILURE);
19963db86aabSstevel 		*(int *)result = lcip->nregs;
19973db86aabSstevel 		return (DDI_SUCCESS);
19983db86aabSstevel 	}
19993db86aabSstevel 
20003db86aabSstevel 	return (ddi_ctlops(dip, rdip, op, arg, result));
20013db86aabSstevel }
20023db86aabSstevel 
20033db86aabSstevel 
20043db86aabSstevel /*
20053db86aabSstevel  *  Clean up on detach or failure of attach
20063db86aabSstevel  */
20073db86aabSstevel static int
lombus_unattach(struct lombus_state * ssp,int instance)20083db86aabSstevel lombus_unattach(struct lombus_state *ssp, int instance)
20093db86aabSstevel {
20103db86aabSstevel 	if (ssp != NULL) {
20113db86aabSstevel 		lombus_hw_reset(ssp);
2012dd4eeefdSeota 		if (ssp->cycid != NULL) {
2013dd4eeefdSeota 			ddi_periodic_delete(ssp->cycid);
2014dd4eeefdSeota 			ssp->cycid = NULL;
20153db86aabSstevel 			if (ssp->sio_handle != NULL)
20163db86aabSstevel 				ddi_remove_intr(ssp->dip, 0, ssp->hw_iblk);
20173db86aabSstevel 			ddi_remove_softintr(ssp->softid);
20183db86aabSstevel 			cv_destroy(ssp->lo_cv);
20193db86aabSstevel 			mutex_destroy(ssp->lo_mutex);
20203db86aabSstevel 			mutex_destroy(ssp->hw_mutex);
20213db86aabSstevel 		}
20223db86aabSstevel 		lombus_offline(ssp);
20233db86aabSstevel 		ddi_set_driver_private(ssp->dip, NULL);
20243db86aabSstevel 	}
20253db86aabSstevel 
20263db86aabSstevel 	ddi_soft_state_free(lombus_statep, instance);
20273db86aabSstevel 	return (DDI_FAILURE);
20283db86aabSstevel }
20293db86aabSstevel 
20303db86aabSstevel /*
20313db86aabSstevel  *  Autoconfiguration routines
20323db86aabSstevel  */
20333db86aabSstevel 
20343db86aabSstevel static int
lombus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)20353db86aabSstevel lombus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
20363db86aabSstevel {
20373db86aabSstevel 	struct lombus_state *ssp = NULL;
20383db86aabSstevel 	int instance;
20393db86aabSstevel 	int err;
20403db86aabSstevel 
20413db86aabSstevel 	switch (cmd) {
20423db86aabSstevel 	default:
20433db86aabSstevel 		return (DDI_FAILURE);
20443db86aabSstevel 
20453db86aabSstevel 	case DDI_ATTACH:
20463db86aabSstevel 		break;
20473db86aabSstevel 	}
20483db86aabSstevel 
20493db86aabSstevel 	/*
20503db86aabSstevel 	 *  Allocate the soft-state structure
20513db86aabSstevel 	 */
20523db86aabSstevel 	instance = ddi_get_instance(dip);
20533db86aabSstevel 	if (ddi_soft_state_zalloc(lombus_statep, instance) != DDI_SUCCESS)
20543db86aabSstevel 		return (DDI_FAILURE);
20553db86aabSstevel 	if ((ssp = lombus_getstate(dip, instance, "lombus_attach")) == NULL)
20563db86aabSstevel 		return (lombus_unattach(ssp, instance));
20573db86aabSstevel 	ddi_set_driver_private(dip, ssp);
20583db86aabSstevel 
20593db86aabSstevel 	/*
20603db86aabSstevel 	 *  Initialise devinfo-related fields
20613db86aabSstevel 	 */
20623db86aabSstevel 	ssp->dip = dip;
20633db86aabSstevel 	ssp->majornum = ddi_driver_major(dip);
20643db86aabSstevel 	ssp->instance = instance;
20653db86aabSstevel 
20663db86aabSstevel 	/*
20673db86aabSstevel 	 *  Set various options from .conf properties
20683db86aabSstevel 	 */
20693db86aabSstevel 	ssp->allow_echo = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20703db86aabSstevel 	    DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0;
20713db86aabSstevel 	ssp->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20723db86aabSstevel 	    DDI_PROP_DONTPASS, "baud-rate", 0);
20733db86aabSstevel 	ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20743db86aabSstevel 	    DDI_PROP_DONTPASS, "debug", 0);
20753db86aabSstevel 	ssp->fake_cts = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20763db86aabSstevel 	    DDI_PROP_DONTPASS, "fake-cts", 0) != 0;
20773db86aabSstevel 
20783db86aabSstevel 	/*
20793db86aabSstevel 	 * Initialise current state & time
20803db86aabSstevel 	 */
20813db86aabSstevel 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
20823db86aabSstevel 	ssp->hw_last_pat = gethrtime();
2083dd4eeefdSeota 	ssp->cycid = NULL;
20843db86aabSstevel 
20853db86aabSstevel 	/*
20863db86aabSstevel 	 *  Online the hardware ...
20873db86aabSstevel 	 */
20883db86aabSstevel 	err = lombus_online(ssp);
20893db86aabSstevel 	if (err != 0)
20903db86aabSstevel 		return (lombus_unattach(ssp, instance));
20913db86aabSstevel 
20923db86aabSstevel 	/*
20933db86aabSstevel 	 * Install soft and hard interrupt handler(s)
20943db86aabSstevel 	 * Initialise mutexes and cv
20953db86aabSstevel 	 * Start cyclic callbacks
20963db86aabSstevel 	 * Enable interrupts
20973db86aabSstevel 	 */
20983db86aabSstevel 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ssp->softid,
20993db86aabSstevel 	    &ssp->lo_iblk, NULL, lombus_softint, (caddr_t)ssp);
21003db86aabSstevel 	if (err != DDI_SUCCESS)
21013db86aabSstevel 		return (lombus_unattach(ssp, instance));
21023db86aabSstevel 
21033db86aabSstevel 	if (ssp->sio_handle != NULL)
21043db86aabSstevel 		err = ddi_add_intr(dip, 0, &ssp->hw_iblk, NULL,
21053db86aabSstevel 		    lombus_hi_intr, (caddr_t)ssp);
21063db86aabSstevel 
21073db86aabSstevel 	mutex_init(ssp->hw_mutex, NULL, MUTEX_DRIVER, ssp->hw_iblk);
21083db86aabSstevel 	mutex_init(ssp->lo_mutex, NULL, MUTEX_DRIVER, ssp->lo_iblk);
21093db86aabSstevel 	cv_init(ssp->lo_cv, NULL, CV_DRIVER, NULL);
21103db86aabSstevel 
2111dd4eeefdSeota 	/*
2112dd4eeefdSeota 	 * Register a periodical handler.
2113dd4eeefdSeota 	 */
2114dd4eeefdSeota 	ssp->cycid = ddi_periodic_add(lombus_cyclic, ssp, LOMBUS_ONE_SEC,
2115dd4eeefdSeota 	    DDI_IPL_1);
21163db86aabSstevel 
21173db86aabSstevel 	/*
21183db86aabSstevel 	 * Final check before enabling h/w interrupts - did
21193db86aabSstevel 	 * we successfully install the h/w interrupt handler?
21203db86aabSstevel 	 */
21213db86aabSstevel 	if (err != DDI_SUCCESS)
21223db86aabSstevel 		return (lombus_unattach(ssp, instance));
21233db86aabSstevel 
21243db86aabSstevel 	lombus_set_irq(ssp, B_TRUE);
21253db86aabSstevel 
21263db86aabSstevel 	/*
21273db86aabSstevel 	 *  All done, report success
21283db86aabSstevel 	 */
21293db86aabSstevel 	ddi_report_dev(dip);
21303db86aabSstevel 	return (DDI_SUCCESS);
21313db86aabSstevel }
21323db86aabSstevel 
21333db86aabSstevel 
21343db86aabSstevel static int
lombus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)21353db86aabSstevel lombus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
21363db86aabSstevel {
21373db86aabSstevel 	struct lombus_state *ssp;
21383db86aabSstevel 	int instance;
21393db86aabSstevel 
21403db86aabSstevel 	switch (cmd) {
21413db86aabSstevel 	default:
21423db86aabSstevel 		return (DDI_FAILURE);
21433db86aabSstevel 
21443db86aabSstevel 	case DDI_DETACH:
21453db86aabSstevel 		break;
21463db86aabSstevel 	}
21473db86aabSstevel 
21483db86aabSstevel 	instance = ddi_get_instance(dip);
21493db86aabSstevel 	if ((ssp = lombus_getstate(dip, instance, "lombus_detach")) == NULL)
21503db86aabSstevel 		return (DDI_FAILURE);	/* this "can't happen" */
21513db86aabSstevel 
21523db86aabSstevel 	(void) lombus_unattach(ssp, instance);
21533db86aabSstevel 	return (DDI_SUCCESS);
21543db86aabSstevel }
21553db86aabSstevel 
21563db86aabSstevel static int
lombus_reset(dev_info_t * dip,ddi_reset_cmd_t cmd)21573db86aabSstevel lombus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
21583db86aabSstevel {
21593db86aabSstevel 	struct lombus_state *ssp;
21603db86aabSstevel 
21613db86aabSstevel 	_NOTE(ARGUNUSED(cmd))
21623db86aabSstevel 
21633db86aabSstevel 	if ((ssp = lombus_getstate(dip, -1, "lombus_reset")) == NULL)
21643db86aabSstevel 		return (DDI_FAILURE);
21653db86aabSstevel 
21663db86aabSstevel 	lombus_hw_reset(ssp);
21673db86aabSstevel 	return (DDI_SUCCESS);
21683db86aabSstevel }
21693db86aabSstevel 
21703db86aabSstevel 
21713db86aabSstevel /*
21723db86aabSstevel  * System interface structures
21733db86aabSstevel  */
21743db86aabSstevel 
21753db86aabSstevel static struct cb_ops lombus_cb_ops =
21763db86aabSstevel {
21773db86aabSstevel 	nodev,			/* b/c open	*/
21783db86aabSstevel 	nodev,			/* b/c close	*/
21793db86aabSstevel 	nodev,			/* b   strategy	*/
21803db86aabSstevel 	nodev,			/* b   print	*/
21813db86aabSstevel 	nodev,			/* b   dump 	*/
21823db86aabSstevel 	nodev,			/* c   read	*/
21833db86aabSstevel 	nodev,			/* c   write	*/
21843db86aabSstevel 	nodev,			/* c   ioctl	*/
21853db86aabSstevel 	nodev,			/* c   devmap	*/
21863db86aabSstevel 	nodev,			/* c   mmap	*/
21873db86aabSstevel 	nodev,			/* c   segmap	*/
21883db86aabSstevel 	nochpoll,		/* c   poll	*/
21893db86aabSstevel 	ddi_prop_op,		/* b/c prop_op	*/
21903db86aabSstevel 	NULL,			/* c   streamtab */
21913db86aabSstevel 	D_MP | D_NEW		/* b/c flags	*/
21923db86aabSstevel };
21933db86aabSstevel 
21943db86aabSstevel static struct bus_ops lombus_bus_ops =
21953db86aabSstevel {
21963db86aabSstevel 	BUSO_REV,			/* revision		*/
21973db86aabSstevel 	lombus_map,			/* bus_map		*/
21983db86aabSstevel 	0,				/* get_intrspec		*/
21993db86aabSstevel 	0,				/* add_intrspec		*/
22003db86aabSstevel 	0,				/* remove_intrspec	*/
22013db86aabSstevel 	i_ddi_map_fault,		/* map_fault		*/
22023db86aabSstevel 	ddi_no_dma_map,			/* dma_map		*/
22033db86aabSstevel 	ddi_no_dma_allochdl,		/* allocate DMA handle	*/
22043db86aabSstevel 	ddi_no_dma_freehdl,		/* free DMA handle	*/
22053db86aabSstevel 	ddi_no_dma_bindhdl,		/* bind DMA handle	*/
22063db86aabSstevel 	ddi_no_dma_unbindhdl,		/* unbind DMA handle	*/
22073db86aabSstevel 	ddi_no_dma_flush,		/* flush DMA		*/
22083db86aabSstevel 	ddi_no_dma_win,			/* move DMA window	*/
22093db86aabSstevel 	ddi_no_dma_mctl,		/* generic DMA control	*/
22103db86aabSstevel 	lombus_ctlops,			/* generic control	*/
22113db86aabSstevel 	ddi_bus_prop_op,		/* prop_op		*/
22123db86aabSstevel 	ndi_busop_get_eventcookie,	/* get_eventcookie	*/
22133db86aabSstevel 	ndi_busop_add_eventcall,	/* add_eventcall	*/
22143db86aabSstevel 	ndi_busop_remove_eventcall,	/* remove_eventcall	*/
22153db86aabSstevel 	ndi_post_event,			/* post_event		*/
22163db86aabSstevel 	0,				/* interrupt control	*/
22173db86aabSstevel 	0,				/* bus_config		*/
22183db86aabSstevel 	0,				/* bus_unconfig		*/
22193db86aabSstevel 	0,				/* bus_fm_init		*/
22203db86aabSstevel 	0,				/* bus_fm_fini		*/
22213db86aabSstevel 	0,				/* bus_fm_access_enter	*/
22223db86aabSstevel 	0,				/* bus_fm_access_exit	*/
22233db86aabSstevel 	0,				/* bus_power		*/
22243db86aabSstevel 	i_ddi_intr_ops			/* bus_intr_op		*/
22253db86aabSstevel };
22263db86aabSstevel 
22273db86aabSstevel static struct dev_ops lombus_dev_ops =
22283db86aabSstevel {
22293db86aabSstevel 	DEVO_REV,
22303db86aabSstevel 	0,				/* refcount		*/
22313db86aabSstevel 	ddi_no_info,			/* getinfo		*/
22323db86aabSstevel 	nulldev,			/* identify		*/
22333db86aabSstevel 	nulldev,			/* probe		*/
22343db86aabSstevel 	lombus_attach,			/* attach		*/
22353db86aabSstevel 	lombus_detach,			/* detach		*/
22363db86aabSstevel 	lombus_reset,			/* reset		*/
22373db86aabSstevel 	&lombus_cb_ops,			/* driver operations	*/
223819397407SSherry Moore 	&lombus_bus_ops,		/* bus operations	*/
223919397407SSherry Moore 	NULL,				/* power		*/
224019397407SSherry Moore 	ddi_quiesce_not_supported,	/* devo_quiesce		*/
22413db86aabSstevel };
22423db86aabSstevel 
22433db86aabSstevel static struct modldrv modldrv =
22443db86aabSstevel {
22453db86aabSstevel 	&mod_driverops,
224619397407SSherry Moore 	"lombus driver",
22473db86aabSstevel 	&lombus_dev_ops
22483db86aabSstevel };
22493db86aabSstevel 
22503db86aabSstevel static struct modlinkage modlinkage =
22513db86aabSstevel {
22523db86aabSstevel 	MODREV_1,
22533db86aabSstevel 	{
22543db86aabSstevel 		&modldrv,
22553db86aabSstevel 		NULL
22563db86aabSstevel 	}
22573db86aabSstevel };
22583db86aabSstevel 
22593db86aabSstevel 
22603db86aabSstevel /*
22613db86aabSstevel  *  Dynamic loader interface code
22623db86aabSstevel  */
22633db86aabSstevel 
22643db86aabSstevel int
_init(void)22653db86aabSstevel _init(void)
22663db86aabSstevel {
22673db86aabSstevel 	int err;
22683db86aabSstevel 
22693db86aabSstevel 	err = ddi_soft_state_init(&lombus_statep,
22703db86aabSstevel 	    sizeof (struct lombus_state), 0);
22713db86aabSstevel 	if (err == DDI_SUCCESS)
22723db86aabSstevel 		if ((err = mod_install(&modlinkage)) != 0) {
22733db86aabSstevel 			ddi_soft_state_fini(&lombus_statep);
22743db86aabSstevel 		}
22753db86aabSstevel 
22763db86aabSstevel 	return (err);
22773db86aabSstevel }
22783db86aabSstevel 
22793db86aabSstevel int
_info(struct modinfo * mip)22803db86aabSstevel _info(struct modinfo *mip)
22813db86aabSstevel {
22823db86aabSstevel 	return (mod_info(&modlinkage, mip));
22833db86aabSstevel }
22843db86aabSstevel 
22853db86aabSstevel int
_fini(void)22863db86aabSstevel _fini(void)
22873db86aabSstevel {
22883db86aabSstevel 	int err;
22893db86aabSstevel 
22903db86aabSstevel 	if ((err = mod_remove(&modlinkage)) == 0) {
22913db86aabSstevel 		ddi_soft_state_fini(&lombus_statep);
22923db86aabSstevel 		lombus_major = NOMAJOR;
22933db86aabSstevel 	}
22943db86aabSstevel 
22953db86aabSstevel 	return (err);
22963db86aabSstevel }
2297