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
lombus_trace(struct lombus_state * ssp,char code,const char * caller,const char * fmt,...)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 *
lombus_getstate(dev_info_t * dip,int instance,const char * caller)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
sio_put_reg(struct lombus_state * ssp,uint_t reg,uint8_t val)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
sio_get_reg(struct lombus_state * ssp,uint_t reg)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
sio_check_fault_status(struct lombus_state * ssp)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
sio_faulty(struct lombus_state * ssp)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
sio_data_ready(struct lombus_state * ssp)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
sio_lom_ready(struct lombus_state * ssp)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
lombus_set_irq(struct lombus_state * ssp,boolean_t newstate)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
lombus_toggle_rts(struct lombus_state * ssp)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
lombus_hi_intr(caddr_t arg)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
lombus_receive(struct lombus_state * ssp)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
lombus_softint(caddr_t arg)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
lombus_cyclic(void * arg)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
lombus_cmd(HANDLE_TYPE * hdlp,ptrdiff_t vreg,uint_t val,uint_t cmd)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
lombus_vreg_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_vreg_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_vreg_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_vreg_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_pat_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_pat_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_pat_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_pat_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_event_get16(HANDLE_TYPE * hdlp,uint16_t * addr)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
lombus_event_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)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
lombus_event_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_event_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_meta_get32(HANDLE_TYPE * hdlp,uint32_t * addr)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
lombus_meta_put32(HANDLE_TYPE * hdlp,uint32_t * addr,uint32_t val)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
lombus_meta_rep_get32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
lombus_meta_rep_put32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_no_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_no_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get16(HANDLE_TYPE * hdlp,uint16_t * addr)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
lombus_no_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)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
lombus_no_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get64(HANDLE_TYPE * hdlp,uint64_t * addr)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
lombus_no_put64(HANDLE_TYPE * hdlp,uint64_t * addr,uint64_t val)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
lombus_no_rep_get64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
lombus_acc_fault_check(HANDLE_TYPE * hdlp)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
lombus_hw_reset(struct lombus_state * ssp)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
lombus_offline(struct lombus_state * ssp)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
lombus_online(struct lombus_state * ssp)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
lombus_map_handle(struct lombus_state * ssp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ndi_acc_handle_t * hdlp,caddr_t * addrp)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
lombus_map_handle(struct lombus_state * ssp,ddi_map_op_t op,int space,caddr_t vaddr,off_t len,ddi_acc_hdl_t * hdlp,caddr_t * addrp)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
lombus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)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
lombus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)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", ®s, &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
lombus_unattach(struct lombus_state * ssp,int instance)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
lombus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
lombus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
lombus_reset(dev_info_t * dip,ddi_reset_cmd_t cmd)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
_init(void)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
_info(struct modinfo * mip)2280 _info(struct modinfo *mip)
2281 {
2282 return (mod_info(&modlinkage, mip));
2283 }
2284
2285 int
_fini(void)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