xref: /titanic_44/usr/src/uts/sun4u/lw2plus/io/lombus.c (revision c5a5e6f47e8f40ef4f4a14b199b09585e3ecf9a0)
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 2009 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 
36 /*
37  *  Header files
38  */
39 
40 #include <sys/types.h>
41 #include <sys/conf.h>
42 #include <sys/debug.h>
43 #include <sys/errno.h>
44 #include <sys/file.h>
45 #include <sys/intr.h>
46 #include <sys/kmem.h>
47 #include <sys/membar.h>
48 #include <sys/modctl.h>
49 #include <sys/note.h>
50 #include <sys/open.h>
51 #include <sys/poll.h>
52 #include <sys/spl.h>
53 #include <sys/stat.h>
54 #include <sys/strlog.h>
55 
56 #include <sys/ddi.h>
57 #include <sys/sunddi.h>
58 #include <sys/sunndi.h>
59 
60 #include <sys/lombus.h>
61 
62 
63 #if	defined(NDI_ACC_HDL_V2)
64 
65 /*
66  * Compiling for Solaris 9+ with access handle enhancements
67  */
68 #define	HANDLE_TYPE		ndi_acc_handle_t
69 #define	HANDLE_ADDR(hdlp)	(hdlp->ah_addr)
70 #define	HANDLE_FAULT(hdlp)	(hdlp->ah_fault)
71 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ah_len)
72 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ah_bus_private)
73 
74 #else
75 
76 /*
77  * Compatibility definitions for backport to Solaris 8
78  */
79 #define	HANDLE_TYPE		ddi_acc_impl_t
80 #define	HANDLE_ADDR(hdlp)	(hdlp->ahi_common.ah_addr)
81 #define	HANDLE_FAULT(hdlp)	(hdlp->ahi_fault)
82 #define	HANDLE_MAPLEN(hdlp)	(hdlp->ahi_common.ah_len)
83 #define	HANDLE_PRIVATE(hdlp)	(hdlp->ahi_common.ah_bus_private)
84 
85 #define	ddi_driver_major(dip)	ddi_name_to_major(ddi_binding_name(dip))
86 
87 #endif	/* NDI_ACC_HDL_V2 */
88 
89 
90 /*
91  * Local definitions
92  */
93 #define	MYNAME			"lombus"
94 #define	NOMAJOR			(~(major_t)0)
95 #define	DUMMY_VALUE		(~(int8_t)0)
96 
97 #define	LOMBUS_INST_TO_MINOR(i)	(i)
98 #define	LOMBUS_MINOR_TO_INST(m)	(m)
99 
100 #define	LOMBUS_DUMMY_ADDRESS	((caddr_t)0x0CADD1ED)
101 #define	ADDR_TO_OFFSET(a, hdlp)	((caddr_t)(a) - HANDLE_ADDR(hdlp))
102 #define	ADDR_TO_VREG(a)		((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS)
103 #define	VREG_TO_ADDR(v)		(LOMBUS_DUMMY_ADDRESS + (v))
104 
105 
106 /*
107  * The following definitions are taken from the datasheet
108  * for the National Semiconductor PC87317 (SuperIO) chip.
109  *
110  * This chip implements UART functionality as logical device 6.
111  * It provides all sorts of wierd modes and extensions, but we
112  * have chosen to use only the 16550-compatible features
113  * ("non-extended mode").
114  *
115  * Hardware: serial chip register numbers
116  */
117 #define	SIO_RXD			0	/* read		*/
118 #define	SIO_TXD			0	/* write	*/
119 #define	SIO_IER			1
120 #define	SIO_EIR			2	/* read		*/
121 #define	SIO_FCR			2	/* write	*/
122 #define	SIO_LCR			3
123 #define	SIO_BSR			3	/* wierd	*/
124 #define	SIO_MCR			4
125 #define	SIO_LSR			5
126 #define	SIO_MSR			6
127 #define	SIO_SCR			7
128 
129 #define	SIO_LBGDL		0	/* bank 1	*/
130 #define	SIO_LBGDH		1	/* bank 1	*/
131 
132 /*
133  * Hardware: serial chip register bits
134  */
135 #define	SIO_IER_RXHDL_IE	0x01
136 #define	SIO_IER_STD		0x00
137 
138 #define	SIO_EIR_IPF		0x01
139 #define	SIO_EIR_IPR0		0x02
140 #define	SIO_EIR_IPR1		0x04
141 #define	SIO_EIR_RXFT		0x08
142 #define	SIO_EIR_FEN0		0x40
143 #define	SIO_EIR_FEN1		0x80
144 
145 #define	SIO_FCR_FIFO_EN		0x01
146 #define	SIO_FCR_RXSR		0x02
147 #define	SIO_FCR_TXSR		0x04
148 #define	SIO_FCR_RXFTH0		0x40
149 #define	SIO_FCR_RXFTH1		0x80
150 #define	SIO_FCR_STD		(SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN)
151 
152 #define	SIO_LCR_WLS0		0x01
153 #define	SIO_LCR_WLS1		0x02
154 #define	SIO_LCR_STB		0x04
155 #define	SIO_LCR_PEN		0x08
156 #define	SIO_LCR_EPS		0x10
157 #define	SIO_LCR_STKP		0x20
158 #define	SIO_LCR_SBRK		0x40
159 #define	SIO_LCR_BKSE		0x80
160 #define	SIO_LCR_8BIT		(SIO_LCR_WLS0|SIO_LCR_WLS1)
161 #define	SIO_LCR_EPAR		(SIO_LCR_PEN|SIO_LCR_EPS)
162 #define	SIO_LCR_STD		(SIO_LCR_8BIT|SIO_LCR_EPAR)
163 
164 #define	SIO_BSR_BANK0		(SIO_LCR_STD)
165 #define	SIO_BSR_BANK1		(SIO_LCR_BKSE|SIO_LCR_STD)
166 
167 #define	SIO_MCR_DTR		0x01
168 #define	SIO_MCR_RTS		0x02
169 #define	SIO_MCR_ISEN		0x08
170 #define	SIO_MCR_STD		(SIO_MCR_ISEN)
171 
172 #define	SIO_LSR_RXDA		0x01
173 #define	SIO_LSR_OE		0x02
174 #define	SIO_LSR_PE		0x04
175 #define	SIO_LSR_FE		0x08
176 #define	SIO_LSR_BRKE		0x10
177 #define	SIO_LSR_TXRDY		0x20
178 #define	SIO_LSR_TXEMP		0x40
179 #define	SIO_LSR_ER_INF		0x80
180 
181 #define	SIO_MSR_DCTS		0x01
182 #define	SIO_MSR_DDSR		0x02
183 #define	SIO_MSR_TERI		0x04
184 #define	SIO_MSR_DDCD		0x08
185 #define	SIO_MSR_CTS		0x10
186 #define	SIO_MSR_DSR		0x20
187 #define	SIO_MSR_RI		0x40
188 #define	SIO_MSR_DCD		0x80
189 
190 /*
191  * Min/max/default baud rates, and a macro to convert from a baud
192  * rate to the number (divisor) to put in the baud rate registers
193  */
194 #define	SIO_BAUD_MIN		50
195 #define	SIO_BAUD_MAX		115200
196 #define	SIO_BAUD_DEFAULT	38400
197 #define	SIO_BAUD_TO_DIVISOR(b)	(115200 / (b))
198 
199 
200 /*
201  * Packet format ...
202  */
203 #define	LOMBUS_MASK		0xc0	/* Byte-type bits		*/
204 #define	LOMBUS_PARAM		0x00	/* Parameter byte: 0b0xxxxxxx	*/
205 #define	LOMBUS_LAST		0x80	/* Last byte of packet		*/
206 #define	LOMBUS_CMD		0x80	/* Command byte:   0b10###XWV	*/
207 #define	LOMBUS_STATUS		0xc0	/* Status  byte:   0b11###AEV	*/
208 
209 #define	LOMBUS_SEQ		0x38	/* Sequence number bits		*/
210 #define	LOMBUS_SEQ_LSB		0x08	/* Sequence number LSB		*/
211 #define	LOMBUS_CMD_XADDR	0x04	/* Extended (2-byte) addressing	*/
212 #define	LOMBUS_CMD_WRITE	0x02	/* Write command		*/
213 #define	LOMBUS_CMD_WMSB		0x01	/* Set MSB on Write		*/
214 #define	LOMBUS_CMD_READ		0x01	/* Read command			*/
215 #define	LOMBUS_CMD_NOP		0x00	/* NOP command			*/
216 
217 #define	LOMBUS_STATUS_ASYNC	0x04	/* Asynchronous event pending	*/
218 #define	LOMBUS_STATUS_ERR	0x02	/* Error in command processing	*/
219 #define	LOMBUS_STATUS_MSB	0x01	/* MSB of Value read		*/
220 
221 #define	LOMBUS_VREG_LO(x)	((x) & ((1 << 7) - 1))
222 #define	LOMBUS_VREG_HI(x)	((x) >> 7)
223 
224 #define	LOMBUS_BUFSIZE		8
225 
226 
227 /*
228  * Time periods, in nanoseconds
229  *
230  * Note that LOMBUS_ONE_SEC and some other time
231  * periods are defined in <sys/lombus.h>
232  */
233 #define	LOMBUS_CMD_POLL		(LOMBUS_ONE_SEC/20)
234 #define	LOMBUS_CTS_POLL		(LOMBUS_ONE_SEC/20)
235 #define	LOMBUS_CTS_TIMEOUT	(LOMBUS_ONE_SEC*2)
236 
237 
238 /*
239  * Local datatypes
240  */
241 enum lombus_cmdstate {
242 	LOMBUS_CMDSTATE_IDLE,
243 	LOMBUS_CMDSTATE_BUSY,
244 	LOMBUS_CMDSTATE_WAITING,
245 	LOMBUS_CMDSTATE_READY,
246 	LOMBUS_CMDSTATE_ERROR
247 };
248 
249 
250 /*
251  * This driver's soft-state structure
252  */
253 
254 struct lombus_state {
255 	/*
256 	 * Configuration data, set during attach
257 	 */
258 	dev_info_t *dip;
259 	major_t majornum;
260 	int instance;
261 
262 	ddi_acc_handle_t sio_handle;
263 	uint8_t *sio_regs;
264 	ddi_softintr_t softid;
265 	ddi_periodic_t cycid; /* periodical callback */
266 
267 	/*
268 	 * Parameters derived from .conf properties
269 	 */
270 	boolean_t allow_echo;
271 	int baud;
272 	uint32_t debug;
273 	boolean_t fake_cts;
274 
275 	/*
276 	 * Hardware mutex (initialised using <hw_iblk>),
277 	 * used to prevent retriggering the softint while
278 	 * it's still fetching data out of the chip FIFO.
279 	 */
280 	kmutex_t hw_mutex[1];
281 	ddi_iblock_cookie_t hw_iblk;
282 
283 	/*
284 	 * Data protected by the hardware mutex: the watchdog-patting
285 	 * protocol data (since the dog can be patted from a high-level
286 	 * cyclic), and the interrupt-enabled flag.
287 	 */
288 	hrtime_t hw_last_pat;
289 	boolean_t hw_int_enabled;
290 
291 	/*
292 	 * Flag to indicate that we've incurred a hardware fault on
293 	 * accesses to the SIO; once this is set, we fake all further
294 	 * accesses in order not to provoke additional bus errors.
295 	 */
296 	boolean_t sio_fault;
297 
298 	/*
299 	 * Serial protocol state data, protected by lo_mutex
300 	 * (which is initialised using <lo_iblk>)
301 	 */
302 	kmutex_t lo_mutex[1];
303 	ddi_iblock_cookie_t lo_iblk;
304 	kcondvar_t lo_cv[1];
305 
306 	volatile enum lombus_cmdstate cmdstate;
307 	clock_t deadline;
308 	uint8_t cmdbuf[LOMBUS_BUFSIZE];
309 	uint8_t reply[LOMBUS_BUFSIZE];
310 	uint8_t async;
311 	uint8_t index;
312 	uint8_t result;
313 	uint8_t sequence;
314 	uint32_t error;
315 };
316 
317 /*
318  * The auxiliary structure attached to each child
319  * (the child's parent-private-data points to this).
320  */
321 struct lombus_child_info {
322 	lombus_regspec_t *rsp;
323 	int nregs;
324 };
325 
326 
327 /*
328  * Local data
329  */
330 
331 static void *lombus_statep;
332 
333 static major_t lombus_major = NOMAJOR;
334 
335 static ddi_device_acc_attr_t lombus_dev_acc_attr[1] =
336 {
337 	DDI_DEVICE_ATTR_V0,
338 	DDI_STRUCTURE_LE_ACC,
339 	DDI_STRICTORDER_ACC
340 };
341 
342 
343 /*
344  *  General utility routines ...
345  */
346 
347 static void
348 lombus_trace(struct lombus_state *ssp, char code, const char *caller,
349 	const char *fmt, ...)
350 {
351 	char buf[256];
352 	char *p;
353 	va_list va;
354 
355 	if (ssp->debug & (1 << (code-'@'))) {
356 		p = buf;
357 		(void) snprintf(p, sizeof (buf) - (p - buf),
358 		    "%s/%s: ", MYNAME, caller);
359 		p += strlen(p);
360 
361 		va_start(va, fmt);
362 		(void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
363 		va_end(va);
364 
365 		buf[sizeof (buf) - 1] = '\0';
366 		(void) strlog(ssp->majornum, ssp->instance, code, SL_TRACE,
367 		    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 	uint8_t *p;
832 
833 	/*
834 	 * First of all, wait for the interface to be available.
835 	 *
836 	 * NOTE: we blow through all the mutex/cv/state checking and
837 	 * preempt any command in progress if the system is panicking!
838 	 */
839 	ssp = HANDLE_PRIVATE(hdlp);
840 	mutex_enter(ssp->lo_mutex);
841 	while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr)
842 		cv_wait(ssp->lo_cv, ssp->lo_mutex);
843 
844 	ssp->cmdstate = LOMBUS_CMDSTATE_BUSY;
845 	ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ;
846 
847 	/*
848 	 * We have exclusive ownership, so assemble the command (backwards):
849 	 *
850 	 * [byte 0]	Command:	modified by XADDR and/or WMSB bits
851 	 * [Optional] Parameter: 	Value to write (low 7 bits)
852 	 * [Optional] Parameter: 	Register number (high 7 bits)
853 	 * [Optional] Parameter: 	Register number (low 7 bits)
854 	 */
855 	p = &ssp->cmdbuf[0];
856 	*p++ = LOMBUS_CMD | ssp->sequence | cmd;
857 	switch (cmd) {
858 	case LOMBUS_CMD_WRITE:
859 		*p++ = val & 0x7f;
860 		if (val >= 0x80)
861 			ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB;
862 		/*FALLTHRU*/
863 	case LOMBUS_CMD_READ:
864 		if (LOMBUS_VREG_HI(vreg) != 0) {
865 			*p++ = LOMBUS_VREG_HI(vreg);
866 			ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR;
867 		}
868 		*p++ = LOMBUS_VREG_LO(vreg);
869 		/*FALLTHRU*/
870 	case LOMBUS_CMD_NOP:
871 		break;
872 	}
873 
874 	/*
875 	 * Check and update the SIO h/w fault status before accessing
876 	 * the chip registers.  If there's a (new or previous) fault,
877 	 * we'll run through the protocol but won't really touch the
878 	 * hardware and all commands will timeout.  If a previously
879 	 * discovered fault has now gone away (!), then we can (try to)
880 	 * proceed with the new command (probably a probe).
881 	 */
882 	sio_check_fault_status(ssp);
883 
884 	/*
885 	 * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
886 	 * us that it's ready for the next command.  If it doesn't, though,
887 	 * we'll send it anyway, on the basis that the CTS signal might be
888 	 * open- or short-circuited (or the LOM firmware forgot to set it,
889 	 * or the LOM just got reset, or whatever ...)
890 	 */
891 	start = ddi_get_lbolt();
892 	ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000);
893 	while (!sio_lom_ready(ssp)) {
894 		if (ddi_get_lbolt() > ssp->deadline)
895 			break;
896 
897 		(void) cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
898 		    drv_usectohz(LOMBUS_CTS_POLL/1000), TR_CLOCK_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 		if (cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
931 		    drv_usectohz(LOMBUS_CMD_POLL/1000), TR_CLOCK_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 		(void) 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 		(void) 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 	NULL,				/* power		*/
2240 	ddi_quiesce_not_supported,	/* devo_quiesce		*/
2241 };
2242 
2243 static struct modldrv modldrv =
2244 {
2245 	&mod_driverops,
2246 	"lombus driver",
2247 	&lombus_dev_ops
2248 };
2249 
2250 static struct modlinkage modlinkage =
2251 {
2252 	MODREV_1,
2253 	{
2254 		&modldrv,
2255 		NULL
2256 	}
2257 };
2258 
2259 
2260 /*
2261  *  Dynamic loader interface code
2262  */
2263 
2264 int
2265 _init(void)
2266 {
2267 	int err;
2268 
2269 	err = ddi_soft_state_init(&lombus_statep,
2270 	    sizeof (struct lombus_state), 0);
2271 	if (err == DDI_SUCCESS)
2272 		if ((err = mod_install(&modlinkage)) != 0) {
2273 			ddi_soft_state_fini(&lombus_statep);
2274 		}
2275 
2276 	return (err);
2277 }
2278 
2279 int
2280 _info(struct modinfo *mip)
2281 {
2282 	return (mod_info(&modlinkage, mip));
2283 }
2284 
2285 int
2286 _fini(void)
2287 {
2288 	int err;
2289 
2290 	if ((err = mod_remove(&modlinkage)) == 0) {
2291 		ddi_soft_state_fini(&lombus_statep);
2292 		lombus_major = NOMAJOR;
2293 	}
2294 
2295 	return (err);
2296 }
2297