xref: /illumos-gate/usr/src/uts/sun4u/lw2plus/io/lombus.c (revision 9ec394dbf343c1f23c6e13c39df427f238e5a369)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  *
25  * The "lombus" driver provides access to the LOMlite2 virtual registers,
26  * so that its clients (children) need not be concerned with the details
27  * of the access mechanism, which in this case is implemented via a
28  * packet-based protocol over a serial link connected to one of the serial
29  * ports of the SuperIO (SIO) chip.
30  *
31  * On the other hand, this driver doesn't generally know what the virtual
32  * registers signify - only the clients need this information.
33  */
34 
35 #pragma ident	"%Z%%M%	%I%	%E% SMI"
36 
37 /*
38  *  Header files
39  */
40 
41 #include <sys/types.h>
42 #include <sys/conf.h>
43 #include <sys/debug.h>
44 #include <sys/errno.h>
45 #include <sys/file.h>
46 #include <sys/intr.h>
47 #include <sys/kmem.h>
48 #include <sys/membar.h>
49 #include <sys/modctl.h>
50 #include <sys/note.h>
51 #include <sys/open.h>
52 #include <sys/poll.h>
53 #include <sys/spl.h>
54 #include <sys/stat.h>
55 #include <sys/strlog.h>
56 
57 #include <sys/ddi.h>
58 #include <sys/sunddi.h>
59 #include <sys/sunndi.h>
60 
61 #include <sys/lombus.h>
62 
63 
64 #if	defined(NDI_ACC_HDL_V2)
65 
66 /*
67  * Compiling for Solaris 9+ with access handle enhancements
68  */
69 #define	HANDLE_TYPE		ndi_acc_handle_t
70 #define	HANDLE_ADDR(hdlp)	(hdlp->ah_addr)
71 #define	HANDLE_FAULT(hdlp)	(hdlp->ah_fault)
72 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ah_len)
73 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ah_bus_private)
74 
75 #else
76 
77 /*
78  * Compatibility definitions for backport to Solaris 8
79  */
80 #define	HANDLE_TYPE		ddi_acc_impl_t
81 #define	HANDLE_ADDR(hdlp)	(hdlp->ahi_common.ah_addr)
82 #define	HANDLE_FAULT(hdlp)	(hdlp->ahi_fault)
83 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ahi_common.ah_len)
84 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ahi_common.ah_bus_private)
85 
86 #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
87 
88 #endif	/* NDI_ACC_HDL_V2 */
89 
90 
91 /*
92  * Local definitions
93  */
94 #define	MYNAME			"lombus"
95 #define	NOMAJOR			(~(major_t)0)
96 #define	DUMMY_VALUE		(~(int8_t)0)
97 
98 #define	LOMBUS_INST_TO_MINOR(i)	(i)
99 #define	LOMBUS_MINOR_TO_INST(m)	(m)
100 
101 #define	LOMBUS_DUMMY_ADDRESS	((caddr_t)0x0CADD1ED)
102 #define	ADDR_TO_OFFSET(a, hdlp)	((caddr_t)(a) - HANDLE_ADDR(hdlp))
103 #define	ADDR_TO_VREG(a)		((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS)
104 #define	VREG_TO_ADDR(v)		(LOMBUS_DUMMY_ADDRESS + (v))
105 
106 
107 /*
108  * The following definitions are taken from the datasheet
109  * for the National Semiconductor PC87317 (SuperIO) chip.
110  *
111  * This chip implements UART functionality as logical device 6.
112  * It provides all sorts of wierd modes and extensions, but we
113  * have chosen to use only the 16550-compatible features
114  * ("non-extended mode").
115  *
116  * Hardware: serial chip register numbers
117  */
118 #define	SIO_RXD			0	/* read		*/
119 #define	SIO_TXD			0	/* write	*/
120 #define	SIO_IER			1
121 #define	SIO_EIR			2	/* read		*/
122 #define	SIO_FCR			2	/* write	*/
123 #define	SIO_LCR			3
124 #define	SIO_BSR			3	/* wierd	*/
125 #define	SIO_MCR			4
126 #define	SIO_LSR			5
127 #define	SIO_MSR			6
128 #define	SIO_SCR			7
129 
130 #define	SIO_LBGDL		0	/* bank 1	*/
131 #define	SIO_LBGDH		1	/* bank 1	*/
132 
133 /*
134  * Hardware: serial chip register bits
135  */
136 #define	SIO_IER_RXHDL_IE	0x01
137 #define	SIO_IER_STD		0x00
138 
139 #define	SIO_EIR_IPF		0x01
140 #define	SIO_EIR_IPR0		0x02
141 #define	SIO_EIR_IPR1		0x04
142 #define	SIO_EIR_RXFT		0x08
143 #define	SIO_EIR_FEN0		0x40
144 #define	SIO_EIR_FEN1		0x80
145 
146 #define	SIO_FCR_FIFO_EN		0x01
147 #define	SIO_FCR_RXSR		0x02
148 #define	SIO_FCR_TXSR		0x04
149 #define	SIO_FCR_RXFTH0		0x40
150 #define	SIO_FCR_RXFTH1		0x80
151 #define	SIO_FCR_STD		(SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN)
152 
153 #define	SIO_LCR_WLS0		0x01
154 #define	SIO_LCR_WLS1		0x02
155 #define	SIO_LCR_STB		0x04
156 #define	SIO_LCR_PEN		0x08
157 #define	SIO_LCR_EPS		0x10
158 #define	SIO_LCR_STKP		0x20
159 #define	SIO_LCR_SBRK		0x40
160 #define	SIO_LCR_BKSE		0x80
161 #define	SIO_LCR_8BIT		(SIO_LCR_WLS0|SIO_LCR_WLS1)
162 #define	SIO_LCR_EPAR		(SIO_LCR_PEN|SIO_LCR_EPS)
163 #define	SIO_LCR_STD		(SIO_LCR_8BIT|SIO_LCR_EPAR)
164 
165 #define	SIO_BSR_BANK0		(SIO_LCR_STD)
166 #define	SIO_BSR_BANK1		(SIO_LCR_BKSE|SIO_LCR_STD)
167 
168 #define	SIO_MCR_DTR		0x01
169 #define	SIO_MCR_RTS		0x02
170 #define	SIO_MCR_ISEN		0x08
171 #define	SIO_MCR_STD		(SIO_MCR_ISEN)
172 
173 #define	SIO_LSR_RXDA		0x01
174 #define	SIO_LSR_OE		0x02
175 #define	SIO_LSR_PE		0x04
176 #define	SIO_LSR_FE		0x08
177 #define	SIO_LSR_BRKE		0x10
178 #define	SIO_LSR_TXRDY		0x20
179 #define	SIO_LSR_TXEMP		0x40
180 #define	SIO_LSR_ER_INF		0x80
181 
182 #define	SIO_MSR_DCTS		0x01
183 #define	SIO_MSR_DDSR		0x02
184 #define	SIO_MSR_TERI		0x04
185 #define	SIO_MSR_DDCD		0x08
186 #define	SIO_MSR_CTS		0x10
187 #define	SIO_MSR_DSR		0x20
188 #define	SIO_MSR_RI		0x40
189 #define	SIO_MSR_DCD		0x80
190 
191 /*
192  * Min/max/default baud rates, and a macro to convert from a baud
193  * rate to the number (divisor) to put in the baud rate registers
194  */
195 #define	SIO_BAUD_MIN		50
196 #define	SIO_BAUD_MAX		115200
197 #define	SIO_BAUD_DEFAULT	38400
198 #define	SIO_BAUD_TO_DIVISOR(b)	(115200 / (b))
199 
200 
201 /*
202  * Packet format ...
203  */
204 #define	LOMBUS_MASK		0xc0	/* Byte-type bits		*/
205 #define	LOMBUS_PARAM		0x00	/* Parameter byte: 0b0xxxxxxx	*/
206 #define	LOMBUS_LAST		0x80	/* Last byte of packet		*/
207 #define	LOMBUS_CMD		0x80	/* Command byte:   0b10###XWV	*/
208 #define	LOMBUS_STATUS		0xc0	/* Status  byte:   0b11###AEV	*/
209 
210 #define	LOMBUS_SEQ		0x38	/* Sequence number bits		*/
211 #define	LOMBUS_SEQ_LSB		0x08	/* Sequence number LSB		*/
212 #define	LOMBUS_CMD_XADDR	0x04	/* Extended (2-byte) addressing	*/
213 #define	LOMBUS_CMD_WRITE	0x02	/* Write command		*/
214 #define	LOMBUS_CMD_WMSB		0x01	/* Set MSB on Write		*/
215 #define	LOMBUS_CMD_READ		0x01	/* Read command			*/
216 #define	LOMBUS_CMD_NOP		0x00	/* NOP command			*/
217 
218 #define	LOMBUS_STATUS_ASYNC	0x04	/* Asynchronous event pending	*/
219 #define	LOMBUS_STATUS_ERR	0x02	/* Error in command processing	*/
220 #define	LOMBUS_STATUS_MSB	0x01	/* MSB of Value read		*/
221 
222 #define	LOMBUS_VREG_LO(x)	((x) & ((1 << 7) - 1))
223 #define	LOMBUS_VREG_HI(x)	((x) >> 7)
224 
225 #define	LOMBUS_BUFSIZE		8
226 
227 
228 /*
229  * Time periods, in nanoseconds
230  *
231  * Note that LOMBUS_ONE_SEC and some other time
232  * periods are defined in <sys/lombus.h>
233  */
234 #define	LOMBUS_CMD_POLL		(LOMBUS_ONE_SEC/20)
235 #define	LOMBUS_CTS_POLL		(LOMBUS_ONE_SEC/20)
236 #define	LOMBUS_CTS_TIMEOUT	(LOMBUS_ONE_SEC*2)
237 
238 
239 /*
240  * Local datatypes
241  */
242 enum lombus_cmdstate {
243 	LOMBUS_CMDSTATE_IDLE,
244 	LOMBUS_CMDSTATE_BUSY,
245 	LOMBUS_CMDSTATE_WAITING,
246 	LOMBUS_CMDSTATE_READY,
247 	LOMBUS_CMDSTATE_ERROR
248 };
249 
250 
251 /*
252  * This driver's soft-state structure
253  */
254 
255 struct lombus_state {
256 	/*
257 	 * Configuration data, set during attach
258 	 */
259 	dev_info_t *dip;
260 	major_t majornum;
261 	int instance;
262 
263 	ddi_acc_handle_t sio_handle;
264 	uint8_t *sio_regs;
265 	ddi_softintr_t softid;
266 	ddi_periodic_t cycid; /* periodical callback */
267 
268 	/*
269 	 * Parameters derived from .conf properties
270 	 */
271 	boolean_t allow_echo;
272 	int baud;
273 	uint32_t debug;
274 	boolean_t fake_cts;
275 
276 	/*
277 	 * Hardware mutex (initialised using <hw_iblk>),
278 	 * used to prevent retriggering the softint while
279 	 * it's still fetching data out of the chip FIFO.
280 	 */
281 	kmutex_t hw_mutex[1];
282 	ddi_iblock_cookie_t hw_iblk;
283 
284 	/*
285 	 * Data protected by the hardware mutex: the watchdog-patting
286 	 * protocol data (since the dog can be patted from a high-level
287 	 * cyclic), and the interrupt-enabled flag.
288 	 */
289 	hrtime_t hw_last_pat;
290 	boolean_t hw_int_enabled;
291 
292 	/*
293 	 * Flag to indicate that we've incurred a hardware fault on
294 	 * accesses to the SIO; once this is set, we fake all further
295 	 * accesses in order not to provoke additional bus errors.
296 	 */
297 	boolean_t sio_fault;
298 
299 	/*
300 	 * Serial protocol state data, protected by lo_mutex
301 	 * (which is initialised using <lo_iblk>)
302 	 */
303 	kmutex_t lo_mutex[1];
304 	ddi_iblock_cookie_t lo_iblk;
305 	kcondvar_t lo_cv[1];
306 
307 	volatile enum lombus_cmdstate cmdstate;
308 	clock_t deadline;
309 	uint8_t cmdbuf[LOMBUS_BUFSIZE];
310 	uint8_t reply[LOMBUS_BUFSIZE];
311 	uint8_t async;
312 	uint8_t index;
313 	uint8_t result;
314 	uint8_t sequence;
315 	uint32_t error;
316 };
317 
318 /*
319  * The auxiliary structure attached to each child
320  * (the child's parent-private-data points to this).
321  */
322 struct lombus_child_info {
323 	lombus_regspec_t *rsp;
324 	int nregs;
325 };
326 
327 
328 /*
329  * Local data
330  */
331 
332 static void *lombus_statep;
333 
334 static major_t lombus_major = NOMAJOR;
335 
336 static ddi_device_acc_attr_t lombus_dev_acc_attr[1] =
337 {
338 	DDI_DEVICE_ATTR_V0,
339 	DDI_STRUCTURE_LE_ACC,
340 	DDI_STRICTORDER_ACC
341 };
342 
343 
344 /*
345  *  General utility routines ...
346  */
347 
348 static void
349 lombus_trace(struct lombus_state *ssp, char code, const char *caller,
350 	const char *fmt, ...)
351 {
352 	char buf[256];
353 	char *p;
354 	va_list va;
355 
356 	if (ssp->debug & (1 << (code-'@'))) {
357 		p = buf;
358 		snprintf(p, sizeof (buf) - (p - buf),
359 		    "%s/%s: ", MYNAME, caller);
360 		p += strlen(p);
361 
362 		va_start(va, fmt);
363 		vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
364 		va_end(va);
365 
366 		buf[sizeof (buf) - 1] = '\0';
367 		strlog(ssp->majornum, ssp->instance, code, SL_TRACE, buf);
368 	}
369 }
370 
371 static struct lombus_state *
372 lombus_getstate(dev_info_t *dip, int instance, const char *caller)
373 {
374 	struct lombus_state *ssp = NULL;
375 	dev_info_t *sdip = NULL;
376 	major_t dmaj = NOMAJOR;
377 
378 	if (dip != NULL) {
379 		/*
380 		 * Use the instance number from the <dip>; also,
381 		 * check that it really corresponds to this driver
382 		 */
383 		instance = ddi_get_instance(dip);
384 		dmaj = ddi_driver_major(dip);
385 		if (lombus_major == NOMAJOR && dmaj != NOMAJOR)
386 			lombus_major = dmaj;
387 		else if (dmaj != lombus_major) {
388 			cmn_err(CE_WARN,
389 			    "%s: major number mismatch (%d vs. %d) in %s(),"
390 			    "probably due to child misconfiguration",
391 			    MYNAME, lombus_major, dmaj, caller);
392 			instance = -1;
393 		}
394 	}
395 
396 	if (instance >= 0)
397 		ssp = ddi_get_soft_state(lombus_statep, instance);
398 	if (ssp != NULL) {
399 		sdip = ssp->dip;
400 		if (dip == NULL && sdip == NULL)
401 			ssp = NULL;
402 		else if (dip != NULL && sdip != NULL && sdip != dip) {
403 			cmn_err(CE_WARN,
404 			    "%s: devinfo mismatch (%p vs. %p) in %s(), "
405 			    "probably due to child misconfiguration",
406 			    MYNAME, (void *)dip, (void *)sdip, caller);
407 			ssp = NULL;
408 		}
409 	}
410 
411 	return (ssp);
412 }
413 
414 /*
415  * Lowest-level serial I/O chip register read/write
416  */
417 
418 static void
419 sio_put_reg(struct lombus_state *ssp, uint_t reg, uint8_t val)
420 {
421 	lombus_trace(ssp, 'P', "sio_put_reg", "REG[%d] <- $%02x", reg, val);
422 
423 	if (ssp->sio_handle != NULL && !ssp->sio_fault) {
424 		/*
425 		 * The chip is mapped as "I/O" (e.g. with the side-effect
426 		 * bit on SPARC), therefore accesses are required to be
427 		 * in-order, with no value cacheing.  However, there can
428 		 * still be write-behind buffering, so it is not guaranteed
429 		 * that a write actually reaches the chip in a given time.
430 		 *
431 		 * To force the access right through to the chip, we follow
432 		 * the write with another write (to the SCRATCH register)
433 		 * and a read (of the value just written to the SCRATCH
434 		 * register).  The SCRATCH register is specifically provided
435 		 * for temporary data and has no effect on the SIO's own
436 		 * operation, making it ideal as a synchronising mechanism.
437 		 *
438 		 * If we didn't do this, it would be possible that the new
439 		 * value wouldn't reach the chip (and have the *intended*
440 		 * side-effects, such as disabling interrupts), for such a
441 		 * long time that the processor could execute a *lot* of
442 		 * instructions - including exiting the interrupt service
443 		 * routine and re-enabling interrupts.  This effect was
444 		 * observed to lead to spurious (unclaimed) interrupts in
445 		 * some circumstances.
446 		 *
447 		 * This will no longer be needed once "synchronous" access
448 		 * handles are available (see PSARC/2000/269 and 2000/531).
449 		 */
450 		ddi_put8(ssp->sio_handle, ssp->sio_regs + reg, val);
451 		ddi_put8(ssp->sio_handle, ssp->sio_regs + SIO_SCR, val);
452 		membar_sync();
453 		(void) ddi_get8(ssp->sio_handle, ssp->sio_regs + SIO_SCR);
454 	}
455 }
456 
457 static uint8_t
458 sio_get_reg(struct lombus_state *ssp, uint_t reg)
459 {
460 	uint8_t val;
461 
462 	if (ssp->sio_handle && !ssp->sio_fault)
463 		val = ddi_get8(ssp->sio_handle, ssp->sio_regs + reg);
464 	else
465 		val = DUMMY_VALUE;
466 
467 	lombus_trace(ssp, 'G', "sio_get_reg", "$%02x <- REG[%d]", val, reg);
468 
469 	return (val);
470 }
471 
472 static void
473 sio_check_fault_status(struct lombus_state *ssp)
474 {
475 	ssp->sio_fault = ddi_check_acc_handle(ssp->sio_handle) != DDI_SUCCESS;
476 }
477 
478 static boolean_t
479 sio_faulty(struct lombus_state *ssp)
480 {
481 	if (!ssp->sio_fault)
482 		sio_check_fault_status(ssp);
483 	return (ssp->sio_fault);
484 }
485 
486 
487 /*
488  * Check for data ready.
489  */
490 static boolean_t
491 sio_data_ready(struct lombus_state *ssp)
492 {
493 	uint8_t status;
494 
495 	/*
496 	 * Data is available if the RXDA bit in the LSR is nonzero
497 	 * (if reading it didn't incur a fault).
498 	 */
499 	status = sio_get_reg(ssp, SIO_LSR);
500 	return ((status & SIO_LSR_RXDA) != 0 && !sio_faulty(ssp));
501 }
502 
503 /*
504  * Check for LOM ready
505  */
506 static boolean_t
507 sio_lom_ready(struct lombus_state *ssp)
508 {
509 	uint8_t status;
510 	boolean_t rslt;
511 
512 	/*
513 	 * The LOM is ready if the CTS bit in the MSR is 1, meaning
514 	 * that the /CTS signal is being asserted (driven LOW) -
515 	 * unless we incurred a fault in trying to read the MSR!
516 	 *
517 	 * For debugging, we force the result to TRUE if the FAKE flag is set
518 	 */
519 	status = sio_get_reg(ssp, SIO_MSR);
520 	rslt = (status & SIO_MSR_CTS) != 0 && !sio_faulty(ssp);
521 
522 	lombus_trace(ssp, 'R', "sio_lom_ready", "S $%02x R %d F %d",
523 	    status, rslt, ssp->fake_cts);
524 
525 	return (rslt || ssp->fake_cts);
526 }
527 
528 #if	0
529 /*
530  * Check for interrupt pending
531  */
532 static boolean_t
533 sio_irq_pending(struct lombus_state *ssp)
534 {
535 	uint8_t status;
536 	boolean_t rslt;
537 
538 	/*
539 	 * An interrupt is pending if the IPF bit in the EIR is 0,
540 	 * assuming we didn't incur a fault in trying to ready it.
541 	 *
542 	 * Note: we expect that every time we read this register
543 	 * (which is only done from the interrupt service routine),
544 	 * we will see $11001100 (RX FIFO timeout interrupt pending).
545 	 */
546 	status = sio_get_reg(ssp, SIO_EIR);
547 
548 	rslt = (status & SIO_EIR_IPF) == 0 && !sio_faulty(ssp);
549 	lombus_trace(ssp, 'I', "sio_irq_pending", "S $%02x R %d",
550 	    status, rslt);
551 
552 	/*
553 	 * To investigate whether we're getting any abnormal interrupts
554 	 * this code checks that the status value is as expected, and that
555 	 * chip-level interrupts are supposed to be enabled at this time.
556 	 * This will cause a PANIC (on a driver compiled with DEBUG) if
557 	 * all is not as expected ...
558 	 */
559 	ASSERT(status == 0xCC);
560 	ASSERT(ssp->hw_int_enabled);
561 
562 	return (rslt);
563 }
564 #endif	/* 0 */
565 
566 /*
567  * Enable/disable interrupts
568  */
569 static void
570 lombus_set_irq(struct lombus_state *ssp, boolean_t newstate)
571 {
572 	uint8_t val;
573 
574 	val = newstate ? SIO_IER_RXHDL_IE : 0;
575 	sio_put_reg(ssp, SIO_IER, SIO_IER_STD | val);
576 	ssp->hw_int_enabled = newstate;
577 }
578 
579 /*
580  * Assert/deassert RTS
581  */
582 static void
583 lombus_toggle_rts(struct lombus_state *ssp)
584 {
585 	uint8_t val;
586 
587 	val = sio_get_reg(ssp, SIO_MCR);
588 	val &= SIO_MCR_RTS;
589 	val ^= SIO_MCR_RTS;
590 	val |= SIO_MCR_STD;
591 	sio_put_reg(ssp, SIO_MCR, val);
592 }
593 
594 
595 /*
596  * High-level interrupt handler:
597  *	Checks whether initialisation is complete (to avoid a race
598  *	with mutex_init()), and whether chip interrupts are enabled.
599  *	If not, the interrupt's not for us, so just return UNCLAIMED.
600  *	Otherwise, disable the interrupt, trigger a softint, and return
601  *	CLAIMED.  The softint handler will then do all the real work.
602  *
603  *	NOTE: the chip interrupt capability is only re-enabled once the
604  *	receive code has run, but that can be called from a poll loop
605  *	or cyclic callback as well as from the softint.  So it's *not*
606  *	guaranteed that there really is a chip interrupt pending here,
607  *	'cos the work may already have been done and the reason for the
608  *	interrupt gone away before we get here.
609  *
610  *	OTOH, if we come through here twice without the receive code
611  *	having run in between, that's definitely wrong.  In such an
612  *	event, we would notice that chip interrupts haven't yet been
613  *	re-enabled and return UNCLAIMED, allowing the system's jabber
614  *	protect code (if any) to do its job.
615  */
616 static uint_t
617 lombus_hi_intr(caddr_t arg)
618 {
619 	struct lombus_state *ssp = (void *)arg;
620 	uint_t claim;
621 
622 	claim = DDI_INTR_UNCLAIMED;
623 	if (ssp->cycid != NULL) {
624 		mutex_enter(ssp->hw_mutex);
625 		if (ssp->hw_int_enabled) {
626 			lombus_set_irq(ssp, B_FALSE);
627 			ddi_trigger_softintr(ssp->softid);
628 			claim = DDI_INTR_CLAIMED;
629 		}
630 		mutex_exit(ssp->hw_mutex);
631 	}
632 
633 	return (claim);
634 }
635 
636 /*
637  * Packet receive handler
638  *
639  * This routine should be called from the low-level softint, or the
640  * cyclic callback, or lombus_cmd() (for polled operation), with the
641  * low-level mutex already held.
642  */
643 static void
644 lombus_receive(struct lombus_state *ssp)
645 {
646 	boolean_t ready = B_FALSE;
647 	uint8_t data = 0;
648 	uint8_t rcvd = 0;
649 	uint8_t tmp;
650 
651 	lombus_trace(ssp, 'S', "lombus_receive",
652 	    "state %d; error $%x",
653 	    ssp->cmdstate, ssp->error);
654 
655 	/*
656 	 * Check for access faults before starting the receive
657 	 * loop (we don't want to cause bus errors or suchlike
658 	 * unpleasantness in the event that the SIO has died).
659 	 */
660 	if (!sio_faulty(ssp)) {
661 		/*
662 		 * Read bytes from the FIFO until they're all gone,
663 		 * or we find the 'END OF PACKET' set on one, or
664 		 * our buffer overflows (which must be an error)
665 		 */
666 		mutex_enter(ssp->hw_mutex);
667 		while (sio_data_ready(ssp)) {
668 			data = sio_get_reg(ssp, SIO_RXD);
669 			ssp->reply[rcvd = ssp->index] = data;
670 			if (++rcvd >= LOMBUS_BUFSIZE)
671 				break;
672 			ssp->index = rcvd;
673 			if (data & LOMBUS_LAST)
674 				break;
675 		}
676 		lombus_set_irq(ssp, B_TRUE);
677 		mutex_exit(ssp->hw_mutex);
678 	}
679 
680 	lombus_trace(ssp, 'S', "lombus_receive",
681 	    "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
682 	    rcvd,
683 	    ssp->reply[0], ssp->reply[1],
684 	    ssp->reply[2], ssp->reply[3],
685 	    ssp->reply[4], ssp->reply[5],
686 	    ssp->reply[6], ssp->reply[7]);
687 
688 	if (ssp->cmdstate != LOMBUS_CMDSTATE_WAITING) {
689 		/*
690 		 * We're not expecting any data in this state, so if
691 		 * we DID receive any data, we just throw it away by
692 		 * resetting the buffer index to 0.
693 		 */
694 		ssp->index = 0;
695 	} else if (rcvd == 0) {
696 		/*
697 		 * No bytes received this time through (though there
698 		 * might be a partial packet sitting in the buffer).
699 		 * If it seems the LOM is taking too long to respond,
700 		 * we'll assume it's died and return an error.
701 		 */
702 		if (ddi_get_lbolt() > ssp->deadline) {
703 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
704 			ssp->error = LOMBUS_ERR_TIMEOUT;
705 			ready = B_TRUE;
706 		}
707 	} else if (rcvd >= LOMBUS_BUFSIZE) {
708 		/*
709 		 * Buffer overflow; discard the data & treat as an error
710 		 * (even if the last byte read did claim to terminate a
711 		 * packet, it can't be a valid one 'cos it's too long!)
712 		 */
713 		ssp->index = 0;
714 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
715 		ssp->error = LOMBUS_ERR_OFLOW;
716 		ready = B_TRUE;
717 	} else if ((data & LOMBUS_LAST) == 0) {
718 		/*
719 		 * Packet not yet complete; leave the partial packet in
720 		 * the buffer for later ...
721 		 */
722 		_NOTE(EMPTY)
723 		;
724 	} else if ((data & LOMBUS_MASK) != LOMBUS_STATUS) {
725 		/*
726 		 * Invalid "status" byte - maybe an echo of the command?
727 		 *
728 		 * As a debugging feature, we allow for this, assuming
729 		 * that if the LOM has echoed the command byte, it has
730 		 * also echoed all the parameter bytes before starting
731 		 * command processing.  So, we dump out the buffer and
732 		 * then clear it, so we can go back to looking for the
733 		 * real reply.
734 		 *
735 		 * Otherwise, we just drop the data & flag an error.
736 		 */
737 		if (ssp->allow_echo) {
738 			lombus_trace(ssp, 'E', "lombus_receive",
739 			    "echo $%02x $%02x $%02x $%02x "
740 			    "$%02x $%02x $%02x $%02x",
741 			    ssp->reply[0], ssp->reply[1],
742 			    ssp->reply[2], ssp->reply[3],
743 			    ssp->reply[4], ssp->reply[5],
744 			    ssp->reply[6], ssp->reply[7]);
745 			ssp->index = 0;
746 		} else {
747 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
748 			ssp->error = LOMBUS_ERR_BADSTATUS;
749 			ready = B_TRUE;
750 		}
751 	} else if ((data & LOMBUS_SEQ) != ssp->sequence) {
752 		/*
753 		 * Wrong sequence number!  Flag this as an error
754 		 */
755 		ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
756 		ssp->error = LOMBUS_ERR_SEQUENCE;
757 		ready = B_TRUE;
758 	} else {
759 		/*
760 		 * Finally, we know that's it's a valid reply to our
761 		 * last command.  Update the ASYNC status, derive the
762 		 * reply parameter (if any), and check the ERROR bit
763 		 * to find out what the parameter means.
764 		 *
765 		 * Note that not all the values read/assigned here
766 		 * are meaningful, but it doesn't matter; the waiting
767 		 * thread will know which one(s) it should check.
768 		 */
769 		ssp->async = (data & LOMBUS_STATUS_ASYNC) ? 1 : 0;
770 		tmp = ((data & LOMBUS_STATUS_MSB) ? 0x80 : 0) | ssp->reply[0];
771 		if (data & LOMBUS_STATUS_ERR) {
772 			ssp->cmdstate = LOMBUS_CMDSTATE_ERROR;
773 			ssp->error = tmp;
774 		} else {
775 			ssp->cmdstate = LOMBUS_CMDSTATE_READY;
776 			ssp->result = tmp;
777 		}
778 		ready = B_TRUE;
779 	}
780 
781 	lombus_trace(ssp, 'T', "lombus_receive",
782 	    "rcvd %d; last $%02x; state %d; error $%x; ready %d",
783 	    rcvd, data, ssp->cmdstate, ssp->error, ready);
784 
785 	if (ready)
786 		cv_broadcast(ssp->lo_cv);
787 }
788 
789 /*
790  * Low-level softint handler
791  *
792  * This routine should be triggered whenever there's a byte to be read
793  */
794 static uint_t
795 lombus_softint(caddr_t arg)
796 {
797 	struct lombus_state *ssp = (void *)arg;
798 
799 	mutex_enter(ssp->lo_mutex);
800 	lombus_receive(ssp);
801 	mutex_exit(ssp->lo_mutex);
802 
803 	return (DDI_INTR_CLAIMED);
804 }
805 
806 /*
807  * Cyclic handler: just calls the receive routine, in case interrupts
808  * are not being delivered and in order to handle command timeout
809  */
810 static void
811 lombus_cyclic(void *arg)
812 {
813 	struct lombus_state *ssp = (void *)arg;
814 
815 	mutex_enter(ssp->lo_mutex);
816 	lombus_receive(ssp);
817 	mutex_exit(ssp->lo_mutex);
818 }
819 
820 
821 /*
822  * Serial protocol
823  *
824  * This routine builds a command and sets it in progress.
825  */
826 static uint8_t
827 lombus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd)
828 {
829 	struct lombus_state *ssp;
830 	clock_t start;
831 	clock_t tick;
832 	uint8_t *p;
833 
834 	/*
835 	 * First of all, wait for the interface to be available.
836 	 *
837 	 * NOTE: we blow through all the mutex/cv/state checking and
838 	 * preempt any command in progress if the system is panicking!
839 	 */
840 	ssp = HANDLE_PRIVATE(hdlp);
841 	mutex_enter(ssp->lo_mutex);
842 	while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr)
843 		cv_wait(ssp->lo_cv, ssp->lo_mutex);
844 
845 	ssp->cmdstate = LOMBUS_CMDSTATE_BUSY;
846 	ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ;
847 
848 	/*
849 	 * We have exclusive ownership, so assemble the command (backwards):
850 	 *
851 	 * [byte 0]	Command:	modified by XADDR and/or WMSB bits
852 	 * [Optional] Parameter: 	Value to write (low 7 bits)
853 	 * [Optional] Parameter: 	Register number (high 7 bits)
854 	 * [Optional] Parameter: 	Register number (low 7 bits)
855 	 */
856 	p = &ssp->cmdbuf[0];
857 	*p++ = LOMBUS_CMD | ssp->sequence | cmd;
858 	switch (cmd) {
859 	case LOMBUS_CMD_WRITE:
860 		*p++ = val & 0x7f;
861 		if (val >= 0x80)
862 			ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB;
863 		/*FALLTHRU*/
864 	case LOMBUS_CMD_READ:
865 		if (LOMBUS_VREG_HI(vreg) != 0) {
866 			*p++ = LOMBUS_VREG_HI(vreg);
867 			ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR;
868 		}
869 		*p++ = LOMBUS_VREG_LO(vreg);
870 		/*FALLTHRU*/
871 	case LOMBUS_CMD_NOP:
872 		break;
873 	}
874 
875 	/*
876 	 * Check and update the SIO h/w fault status before accessing
877 	 * the chip registers.  If there's a (new or previous) fault,
878 	 * we'll run through the protocol but won't really touch the
879 	 * hardware and all commands will timeout.  If a previously
880 	 * discovered fault has now gone away (!), then we can (try to)
881 	 * proceed with the new command (probably a probe).
882 	 */
883 	sio_check_fault_status(ssp);
884 
885 	/*
886 	 * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
887 	 * us that it's ready for the next command.  If it doesn't, though,
888 	 * we'll send it anyway, on the basis that the CTS signal might be
889 	 * open- or short-circuited (or the LOM firmware forgot to set it,
890 	 * or the LOM just got reset, or whatever ...)
891 	 */
892 	start = ddi_get_lbolt();
893 	ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000);
894 	while (!sio_lom_ready(ssp)) {
895 		if ((tick = ddi_get_lbolt()) > ssp->deadline)
896 			break;
897 		tick += drv_usectohz(LOMBUS_CTS_POLL/1000);
898 		cv_timedwait(ssp->lo_cv, ssp->lo_mutex, tick);
899 	}
900 
901 	/*
902 	 * Either the LOM is ready, or we timed out waiting for CTS.
903 	 * In either case, we're going to send the command now by
904 	 * stuffing the packet into the Tx FIFO, reversing it as we go.
905 	 * We call lombus_receive() first to ensure there isn't any
906 	 * garbage left in the Rx FIFO from an earlier command that
907 	 * timed out (or was pre-empted by a PANIC!).  This also makes
908 	 * sure that SIO interrupts are enabled so we'll see the reply
909 	 * more quickly (the poll loop below will still work even if
910 	 * interrupts aren't enabled, but it will take longer).
911 	 */
912 	lombus_receive(ssp);
913 	mutex_enter(ssp->hw_mutex);
914 	while (p > ssp->cmdbuf)
915 		sio_put_reg(ssp, SIO_TXD, *--p);
916 	mutex_exit(ssp->hw_mutex);
917 
918 	/*
919 	 * Prepare for the reply (to be processed by the interrupt/cyclic
920 	 * handler and/or polling loop below), then wait for a response
921 	 * or timeout.
922 	 */
923 	start = ddi_get_lbolt();
924 	ssp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000);
925 	ssp->error = 0;
926 	ssp->index = 0;
927 	ssp->result = DUMMY_VALUE;
928 	ssp->cmdstate = LOMBUS_CMDSTATE_WAITING;
929 	while (ssp->cmdstate == LOMBUS_CMDSTATE_WAITING) {
930 		tick = ddi_get_lbolt() + drv_usectohz(LOMBUS_CMD_POLL/1000);
931 		if (cv_timedwait(ssp->lo_cv, ssp->lo_mutex, tick) == -1)
932 			lombus_receive(ssp);
933 	}
934 
935 	/*
936 	 * The return value may not be meaningful but retrieve it anyway
937 	 */
938 	val = ssp->result;
939 	if (sio_faulty(ssp)) {
940 		val = DUMMY_VALUE;
941 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW;
942 	} else if (ssp->cmdstate != LOMBUS_CMDSTATE_READY) {
943 		/*
944 		 * Some problem here ... transfer the error code from
945 		 * the per-instance state to the per-handle fault flag.
946 		 * The error code shouldn't be zero!
947 		 */
948 		if (ssp->error != 0)
949 			HANDLE_FAULT(hdlp) = ssp->error;
950 		else
951 			HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE;
952 	}
953 
954 	/*
955 	 * All done now!
956 	 */
957 	ssp->index = 0;
958 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
959 	cv_broadcast(ssp->lo_cv);
960 	mutex_exit(ssp->lo_mutex);
961 
962 	return (val);
963 }
964 
965 
966 /*
967  * Space 0 - LOM virtual register access
968  * Only 8-bit accesses are supported.
969  */
970 static uint8_t
971 lombus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
972 {
973 	ptrdiff_t offset;
974 
975 	/*
976 	 * Check the offset that the caller has added to the base address
977 	 * against the length of the mapping originally requested.
978 	 */
979 	offset = ADDR_TO_OFFSET(addr, hdlp);
980 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
981 		/*
982 		 * Invalid access - flag a fault and return a dummy value
983 		 */
984 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
985 		return (DUMMY_VALUE);
986 	}
987 
988 	/*
989 	 * Derive the virtual register number and run the command
990 	 */
991 	return (lombus_cmd(hdlp, ADDR_TO_VREG(addr), 0, LOMBUS_CMD_READ));
992 }
993 
994 static void
995 lombus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
996 {
997 	ptrdiff_t offset;
998 
999 	/*
1000 	 * Check the offset that the caller has added to the base address
1001 	 * against the length of the mapping originally requested.
1002 	 */
1003 	offset = ADDR_TO_OFFSET(addr, hdlp);
1004 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
1005 		/*
1006 		 * Invalid access - flag a fault and return
1007 		 */
1008 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
1009 		return;
1010 	}
1011 
1012 	/*
1013 	 * Derive the virtual register number and run the command
1014 	 */
1015 	(void) lombus_cmd(hdlp, ADDR_TO_VREG(addr), val, LOMBUS_CMD_WRITE);
1016 }
1017 
1018 static void
1019 lombus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1020 	uint8_t *dev_addr, size_t repcount, uint_t flags)
1021 {
1022 	size_t inc;
1023 
1024 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1025 	for (; repcount--; dev_addr += inc)
1026 		*host_addr++ = lombus_vreg_get8(hdlp, dev_addr);
1027 }
1028 
1029 static void
1030 lombus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1031 	uint8_t *dev_addr, size_t repcount, uint_t flags)
1032 {
1033 	size_t inc;
1034 
1035 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1036 	for (; repcount--; dev_addr += inc)
1037 		lombus_vreg_put8(hdlp, dev_addr, *host_addr++);
1038 }
1039 
1040 
1041 /*
1042  * Space 1 - LOM watchdog pat register access
1043  * Only 8-bit accesses are supported.
1044  *
1045  * Reads have no effect and return 0.
1046  *
1047  * Writes pat the dog by toggling the RTS line iff enough time has
1048  * elapsed since last time we toggled it.
1049  *
1050  * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
1051  * way of zeroing the destination area ;-) and still won't pat the dog.
1052  *
1053  * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
1054  * only count as a single pat, no matter how many bytes the caller
1055  * says to write, as the inter-pat time is VERY long compared with
1056  * the time it will take to read the memory source area.
1057  */
1058 
1059 static uint8_t
1060 lombus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
1061 {
1062 	ptrdiff_t offset;
1063 
1064 	/*
1065 	 * Check the offset that the caller has added to the base address
1066 	 * against the length of the mapping originally requested.
1067 	 */
1068 	offset = ADDR_TO_OFFSET(addr, hdlp);
1069 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
1070 		/*
1071 		 * Invalid access - flag a fault and return a dummy value
1072 		 */
1073 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
1074 		return (DUMMY_VALUE);
1075 	}
1076 
1077 	return (0);
1078 }
1079 
1080 static void
1081 lombus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
1082 {
1083 	struct lombus_state *ssp;
1084 	ptrdiff_t offset;
1085 	hrtime_t now;
1086 
1087 	_NOTE(ARGUNUSED(val))
1088 
1089 	/*
1090 	 * Check the offset that the caller has added to the base address
1091 	 * against the length of the mapping originally requested.
1092 	 */
1093 	offset = ADDR_TO_OFFSET(addr, hdlp);
1094 	if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) {
1095 		/*
1096 		 * Invalid access - flag a fault and return
1097 		 */
1098 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
1099 		return;
1100 	}
1101 
1102 	ssp = HANDLE_PRIVATE(hdlp);
1103 	mutex_enter(ssp->hw_mutex);
1104 	now = gethrtime();
1105 	if ((now - ssp->hw_last_pat) >= LOMBUS_MIN_PAT) {
1106 		lombus_toggle_rts(ssp);
1107 		ssp->hw_last_pat = now;
1108 	}
1109 	mutex_exit(ssp->hw_mutex);
1110 }
1111 
1112 static void
1113 lombus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1114 	uint8_t *dev_addr, size_t repcount, uint_t flags)
1115 {
1116 	size_t inc;
1117 
1118 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1119 	for (; repcount--; dev_addr += inc)
1120 		*host_addr++ = lombus_pat_get8(hdlp, dev_addr);
1121 }
1122 
1123 static void
1124 lombus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1125 	uint8_t *dev_addr, size_t repcount, uint_t flags)
1126 {
1127 	size_t inc;
1128 
1129 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1130 	for (; repcount--; dev_addr += inc)
1131 		lombus_pat_put8(hdlp, dev_addr, *host_addr++);
1132 }
1133 
1134 
1135 /*
1136  * Space 2 - LOM async event flag register access
1137  * Only 16-bit accesses are supported.
1138  */
1139 static uint16_t
1140 lombus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
1141 {
1142 	struct lombus_state *ssp;
1143 	ptrdiff_t offset;
1144 
1145 	/*
1146 	 * Check the offset that the caller has added to the base address
1147 	 * against the length of the mapping orignally requested.
1148 	 */
1149 	offset = ADDR_TO_OFFSET(addr, hdlp);
1150 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
1151 		/*
1152 		 * Invalid access - flag a fault and return a dummy value
1153 		 */
1154 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
1155 		return (DUMMY_VALUE);
1156 	}
1157 
1158 	/*
1159 	 * Return the value of the asynchronous-event-pending flag
1160 	 * as passed back by the LOM at the end of the last command.
1161 	 */
1162 	ssp = HANDLE_PRIVATE(hdlp);
1163 	return (ssp->async);
1164 }
1165 
1166 static void
1167 lombus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
1168 {
1169 	ptrdiff_t offset;
1170 
1171 	_NOTE(ARGUNUSED(val))
1172 
1173 	/*
1174 	 * Check the offset that the caller has added to the base address
1175 	 * against the length of the mapping originally requested.
1176 	 */
1177 	offset = ADDR_TO_OFFSET(addr, hdlp);
1178 	if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) {
1179 		/*
1180 		 * Invalid access - flag a fault and return
1181 		 */
1182 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM;
1183 		return;
1184 	}
1185 
1186 	/*
1187 	 * The user can't overwrite the asynchronous-event-pending flag!
1188 	 */
1189 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO;
1190 }
1191 
1192 static void
1193 lombus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
1194 	uint16_t *dev_addr, size_t repcount, uint_t flags)
1195 {
1196 	size_t inc;
1197 
1198 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1199 	for (; repcount--; dev_addr += inc)
1200 		*host_addr++ = lombus_event_get16(hdlp, dev_addr);
1201 }
1202 
1203 static void
1204 lombus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
1205 	uint16_t *dev_addr, size_t repcount, uint_t flags)
1206 {
1207 	size_t inc;
1208 
1209 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1210 	for (; repcount--; dev_addr += inc)
1211 		lombus_event_put16(hdlp, dev_addr, *host_addr++);
1212 }
1213 
1214 
1215 /*
1216  * All spaces - access handle fault information
1217  * Only 32-bit accesses are supported.
1218  */
1219 static uint32_t
1220 lombus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr)
1221 {
1222 	struct lombus_state *ssp;
1223 	ptrdiff_t offset;
1224 
1225 	/*
1226 	 * Derive the offset that the caller has added to the base
1227 	 * address originally returned, and use it to determine
1228 	 * which meta-register is to be accessed ...
1229 	 */
1230 	offset = ADDR_TO_OFFSET(addr, hdlp);
1231 	switch (offset) {
1232 	case LOMBUS_FAULT_REG:
1233 		/*
1234 		 * This meta-register provides a code for the most
1235 		 * recent virtual register access fault, if any.
1236 		 */
1237 		return (HANDLE_FAULT(hdlp));
1238 
1239 	case LOMBUS_PROBE_REG:
1240 		/*
1241 		 * Reading this meta-register clears any existing fault
1242 		 * (at the virtual, not the hardware access layer), then
1243 		 * runs a NOP command and returns the fault code from that.
1244 		 */
1245 		HANDLE_FAULT(hdlp) = 0;
1246 		lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
1247 		return (HANDLE_FAULT(hdlp));
1248 
1249 	case LOMBUS_ASYNC_REG:
1250 		/*
1251 		 * Obsolescent - but still supported for backwards
1252 		 * compatibility.  This is an alias for the newer
1253 		 * LOMBUS_EVENT_REG, but doesn't require a separate
1254 		 * "reg" entry and ddi_regs_map_setup() call.
1255 		 *
1256 		 * It returns the value of the asynchronous-event-pending
1257 		 * flag as passed back by the LOM at the end of the last
1258 		 * completed command.
1259 		 */
1260 		ssp = HANDLE_PRIVATE(hdlp);
1261 		return (ssp->async);
1262 
1263 	default:
1264 		/*
1265 		 * Invalid access - flag a fault and return a dummy value
1266 		 */
1267 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1268 		return (DUMMY_VALUE);
1269 	}
1270 }
1271 
1272 static void
1273 lombus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val)
1274 {
1275 	ptrdiff_t offset;
1276 
1277 	/*
1278 	 * Derive the offset that the caller has added to the base
1279 	 * address originally returned, and use it to determine
1280 	 * which meta-register is to be accessed ...
1281 	 */
1282 	offset = ADDR_TO_OFFSET(addr, hdlp);
1283 	switch (offset) {
1284 	case LOMBUS_FAULT_REG:
1285 		/*
1286 		 * This meta-register contains a code for the most
1287 		 * recent virtual register access fault, if any.
1288 		 * It can be cleared simply by writing 0 to it.
1289 		 */
1290 		HANDLE_FAULT(hdlp) = val;
1291 		return;
1292 
1293 	case LOMBUS_PROBE_REG:
1294 		/*
1295 		 * Writing this meta-register clears any existing fault
1296 		 * (at the virtual, not the hardware acess layer), then
1297 		 * runs a NOP command.  The caller can check the fault
1298 		 * code later if required.
1299 		 */
1300 		HANDLE_FAULT(hdlp) = 0;
1301 		lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP);
1302 		return;
1303 
1304 	default:
1305 		/*
1306 		 * Invalid access - flag a fault
1307 		 */
1308 		HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1309 		return;
1310 	}
1311 }
1312 
1313 static void
1314 lombus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
1315 	uint32_t *dev_addr, size_t repcount, uint_t flags)
1316 {
1317 	size_t inc;
1318 
1319 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1320 	for (; repcount--; dev_addr += inc)
1321 		*host_addr++ = lombus_meta_get32(hdlp, dev_addr);
1322 }
1323 
1324 static void
1325 lombus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr,
1326 	uint32_t *dev_addr, size_t repcount, uint_t flags)
1327 {
1328 	size_t inc;
1329 
1330 	inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0;
1331 	for (; repcount--; dev_addr += inc)
1332 		lombus_meta_put32(hdlp, dev_addr, *host_addr++);
1333 }
1334 
1335 
1336 /*
1337  * Finally, some dummy functions for all unsupported access
1338  * space/size/mode combinations ...
1339  */
1340 static uint8_t
1341 lombus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr)
1342 {
1343 	_NOTE(ARGUNUSED(addr))
1344 
1345 	/*
1346 	 * Invalid access - flag a fault and return a dummy value
1347 	 */
1348 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1349 	return (DUMMY_VALUE);
1350 }
1351 
1352 static void
1353 lombus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val)
1354 {
1355 	_NOTE(ARGUNUSED(addr, val))
1356 
1357 	/*
1358 	 * Invalid access - flag a fault
1359 	 */
1360 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1361 }
1362 
1363 static void
1364 lombus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1365 		uint8_t *dev_addr, size_t repcount, uint_t flags)
1366 {
1367 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1368 
1369 	/*
1370 	 * Invalid access - flag a fault
1371 	 */
1372 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1373 }
1374 
1375 static void
1376 lombus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr,
1377 	uint8_t *dev_addr, size_t repcount, uint_t flags)
1378 {
1379 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1380 
1381 	/*
1382 	 * Invalid access - flag a fault
1383 	 */
1384 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1385 }
1386 
1387 static uint16_t
1388 lombus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr)
1389 {
1390 	_NOTE(ARGUNUSED(addr))
1391 
1392 	/*
1393 	 * Invalid access - flag a fault and return a dummy value
1394 	 */
1395 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1396 	return (DUMMY_VALUE);
1397 }
1398 
1399 static void
1400 lombus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val)
1401 {
1402 	_NOTE(ARGUNUSED(addr, val))
1403 
1404 	/*
1405 	 * Invalid access - flag a fault
1406 	 */
1407 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1408 }
1409 
1410 static void
1411 lombus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
1412 		uint16_t *dev_addr, size_t repcount, uint_t flags)
1413 {
1414 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1415 
1416 	/*
1417 	 * Invalid access - flag a fault
1418 	 */
1419 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1420 }
1421 
1422 static void
1423 lombus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr,
1424 	uint16_t *dev_addr, size_t repcount, uint_t flags)
1425 {
1426 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1427 
1428 	/*
1429 	 * Invalid access - flag a fault
1430 	 */
1431 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1432 }
1433 
1434 static uint64_t
1435 lombus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr)
1436 {
1437 	_NOTE(ARGUNUSED(addr))
1438 
1439 	/*
1440 	 * Invalid access - flag a fault and return a dummy value
1441 	 */
1442 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1443 	return (DUMMY_VALUE);
1444 }
1445 
1446 static void
1447 lombus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val)
1448 {
1449 	_NOTE(ARGUNUSED(addr, val))
1450 
1451 	/*
1452 	 * Invalid access - flag a fault
1453 	 */
1454 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1455 }
1456 
1457 static void
1458 lombus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
1459 	uint64_t *dev_addr, size_t repcount, uint_t flags)
1460 {
1461 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1462 
1463 	/*
1464 	 * Invalid access - flag a fault
1465 	 */
1466 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1467 }
1468 
1469 static void
1470 lombus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr,
1471 	uint64_t *dev_addr, size_t repcount, uint_t flags)
1472 {
1473 	_NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags))
1474 
1475 	/*
1476 	 * Invalid access - flag a fault
1477 	 */
1478 	HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE;
1479 }
1480 
1481 static int
1482 lombus_acc_fault_check(HANDLE_TYPE *hdlp)
1483 {
1484 	return (HANDLE_FAULT(hdlp) != 0);
1485 }
1486 
1487 
1488 /*
1489  * Hardware setup - put the SIO chip in the required operational
1490  * state,  with all our favourite parameters programmed correctly.
1491  * This routine leaves all SIO interrupts disabled.
1492  */
1493 
1494 static void
1495 lombus_hw_reset(struct lombus_state *ssp)
1496 {
1497 	uint16_t divisor;
1498 
1499 	/*
1500 	 * Disable interrupts, soft reset Tx and Rx circuitry,
1501 	 * reselect standard modes (bits/char, parity, etc).
1502 	 */
1503 	lombus_set_irq(ssp, B_FALSE);
1504 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR);
1505 	sio_put_reg(ssp, SIO_LCR, SIO_LCR_STD);
1506 
1507 	/*
1508 	 * Select the proper baud rate; if the value is invalid
1509 	 * (presumably 0, i.e. not specified, but also if the
1510 	 * "baud" property is set to some silly value), we assume
1511 	 * the default.
1512 	 */
1513 	if (ssp->baud < SIO_BAUD_MIN || ssp->baud > SIO_BAUD_MAX)
1514 		divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT);
1515 	else
1516 		divisor = SIO_BAUD_TO_DIVISOR(ssp->baud);
1517 
1518 	/*
1519 	 * According to the datasheet, it is forbidden for the divisor
1520 	 * register to be zero.  So when loading the register in two
1521 	 * steps, we have to make sure that the temporary value formed
1522 	 * between loads is nonzero.  However, we can't rely on either
1523 	 * half already having a nonzero value, as the datasheet also
1524 	 * says that these registers are indeterminate after a reset!
1525 	 * So, we explicitly set the low byte to a non-zero value first;
1526 	 * then we can safely load the high byte, and then the correct
1527 	 * value for the low byte, without the result ever being zero.
1528 	 */
1529 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK1);
1530 	sio_put_reg(ssp, SIO_LBGDL, 0xff);
1531 	sio_put_reg(ssp, SIO_LBGDH, divisor >> 8);
1532 	sio_put_reg(ssp, SIO_LBGDL, divisor & 0xff);
1533 	sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK0);
1534 
1535 	/*
1536 	 * Program the remaining device registers as required
1537 	 */
1538 	sio_put_reg(ssp, SIO_MCR, SIO_MCR_STD);
1539 	sio_put_reg(ssp, SIO_FCR, SIO_FCR_STD);
1540 }
1541 
1542 
1543 /*
1544  * Higher-level setup & teardown
1545  */
1546 
1547 static void
1548 lombus_offline(struct lombus_state *ssp)
1549 {
1550 	if (ssp->sio_handle != NULL)
1551 		ddi_regs_map_free(&ssp->sio_handle);
1552 	ssp->sio_handle = NULL;
1553 	ssp->sio_regs = NULL;
1554 }
1555 
1556 static int
1557 lombus_online(struct lombus_state *ssp)
1558 {
1559 	ddi_acc_handle_t h;
1560 	caddr_t p;
1561 	int nregs;
1562 	int err;
1563 
1564 	if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS)
1565 		nregs = 0;
1566 
1567 	switch (nregs) {
1568 	default:
1569 	case 1:
1570 		/*
1571 		 *  regset 0 represents the SIO operating registers
1572 		 */
1573 		err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0,
1574 		    lombus_dev_acc_attr, &h);
1575 		lombus_trace(ssp, 'O', "online",
1576 		    "regmap 0 status %d addr $%p", err, p);
1577 		if (err != DDI_SUCCESS)
1578 			return (EIO);
1579 
1580 		ssp->sio_handle = h;
1581 		ssp->sio_regs = (void *)p;
1582 		break;
1583 
1584 	case 0:
1585 		/*
1586 		 *  If no registers are defined, succeed vacuously;
1587 		 *  commands will be accepted, but we fake the accesses.
1588 		 */
1589 		break;
1590 	}
1591 
1592 	/*
1593 	 * Now that the registers are mapped, we can initialise the SIO h/w
1594 	 */
1595 	lombus_hw_reset(ssp);
1596 	return (0);
1597 }
1598 
1599 
1600 /*
1601  *  Nexus routines
1602  */
1603 
1604 #if	defined(NDI_ACC_HDL_V2)
1605 
1606 static const ndi_acc_fns_t lombus_vreg_acc_fns = {
1607 	NDI_ACC_FNS_CURRENT,
1608 	NDI_ACC_FNS_V1,
1609 
1610 	lombus_vreg_get8,
1611 	lombus_vreg_put8,
1612 	lombus_vreg_rep_get8,
1613 	lombus_vreg_rep_put8,
1614 
1615 	lombus_no_get16,
1616 	lombus_no_put16,
1617 	lombus_no_rep_get16,
1618 	lombus_no_rep_put16,
1619 
1620 	lombus_meta_get32,
1621 	lombus_meta_put32,
1622 	lombus_meta_rep_get32,
1623 	lombus_meta_rep_put32,
1624 
1625 	lombus_no_get64,
1626 	lombus_no_put64,
1627 	lombus_no_rep_get64,
1628 	lombus_no_rep_put64,
1629 
1630 	lombus_acc_fault_check
1631 };
1632 
1633 static const ndi_acc_fns_t lombus_pat_acc_fns = {
1634 	NDI_ACC_FNS_CURRENT,
1635 	NDI_ACC_FNS_V1,
1636 
1637 	lombus_pat_get8,
1638 	lombus_pat_put8,
1639 	lombus_pat_rep_get8,
1640 	lombus_pat_rep_put8,
1641 
1642 	lombus_no_get16,
1643 	lombus_no_put16,
1644 	lombus_no_rep_get16,
1645 	lombus_no_rep_put16,
1646 
1647 	lombus_meta_get32,
1648 	lombus_meta_put32,
1649 	lombus_meta_rep_get32,
1650 	lombus_meta_rep_put32,
1651 
1652 	lombus_no_get64,
1653 	lombus_no_put64,
1654 	lombus_no_rep_get64,
1655 	lombus_no_rep_put64,
1656 
1657 	lombus_acc_fault_check
1658 };
1659 
1660 static const ndi_acc_fns_t lombus_event_acc_fns = {
1661 	NDI_ACC_FNS_CURRENT,
1662 	NDI_ACC_FNS_V1,
1663 
1664 	lombus_no_get8,
1665 	lombus_no_put8,
1666 	lombus_no_rep_get8,
1667 	lombus_no_rep_put8,
1668 
1669 	lombus_event_get16,
1670 	lombus_event_put16,
1671 	lombus_event_rep_get16,
1672 	lombus_event_rep_put16,
1673 
1674 	lombus_meta_get32,
1675 	lombus_meta_put32,
1676 	lombus_meta_rep_get32,
1677 	lombus_meta_rep_put32,
1678 
1679 	lombus_no_get64,
1680 	lombus_no_put64,
1681 	lombus_no_rep_get64,
1682 	lombus_no_rep_put64,
1683 
1684 	lombus_acc_fault_check
1685 };
1686 
1687 static int
1688 lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
1689 	int space, caddr_t vaddr, off_t len,
1690 	ndi_acc_handle_t *hdlp, caddr_t *addrp)
1691 {
1692 	switch (op) {
1693 	default:
1694 		return (DDI_ME_UNIMPLEMENTED);
1695 
1696 	case DDI_MO_MAP_LOCKED:
1697 		switch (space) {
1698 		default:
1699 			return (DDI_ME_REGSPEC_RANGE);
1700 
1701 		case LOMBUS_VREG_SPACE:
1702 			ndi_set_acc_fns(hdlp, &lombus_vreg_acc_fns);
1703 			break;
1704 
1705 		case LOMBUS_PAT_SPACE:
1706 			ndi_set_acc_fns(hdlp, &lombus_pat_acc_fns);
1707 			break;
1708 
1709 		case LOMBUS_EVENT_SPACE:
1710 			ndi_set_acc_fns(hdlp, &lombus_event_acc_fns);
1711 			break;
1712 		}
1713 		hdlp->ah_addr = *addrp = vaddr;
1714 		hdlp->ah_len = len;
1715 		hdlp->ah_bus_private = ssp;
1716 		return (DDI_SUCCESS);
1717 
1718 	case DDI_MO_UNMAP:
1719 		*addrp = NULL;
1720 		hdlp->ah_bus_private = NULL;
1721 		return (DDI_SUCCESS);
1722 	}
1723 }
1724 
1725 #else
1726 
1727 static int
1728 lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op,
1729 	int space, caddr_t vaddr, off_t len,
1730 	ddi_acc_hdl_t *hdlp, caddr_t *addrp)
1731 {
1732 	ddi_acc_impl_t *aip = hdlp->ah_platform_private;
1733 
1734 	switch (op) {
1735 	default:
1736 		return (DDI_ME_UNIMPLEMENTED);
1737 
1738 	case DDI_MO_MAP_LOCKED:
1739 		switch (space) {
1740 		default:
1741 			return (DDI_ME_REGSPEC_RANGE);
1742 
1743 		case LOMBUS_VREG_SPACE:
1744 			aip->ahi_get8 = lombus_vreg_get8;
1745 			aip->ahi_put8 = lombus_vreg_put8;
1746 			aip->ahi_rep_get8 = lombus_vreg_rep_get8;
1747 			aip->ahi_rep_put8 = lombus_vreg_rep_put8;
1748 
1749 			aip->ahi_get16 = lombus_no_get16;
1750 			aip->ahi_put16 = lombus_no_put16;
1751 			aip->ahi_rep_get16 = lombus_no_rep_get16;
1752 			aip->ahi_rep_put16 = lombus_no_rep_put16;
1753 
1754 			aip->ahi_get32 = lombus_meta_get32;
1755 			aip->ahi_put32 = lombus_meta_put32;
1756 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
1757 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
1758 
1759 			aip->ahi_get64 = lombus_no_get64;
1760 			aip->ahi_put64 = lombus_no_put64;
1761 			aip->ahi_rep_get64 = lombus_no_rep_get64;
1762 			aip->ahi_rep_put64 = lombus_no_rep_put64;
1763 
1764 			aip->ahi_fault_check = lombus_acc_fault_check;
1765 			break;
1766 
1767 		case LOMBUS_PAT_SPACE:
1768 			aip->ahi_get8 = lombus_pat_get8;
1769 			aip->ahi_put8 = lombus_pat_put8;
1770 			aip->ahi_rep_get8 = lombus_pat_rep_get8;
1771 			aip->ahi_rep_put8 = lombus_pat_rep_put8;
1772 
1773 			aip->ahi_get16 = lombus_no_get16;
1774 			aip->ahi_put16 = lombus_no_put16;
1775 			aip->ahi_rep_get16 = lombus_no_rep_get16;
1776 			aip->ahi_rep_put16 = lombus_no_rep_put16;
1777 
1778 			aip->ahi_get32 = lombus_meta_get32;
1779 			aip->ahi_put32 = lombus_meta_put32;
1780 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
1781 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
1782 
1783 			aip->ahi_get64 = lombus_no_get64;
1784 			aip->ahi_put64 = lombus_no_put64;
1785 			aip->ahi_rep_get64 = lombus_no_rep_get64;
1786 			aip->ahi_rep_put64 = lombus_no_rep_put64;
1787 
1788 			aip->ahi_fault_check = lombus_acc_fault_check;
1789 			break;
1790 
1791 		case LOMBUS_EVENT_SPACE:
1792 			aip->ahi_get8 = lombus_no_get8;
1793 			aip->ahi_put8 = lombus_no_put8;
1794 			aip->ahi_rep_get8 = lombus_no_rep_get8;
1795 			aip->ahi_rep_put8 = lombus_no_rep_put8;
1796 
1797 			aip->ahi_get16 = lombus_event_get16;
1798 			aip->ahi_put16 = lombus_event_put16;
1799 			aip->ahi_rep_get16 = lombus_event_rep_get16;
1800 			aip->ahi_rep_put16 = lombus_event_rep_put16;
1801 
1802 			aip->ahi_get32 = lombus_meta_get32;
1803 			aip->ahi_put32 = lombus_meta_put32;
1804 			aip->ahi_rep_get32 = lombus_meta_rep_get32;
1805 			aip->ahi_rep_put32 = lombus_meta_rep_put32;
1806 
1807 			aip->ahi_get64 = lombus_no_get64;
1808 			aip->ahi_put64 = lombus_no_put64;
1809 			aip->ahi_rep_get64 = lombus_no_rep_get64;
1810 			aip->ahi_rep_put64 = lombus_no_rep_put64;
1811 
1812 			aip->ahi_fault_check = lombus_acc_fault_check;
1813 			break;
1814 		}
1815 		hdlp->ah_addr = *addrp = vaddr;
1816 		hdlp->ah_len = len;
1817 		hdlp->ah_bus_private = ssp;
1818 		return (DDI_SUCCESS);
1819 
1820 	case DDI_MO_UNMAP:
1821 		*addrp = NULL;
1822 		hdlp->ah_bus_private = NULL;
1823 		return (DDI_SUCCESS);
1824 	}
1825 }
1826 
1827 #endif	/* NDI_ACC_HDL_V2 */
1828 
1829 static int
1830 lombus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp,
1831 	off_t off, off_t len, caddr_t *addrp)
1832 {
1833 	struct lombus_child_info *lcip;
1834 	struct lombus_state *ssp;
1835 	lombus_regspec_t *rsp;
1836 
1837 	if ((ssp = lombus_getstate(dip, -1, "lombus_map")) == NULL)
1838 		return (DDI_FAILURE);	/* this "can't happen" */
1839 
1840 	/*
1841 	 * Validate mapping request ...
1842 	 */
1843 
1844 	if (mp->map_flags != DDI_MF_KERNEL_MAPPING)
1845 		return (DDI_ME_UNSUPPORTED);
1846 	if (mp->map_handlep == NULL)
1847 		return (DDI_ME_UNSUPPORTED);
1848 	if (mp->map_type != DDI_MT_RNUMBER)
1849 		return (DDI_ME_UNIMPLEMENTED);
1850 	if ((lcip = ddi_get_parent_data(rdip)) == NULL)
1851 		return (DDI_ME_INVAL);
1852 	if ((rsp = lcip->rsp) == NULL)
1853 		return (DDI_ME_INVAL);
1854 	if (mp->map_obj.rnumber >= lcip->nregs)
1855 		return (DDI_ME_RNUMBER_RANGE);
1856 	rsp += mp->map_obj.rnumber;
1857 	if (off < 0 || off >= rsp->lombus_size)
1858 		return (DDI_ME_INVAL);
1859 	if (len == 0)
1860 		len = rsp->lombus_size-off;
1861 	if (len < 0)
1862 		return (DDI_ME_INVAL);
1863 	if (off+len < 0 || off+len > rsp->lombus_size)
1864 		return (DDI_ME_INVAL);
1865 
1866 	return (lombus_map_handle(ssp, mp->map_op,
1867 	    rsp->lombus_space, VREG_TO_ADDR(rsp->lombus_base+off), len,
1868 	    mp->map_handlep, addrp));
1869 }
1870 
1871 static int
1872 lombus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op,
1873 	void *arg, void *result)
1874 {
1875 	struct lombus_child_info *lcip;
1876 	struct lombus_state *ssp;
1877 	lombus_regspec_t *rsp;
1878 	dev_info_t *cdip;
1879 	char addr[32];
1880 	uint_t nregs;
1881 	uint_t rnum;
1882 	int *regs;
1883 	int limit;
1884 	int err;
1885 	int i;
1886 
1887 	if ((ssp = lombus_getstate(dip, -1, "lombus_ctlops")) == NULL)
1888 		return (DDI_FAILURE);	/* this "can't happen" */
1889 
1890 	switch (op) {
1891 	default:
1892 		break;
1893 
1894 	case DDI_CTLOPS_INITCHILD:
1895 		/*
1896 		 * First, look up and validate the "reg" property.
1897 		 *
1898 		 * It must be a non-empty integer array containing a set
1899 		 * of triples.  Once we've verified that, we can treat it
1900 		 * as an array of type lombus_regspec_t[], which defines
1901 		 * the meaning of the elements of each triple:
1902 		 * +  the first element of each triple must be a valid space
1903 		 * +  the second and third elements (base, size) of each
1904 		 *	triple must define a valid subrange of that space
1905 		 * If it passes all the tests, we save it away for future
1906 		 * reference in the child's parent-private-data field.
1907 		 */
1908 		cdip = arg;
1909 		err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip,
1910 		    DDI_PROP_DONTPASS, "reg", &regs, &nregs);
1911 		lombus_trace(ssp, 'C', "initchild",
1912 		    "prop status %d size %d", err, nregs);
1913 		if (err != DDI_PROP_SUCCESS)
1914 			return (DDI_FAILURE);
1915 
1916 		err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0);
1917 		nregs /= LOMBUS_REGSPEC_SIZE;
1918 		rsp = (lombus_regspec_t *)regs;
1919 		for (i = 0; i < nregs && !err; ++i) {
1920 			switch (rsp[i].lombus_space) {
1921 			default:
1922 				limit = 0;
1923 				err = 1;
1924 				break;
1925 
1926 			case LOMBUS_VREG_SPACE:
1927 				limit = LOMBUS_MAX_REG+1;
1928 				break;
1929 
1930 			case LOMBUS_PAT_SPACE:
1931 				limit = LOMBUS_PAT_REG+1;
1932 				break;
1933 
1934 			case LOMBUS_EVENT_SPACE:
1935 				limit = LOMBUS_EVENT_REG+1;
1936 				break;
1937 			}
1938 
1939 			err |= (rsp[i].lombus_base < 0);
1940 			err |= (rsp[i].lombus_base >= limit);
1941 
1942 			if (rsp[i].lombus_size == 0)
1943 				rsp[i].lombus_size = limit-rsp[i].lombus_base;
1944 			err |= (rsp[i].lombus_size < 0);
1945 
1946 			err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0);
1947 			err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit);
1948 		}
1949 
1950 		if (err) {
1951 			ddi_prop_free(regs);
1952 			return (DDI_FAILURE);
1953 		}
1954 
1955 		lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP);
1956 		lcip->nregs = nregs;
1957 		lcip->rsp = rsp;
1958 		ddi_set_parent_data(cdip, lcip);
1959 
1960 		(void) snprintf(addr, sizeof (addr),
1961 		    "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base);
1962 		ddi_set_name_addr(cdip, addr);
1963 
1964 		return (DDI_SUCCESS);
1965 
1966 	case DDI_CTLOPS_UNINITCHILD:
1967 		cdip = arg;
1968 		ddi_set_name_addr(cdip, NULL);
1969 		lcip = ddi_get_parent_data(cdip);
1970 		ddi_set_parent_data(cdip, NULL);
1971 		ddi_prop_free(lcip->rsp);
1972 		kmem_free(lcip, sizeof (*lcip));
1973 		return (DDI_SUCCESS);
1974 
1975 	case DDI_CTLOPS_REPORTDEV:
1976 		if (rdip == NULL)
1977 			return (DDI_FAILURE);
1978 
1979 		cmn_err(CE_CONT, "?LOM device: %s@%s, %s#%d\n",
1980 		    ddi_node_name(rdip), ddi_get_name_addr(rdip),
1981 		    ddi_driver_name(dip), ddi_get_instance(dip));
1982 
1983 		return (DDI_SUCCESS);
1984 
1985 	case DDI_CTLOPS_REGSIZE:
1986 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
1987 			return (DDI_FAILURE);
1988 		if ((rnum = *(uint_t *)arg) >= lcip->nregs)
1989 			return (DDI_FAILURE);
1990 		*(off_t *)result = lcip->rsp[rnum].lombus_size;
1991 		return (DDI_SUCCESS);
1992 
1993 	case DDI_CTLOPS_NREGS:
1994 		if ((lcip = ddi_get_parent_data(rdip)) == NULL)
1995 			return (DDI_FAILURE);
1996 		*(int *)result = lcip->nregs;
1997 		return (DDI_SUCCESS);
1998 	}
1999 
2000 	return (ddi_ctlops(dip, rdip, op, arg, result));
2001 }
2002 
2003 
2004 /*
2005  *  Clean up on detach or failure of attach
2006  */
2007 static int
2008 lombus_unattach(struct lombus_state *ssp, int instance)
2009 {
2010 	if (ssp != NULL) {
2011 		lombus_hw_reset(ssp);
2012 		if (ssp->cycid != NULL) {
2013 			ddi_periodic_delete(ssp->cycid);
2014 			ssp->cycid = NULL;
2015 			if (ssp->sio_handle != NULL)
2016 				ddi_remove_intr(ssp->dip, 0, ssp->hw_iblk);
2017 			ddi_remove_softintr(ssp->softid);
2018 			cv_destroy(ssp->lo_cv);
2019 			mutex_destroy(ssp->lo_mutex);
2020 			mutex_destroy(ssp->hw_mutex);
2021 		}
2022 		lombus_offline(ssp);
2023 		ddi_set_driver_private(ssp->dip, NULL);
2024 	}
2025 
2026 	ddi_soft_state_free(lombus_statep, instance);
2027 	return (DDI_FAILURE);
2028 }
2029 
2030 /*
2031  *  Autoconfiguration routines
2032  */
2033 
2034 static int
2035 lombus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2036 {
2037 	struct lombus_state *ssp = NULL;
2038 	int instance;
2039 	int err;
2040 
2041 	switch (cmd) {
2042 	default:
2043 		return (DDI_FAILURE);
2044 
2045 	case DDI_ATTACH:
2046 		break;
2047 	}
2048 
2049 	/*
2050 	 *  Allocate the soft-state structure
2051 	 */
2052 	instance = ddi_get_instance(dip);
2053 	if (ddi_soft_state_zalloc(lombus_statep, instance) != DDI_SUCCESS)
2054 		return (DDI_FAILURE);
2055 	if ((ssp = lombus_getstate(dip, instance, "lombus_attach")) == NULL)
2056 		return (lombus_unattach(ssp, instance));
2057 	ddi_set_driver_private(dip, ssp);
2058 
2059 	/*
2060 	 *  Initialise devinfo-related fields
2061 	 */
2062 	ssp->dip = dip;
2063 	ssp->majornum = ddi_driver_major(dip);
2064 	ssp->instance = instance;
2065 
2066 	/*
2067 	 *  Set various options from .conf properties
2068 	 */
2069 	ssp->allow_echo = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2070 	    DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0;
2071 	ssp->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2072 	    DDI_PROP_DONTPASS, "baud-rate", 0);
2073 	ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2074 	    DDI_PROP_DONTPASS, "debug", 0);
2075 	ssp->fake_cts = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2076 	    DDI_PROP_DONTPASS, "fake-cts", 0) != 0;
2077 
2078 	/*
2079 	 * Initialise current state & time
2080 	 */
2081 	ssp->cmdstate = LOMBUS_CMDSTATE_IDLE;
2082 	ssp->hw_last_pat = gethrtime();
2083 	ssp->cycid = NULL;
2084 
2085 	/*
2086 	 *  Online the hardware ...
2087 	 */
2088 	err = lombus_online(ssp);
2089 	if (err != 0)
2090 		return (lombus_unattach(ssp, instance));
2091 
2092 	/*
2093 	 * Install soft and hard interrupt handler(s)
2094 	 * Initialise mutexes and cv
2095 	 * Start cyclic callbacks
2096 	 * Enable interrupts
2097 	 */
2098 	err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ssp->softid,
2099 	    &ssp->lo_iblk, NULL, lombus_softint, (caddr_t)ssp);
2100 	if (err != DDI_SUCCESS)
2101 		return (lombus_unattach(ssp, instance));
2102 
2103 	if (ssp->sio_handle != NULL)
2104 		err = ddi_add_intr(dip, 0, &ssp->hw_iblk, NULL,
2105 		    lombus_hi_intr, (caddr_t)ssp);
2106 
2107 	mutex_init(ssp->hw_mutex, NULL, MUTEX_DRIVER, ssp->hw_iblk);
2108 	mutex_init(ssp->lo_mutex, NULL, MUTEX_DRIVER, ssp->lo_iblk);
2109 	cv_init(ssp->lo_cv, NULL, CV_DRIVER, NULL);
2110 
2111 	/*
2112 	 * Register a periodical handler.
2113 	 */
2114 	ssp->cycid = ddi_periodic_add(lombus_cyclic, ssp, LOMBUS_ONE_SEC,
2115 	    DDI_IPL_1);
2116 
2117 	/*
2118 	 * Final check before enabling h/w interrupts - did
2119 	 * we successfully install the h/w interrupt handler?
2120 	 */
2121 	if (err != DDI_SUCCESS)
2122 		return (lombus_unattach(ssp, instance));
2123 
2124 	lombus_set_irq(ssp, B_TRUE);
2125 
2126 	/*
2127 	 *  All done, report success
2128 	 */
2129 	ddi_report_dev(dip);
2130 	return (DDI_SUCCESS);
2131 }
2132 
2133 
2134 static int
2135 lombus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2136 {
2137 	struct lombus_state *ssp;
2138 	int instance;
2139 
2140 	switch (cmd) {
2141 	default:
2142 		return (DDI_FAILURE);
2143 
2144 	case DDI_DETACH:
2145 		break;
2146 	}
2147 
2148 	instance = ddi_get_instance(dip);
2149 	if ((ssp = lombus_getstate(dip, instance, "lombus_detach")) == NULL)
2150 		return (DDI_FAILURE);	/* this "can't happen" */
2151 
2152 	(void) lombus_unattach(ssp, instance);
2153 	return (DDI_SUCCESS);
2154 }
2155 
2156 static int
2157 lombus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd)
2158 {
2159 	struct lombus_state *ssp;
2160 
2161 	_NOTE(ARGUNUSED(cmd))
2162 
2163 	if ((ssp = lombus_getstate(dip, -1, "lombus_reset")) == NULL)
2164 		return (DDI_FAILURE);
2165 
2166 	lombus_hw_reset(ssp);
2167 	return (DDI_SUCCESS);
2168 }
2169 
2170 
2171 /*
2172  * System interface structures
2173  */
2174 
2175 static struct cb_ops lombus_cb_ops =
2176 {
2177 	nodev,			/* b/c open	*/
2178 	nodev,			/* b/c close	*/
2179 	nodev,			/* b   strategy	*/
2180 	nodev,			/* b   print	*/
2181 	nodev,			/* b   dump 	*/
2182 	nodev,			/* c   read	*/
2183 	nodev,			/* c   write	*/
2184 	nodev,			/* c   ioctl	*/
2185 	nodev,			/* c   devmap	*/
2186 	nodev,			/* c   mmap	*/
2187 	nodev,			/* c   segmap	*/
2188 	nochpoll,		/* c   poll	*/
2189 	ddi_prop_op,		/* b/c prop_op	*/
2190 	NULL,			/* c   streamtab */
2191 	D_MP | D_NEW		/* b/c flags	*/
2192 };
2193 
2194 static struct bus_ops lombus_bus_ops =
2195 {
2196 	BUSO_REV,			/* revision		*/
2197 	lombus_map,			/* bus_map		*/
2198 	0,				/* get_intrspec		*/
2199 	0,				/* add_intrspec		*/
2200 	0,				/* remove_intrspec	*/
2201 	i_ddi_map_fault,		/* map_fault		*/
2202 	ddi_no_dma_map,			/* dma_map		*/
2203 	ddi_no_dma_allochdl,		/* allocate DMA handle	*/
2204 	ddi_no_dma_freehdl,		/* free DMA handle	*/
2205 	ddi_no_dma_bindhdl,		/* bind DMA handle	*/
2206 	ddi_no_dma_unbindhdl,		/* unbind DMA handle	*/
2207 	ddi_no_dma_flush,		/* flush DMA		*/
2208 	ddi_no_dma_win,			/* move DMA window	*/
2209 	ddi_no_dma_mctl,		/* generic DMA control	*/
2210 	lombus_ctlops,			/* generic control	*/
2211 	ddi_bus_prop_op,		/* prop_op		*/
2212 	ndi_busop_get_eventcookie,	/* get_eventcookie	*/
2213 	ndi_busop_add_eventcall,	/* add_eventcall	*/
2214 	ndi_busop_remove_eventcall,	/* remove_eventcall	*/
2215 	ndi_post_event,			/* post_event		*/
2216 	0,				/* interrupt control	*/
2217 	0,				/* bus_config		*/
2218 	0,				/* bus_unconfig		*/
2219 	0,				/* bus_fm_init		*/
2220 	0,				/* bus_fm_fini		*/
2221 	0,				/* bus_fm_access_enter	*/
2222 	0,				/* bus_fm_access_exit	*/
2223 	0,				/* bus_power		*/
2224 	i_ddi_intr_ops			/* bus_intr_op		*/
2225 };
2226 
2227 static struct dev_ops lombus_dev_ops =
2228 {
2229 	DEVO_REV,
2230 	0,				/* refcount		*/
2231 	ddi_no_info,			/* getinfo		*/
2232 	nulldev,			/* identify		*/
2233 	nulldev,			/* probe		*/
2234 	lombus_attach,			/* attach		*/
2235 	lombus_detach,			/* detach		*/
2236 	lombus_reset,			/* reset		*/
2237 	&lombus_cb_ops,			/* driver operations	*/
2238 	&lombus_bus_ops			/* bus operations	*/
2239 };
2240 
2241 static struct modldrv modldrv =
2242 {
2243 	&mod_driverops,
2244 	"lombus driver, v%I%",
2245 	&lombus_dev_ops
2246 };
2247 
2248 static struct modlinkage modlinkage =
2249 {
2250 	MODREV_1,
2251 	{
2252 		&modldrv,
2253 		NULL
2254 	}
2255 };
2256 
2257 
2258 /*
2259  *  Dynamic loader interface code
2260  */
2261 
2262 int
2263 _init(void)
2264 {
2265 	int err;
2266 
2267 	err = ddi_soft_state_init(&lombus_statep,
2268 	    sizeof (struct lombus_state), 0);
2269 	if (err == DDI_SUCCESS)
2270 		if ((err = mod_install(&modlinkage)) != 0) {
2271 			ddi_soft_state_fini(&lombus_statep);
2272 		}
2273 
2274 	return (err);
2275 }
2276 
2277 int
2278 _info(struct modinfo *mip)
2279 {
2280 	return (mod_info(&modlinkage, mip));
2281 }
2282 
2283 int
2284 _fini(void)
2285 {
2286 	int err;
2287 
2288 	if ((err = mod_remove(&modlinkage)) == 0) {
2289 		ddi_soft_state_fini(&lombus_statep);
2290 		lombus_major = NOMAJOR;
2291 	}
2292 
2293 	return (err);
2294 }
2295