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