12305Sstevel /* 22305Sstevel * CDDL HEADER START 32305Sstevel * 42305Sstevel * The contents of this file are subject to the terms of the 5*5107Seota * Common Development and Distribution License (the "License"). 6*5107Seota * You may not use this file except in compliance with the License. 72305Sstevel * 82305Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92305Sstevel * or http://www.opensolaris.org/os/licensing. 102305Sstevel * See the License for the specific language governing permissions 112305Sstevel * and limitations under the License. 122305Sstevel * 132305Sstevel * When distributing Covered Code, include this CDDL HEADER in each 142305Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152305Sstevel * If applicable, add the following below this CDDL HEADER, with the 162305Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 172305Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 182305Sstevel * 192305Sstevel * CDDL HEADER END 202305Sstevel */ 212305Sstevel /* 22*5107Seota * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 232305Sstevel * Use is subject to license terms. 242305Sstevel * 252305Sstevel * The "lombus" driver provides access to the LOMlite2 virtual registers, 262305Sstevel * so that its clients (children) need not be concerned with the details 272305Sstevel * of the access mechanism, which in this case is implemented via a 282305Sstevel * packet-based protocol over a serial link connected to one of the serial 292305Sstevel * ports of the SuperIO (SIO) chip. 302305Sstevel * 312305Sstevel * On the other hand, this driver doesn't generally know what the virtual 322305Sstevel * registers signify - only the clients need this information. 332305Sstevel */ 342305Sstevel 352305Sstevel #pragma ident "%Z%%M% %I% %E% SMI" 362305Sstevel 372305Sstevel /* 382305Sstevel * Header files 392305Sstevel */ 402305Sstevel 412305Sstevel #include <sys/types.h> 422305Sstevel #include <sys/conf.h> 432305Sstevel #include <sys/debug.h> 442305Sstevel #include <sys/errno.h> 452305Sstevel #include <sys/file.h> 462305Sstevel #include <sys/intr.h> 472305Sstevel #include <sys/kmem.h> 482305Sstevel #include <sys/membar.h> 492305Sstevel #include <sys/modctl.h> 502305Sstevel #include <sys/note.h> 512305Sstevel #include <sys/open.h> 522305Sstevel #include <sys/poll.h> 532305Sstevel #include <sys/spl.h> 542305Sstevel #include <sys/stat.h> 552305Sstevel #include <sys/strlog.h> 562305Sstevel 572305Sstevel #include <sys/ddi.h> 582305Sstevel #include <sys/sunddi.h> 592305Sstevel #include <sys/sunndi.h> 602305Sstevel 612305Sstevel #include <sys/lombus.h> 622305Sstevel 632305Sstevel 642305Sstevel #if defined(NDI_ACC_HDL_V2) 652305Sstevel 662305Sstevel /* 672305Sstevel * Compiling for Solaris 9+ with access handle enhancements 682305Sstevel */ 692305Sstevel #define HANDLE_TYPE ndi_acc_handle_t 702305Sstevel #define HANDLE_ADDR(hdlp) (hdlp->ah_addr) 712305Sstevel #define HANDLE_FAULT(hdlp) (hdlp->ah_fault) 722305Sstevel #define HANDLE_MAPLEN(hdlp) (hdlp->ah_len) 732305Sstevel #define HANDLE_PRIVATE(hdlp) (hdlp->ah_bus_private) 742305Sstevel 752305Sstevel #else 762305Sstevel 772305Sstevel /* 782305Sstevel * Compatibility definitions for backport to Solaris 8 792305Sstevel */ 802305Sstevel #define HANDLE_TYPE ddi_acc_impl_t 812305Sstevel #define HANDLE_ADDR(hdlp) (hdlp->ahi_common.ah_addr) 822305Sstevel #define HANDLE_FAULT(hdlp) (hdlp->ahi_fault) 832305Sstevel #define HANDLE_MAPLEN(hdlp) (hdlp->ahi_common.ah_len) 842305Sstevel #define HANDLE_PRIVATE(hdlp) (hdlp->ahi_common.ah_bus_private) 852305Sstevel 862305Sstevel #define ddi_driver_major(dip) ddi_name_to_major(ddi_binding_name(dip)) 872305Sstevel 882305Sstevel #endif /* NDI_ACC_HDL_V2 */ 892305Sstevel 902305Sstevel 912305Sstevel /* 922305Sstevel * Local definitions 932305Sstevel */ 942305Sstevel #define MYNAME "lombus" 952305Sstevel #define NOMAJOR (~(major_t)0) 962305Sstevel #define DUMMY_VALUE (~(int8_t)0) 972305Sstevel 982305Sstevel #define LOMBUS_INST_TO_MINOR(i) (i) 992305Sstevel #define LOMBUS_MINOR_TO_INST(m) (m) 1002305Sstevel 1012305Sstevel #define LOMBUS_DUMMY_ADDRESS ((caddr_t)0x0CADD1ED) 1022305Sstevel #define ADDR_TO_OFFSET(a, hdlp) ((caddr_t)(a) - HANDLE_ADDR(hdlp)) 1032305Sstevel #define ADDR_TO_VREG(a) ((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS) 1042305Sstevel #define VREG_TO_ADDR(v) (LOMBUS_DUMMY_ADDRESS + (v)) 1052305Sstevel 1062305Sstevel 1072305Sstevel /* 1082305Sstevel * The following definitions are taken from the datasheet 1092305Sstevel * for the National Semiconductor PC87317 (SuperIO) chip. 1102305Sstevel * 1112305Sstevel * This chip implements UART functionality as logical device 6. 1122305Sstevel * It provides all sorts of wierd modes and extensions, but we 1132305Sstevel * have chosen to use only the 16550-compatible features 1142305Sstevel * ("non-extended mode"). 1152305Sstevel * 1162305Sstevel * Hardware: serial chip register numbers 1172305Sstevel */ 1182305Sstevel #define SIO_RXD 0 /* read */ 1192305Sstevel #define SIO_TXD 0 /* write */ 1202305Sstevel #define SIO_IER 1 1212305Sstevel #define SIO_EIR 2 /* read */ 1222305Sstevel #define SIO_FCR 2 /* write */ 1232305Sstevel #define SIO_LCR 3 1242305Sstevel #define SIO_BSR 3 /* wierd */ 1252305Sstevel #define SIO_MCR 4 1262305Sstevel #define SIO_LSR 5 1272305Sstevel #define SIO_MSR 6 1282305Sstevel #define SIO_SCR 7 1292305Sstevel 1302305Sstevel #define SIO_LBGDL 0 /* bank 1 */ 1312305Sstevel #define SIO_LBGDH 1 /* bank 1 */ 1322305Sstevel 1332305Sstevel /* 1342305Sstevel * Hardware: serial chip register bits 1352305Sstevel */ 1362305Sstevel #define SIO_IER_RXHDL_IE 0x01 1372305Sstevel #define SIO_IER_STD 0x00 1382305Sstevel 1392305Sstevel #define SIO_EIR_IPF 0x01 1402305Sstevel #define SIO_EIR_IPR0 0x02 1412305Sstevel #define SIO_EIR_IPR1 0x04 1422305Sstevel #define SIO_EIR_RXFT 0x08 1432305Sstevel #define SIO_EIR_FEN0 0x40 1442305Sstevel #define SIO_EIR_FEN1 0x80 1452305Sstevel 1462305Sstevel #define SIO_FCR_FIFO_EN 0x01 1472305Sstevel #define SIO_FCR_RXSR 0x02 1482305Sstevel #define SIO_FCR_TXSR 0x04 1492305Sstevel #define SIO_FCR_RXFTH0 0x40 1502305Sstevel #define SIO_FCR_RXFTH1 0x80 1512305Sstevel #define SIO_FCR_STD (SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN) 1522305Sstevel 1532305Sstevel #define SIO_LCR_WLS0 0x01 1542305Sstevel #define SIO_LCR_WLS1 0x02 1552305Sstevel #define SIO_LCR_STB 0x04 1562305Sstevel #define SIO_LCR_PEN 0x08 1572305Sstevel #define SIO_LCR_EPS 0x10 1582305Sstevel #define SIO_LCR_STKP 0x20 1592305Sstevel #define SIO_LCR_SBRK 0x40 1602305Sstevel #define SIO_LCR_BKSE 0x80 1612305Sstevel #define SIO_LCR_8BIT (SIO_LCR_WLS0|SIO_LCR_WLS1) 1622305Sstevel #define SIO_LCR_EPAR (SIO_LCR_PEN|SIO_LCR_EPS) 1632305Sstevel #define SIO_LCR_STD (SIO_LCR_8BIT|SIO_LCR_EPAR) 1642305Sstevel 1652305Sstevel #define SIO_BSR_BANK0 (SIO_LCR_STD) 1662305Sstevel #define SIO_BSR_BANK1 (SIO_LCR_BKSE|SIO_LCR_STD) 1672305Sstevel 1682305Sstevel #define SIO_MCR_DTR 0x01 1692305Sstevel #define SIO_MCR_RTS 0x02 1702305Sstevel #define SIO_MCR_ISEN 0x08 1712305Sstevel #define SIO_MCR_STD (SIO_MCR_ISEN) 1722305Sstevel 1732305Sstevel #define SIO_LSR_RXDA 0x01 1742305Sstevel #define SIO_LSR_OE 0x02 1752305Sstevel #define SIO_LSR_PE 0x04 1762305Sstevel #define SIO_LSR_FE 0x08 1772305Sstevel #define SIO_LSR_BRKE 0x10 1782305Sstevel #define SIO_LSR_TXRDY 0x20 1792305Sstevel #define SIO_LSR_TXEMP 0x40 1802305Sstevel #define SIO_LSR_ER_INF 0x80 1812305Sstevel 1822305Sstevel #define SIO_MSR_DCTS 0x01 1832305Sstevel #define SIO_MSR_DDSR 0x02 1842305Sstevel #define SIO_MSR_TERI 0x04 1852305Sstevel #define SIO_MSR_DDCD 0x08 1862305Sstevel #define SIO_MSR_CTS 0x10 1872305Sstevel #define SIO_MSR_DSR 0x20 1882305Sstevel #define SIO_MSR_RI 0x40 1892305Sstevel #define SIO_MSR_DCD 0x80 1902305Sstevel 1912305Sstevel /* 1922305Sstevel * Min/max/default baud rates, and a macro to convert from a baud 1932305Sstevel * rate to the number (divisor) to put in the baud rate registers 1942305Sstevel */ 1952305Sstevel #define SIO_BAUD_MIN 50 1962305Sstevel #define SIO_BAUD_MAX 115200 1972305Sstevel #define SIO_BAUD_DEFAULT 38400 1982305Sstevel #define SIO_BAUD_TO_DIVISOR(b) (115200 / (b)) 1992305Sstevel 2002305Sstevel 2012305Sstevel /* 2022305Sstevel * Packet format ... 2032305Sstevel */ 2042305Sstevel #define LOMBUS_MASK 0xc0 /* Byte-type bits */ 2052305Sstevel #define LOMBUS_PARAM 0x00 /* Parameter byte: 0b0xxxxxxx */ 2062305Sstevel #define LOMBUS_LAST 0x80 /* Last byte of packet */ 2072305Sstevel #define LOMBUS_CMD 0x80 /* Command byte: 0b10###XWV */ 2082305Sstevel #define LOMBUS_STATUS 0xc0 /* Status byte: 0b11###AEV */ 2092305Sstevel 2102305Sstevel #define LOMBUS_SEQ 0x38 /* Sequence number bits */ 2112305Sstevel #define LOMBUS_SEQ_LSB 0x08 /* Sequence number LSB */ 2122305Sstevel #define LOMBUS_CMD_XADDR 0x04 /* Extended (2-byte) addressing */ 2132305Sstevel #define LOMBUS_CMD_WRITE 0x02 /* Write command */ 2142305Sstevel #define LOMBUS_CMD_WMSB 0x01 /* Set MSB on Write */ 2152305Sstevel #define LOMBUS_CMD_READ 0x01 /* Read command */ 2162305Sstevel #define LOMBUS_CMD_NOP 0x00 /* NOP command */ 2172305Sstevel 2182305Sstevel #define LOMBUS_STATUS_ASYNC 0x04 /* Asynchronous event pending */ 2192305Sstevel #define LOMBUS_STATUS_ERR 0x02 /* Error in command processing */ 2202305Sstevel #define LOMBUS_STATUS_MSB 0x01 /* MSB of Value read */ 2212305Sstevel 2222305Sstevel #define LOMBUS_VREG_LO(x) ((x) & ((1 << 7) - 1)) 2232305Sstevel #define LOMBUS_VREG_HI(x) ((x) >> 7) 2242305Sstevel 2252305Sstevel #define LOMBUS_BUFSIZE 8 2262305Sstevel 2272305Sstevel 2282305Sstevel /* 2292305Sstevel * Time periods, in nanoseconds 2302305Sstevel * 2312305Sstevel * Note that LOMBUS_ONE_SEC and some other time 2322305Sstevel * periods are defined in <sys/lombus.h> 2332305Sstevel */ 2342305Sstevel #define LOMBUS_CMD_POLL (LOMBUS_ONE_SEC/20) 2352305Sstevel #define LOMBUS_CTS_POLL (LOMBUS_ONE_SEC/20) 2362305Sstevel #define LOMBUS_CTS_TIMEOUT (LOMBUS_ONE_SEC*2) 2372305Sstevel 2382305Sstevel 2392305Sstevel /* 2402305Sstevel * Local datatypes 2412305Sstevel */ 2422305Sstevel enum lombus_cmdstate { 2432305Sstevel LOMBUS_CMDSTATE_IDLE, 2442305Sstevel LOMBUS_CMDSTATE_BUSY, 2452305Sstevel LOMBUS_CMDSTATE_WAITING, 2462305Sstevel LOMBUS_CMDSTATE_READY, 2472305Sstevel LOMBUS_CMDSTATE_ERROR 2482305Sstevel }; 2492305Sstevel 2502305Sstevel 2512305Sstevel /* 2522305Sstevel * This driver's soft-state structure 2532305Sstevel */ 2542305Sstevel 2552305Sstevel struct lombus_state { 2562305Sstevel /* 2572305Sstevel * Configuration data, set during attach 2582305Sstevel */ 2592305Sstevel dev_info_t *dip; 2602305Sstevel major_t majornum; 2612305Sstevel int instance; 2622305Sstevel 2632305Sstevel ddi_acc_handle_t sio_handle; 2642305Sstevel uint8_t *sio_regs; 2652305Sstevel ddi_softintr_t softid; 266*5107Seota ddi_periodic_t cycid; /* periodical callback */ 2672305Sstevel 2682305Sstevel /* 2692305Sstevel * Parameters derived from .conf properties 2702305Sstevel */ 2712305Sstevel boolean_t allow_echo; 2722305Sstevel int baud; 2732305Sstevel uint32_t debug; 2742305Sstevel boolean_t fake_cts; 2752305Sstevel 2762305Sstevel /* 2772305Sstevel * Hardware mutex (initialised using <hw_iblk>), 2782305Sstevel * used to prevent retriggering the softint while 2792305Sstevel * it's still fetching data out of the chip FIFO. 2802305Sstevel */ 2812305Sstevel kmutex_t hw_mutex[1]; 2822305Sstevel ddi_iblock_cookie_t hw_iblk; 2832305Sstevel 2842305Sstevel /* 2852305Sstevel * Data protected by the hardware mutex: the watchdog-patting 2862305Sstevel * protocol data (since the dog can be patted from a high-level 2872305Sstevel * cyclic), and the interrupt-enabled flag. 2882305Sstevel */ 2892305Sstevel hrtime_t hw_last_pat; 2902305Sstevel boolean_t hw_int_enabled; 2912305Sstevel 2922305Sstevel /* 2932305Sstevel * Flag to indicate that we've incurred a hardware fault on 2942305Sstevel * accesses to the SIO; once this is set, we fake all further 2952305Sstevel * accesses in order not to provoke additional bus errors. 2962305Sstevel */ 2972305Sstevel boolean_t sio_fault; 2982305Sstevel 2992305Sstevel /* 3002305Sstevel * Serial protocol state data, protected by lo_mutex 3012305Sstevel * (which is initialised using <lo_iblk>) 3022305Sstevel */ 3032305Sstevel kmutex_t lo_mutex[1]; 3042305Sstevel ddi_iblock_cookie_t lo_iblk; 3052305Sstevel kcondvar_t lo_cv[1]; 3062305Sstevel 3072305Sstevel volatile enum lombus_cmdstate cmdstate; 3082305Sstevel clock_t deadline; 3092305Sstevel uint8_t cmdbuf[LOMBUS_BUFSIZE]; 3102305Sstevel uint8_t reply[LOMBUS_BUFSIZE]; 3112305Sstevel uint8_t async; 3122305Sstevel uint8_t index; 3132305Sstevel uint8_t result; 3142305Sstevel uint8_t sequence; 3152305Sstevel uint32_t error; 3162305Sstevel }; 3172305Sstevel 3182305Sstevel /* 3192305Sstevel * The auxiliary structure attached to each child 3202305Sstevel * (the child's parent-private-data points to this). 3212305Sstevel */ 3222305Sstevel struct lombus_child_info { 3232305Sstevel lombus_regspec_t *rsp; 3242305Sstevel int nregs; 3252305Sstevel }; 3262305Sstevel 3272305Sstevel 3282305Sstevel /* 3292305Sstevel * Local data 3302305Sstevel */ 3312305Sstevel 3322305Sstevel static void *lombus_statep; 3332305Sstevel 3342305Sstevel static major_t lombus_major = NOMAJOR; 3352305Sstevel 3362305Sstevel static ddi_device_acc_attr_t lombus_dev_acc_attr[1] = 3372305Sstevel { 3382305Sstevel DDI_DEVICE_ATTR_V0, 3392305Sstevel DDI_STRUCTURE_LE_ACC, 3402305Sstevel DDI_STRICTORDER_ACC 3412305Sstevel }; 3422305Sstevel 3432305Sstevel 3442305Sstevel /* 3452305Sstevel * General utility routines ... 3462305Sstevel */ 3472305Sstevel 3482305Sstevel static void 3492305Sstevel lombus_trace(struct lombus_state *ssp, char code, const char *caller, 3502305Sstevel const char *fmt, ...) 3512305Sstevel { 3522305Sstevel char buf[256]; 3532305Sstevel char *p; 3542305Sstevel va_list va; 3552305Sstevel 3562305Sstevel if (ssp->debug & (1 << (code-'@'))) { 3572305Sstevel p = buf; 3582305Sstevel snprintf(p, sizeof (buf) - (p - buf), 359*5107Seota "%s/%s: ", MYNAME, caller); 3602305Sstevel p += strlen(p); 3612305Sstevel 3622305Sstevel va_start(va, fmt); 3632305Sstevel vsnprintf(p, sizeof (buf) - (p - buf), fmt, va); 3642305Sstevel va_end(va); 3652305Sstevel 3662305Sstevel buf[sizeof (buf) - 1] = '\0'; 3672305Sstevel strlog(ssp->majornum, ssp->instance, code, SL_TRACE, buf); 3682305Sstevel } 3692305Sstevel } 3702305Sstevel 3712305Sstevel static struct lombus_state * 3722305Sstevel lombus_getstate(dev_info_t *dip, int instance, const char *caller) 3732305Sstevel { 3742305Sstevel struct lombus_state *ssp = NULL; 3752305Sstevel dev_info_t *sdip = NULL; 3762305Sstevel major_t dmaj = NOMAJOR; 3772305Sstevel 3782305Sstevel if (dip != NULL) { 3792305Sstevel /* 3802305Sstevel * Use the instance number from the <dip>; also, 3812305Sstevel * check that it really corresponds to this driver 3822305Sstevel */ 3832305Sstevel instance = ddi_get_instance(dip); 3842305Sstevel dmaj = ddi_driver_major(dip); 3852305Sstevel if (lombus_major == NOMAJOR && dmaj != NOMAJOR) 3862305Sstevel lombus_major = dmaj; 3872305Sstevel else if (dmaj != lombus_major) { 3882305Sstevel cmn_err(CE_WARN, 3892305Sstevel "%s: major number mismatch (%d vs. %d) in %s()," 3902305Sstevel "probably due to child misconfiguration", 3912305Sstevel MYNAME, lombus_major, dmaj, caller); 3922305Sstevel instance = -1; 3932305Sstevel } 3942305Sstevel } 3952305Sstevel 3962305Sstevel if (instance >= 0) 3972305Sstevel ssp = ddi_get_soft_state(lombus_statep, instance); 3982305Sstevel if (ssp != NULL) { 3992305Sstevel sdip = ssp->dip; 4002305Sstevel if (dip == NULL && sdip == NULL) 4012305Sstevel ssp = NULL; 4022305Sstevel else if (dip != NULL && sdip != NULL && sdip != dip) { 4032305Sstevel cmn_err(CE_WARN, 4042305Sstevel "%s: devinfo mismatch (%p vs. %p) in %s(), " 4052305Sstevel "probably due to child misconfiguration", 4062305Sstevel MYNAME, (void *)dip, (void *)sdip, caller); 4072305Sstevel ssp = NULL; 4082305Sstevel } 4092305Sstevel } 4102305Sstevel 4112305Sstevel return (ssp); 4122305Sstevel } 4132305Sstevel 4142305Sstevel /* 4152305Sstevel * Lowest-level serial I/O chip register read/write 4162305Sstevel */ 4172305Sstevel 4182305Sstevel static void 4192305Sstevel sio_put_reg(struct lombus_state *ssp, uint_t reg, uint8_t val) 4202305Sstevel { 4212305Sstevel lombus_trace(ssp, 'P', "sio_put_reg", "REG[%d] <- $%02x", reg, val); 4222305Sstevel 4232305Sstevel if (ssp->sio_handle != NULL && !ssp->sio_fault) { 4242305Sstevel /* 4252305Sstevel * The chip is mapped as "I/O" (e.g. with the side-effect 4262305Sstevel * bit on SPARC), therefore accesses are required to be 4272305Sstevel * in-order, with no value cacheing. However, there can 4282305Sstevel * still be write-behind buffering, so it is not guaranteed 4292305Sstevel * that a write actually reaches the chip in a given time. 4302305Sstevel * 4312305Sstevel * To force the access right through to the chip, we follow 4322305Sstevel * the write with another write (to the SCRATCH register) 4332305Sstevel * and a read (of the value just written to the SCRATCH 4342305Sstevel * register). The SCRATCH register is specifically provided 4352305Sstevel * for temporary data and has no effect on the SIO's own 4362305Sstevel * operation, making it ideal as a synchronising mechanism. 4372305Sstevel * 4382305Sstevel * If we didn't do this, it would be possible that the new 4392305Sstevel * value wouldn't reach the chip (and have the *intended* 4402305Sstevel * side-effects, such as disabling interrupts), for such a 4412305Sstevel * long time that the processor could execute a *lot* of 4422305Sstevel * instructions - including exiting the interrupt service 4432305Sstevel * routine and re-enabling interrupts. This effect was 4442305Sstevel * observed to lead to spurious (unclaimed) interrupts in 4452305Sstevel * some circumstances. 4462305Sstevel * 4472305Sstevel * This will no longer be needed once "synchronous" access 4482305Sstevel * handles are available (see PSARC/2000/269 and 2000/531). 4492305Sstevel */ 4502305Sstevel ddi_put8(ssp->sio_handle, ssp->sio_regs + reg, val); 4512305Sstevel ddi_put8(ssp->sio_handle, ssp->sio_regs + SIO_SCR, val); 4522305Sstevel membar_sync(); 4532305Sstevel (void) ddi_get8(ssp->sio_handle, ssp->sio_regs + SIO_SCR); 4542305Sstevel } 4552305Sstevel } 4562305Sstevel 4572305Sstevel static uint8_t 4582305Sstevel sio_get_reg(struct lombus_state *ssp, uint_t reg) 4592305Sstevel { 4602305Sstevel uint8_t val; 4612305Sstevel 4622305Sstevel if (ssp->sio_handle && !ssp->sio_fault) 4632305Sstevel val = ddi_get8(ssp->sio_handle, ssp->sio_regs + reg); 4642305Sstevel else 4652305Sstevel val = DUMMY_VALUE; 4662305Sstevel 4672305Sstevel lombus_trace(ssp, 'G', "sio_get_reg", "$%02x <- REG[%d]", val, reg); 4682305Sstevel 4692305Sstevel return (val); 4702305Sstevel } 4712305Sstevel 4722305Sstevel static void 4732305Sstevel sio_check_fault_status(struct lombus_state *ssp) 4742305Sstevel { 4752305Sstevel ssp->sio_fault = ddi_check_acc_handle(ssp->sio_handle) != DDI_SUCCESS; 4762305Sstevel } 4772305Sstevel 4782305Sstevel static boolean_t 4792305Sstevel sio_faulty(struct lombus_state *ssp) 4802305Sstevel { 4812305Sstevel if (!ssp->sio_fault) 4822305Sstevel sio_check_fault_status(ssp); 4832305Sstevel return (ssp->sio_fault); 4842305Sstevel } 4852305Sstevel 4862305Sstevel 4872305Sstevel /* 4882305Sstevel * Check for data ready. 4892305Sstevel */ 4902305Sstevel static boolean_t 4912305Sstevel sio_data_ready(struct lombus_state *ssp) 4922305Sstevel { 4932305Sstevel uint8_t status; 4942305Sstevel 4952305Sstevel /* 4962305Sstevel * Data is available if the RXDA bit in the LSR is nonzero 4972305Sstevel * (if reading it didn't incur a fault). 4982305Sstevel */ 4992305Sstevel status = sio_get_reg(ssp, SIO_LSR); 5002305Sstevel return ((status & SIO_LSR_RXDA) != 0 && !sio_faulty(ssp)); 5012305Sstevel } 5022305Sstevel 5032305Sstevel /* 5042305Sstevel * Check for LOM ready 5052305Sstevel */ 5062305Sstevel static boolean_t 5072305Sstevel sio_lom_ready(struct lombus_state *ssp) 5082305Sstevel { 5092305Sstevel uint8_t status; 5102305Sstevel boolean_t rslt; 5112305Sstevel 5122305Sstevel /* 5132305Sstevel * The LOM is ready if the CTS bit in the MSR is 1, meaning 5142305Sstevel * that the /CTS signal is being asserted (driven LOW) - 5152305Sstevel * unless we incurred a fault in trying to read the MSR! 5162305Sstevel * 5172305Sstevel * For debugging, we force the result to TRUE if the FAKE flag is set 5182305Sstevel */ 5192305Sstevel status = sio_get_reg(ssp, SIO_MSR); 5202305Sstevel rslt = (status & SIO_MSR_CTS) != 0 && !sio_faulty(ssp); 5212305Sstevel 5222305Sstevel lombus_trace(ssp, 'R', "sio_lom_ready", "S $%02x R %d F %d", 523*5107Seota status, rslt, ssp->fake_cts); 5242305Sstevel 5252305Sstevel return (rslt || ssp->fake_cts); 5262305Sstevel } 5272305Sstevel 5282305Sstevel #if 0 5292305Sstevel /* 5302305Sstevel * Check for interrupt pending 5312305Sstevel */ 5322305Sstevel static boolean_t 5332305Sstevel sio_irq_pending(struct lombus_state *ssp) 5342305Sstevel { 5352305Sstevel uint8_t status; 5362305Sstevel boolean_t rslt; 5372305Sstevel 5382305Sstevel /* 5392305Sstevel * An interrupt is pending if the IPF bit in the EIR is 0, 5402305Sstevel * assuming we didn't incur a fault in trying to ready it. 5412305Sstevel * 5422305Sstevel * Note: we expect that every time we read this register 5432305Sstevel * (which is only done from the interrupt service routine), 5442305Sstevel * we will see $11001100 (RX FIFO timeout interrupt pending). 5452305Sstevel */ 5462305Sstevel status = sio_get_reg(ssp, SIO_EIR); 5472305Sstevel 5482305Sstevel rslt = (status & SIO_EIR_IPF) == 0 && !sio_faulty(ssp); 5492305Sstevel lombus_trace(ssp, 'I', "sio_irq_pending", "S $%02x R %d", 550*5107Seota status, rslt); 5512305Sstevel 5522305Sstevel /* 5532305Sstevel * To investigate whether we're getting any abnormal interrupts 5542305Sstevel * this code checks that the status value is as expected, and that 5552305Sstevel * chip-level interrupts are supposed to be enabled at this time. 5562305Sstevel * This will cause a PANIC (on a driver compiled with DEBUG) if 5572305Sstevel * all is not as expected ... 5582305Sstevel */ 5592305Sstevel ASSERT(status == 0xCC); 5602305Sstevel ASSERT(ssp->hw_int_enabled); 5612305Sstevel 5622305Sstevel return (rslt); 5632305Sstevel } 5642305Sstevel #endif /* 0 */ 5652305Sstevel 5662305Sstevel /* 5672305Sstevel * Enable/disable interrupts 5682305Sstevel */ 5692305Sstevel static void 5702305Sstevel lombus_set_irq(struct lombus_state *ssp, boolean_t newstate) 5712305Sstevel { 5722305Sstevel uint8_t val; 5732305Sstevel 5742305Sstevel val = newstate ? SIO_IER_RXHDL_IE : 0; 5752305Sstevel sio_put_reg(ssp, SIO_IER, SIO_IER_STD | val); 5762305Sstevel ssp->hw_int_enabled = newstate; 5772305Sstevel } 5782305Sstevel 5792305Sstevel /* 5802305Sstevel * Assert/deassert RTS 5812305Sstevel */ 5822305Sstevel static void 5832305Sstevel lombus_toggle_rts(struct lombus_state *ssp) 5842305Sstevel { 5852305Sstevel uint8_t val; 5862305Sstevel 5872305Sstevel val = sio_get_reg(ssp, SIO_MCR); 5882305Sstevel val &= SIO_MCR_RTS; 5892305Sstevel val ^= SIO_MCR_RTS; 5902305Sstevel val |= SIO_MCR_STD; 5912305Sstevel sio_put_reg(ssp, SIO_MCR, val); 5922305Sstevel } 5932305Sstevel 5942305Sstevel 5952305Sstevel /* 5962305Sstevel * High-level interrupt handler: 5972305Sstevel * Checks whether initialisation is complete (to avoid a race 5982305Sstevel * with mutex_init()), and whether chip interrupts are enabled. 5992305Sstevel * If not, the interrupt's not for us, so just return UNCLAIMED. 6002305Sstevel * Otherwise, disable the interrupt, trigger a softint, and return 6012305Sstevel * CLAIMED. The softint handler will then do all the real work. 6022305Sstevel * 6032305Sstevel * NOTE: the chip interrupt capability is only re-enabled once the 6042305Sstevel * receive code has run, but that can be called from a poll loop 6052305Sstevel * or cyclic callback as well as from the softint. So it's *not* 6062305Sstevel * guaranteed that there really is a chip interrupt pending here, 6072305Sstevel * 'cos the work may already have been done and the reason for the 6082305Sstevel * interrupt gone away before we get here. 6092305Sstevel * 6102305Sstevel * OTOH, if we come through here twice without the receive code 6112305Sstevel * having run in between, that's definitely wrong. In such an 6122305Sstevel * event, we would notice that chip interrupts haven't yet been 6132305Sstevel * re-enabled and return UNCLAIMED, allowing the system's jabber 6142305Sstevel * protect code (if any) to do its job. 6152305Sstevel */ 6162305Sstevel static uint_t 6172305Sstevel lombus_hi_intr(caddr_t arg) 6182305Sstevel { 6192305Sstevel struct lombus_state *ssp = (void *)arg; 6202305Sstevel uint_t claim; 6212305Sstevel 6222305Sstevel claim = DDI_INTR_UNCLAIMED; 623*5107Seota if (ssp->cycid != NULL) { 6242305Sstevel mutex_enter(ssp->hw_mutex); 6252305Sstevel if (ssp->hw_int_enabled) { 6262305Sstevel lombus_set_irq(ssp, B_FALSE); 6272305Sstevel ddi_trigger_softintr(ssp->softid); 6282305Sstevel claim = DDI_INTR_CLAIMED; 6292305Sstevel } 6302305Sstevel mutex_exit(ssp->hw_mutex); 6312305Sstevel } 6322305Sstevel 6332305Sstevel return (claim); 6342305Sstevel } 6352305Sstevel 6362305Sstevel /* 6372305Sstevel * Packet receive handler 6382305Sstevel * 6392305Sstevel * This routine should be called from the low-level softint, or the 6402305Sstevel * cyclic callback, or lombus_cmd() (for polled operation), with the 6412305Sstevel * low-level mutex already held. 6422305Sstevel */ 6432305Sstevel static void 6442305Sstevel lombus_receive(struct lombus_state *ssp) 6452305Sstevel { 6462305Sstevel boolean_t ready = B_FALSE; 6472305Sstevel uint8_t data = 0; 6482305Sstevel uint8_t rcvd = 0; 6492305Sstevel uint8_t tmp; 6502305Sstevel 6512305Sstevel lombus_trace(ssp, 'S', "lombus_receive", 652*5107Seota "state %d; error $%x", 653*5107Seota ssp->cmdstate, ssp->error); 6542305Sstevel 6552305Sstevel /* 6562305Sstevel * Check for access faults before starting the receive 6572305Sstevel * loop (we don't want to cause bus errors or suchlike 6582305Sstevel * unpleasantness in the event that the SIO has died). 6592305Sstevel */ 6602305Sstevel if (!sio_faulty(ssp)) { 6612305Sstevel /* 6622305Sstevel * Read bytes from the FIFO until they're all gone, 6632305Sstevel * or we find the 'END OF PACKET' set on one, or 6642305Sstevel * our buffer overflows (which must be an error) 6652305Sstevel */ 6662305Sstevel mutex_enter(ssp->hw_mutex); 6672305Sstevel while (sio_data_ready(ssp)) { 6682305Sstevel data = sio_get_reg(ssp, SIO_RXD); 6692305Sstevel ssp->reply[rcvd = ssp->index] = data; 6702305Sstevel if (++rcvd >= LOMBUS_BUFSIZE) 6712305Sstevel break; 6722305Sstevel ssp->index = rcvd; 6732305Sstevel if (data & LOMBUS_LAST) 6742305Sstevel break; 6752305Sstevel } 6762305Sstevel lombus_set_irq(ssp, B_TRUE); 6772305Sstevel mutex_exit(ssp->hw_mutex); 6782305Sstevel } 6792305Sstevel 6802305Sstevel lombus_trace(ssp, 'S', "lombus_receive", 681*5107Seota "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x", 682*5107Seota rcvd, 683*5107Seota ssp->reply[0], ssp->reply[1], 684*5107Seota ssp->reply[2], ssp->reply[3], 685*5107Seota ssp->reply[4], ssp->reply[5], 686*5107Seota ssp->reply[6], ssp->reply[7]); 6872305Sstevel 6882305Sstevel if (ssp->cmdstate != LOMBUS_CMDSTATE_WAITING) { 6892305Sstevel /* 6902305Sstevel * We're not expecting any data in this state, so if 6912305Sstevel * we DID receive any data, we just throw it away by 6922305Sstevel * resetting the buffer index to 0. 6932305Sstevel */ 6942305Sstevel ssp->index = 0; 6952305Sstevel } else if (rcvd == 0) { 6962305Sstevel /* 6972305Sstevel * No bytes received this time through (though there 6982305Sstevel * might be a partial packet sitting in the buffer). 6992305Sstevel * If it seems the LOM is taking too long to respond, 7002305Sstevel * we'll assume it's died and return an error. 7012305Sstevel */ 7022305Sstevel if (ddi_get_lbolt() > ssp->deadline) { 7032305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_ERROR; 7042305Sstevel ssp->error = LOMBUS_ERR_TIMEOUT; 7052305Sstevel ready = B_TRUE; 7062305Sstevel } 7072305Sstevel } else if (rcvd >= LOMBUS_BUFSIZE) { 7082305Sstevel /* 7092305Sstevel * Buffer overflow; discard the data & treat as an error 7102305Sstevel * (even if the last byte read did claim to terminate a 7112305Sstevel * packet, it can't be a valid one 'cos it's too long!) 7122305Sstevel */ 7132305Sstevel ssp->index = 0; 7142305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_ERROR; 7152305Sstevel ssp->error = LOMBUS_ERR_OFLOW; 7162305Sstevel ready = B_TRUE; 7172305Sstevel } else if ((data & LOMBUS_LAST) == 0) { 7182305Sstevel /* 7192305Sstevel * Packet not yet complete; leave the partial packet in 7202305Sstevel * the buffer for later ... 7212305Sstevel */ 7222305Sstevel _NOTE(EMPTY) 7232305Sstevel ; 7242305Sstevel } else if ((data & LOMBUS_MASK) != LOMBUS_STATUS) { 7252305Sstevel /* 7262305Sstevel * Invalid "status" byte - maybe an echo of the command? 7272305Sstevel * 7282305Sstevel * As a debugging feature, we allow for this, assuming 7292305Sstevel * that if the LOM has echoed the command byte, it has 7302305Sstevel * also echoed all the parameter bytes before starting 7312305Sstevel * command processing. So, we dump out the buffer and 7322305Sstevel * then clear it, so we can go back to looking for the 7332305Sstevel * real reply. 7342305Sstevel * 7352305Sstevel * Otherwise, we just drop the data & flag an error. 7362305Sstevel */ 7372305Sstevel if (ssp->allow_echo) { 7382305Sstevel lombus_trace(ssp, 'E', "lombus_receive", 739*5107Seota "echo $%02x $%02x $%02x $%02x " 740*5107Seota "$%02x $%02x $%02x $%02x", 741*5107Seota ssp->reply[0], ssp->reply[1], 742*5107Seota ssp->reply[2], ssp->reply[3], 743*5107Seota ssp->reply[4], ssp->reply[5], 744*5107Seota ssp->reply[6], ssp->reply[7]); 7452305Sstevel ssp->index = 0; 7462305Sstevel } else { 7472305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_ERROR; 7482305Sstevel ssp->error = LOMBUS_ERR_BADSTATUS; 7492305Sstevel ready = B_TRUE; 7502305Sstevel } 7512305Sstevel } else if ((data & LOMBUS_SEQ) != ssp->sequence) { 7522305Sstevel /* 7532305Sstevel * Wrong sequence number! Flag this as an error 7542305Sstevel */ 7552305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_ERROR; 7562305Sstevel ssp->error = LOMBUS_ERR_SEQUENCE; 7572305Sstevel ready = B_TRUE; 7582305Sstevel } else { 7592305Sstevel /* 7602305Sstevel * Finally, we know that's it's a valid reply to our 7612305Sstevel * last command. Update the ASYNC status, derive the 7622305Sstevel * reply parameter (if any), and check the ERROR bit 7632305Sstevel * to find out what the parameter means. 7642305Sstevel * 7652305Sstevel * Note that not all the values read/assigned here 7662305Sstevel * are meaningful, but it doesn't matter; the waiting 7672305Sstevel * thread will know which one(s) it should check. 7682305Sstevel */ 7692305Sstevel ssp->async = (data & LOMBUS_STATUS_ASYNC) ? 1 : 0; 7702305Sstevel tmp = ((data & LOMBUS_STATUS_MSB) ? 0x80 : 0) | ssp->reply[0]; 7712305Sstevel if (data & LOMBUS_STATUS_ERR) { 7722305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_ERROR; 7732305Sstevel ssp->error = tmp; 7742305Sstevel } else { 7752305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_READY; 7762305Sstevel ssp->result = tmp; 7772305Sstevel } 7782305Sstevel ready = B_TRUE; 7792305Sstevel } 7802305Sstevel 7812305Sstevel lombus_trace(ssp, 'T', "lombus_receive", 782*5107Seota "rcvd %d; last $%02x; state %d; error $%x; ready %d", 783*5107Seota rcvd, data, ssp->cmdstate, ssp->error, ready); 7842305Sstevel 7852305Sstevel if (ready) 7862305Sstevel cv_broadcast(ssp->lo_cv); 7872305Sstevel } 7882305Sstevel 7892305Sstevel /* 7902305Sstevel * Low-level softint handler 7912305Sstevel * 7922305Sstevel * This routine should be triggered whenever there's a byte to be read 7932305Sstevel */ 7942305Sstevel static uint_t 7952305Sstevel lombus_softint(caddr_t arg) 7962305Sstevel { 7972305Sstevel struct lombus_state *ssp = (void *)arg; 7982305Sstevel 7992305Sstevel mutex_enter(ssp->lo_mutex); 8002305Sstevel lombus_receive(ssp); 8012305Sstevel mutex_exit(ssp->lo_mutex); 8022305Sstevel 8032305Sstevel return (DDI_INTR_CLAIMED); 8042305Sstevel } 8052305Sstevel 8062305Sstevel /* 8072305Sstevel * Cyclic handler: just calls the receive routine, in case interrupts 8082305Sstevel * are not being delivered and in order to handle command timeout 8092305Sstevel */ 8102305Sstevel static void 8112305Sstevel lombus_cyclic(void *arg) 8122305Sstevel { 8132305Sstevel struct lombus_state *ssp = (void *)arg; 8142305Sstevel 8152305Sstevel mutex_enter(ssp->lo_mutex); 8162305Sstevel lombus_receive(ssp); 8172305Sstevel mutex_exit(ssp->lo_mutex); 8182305Sstevel } 8192305Sstevel 8202305Sstevel 8212305Sstevel /* 8222305Sstevel * Serial protocol 8232305Sstevel * 8242305Sstevel * This routine builds a command and sets it in progress. 8252305Sstevel */ 8262305Sstevel static uint8_t 8272305Sstevel lombus_cmd(HANDLE_TYPE *hdlp, ptrdiff_t vreg, uint_t val, uint_t cmd) 8282305Sstevel { 8292305Sstevel struct lombus_state *ssp; 8302305Sstevel clock_t start; 8312305Sstevel clock_t tick; 8322305Sstevel uint8_t *p; 8332305Sstevel 8342305Sstevel /* 8352305Sstevel * First of all, wait for the interface to be available. 8362305Sstevel * 8372305Sstevel * NOTE: we blow through all the mutex/cv/state checking and 8382305Sstevel * preempt any command in progress if the system is panicking! 8392305Sstevel */ 8402305Sstevel ssp = HANDLE_PRIVATE(hdlp); 8412305Sstevel mutex_enter(ssp->lo_mutex); 8422305Sstevel while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr) 8432305Sstevel cv_wait(ssp->lo_cv, ssp->lo_mutex); 8442305Sstevel 8452305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_BUSY; 8462305Sstevel ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ; 8472305Sstevel 8482305Sstevel /* 8492305Sstevel * We have exclusive ownership, so assemble the command (backwards): 8502305Sstevel * 8512305Sstevel * [byte 0] Command: modified by XADDR and/or WMSB bits 8522305Sstevel * [Optional] Parameter: Value to write (low 7 bits) 8532305Sstevel * [Optional] Parameter: Register number (high 7 bits) 8542305Sstevel * [Optional] Parameter: Register number (low 7 bits) 8552305Sstevel */ 8562305Sstevel p = &ssp->cmdbuf[0]; 8572305Sstevel *p++ = LOMBUS_CMD | ssp->sequence | cmd; 8582305Sstevel switch (cmd) { 8592305Sstevel case LOMBUS_CMD_WRITE: 8602305Sstevel *p++ = val & 0x7f; 8612305Sstevel if (val >= 0x80) 8622305Sstevel ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB; 8632305Sstevel /*FALLTHRU*/ 8642305Sstevel case LOMBUS_CMD_READ: 8652305Sstevel if (LOMBUS_VREG_HI(vreg) != 0) { 8662305Sstevel *p++ = LOMBUS_VREG_HI(vreg); 8672305Sstevel ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR; 8682305Sstevel } 8692305Sstevel *p++ = LOMBUS_VREG_LO(vreg); 8702305Sstevel /*FALLTHRU*/ 8712305Sstevel case LOMBUS_CMD_NOP: 8722305Sstevel break; 8732305Sstevel } 8742305Sstevel 8752305Sstevel /* 8762305Sstevel * Check and update the SIO h/w fault status before accessing 8772305Sstevel * the chip registers. If there's a (new or previous) fault, 8782305Sstevel * we'll run through the protocol but won't really touch the 8792305Sstevel * hardware and all commands will timeout. If a previously 8802305Sstevel * discovered fault has now gone away (!), then we can (try to) 8812305Sstevel * proceed with the new command (probably a probe). 8822305Sstevel */ 8832305Sstevel sio_check_fault_status(ssp); 8842305Sstevel 8852305Sstevel /* 8862305Sstevel * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell 8872305Sstevel * us that it's ready for the next command. If it doesn't, though, 8882305Sstevel * we'll send it anyway, on the basis that the CTS signal might be 8892305Sstevel * open- or short-circuited (or the LOM firmware forgot to set it, 8902305Sstevel * or the LOM just got reset, or whatever ...) 8912305Sstevel */ 8922305Sstevel start = ddi_get_lbolt(); 8932305Sstevel ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000); 8942305Sstevel while (!sio_lom_ready(ssp)) { 8952305Sstevel if ((tick = ddi_get_lbolt()) > ssp->deadline) 8962305Sstevel break; 8972305Sstevel tick += drv_usectohz(LOMBUS_CTS_POLL/1000); 8982305Sstevel cv_timedwait(ssp->lo_cv, ssp->lo_mutex, tick); 8992305Sstevel } 9002305Sstevel 9012305Sstevel /* 9022305Sstevel * Either the LOM is ready, or we timed out waiting for CTS. 9032305Sstevel * In either case, we're going to send the command now by 9042305Sstevel * stuffing the packet into the Tx FIFO, reversing it as we go. 9052305Sstevel * We call lombus_receive() first to ensure there isn't any 9062305Sstevel * garbage left in the Rx FIFO from an earlier command that 9072305Sstevel * timed out (or was pre-empted by a PANIC!). This also makes 9082305Sstevel * sure that SIO interrupts are enabled so we'll see the reply 9092305Sstevel * more quickly (the poll loop below will still work even if 9102305Sstevel * interrupts aren't enabled, but it will take longer). 9112305Sstevel */ 9122305Sstevel lombus_receive(ssp); 9132305Sstevel mutex_enter(ssp->hw_mutex); 9142305Sstevel while (p > ssp->cmdbuf) 9152305Sstevel sio_put_reg(ssp, SIO_TXD, *--p); 9162305Sstevel mutex_exit(ssp->hw_mutex); 9172305Sstevel 9182305Sstevel /* 9192305Sstevel * Prepare for the reply (to be processed by the interrupt/cyclic 9202305Sstevel * handler and/or polling loop below), then wait for a response 9212305Sstevel * or timeout. 9222305Sstevel */ 9232305Sstevel start = ddi_get_lbolt(); 9242305Sstevel ssp->deadline = start + drv_usectohz(LOMBUS_CMD_TIMEOUT/1000); 9252305Sstevel ssp->error = 0; 9262305Sstevel ssp->index = 0; 9272305Sstevel ssp->result = DUMMY_VALUE; 9282305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_WAITING; 9292305Sstevel while (ssp->cmdstate == LOMBUS_CMDSTATE_WAITING) { 9302305Sstevel tick = ddi_get_lbolt() + drv_usectohz(LOMBUS_CMD_POLL/1000); 9312305Sstevel if (cv_timedwait(ssp->lo_cv, ssp->lo_mutex, tick) == -1) 9322305Sstevel lombus_receive(ssp); 9332305Sstevel } 9342305Sstevel 9352305Sstevel /* 9362305Sstevel * The return value may not be meaningful but retrieve it anyway 9372305Sstevel */ 9382305Sstevel val = ssp->result; 9392305Sstevel if (sio_faulty(ssp)) { 9402305Sstevel val = DUMMY_VALUE; 9412305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_SIOHW; 9422305Sstevel } else if (ssp->cmdstate != LOMBUS_CMDSTATE_READY) { 9432305Sstevel /* 9442305Sstevel * Some problem here ... transfer the error code from 9452305Sstevel * the per-instance state to the per-handle fault flag. 9462305Sstevel * The error code shouldn't be zero! 9472305Sstevel */ 9482305Sstevel if (ssp->error != 0) 9492305Sstevel HANDLE_FAULT(hdlp) = ssp->error; 9502305Sstevel else 9512305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_BADERRCODE; 9522305Sstevel } 9532305Sstevel 9542305Sstevel /* 9552305Sstevel * All done now! 9562305Sstevel */ 9572305Sstevel ssp->index = 0; 9582305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_IDLE; 9592305Sstevel cv_broadcast(ssp->lo_cv); 9602305Sstevel mutex_exit(ssp->lo_mutex); 9612305Sstevel 9622305Sstevel return (val); 9632305Sstevel } 9642305Sstevel 9652305Sstevel 9662305Sstevel /* 9672305Sstevel * Space 0 - LOM virtual register access 9682305Sstevel * Only 8-bit accesses are supported. 9692305Sstevel */ 9702305Sstevel static uint8_t 9712305Sstevel lombus_vreg_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 9722305Sstevel { 9732305Sstevel ptrdiff_t offset; 9742305Sstevel 9752305Sstevel /* 9762305Sstevel * Check the offset that the caller has added to the base address 9772305Sstevel * against the length of the mapping originally requested. 9782305Sstevel */ 9792305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 9802305Sstevel if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 9812305Sstevel /* 9822305Sstevel * Invalid access - flag a fault and return a dummy value 9832305Sstevel */ 9842305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 9852305Sstevel return (DUMMY_VALUE); 9862305Sstevel } 9872305Sstevel 9882305Sstevel /* 9892305Sstevel * Derive the virtual register number and run the command 9902305Sstevel */ 9912305Sstevel return (lombus_cmd(hdlp, ADDR_TO_VREG(addr), 0, LOMBUS_CMD_READ)); 9922305Sstevel } 9932305Sstevel 9942305Sstevel static void 9952305Sstevel lombus_vreg_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 9962305Sstevel { 9972305Sstevel ptrdiff_t offset; 9982305Sstevel 9992305Sstevel /* 10002305Sstevel * Check the offset that the caller has added to the base address 10012305Sstevel * against the length of the mapping originally requested. 10022305Sstevel */ 10032305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 10042305Sstevel if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 10052305Sstevel /* 10062305Sstevel * Invalid access - flag a fault and return 10072305Sstevel */ 10082305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 10092305Sstevel return; 10102305Sstevel } 10112305Sstevel 10122305Sstevel /* 10132305Sstevel * Derive the virtual register number and run the command 10142305Sstevel */ 10152305Sstevel (void) lombus_cmd(hdlp, ADDR_TO_VREG(addr), val, LOMBUS_CMD_WRITE); 10162305Sstevel } 10172305Sstevel 10182305Sstevel static void 10192305Sstevel lombus_vreg_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 10202305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 10212305Sstevel { 10222305Sstevel size_t inc; 10232305Sstevel 10242305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 10252305Sstevel for (; repcount--; dev_addr += inc) 10262305Sstevel *host_addr++ = lombus_vreg_get8(hdlp, dev_addr); 10272305Sstevel } 10282305Sstevel 10292305Sstevel static void 10302305Sstevel lombus_vreg_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 10312305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 10322305Sstevel { 10332305Sstevel size_t inc; 10342305Sstevel 10352305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 10362305Sstevel for (; repcount--; dev_addr += inc) 10372305Sstevel lombus_vreg_put8(hdlp, dev_addr, *host_addr++); 10382305Sstevel } 10392305Sstevel 10402305Sstevel 10412305Sstevel /* 10422305Sstevel * Space 1 - LOM watchdog pat register access 10432305Sstevel * Only 8-bit accesses are supported. 10442305Sstevel * 10452305Sstevel * Reads have no effect and return 0. 10462305Sstevel * 10472305Sstevel * Writes pat the dog by toggling the RTS line iff enough time has 10482305Sstevel * elapsed since last time we toggled it. 10492305Sstevel * 10502305Sstevel * Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient 10512305Sstevel * way of zeroing the destination area ;-) and still won't pat the dog. 10522305Sstevel * 10532305Sstevel * Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly 10542305Sstevel * only count as a single pat, no matter how many bytes the caller 10552305Sstevel * says to write, as the inter-pat time is VERY long compared with 10562305Sstevel * the time it will take to read the memory source area. 10572305Sstevel */ 10582305Sstevel 10592305Sstevel static uint8_t 10602305Sstevel lombus_pat_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 10612305Sstevel { 10622305Sstevel ptrdiff_t offset; 10632305Sstevel 10642305Sstevel /* 10652305Sstevel * Check the offset that the caller has added to the base address 10662305Sstevel * against the length of the mapping originally requested. 10672305Sstevel */ 10682305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 10692305Sstevel if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 10702305Sstevel /* 10712305Sstevel * Invalid access - flag a fault and return a dummy value 10722305Sstevel */ 10732305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 10742305Sstevel return (DUMMY_VALUE); 10752305Sstevel } 10762305Sstevel 10772305Sstevel return (0); 10782305Sstevel } 10792305Sstevel 10802305Sstevel static void 10812305Sstevel lombus_pat_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 10822305Sstevel { 10832305Sstevel struct lombus_state *ssp; 10842305Sstevel ptrdiff_t offset; 10852305Sstevel hrtime_t now; 10862305Sstevel 10872305Sstevel _NOTE(ARGUNUSED(val)) 10882305Sstevel 10892305Sstevel /* 10902305Sstevel * Check the offset that the caller has added to the base address 10912305Sstevel * against the length of the mapping originally requested. 10922305Sstevel */ 10932305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 10942305Sstevel if (offset < 0 || offset >= HANDLE_MAPLEN(hdlp)) { 10952305Sstevel /* 10962305Sstevel * Invalid access - flag a fault and return 10972305Sstevel */ 10982305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 10992305Sstevel return; 11002305Sstevel } 11012305Sstevel 11022305Sstevel ssp = HANDLE_PRIVATE(hdlp); 11032305Sstevel mutex_enter(ssp->hw_mutex); 11042305Sstevel now = gethrtime(); 11052305Sstevel if ((now - ssp->hw_last_pat) >= LOMBUS_MIN_PAT) { 11062305Sstevel lombus_toggle_rts(ssp); 11072305Sstevel ssp->hw_last_pat = now; 11082305Sstevel } 11092305Sstevel mutex_exit(ssp->hw_mutex); 11102305Sstevel } 11112305Sstevel 11122305Sstevel static void 11132305Sstevel lombus_pat_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 11142305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 11152305Sstevel { 11162305Sstevel size_t inc; 11172305Sstevel 11182305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 11192305Sstevel for (; repcount--; dev_addr += inc) 11202305Sstevel *host_addr++ = lombus_pat_get8(hdlp, dev_addr); 11212305Sstevel } 11222305Sstevel 11232305Sstevel static void 11242305Sstevel lombus_pat_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 11252305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 11262305Sstevel { 11272305Sstevel size_t inc; 11282305Sstevel 11292305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 11302305Sstevel for (; repcount--; dev_addr += inc) 11312305Sstevel lombus_pat_put8(hdlp, dev_addr, *host_addr++); 11322305Sstevel } 11332305Sstevel 11342305Sstevel 11352305Sstevel /* 11362305Sstevel * Space 2 - LOM async event flag register access 11372305Sstevel * Only 16-bit accesses are supported. 11382305Sstevel */ 11392305Sstevel static uint16_t 11402305Sstevel lombus_event_get16(HANDLE_TYPE *hdlp, uint16_t *addr) 11412305Sstevel { 11422305Sstevel struct lombus_state *ssp; 11432305Sstevel ptrdiff_t offset; 11442305Sstevel 11452305Sstevel /* 11462305Sstevel * Check the offset that the caller has added to the base address 11472305Sstevel * against the length of the mapping orignally requested. 11482305Sstevel */ 11492305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 11502305Sstevel if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) { 11512305Sstevel /* 11522305Sstevel * Invalid access - flag a fault and return a dummy value 11532305Sstevel */ 11542305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 11552305Sstevel return (DUMMY_VALUE); 11562305Sstevel } 11572305Sstevel 11582305Sstevel /* 11592305Sstevel * Return the value of the asynchronous-event-pending flag 11602305Sstevel * as passed back by the LOM at the end of the last command. 11612305Sstevel */ 11622305Sstevel ssp = HANDLE_PRIVATE(hdlp); 11632305Sstevel return (ssp->async); 11642305Sstevel } 11652305Sstevel 11662305Sstevel static void 11672305Sstevel lombus_event_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val) 11682305Sstevel { 11692305Sstevel ptrdiff_t offset; 11702305Sstevel 11712305Sstevel _NOTE(ARGUNUSED(val)) 11722305Sstevel 11732305Sstevel /* 11742305Sstevel * Check the offset that the caller has added to the base address 11752305Sstevel * against the length of the mapping originally requested. 11762305Sstevel */ 11772305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 11782305Sstevel if (offset < 0 || (offset%2) != 0 || offset >= HANDLE_MAPLEN(hdlp)) { 11792305Sstevel /* 11802305Sstevel * Invalid access - flag a fault and return 11812305Sstevel */ 11822305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_NUM; 11832305Sstevel return; 11842305Sstevel } 11852305Sstevel 11862305Sstevel /* 11872305Sstevel * The user can't overwrite the asynchronous-event-pending flag! 11882305Sstevel */ 11892305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_RO; 11902305Sstevel } 11912305Sstevel 11922305Sstevel static void 11932305Sstevel lombus_event_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 11942305Sstevel uint16_t *dev_addr, size_t repcount, uint_t flags) 11952305Sstevel { 11962305Sstevel size_t inc; 11972305Sstevel 11982305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 11992305Sstevel for (; repcount--; dev_addr += inc) 12002305Sstevel *host_addr++ = lombus_event_get16(hdlp, dev_addr); 12012305Sstevel } 12022305Sstevel 12032305Sstevel static void 12042305Sstevel lombus_event_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 12052305Sstevel uint16_t *dev_addr, size_t repcount, uint_t flags) 12062305Sstevel { 12072305Sstevel size_t inc; 12082305Sstevel 12092305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 12102305Sstevel for (; repcount--; dev_addr += inc) 12112305Sstevel lombus_event_put16(hdlp, dev_addr, *host_addr++); 12122305Sstevel } 12132305Sstevel 12142305Sstevel 12152305Sstevel /* 12162305Sstevel * All spaces - access handle fault information 12172305Sstevel * Only 32-bit accesses are supported. 12182305Sstevel */ 12192305Sstevel static uint32_t 12202305Sstevel lombus_meta_get32(HANDLE_TYPE *hdlp, uint32_t *addr) 12212305Sstevel { 12222305Sstevel struct lombus_state *ssp; 12232305Sstevel ptrdiff_t offset; 12242305Sstevel 12252305Sstevel /* 12262305Sstevel * Derive the offset that the caller has added to the base 12272305Sstevel * address originally returned, and use it to determine 12282305Sstevel * which meta-register is to be accessed ... 12292305Sstevel */ 12302305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 12312305Sstevel switch (offset) { 12322305Sstevel case LOMBUS_FAULT_REG: 12332305Sstevel /* 12342305Sstevel * This meta-register provides a code for the most 12352305Sstevel * recent virtual register access fault, if any. 12362305Sstevel */ 12372305Sstevel return (HANDLE_FAULT(hdlp)); 12382305Sstevel 12392305Sstevel case LOMBUS_PROBE_REG: 12402305Sstevel /* 12412305Sstevel * Reading this meta-register clears any existing fault 12422305Sstevel * (at the virtual, not the hardware access layer), then 12432305Sstevel * runs a NOP command and returns the fault code from that. 12442305Sstevel */ 12452305Sstevel HANDLE_FAULT(hdlp) = 0; 12462305Sstevel lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP); 12472305Sstevel return (HANDLE_FAULT(hdlp)); 12482305Sstevel 12492305Sstevel case LOMBUS_ASYNC_REG: 12502305Sstevel /* 12512305Sstevel * Obsolescent - but still supported for backwards 12522305Sstevel * compatibility. This is an alias for the newer 12532305Sstevel * LOMBUS_EVENT_REG, but doesn't require a separate 12542305Sstevel * "reg" entry and ddi_regs_map_setup() call. 12552305Sstevel * 12562305Sstevel * It returns the value of the asynchronous-event-pending 12572305Sstevel * flag as passed back by the LOM at the end of the last 12582305Sstevel * completed command. 12592305Sstevel */ 12602305Sstevel ssp = HANDLE_PRIVATE(hdlp); 12612305Sstevel return (ssp->async); 12622305Sstevel 12632305Sstevel default: 12642305Sstevel /* 12652305Sstevel * Invalid access - flag a fault and return a dummy value 12662305Sstevel */ 12672305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 12682305Sstevel return (DUMMY_VALUE); 12692305Sstevel } 12702305Sstevel } 12712305Sstevel 12722305Sstevel static void 12732305Sstevel lombus_meta_put32(HANDLE_TYPE *hdlp, uint32_t *addr, uint32_t val) 12742305Sstevel { 12752305Sstevel ptrdiff_t offset; 12762305Sstevel 12772305Sstevel /* 12782305Sstevel * Derive the offset that the caller has added to the base 12792305Sstevel * address originally returned, and use it to determine 12802305Sstevel * which meta-register is to be accessed ... 12812305Sstevel */ 12822305Sstevel offset = ADDR_TO_OFFSET(addr, hdlp); 12832305Sstevel switch (offset) { 12842305Sstevel case LOMBUS_FAULT_REG: 12852305Sstevel /* 12862305Sstevel * This meta-register contains a code for the most 12872305Sstevel * recent virtual register access fault, if any. 12882305Sstevel * It can be cleared simply by writing 0 to it. 12892305Sstevel */ 12902305Sstevel HANDLE_FAULT(hdlp) = val; 12912305Sstevel return; 12922305Sstevel 12932305Sstevel case LOMBUS_PROBE_REG: 12942305Sstevel /* 12952305Sstevel * Writing this meta-register clears any existing fault 12962305Sstevel * (at the virtual, not the hardware acess layer), then 12972305Sstevel * runs a NOP command. The caller can check the fault 12982305Sstevel * code later if required. 12992305Sstevel */ 13002305Sstevel HANDLE_FAULT(hdlp) = 0; 13012305Sstevel lombus_cmd(hdlp, 0, 0, LOMBUS_CMD_NOP); 13022305Sstevel return; 13032305Sstevel 13042305Sstevel default: 13052305Sstevel /* 13062305Sstevel * Invalid access - flag a fault 13072305Sstevel */ 13082305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13092305Sstevel return; 13102305Sstevel } 13112305Sstevel } 13122305Sstevel 13132305Sstevel static void 13142305Sstevel lombus_meta_rep_get32(HANDLE_TYPE *hdlp, uint32_t *host_addr, 13152305Sstevel uint32_t *dev_addr, size_t repcount, uint_t flags) 13162305Sstevel { 13172305Sstevel size_t inc; 13182305Sstevel 13192305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 13202305Sstevel for (; repcount--; dev_addr += inc) 13212305Sstevel *host_addr++ = lombus_meta_get32(hdlp, dev_addr); 13222305Sstevel } 13232305Sstevel 13242305Sstevel static void 13252305Sstevel lombus_meta_rep_put32(HANDLE_TYPE *hdlp, uint32_t *host_addr, 13262305Sstevel uint32_t *dev_addr, size_t repcount, uint_t flags) 13272305Sstevel { 13282305Sstevel size_t inc; 13292305Sstevel 13302305Sstevel inc = (flags & DDI_DEV_AUTOINCR) ? 1 : 0; 13312305Sstevel for (; repcount--; dev_addr += inc) 13322305Sstevel lombus_meta_put32(hdlp, dev_addr, *host_addr++); 13332305Sstevel } 13342305Sstevel 13352305Sstevel 13362305Sstevel /* 13372305Sstevel * Finally, some dummy functions for all unsupported access 13382305Sstevel * space/size/mode combinations ... 13392305Sstevel */ 13402305Sstevel static uint8_t 13412305Sstevel lombus_no_get8(HANDLE_TYPE *hdlp, uint8_t *addr) 13422305Sstevel { 13432305Sstevel _NOTE(ARGUNUSED(addr)) 13442305Sstevel 13452305Sstevel /* 13462305Sstevel * Invalid access - flag a fault and return a dummy value 13472305Sstevel */ 13482305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13492305Sstevel return (DUMMY_VALUE); 13502305Sstevel } 13512305Sstevel 13522305Sstevel static void 13532305Sstevel lombus_no_put8(HANDLE_TYPE *hdlp, uint8_t *addr, uint8_t val) 13542305Sstevel { 13552305Sstevel _NOTE(ARGUNUSED(addr, val)) 13562305Sstevel 13572305Sstevel /* 13582305Sstevel * Invalid access - flag a fault 13592305Sstevel */ 13602305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13612305Sstevel } 13622305Sstevel 13632305Sstevel static void 13642305Sstevel lombus_no_rep_get8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 13652305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 13662305Sstevel { 13672305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 13682305Sstevel 13692305Sstevel /* 13702305Sstevel * Invalid access - flag a fault 13712305Sstevel */ 13722305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13732305Sstevel } 13742305Sstevel 13752305Sstevel static void 13762305Sstevel lombus_no_rep_put8(HANDLE_TYPE *hdlp, uint8_t *host_addr, 13772305Sstevel uint8_t *dev_addr, size_t repcount, uint_t flags) 13782305Sstevel { 13792305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 13802305Sstevel 13812305Sstevel /* 13822305Sstevel * Invalid access - flag a fault 13832305Sstevel */ 13842305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13852305Sstevel } 13862305Sstevel 13872305Sstevel static uint16_t 13882305Sstevel lombus_no_get16(HANDLE_TYPE *hdlp, uint16_t *addr) 13892305Sstevel { 13902305Sstevel _NOTE(ARGUNUSED(addr)) 13912305Sstevel 13922305Sstevel /* 13932305Sstevel * Invalid access - flag a fault and return a dummy value 13942305Sstevel */ 13952305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 13962305Sstevel return (DUMMY_VALUE); 13972305Sstevel } 13982305Sstevel 13992305Sstevel static void 14002305Sstevel lombus_no_put16(HANDLE_TYPE *hdlp, uint16_t *addr, uint16_t val) 14012305Sstevel { 14022305Sstevel _NOTE(ARGUNUSED(addr, val)) 14032305Sstevel 14042305Sstevel /* 14052305Sstevel * Invalid access - flag a fault 14062305Sstevel */ 14072305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14082305Sstevel } 14092305Sstevel 14102305Sstevel static void 14112305Sstevel lombus_no_rep_get16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 14122305Sstevel uint16_t *dev_addr, size_t repcount, uint_t flags) 14132305Sstevel { 14142305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14152305Sstevel 14162305Sstevel /* 14172305Sstevel * Invalid access - flag a fault 14182305Sstevel */ 14192305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14202305Sstevel } 14212305Sstevel 14222305Sstevel static void 14232305Sstevel lombus_no_rep_put16(HANDLE_TYPE *hdlp, uint16_t *host_addr, 14242305Sstevel uint16_t *dev_addr, size_t repcount, uint_t flags) 14252305Sstevel { 14262305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14272305Sstevel 14282305Sstevel /* 14292305Sstevel * Invalid access - flag a fault 14302305Sstevel */ 14312305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14322305Sstevel } 14332305Sstevel 14342305Sstevel static uint64_t 14352305Sstevel lombus_no_get64(HANDLE_TYPE *hdlp, uint64_t *addr) 14362305Sstevel { 14372305Sstevel _NOTE(ARGUNUSED(addr)) 14382305Sstevel 14392305Sstevel /* 14402305Sstevel * Invalid access - flag a fault and return a dummy value 14412305Sstevel */ 14422305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14432305Sstevel return (DUMMY_VALUE); 14442305Sstevel } 14452305Sstevel 14462305Sstevel static void 14472305Sstevel lombus_no_put64(HANDLE_TYPE *hdlp, uint64_t *addr, uint64_t val) 14482305Sstevel { 14492305Sstevel _NOTE(ARGUNUSED(addr, val)) 14502305Sstevel 14512305Sstevel /* 14522305Sstevel * Invalid access - flag a fault 14532305Sstevel */ 14542305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14552305Sstevel } 14562305Sstevel 14572305Sstevel static void 14582305Sstevel lombus_no_rep_get64(HANDLE_TYPE *hdlp, uint64_t *host_addr, 14592305Sstevel uint64_t *dev_addr, size_t repcount, uint_t flags) 14602305Sstevel { 14612305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14622305Sstevel 14632305Sstevel /* 14642305Sstevel * Invalid access - flag a fault 14652305Sstevel */ 14662305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14672305Sstevel } 14682305Sstevel 14692305Sstevel static void 14702305Sstevel lombus_no_rep_put64(HANDLE_TYPE *hdlp, uint64_t *host_addr, 14712305Sstevel uint64_t *dev_addr, size_t repcount, uint_t flags) 14722305Sstevel { 14732305Sstevel _NOTE(ARGUNUSED(host_addr, dev_addr, repcount, flags)) 14742305Sstevel 14752305Sstevel /* 14762305Sstevel * Invalid access - flag a fault 14772305Sstevel */ 14782305Sstevel HANDLE_FAULT(hdlp) = LOMBUS_ERR_REG_SIZE; 14792305Sstevel } 14802305Sstevel 14812305Sstevel static int 14822305Sstevel lombus_acc_fault_check(HANDLE_TYPE *hdlp) 14832305Sstevel { 14842305Sstevel return (HANDLE_FAULT(hdlp) != 0); 14852305Sstevel } 14862305Sstevel 14872305Sstevel 14882305Sstevel /* 14892305Sstevel * Hardware setup - put the SIO chip in the required operational 14902305Sstevel * state, with all our favourite parameters programmed correctly. 14912305Sstevel * This routine leaves all SIO interrupts disabled. 14922305Sstevel */ 14932305Sstevel 14942305Sstevel static void 14952305Sstevel lombus_hw_reset(struct lombus_state *ssp) 14962305Sstevel { 14972305Sstevel uint16_t divisor; 14982305Sstevel 14992305Sstevel /* 15002305Sstevel * Disable interrupts, soft reset Tx and Rx circuitry, 15012305Sstevel * reselect standard modes (bits/char, parity, etc). 15022305Sstevel */ 15032305Sstevel lombus_set_irq(ssp, B_FALSE); 15042305Sstevel sio_put_reg(ssp, SIO_FCR, SIO_FCR_RXSR | SIO_FCR_TXSR); 15052305Sstevel sio_put_reg(ssp, SIO_LCR, SIO_LCR_STD); 15062305Sstevel 15072305Sstevel /* 15082305Sstevel * Select the proper baud rate; if the value is invalid 15092305Sstevel * (presumably 0, i.e. not specified, but also if the 15102305Sstevel * "baud" property is set to some silly value), we assume 15112305Sstevel * the default. 15122305Sstevel */ 15132305Sstevel if (ssp->baud < SIO_BAUD_MIN || ssp->baud > SIO_BAUD_MAX) 15142305Sstevel divisor = SIO_BAUD_TO_DIVISOR(SIO_BAUD_DEFAULT); 15152305Sstevel else 15162305Sstevel divisor = SIO_BAUD_TO_DIVISOR(ssp->baud); 15172305Sstevel 15182305Sstevel /* 15192305Sstevel * According to the datasheet, it is forbidden for the divisor 15202305Sstevel * register to be zero. So when loading the register in two 15212305Sstevel * steps, we have to make sure that the temporary value formed 15222305Sstevel * between loads is nonzero. However, we can't rely on either 15232305Sstevel * half already having a nonzero value, as the datasheet also 15242305Sstevel * says that these registers are indeterminate after a reset! 15252305Sstevel * So, we explicitly set the low byte to a non-zero value first; 15262305Sstevel * then we can safely load the high byte, and then the correct 15272305Sstevel * value for the low byte, without the result ever being zero. 15282305Sstevel */ 15292305Sstevel sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK1); 15302305Sstevel sio_put_reg(ssp, SIO_LBGDL, 0xff); 15312305Sstevel sio_put_reg(ssp, SIO_LBGDH, divisor >> 8); 15322305Sstevel sio_put_reg(ssp, SIO_LBGDL, divisor & 0xff); 15332305Sstevel sio_put_reg(ssp, SIO_BSR, SIO_BSR_BANK0); 15342305Sstevel 15352305Sstevel /* 15362305Sstevel * Program the remaining device registers as required 15372305Sstevel */ 15382305Sstevel sio_put_reg(ssp, SIO_MCR, SIO_MCR_STD); 15392305Sstevel sio_put_reg(ssp, SIO_FCR, SIO_FCR_STD); 15402305Sstevel } 15412305Sstevel 15422305Sstevel 15432305Sstevel /* 15442305Sstevel * Higher-level setup & teardown 15452305Sstevel */ 15462305Sstevel 15472305Sstevel static void 15482305Sstevel lombus_offline(struct lombus_state *ssp) 15492305Sstevel { 15502305Sstevel if (ssp->sio_handle != NULL) 15512305Sstevel ddi_regs_map_free(&ssp->sio_handle); 15522305Sstevel ssp->sio_handle = NULL; 15532305Sstevel ssp->sio_regs = NULL; 15542305Sstevel } 15552305Sstevel 15562305Sstevel static int 15572305Sstevel lombus_online(struct lombus_state *ssp) 15582305Sstevel { 15592305Sstevel ddi_acc_handle_t h; 15602305Sstevel caddr_t p; 15612305Sstevel int nregs; 15622305Sstevel int err; 15632305Sstevel 15642305Sstevel if (ddi_dev_nregs(ssp->dip, &nregs) != DDI_SUCCESS) 15652305Sstevel nregs = 0; 15662305Sstevel 15672305Sstevel switch (nregs) { 15682305Sstevel default: 15692305Sstevel case 1: 15702305Sstevel /* 15712305Sstevel * regset 0 represents the SIO operating registers 15722305Sstevel */ 15732305Sstevel err = ddi_regs_map_setup(ssp->dip, 0, &p, 0, 0, 15742305Sstevel lombus_dev_acc_attr, &h); 15752305Sstevel lombus_trace(ssp, 'O', "online", 15762305Sstevel "regmap 0 status %d addr $%p", err, p); 15772305Sstevel if (err != DDI_SUCCESS) 15782305Sstevel return (EIO); 15792305Sstevel 15802305Sstevel ssp->sio_handle = h; 15812305Sstevel ssp->sio_regs = (void *)p; 15822305Sstevel break; 15832305Sstevel 15842305Sstevel case 0: 15852305Sstevel /* 15862305Sstevel * If no registers are defined, succeed vacuously; 15872305Sstevel * commands will be accepted, but we fake the accesses. 15882305Sstevel */ 15892305Sstevel break; 15902305Sstevel } 15912305Sstevel 15922305Sstevel /* 15932305Sstevel * Now that the registers are mapped, we can initialise the SIO h/w 15942305Sstevel */ 15952305Sstevel lombus_hw_reset(ssp); 15962305Sstevel return (0); 15972305Sstevel } 15982305Sstevel 15992305Sstevel 16002305Sstevel /* 16012305Sstevel * Nexus routines 16022305Sstevel */ 16032305Sstevel 16042305Sstevel #if defined(NDI_ACC_HDL_V2) 16052305Sstevel 16062305Sstevel static const ndi_acc_fns_t lombus_vreg_acc_fns = { 16072305Sstevel NDI_ACC_FNS_CURRENT, 16082305Sstevel NDI_ACC_FNS_V1, 16092305Sstevel 16102305Sstevel lombus_vreg_get8, 16112305Sstevel lombus_vreg_put8, 16122305Sstevel lombus_vreg_rep_get8, 16132305Sstevel lombus_vreg_rep_put8, 16142305Sstevel 16152305Sstevel lombus_no_get16, 16162305Sstevel lombus_no_put16, 16172305Sstevel lombus_no_rep_get16, 16182305Sstevel lombus_no_rep_put16, 16192305Sstevel 16202305Sstevel lombus_meta_get32, 16212305Sstevel lombus_meta_put32, 16222305Sstevel lombus_meta_rep_get32, 16232305Sstevel lombus_meta_rep_put32, 16242305Sstevel 16252305Sstevel lombus_no_get64, 16262305Sstevel lombus_no_put64, 16272305Sstevel lombus_no_rep_get64, 16282305Sstevel lombus_no_rep_put64, 16292305Sstevel 16302305Sstevel lombus_acc_fault_check 16312305Sstevel }; 16322305Sstevel 16332305Sstevel static const ndi_acc_fns_t lombus_pat_acc_fns = { 16342305Sstevel NDI_ACC_FNS_CURRENT, 16352305Sstevel NDI_ACC_FNS_V1, 16362305Sstevel 16372305Sstevel lombus_pat_get8, 16382305Sstevel lombus_pat_put8, 16392305Sstevel lombus_pat_rep_get8, 16402305Sstevel lombus_pat_rep_put8, 16412305Sstevel 16422305Sstevel lombus_no_get16, 16432305Sstevel lombus_no_put16, 16442305Sstevel lombus_no_rep_get16, 16452305Sstevel lombus_no_rep_put16, 16462305Sstevel 16472305Sstevel lombus_meta_get32, 16482305Sstevel lombus_meta_put32, 16492305Sstevel lombus_meta_rep_get32, 16502305Sstevel lombus_meta_rep_put32, 16512305Sstevel 16522305Sstevel lombus_no_get64, 16532305Sstevel lombus_no_put64, 16542305Sstevel lombus_no_rep_get64, 16552305Sstevel lombus_no_rep_put64, 16562305Sstevel 16572305Sstevel lombus_acc_fault_check 16582305Sstevel }; 16592305Sstevel 16602305Sstevel static const ndi_acc_fns_t lombus_event_acc_fns = { 16612305Sstevel NDI_ACC_FNS_CURRENT, 16622305Sstevel NDI_ACC_FNS_V1, 16632305Sstevel 16642305Sstevel lombus_no_get8, 16652305Sstevel lombus_no_put8, 16662305Sstevel lombus_no_rep_get8, 16672305Sstevel lombus_no_rep_put8, 16682305Sstevel 16692305Sstevel lombus_event_get16, 16702305Sstevel lombus_event_put16, 16712305Sstevel lombus_event_rep_get16, 16722305Sstevel lombus_event_rep_put16, 16732305Sstevel 16742305Sstevel lombus_meta_get32, 16752305Sstevel lombus_meta_put32, 16762305Sstevel lombus_meta_rep_get32, 16772305Sstevel lombus_meta_rep_put32, 16782305Sstevel 16792305Sstevel lombus_no_get64, 16802305Sstevel lombus_no_put64, 16812305Sstevel lombus_no_rep_get64, 16822305Sstevel lombus_no_rep_put64, 16832305Sstevel 16842305Sstevel lombus_acc_fault_check 16852305Sstevel }; 16862305Sstevel 16872305Sstevel static int 16882305Sstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op, 16892305Sstevel int space, caddr_t vaddr, off_t len, 16902305Sstevel ndi_acc_handle_t *hdlp, caddr_t *addrp) 16912305Sstevel { 16922305Sstevel switch (op) { 16932305Sstevel default: 16942305Sstevel return (DDI_ME_UNIMPLEMENTED); 16952305Sstevel 16962305Sstevel case DDI_MO_MAP_LOCKED: 16972305Sstevel switch (space) { 16982305Sstevel default: 16992305Sstevel return (DDI_ME_REGSPEC_RANGE); 17002305Sstevel 17012305Sstevel case LOMBUS_VREG_SPACE: 17022305Sstevel ndi_set_acc_fns(hdlp, &lombus_vreg_acc_fns); 17032305Sstevel break; 17042305Sstevel 17052305Sstevel case LOMBUS_PAT_SPACE: 17062305Sstevel ndi_set_acc_fns(hdlp, &lombus_pat_acc_fns); 17072305Sstevel break; 17082305Sstevel 17092305Sstevel case LOMBUS_EVENT_SPACE: 17102305Sstevel ndi_set_acc_fns(hdlp, &lombus_event_acc_fns); 17112305Sstevel break; 17122305Sstevel } 17132305Sstevel hdlp->ah_addr = *addrp = vaddr; 17142305Sstevel hdlp->ah_len = len; 17152305Sstevel hdlp->ah_bus_private = ssp; 17162305Sstevel return (DDI_SUCCESS); 17172305Sstevel 17182305Sstevel case DDI_MO_UNMAP: 17192305Sstevel *addrp = NULL; 17202305Sstevel hdlp->ah_bus_private = NULL; 17212305Sstevel return (DDI_SUCCESS); 17222305Sstevel } 17232305Sstevel } 17242305Sstevel 17252305Sstevel #else 17262305Sstevel 17272305Sstevel static int 17282305Sstevel lombus_map_handle(struct lombus_state *ssp, ddi_map_op_t op, 17292305Sstevel int space, caddr_t vaddr, off_t len, 17302305Sstevel ddi_acc_hdl_t *hdlp, caddr_t *addrp) 17312305Sstevel { 17322305Sstevel ddi_acc_impl_t *aip = hdlp->ah_platform_private; 17332305Sstevel 17342305Sstevel switch (op) { 17352305Sstevel default: 17362305Sstevel return (DDI_ME_UNIMPLEMENTED); 17372305Sstevel 17382305Sstevel case DDI_MO_MAP_LOCKED: 17392305Sstevel switch (space) { 17402305Sstevel default: 17412305Sstevel return (DDI_ME_REGSPEC_RANGE); 17422305Sstevel 17432305Sstevel case LOMBUS_VREG_SPACE: 17442305Sstevel aip->ahi_get8 = lombus_vreg_get8; 17452305Sstevel aip->ahi_put8 = lombus_vreg_put8; 17462305Sstevel aip->ahi_rep_get8 = lombus_vreg_rep_get8; 17472305Sstevel aip->ahi_rep_put8 = lombus_vreg_rep_put8; 17482305Sstevel 17492305Sstevel aip->ahi_get16 = lombus_no_get16; 17502305Sstevel aip->ahi_put16 = lombus_no_put16; 17512305Sstevel aip->ahi_rep_get16 = lombus_no_rep_get16; 17522305Sstevel aip->ahi_rep_put16 = lombus_no_rep_put16; 17532305Sstevel 17542305Sstevel aip->ahi_get32 = lombus_meta_get32; 17552305Sstevel aip->ahi_put32 = lombus_meta_put32; 17562305Sstevel aip->ahi_rep_get32 = lombus_meta_rep_get32; 17572305Sstevel aip->ahi_rep_put32 = lombus_meta_rep_put32; 17582305Sstevel 17592305Sstevel aip->ahi_get64 = lombus_no_get64; 17602305Sstevel aip->ahi_put64 = lombus_no_put64; 17612305Sstevel aip->ahi_rep_get64 = lombus_no_rep_get64; 17622305Sstevel aip->ahi_rep_put64 = lombus_no_rep_put64; 17632305Sstevel 17642305Sstevel aip->ahi_fault_check = lombus_acc_fault_check; 17652305Sstevel break; 17662305Sstevel 17672305Sstevel case LOMBUS_PAT_SPACE: 17682305Sstevel aip->ahi_get8 = lombus_pat_get8; 17692305Sstevel aip->ahi_put8 = lombus_pat_put8; 17702305Sstevel aip->ahi_rep_get8 = lombus_pat_rep_get8; 17712305Sstevel aip->ahi_rep_put8 = lombus_pat_rep_put8; 17722305Sstevel 17732305Sstevel aip->ahi_get16 = lombus_no_get16; 17742305Sstevel aip->ahi_put16 = lombus_no_put16; 17752305Sstevel aip->ahi_rep_get16 = lombus_no_rep_get16; 17762305Sstevel aip->ahi_rep_put16 = lombus_no_rep_put16; 17772305Sstevel 17782305Sstevel aip->ahi_get32 = lombus_meta_get32; 17792305Sstevel aip->ahi_put32 = lombus_meta_put32; 17802305Sstevel aip->ahi_rep_get32 = lombus_meta_rep_get32; 17812305Sstevel aip->ahi_rep_put32 = lombus_meta_rep_put32; 17822305Sstevel 17832305Sstevel aip->ahi_get64 = lombus_no_get64; 17842305Sstevel aip->ahi_put64 = lombus_no_put64; 17852305Sstevel aip->ahi_rep_get64 = lombus_no_rep_get64; 17862305Sstevel aip->ahi_rep_put64 = lombus_no_rep_put64; 17872305Sstevel 17882305Sstevel aip->ahi_fault_check = lombus_acc_fault_check; 17892305Sstevel break; 17902305Sstevel 17912305Sstevel case LOMBUS_EVENT_SPACE: 17922305Sstevel aip->ahi_get8 = lombus_no_get8; 17932305Sstevel aip->ahi_put8 = lombus_no_put8; 17942305Sstevel aip->ahi_rep_get8 = lombus_no_rep_get8; 17952305Sstevel aip->ahi_rep_put8 = lombus_no_rep_put8; 17962305Sstevel 17972305Sstevel aip->ahi_get16 = lombus_event_get16; 17982305Sstevel aip->ahi_put16 = lombus_event_put16; 17992305Sstevel aip->ahi_rep_get16 = lombus_event_rep_get16; 18002305Sstevel aip->ahi_rep_put16 = lombus_event_rep_put16; 18012305Sstevel 18022305Sstevel aip->ahi_get32 = lombus_meta_get32; 18032305Sstevel aip->ahi_put32 = lombus_meta_put32; 18042305Sstevel aip->ahi_rep_get32 = lombus_meta_rep_get32; 18052305Sstevel aip->ahi_rep_put32 = lombus_meta_rep_put32; 18062305Sstevel 18072305Sstevel aip->ahi_get64 = lombus_no_get64; 18082305Sstevel aip->ahi_put64 = lombus_no_put64; 18092305Sstevel aip->ahi_rep_get64 = lombus_no_rep_get64; 18102305Sstevel aip->ahi_rep_put64 = lombus_no_rep_put64; 18112305Sstevel 18122305Sstevel aip->ahi_fault_check = lombus_acc_fault_check; 18132305Sstevel break; 18142305Sstevel } 18152305Sstevel hdlp->ah_addr = *addrp = vaddr; 18162305Sstevel hdlp->ah_len = len; 18172305Sstevel hdlp->ah_bus_private = ssp; 18182305Sstevel return (DDI_SUCCESS); 18192305Sstevel 18202305Sstevel case DDI_MO_UNMAP: 18212305Sstevel *addrp = NULL; 18222305Sstevel hdlp->ah_bus_private = NULL; 18232305Sstevel return (DDI_SUCCESS); 18242305Sstevel } 18252305Sstevel } 18262305Sstevel 18272305Sstevel #endif /* NDI_ACC_HDL_V2 */ 18282305Sstevel 18292305Sstevel static int 18302305Sstevel lombus_map(dev_info_t *dip, dev_info_t *rdip, ddi_map_req_t *mp, 18312305Sstevel off_t off, off_t len, caddr_t *addrp) 18322305Sstevel { 18332305Sstevel struct lombus_child_info *lcip; 18342305Sstevel struct lombus_state *ssp; 18352305Sstevel lombus_regspec_t *rsp; 18362305Sstevel 18372305Sstevel if ((ssp = lombus_getstate(dip, -1, "lombus_map")) == NULL) 18382305Sstevel return (DDI_FAILURE); /* this "can't happen" */ 18392305Sstevel 18402305Sstevel /* 18412305Sstevel * Validate mapping request ... 18422305Sstevel */ 18432305Sstevel 18442305Sstevel if (mp->map_flags != DDI_MF_KERNEL_MAPPING) 18452305Sstevel return (DDI_ME_UNSUPPORTED); 18462305Sstevel if (mp->map_handlep == NULL) 18472305Sstevel return (DDI_ME_UNSUPPORTED); 18482305Sstevel if (mp->map_type != DDI_MT_RNUMBER) 18492305Sstevel return (DDI_ME_UNIMPLEMENTED); 18502305Sstevel if ((lcip = ddi_get_parent_data(rdip)) == NULL) 18512305Sstevel return (DDI_ME_INVAL); 18522305Sstevel if ((rsp = lcip->rsp) == NULL) 18532305Sstevel return (DDI_ME_INVAL); 18542305Sstevel if (mp->map_obj.rnumber >= lcip->nregs) 18552305Sstevel return (DDI_ME_RNUMBER_RANGE); 18562305Sstevel rsp += mp->map_obj.rnumber; 18572305Sstevel if (off < 0 || off >= rsp->lombus_size) 18582305Sstevel return (DDI_ME_INVAL); 18592305Sstevel if (len == 0) 18602305Sstevel len = rsp->lombus_size-off; 18612305Sstevel if (len < 0) 18622305Sstevel return (DDI_ME_INVAL); 18632305Sstevel if (off+len < 0 || off+len > rsp->lombus_size) 18642305Sstevel return (DDI_ME_INVAL); 18652305Sstevel 18662305Sstevel return (lombus_map_handle(ssp, mp->map_op, 1867*5107Seota rsp->lombus_space, VREG_TO_ADDR(rsp->lombus_base+off), len, 1868*5107Seota mp->map_handlep, addrp)); 18692305Sstevel } 18702305Sstevel 18712305Sstevel static int 18722305Sstevel lombus_ctlops(dev_info_t *dip, dev_info_t *rdip, ddi_ctl_enum_t op, 18732305Sstevel void *arg, void *result) 18742305Sstevel { 18752305Sstevel struct lombus_child_info *lcip; 18762305Sstevel struct lombus_state *ssp; 18772305Sstevel lombus_regspec_t *rsp; 18782305Sstevel dev_info_t *cdip; 18792305Sstevel char addr[32]; 18802305Sstevel uint_t nregs; 18812305Sstevel uint_t rnum; 18822305Sstevel int *regs; 18832305Sstevel int limit; 18842305Sstevel int err; 18852305Sstevel int i; 18862305Sstevel 18872305Sstevel if ((ssp = lombus_getstate(dip, -1, "lombus_ctlops")) == NULL) 18882305Sstevel return (DDI_FAILURE); /* this "can't happen" */ 18892305Sstevel 18902305Sstevel switch (op) { 18912305Sstevel default: 18922305Sstevel break; 18932305Sstevel 18942305Sstevel case DDI_CTLOPS_INITCHILD: 18952305Sstevel /* 18962305Sstevel * First, look up and validate the "reg" property. 18972305Sstevel * 18982305Sstevel * It must be a non-empty integer array containing a set 18992305Sstevel * of triples. Once we've verified that, we can treat it 19002305Sstevel * as an array of type lombus_regspec_t[], which defines 19012305Sstevel * the meaning of the elements of each triple: 19022305Sstevel * + the first element of each triple must be a valid space 19032305Sstevel * + the second and third elements (base, size) of each 19042305Sstevel * triple must define a valid subrange of that space 19052305Sstevel * If it passes all the tests, we save it away for future 19062305Sstevel * reference in the child's parent-private-data field. 19072305Sstevel */ 19082305Sstevel cdip = arg; 19092305Sstevel err = ddi_prop_lookup_int_array(DDI_DEV_T_ANY, cdip, 1910*5107Seota DDI_PROP_DONTPASS, "reg", ®s, &nregs); 19112305Sstevel lombus_trace(ssp, 'C', "initchild", 19122305Sstevel "prop status %d size %d", err, nregs); 19132305Sstevel if (err != DDI_PROP_SUCCESS) 19142305Sstevel return (DDI_FAILURE); 19152305Sstevel 19162305Sstevel err = (nregs <= 0 || (nregs % LOMBUS_REGSPEC_SIZE) != 0); 19172305Sstevel nregs /= LOMBUS_REGSPEC_SIZE; 19182305Sstevel rsp = (lombus_regspec_t *)regs; 19192305Sstevel for (i = 0; i < nregs && !err; ++i) { 19202305Sstevel switch (rsp[i].lombus_space) { 19212305Sstevel default: 19222305Sstevel limit = 0; 19232305Sstevel err = 1; 19242305Sstevel break; 19252305Sstevel 19262305Sstevel case LOMBUS_VREG_SPACE: 19272305Sstevel limit = LOMBUS_MAX_REG+1; 19282305Sstevel break; 19292305Sstevel 19302305Sstevel case LOMBUS_PAT_SPACE: 19312305Sstevel limit = LOMBUS_PAT_REG+1; 19322305Sstevel break; 19332305Sstevel 19342305Sstevel case LOMBUS_EVENT_SPACE: 19352305Sstevel limit = LOMBUS_EVENT_REG+1; 19362305Sstevel break; 19372305Sstevel } 19382305Sstevel 19392305Sstevel err |= (rsp[i].lombus_base < 0); 19402305Sstevel err |= (rsp[i].lombus_base >= limit); 19412305Sstevel 19422305Sstevel if (rsp[i].lombus_size == 0) 19432305Sstevel rsp[i].lombus_size = limit-rsp[i].lombus_base; 19442305Sstevel err |= (rsp[i].lombus_size < 0); 19452305Sstevel 19462305Sstevel err |= (rsp[i].lombus_base+rsp[i].lombus_size < 0); 19472305Sstevel err |= (rsp[i].lombus_base+rsp[i].lombus_size > limit); 19482305Sstevel } 19492305Sstevel 19502305Sstevel if (err) { 19512305Sstevel ddi_prop_free(regs); 19522305Sstevel return (DDI_FAILURE); 19532305Sstevel } 19542305Sstevel 19552305Sstevel lcip = kmem_zalloc(sizeof (*lcip), KM_SLEEP); 19562305Sstevel lcip->nregs = nregs; 19572305Sstevel lcip->rsp = rsp; 19582305Sstevel ddi_set_parent_data(cdip, lcip); 19592305Sstevel 19602305Sstevel (void) snprintf(addr, sizeof (addr), 1961*5107Seota "%x,%x", rsp[0].lombus_space, rsp[0].lombus_base); 19622305Sstevel ddi_set_name_addr(cdip, addr); 19632305Sstevel 19642305Sstevel return (DDI_SUCCESS); 19652305Sstevel 19662305Sstevel case DDI_CTLOPS_UNINITCHILD: 19672305Sstevel cdip = arg; 19682305Sstevel ddi_set_name_addr(cdip, NULL); 19692305Sstevel lcip = ddi_get_parent_data(cdip); 19702305Sstevel ddi_set_parent_data(cdip, NULL); 19712305Sstevel ddi_prop_free(lcip->rsp); 19722305Sstevel kmem_free(lcip, sizeof (*lcip)); 19732305Sstevel return (DDI_SUCCESS); 19742305Sstevel 19752305Sstevel case DDI_CTLOPS_REPORTDEV: 19762305Sstevel if (rdip == NULL) 19772305Sstevel return (DDI_FAILURE); 19782305Sstevel 19792305Sstevel cmn_err(CE_CONT, "?LOM device: %s@%s, %s#%d\n", 1980*5107Seota ddi_node_name(rdip), ddi_get_name_addr(rdip), 1981*5107Seota ddi_driver_name(dip), ddi_get_instance(dip)); 19822305Sstevel 19832305Sstevel return (DDI_SUCCESS); 19842305Sstevel 19852305Sstevel case DDI_CTLOPS_REGSIZE: 19862305Sstevel if ((lcip = ddi_get_parent_data(rdip)) == NULL) 19872305Sstevel return (DDI_FAILURE); 19882305Sstevel if ((rnum = *(uint_t *)arg) >= lcip->nregs) 19892305Sstevel return (DDI_FAILURE); 19902305Sstevel *(off_t *)result = lcip->rsp[rnum].lombus_size; 19912305Sstevel return (DDI_SUCCESS); 19922305Sstevel 19932305Sstevel case DDI_CTLOPS_NREGS: 19942305Sstevel if ((lcip = ddi_get_parent_data(rdip)) == NULL) 19952305Sstevel return (DDI_FAILURE); 19962305Sstevel *(int *)result = lcip->nregs; 19972305Sstevel return (DDI_SUCCESS); 19982305Sstevel } 19992305Sstevel 20002305Sstevel return (ddi_ctlops(dip, rdip, op, arg, result)); 20012305Sstevel } 20022305Sstevel 20032305Sstevel 20042305Sstevel /* 20052305Sstevel * Clean up on detach or failure of attach 20062305Sstevel */ 20072305Sstevel static int 20082305Sstevel lombus_unattach(struct lombus_state *ssp, int instance) 20092305Sstevel { 20102305Sstevel if (ssp != NULL) { 20112305Sstevel lombus_hw_reset(ssp); 2012*5107Seota if (ssp->cycid != NULL) { 2013*5107Seota ddi_periodic_delete(ssp->cycid); 2014*5107Seota ssp->cycid = NULL; 20152305Sstevel if (ssp->sio_handle != NULL) 20162305Sstevel ddi_remove_intr(ssp->dip, 0, ssp->hw_iblk); 20172305Sstevel ddi_remove_softintr(ssp->softid); 20182305Sstevel cv_destroy(ssp->lo_cv); 20192305Sstevel mutex_destroy(ssp->lo_mutex); 20202305Sstevel mutex_destroy(ssp->hw_mutex); 20212305Sstevel } 20222305Sstevel lombus_offline(ssp); 20232305Sstevel ddi_set_driver_private(ssp->dip, NULL); 20242305Sstevel } 20252305Sstevel 20262305Sstevel ddi_soft_state_free(lombus_statep, instance); 20272305Sstevel return (DDI_FAILURE); 20282305Sstevel } 20292305Sstevel 20302305Sstevel /* 20312305Sstevel * Autoconfiguration routines 20322305Sstevel */ 20332305Sstevel 20342305Sstevel static int 20352305Sstevel lombus_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 20362305Sstevel { 20372305Sstevel struct lombus_state *ssp = NULL; 20382305Sstevel int instance; 20392305Sstevel int err; 20402305Sstevel 20412305Sstevel switch (cmd) { 20422305Sstevel default: 20432305Sstevel return (DDI_FAILURE); 20442305Sstevel 20452305Sstevel case DDI_ATTACH: 20462305Sstevel break; 20472305Sstevel } 20482305Sstevel 20492305Sstevel /* 20502305Sstevel * Allocate the soft-state structure 20512305Sstevel */ 20522305Sstevel instance = ddi_get_instance(dip); 20532305Sstevel if (ddi_soft_state_zalloc(lombus_statep, instance) != DDI_SUCCESS) 20542305Sstevel return (DDI_FAILURE); 20552305Sstevel if ((ssp = lombus_getstate(dip, instance, "lombus_attach")) == NULL) 20562305Sstevel return (lombus_unattach(ssp, instance)); 20572305Sstevel ddi_set_driver_private(dip, ssp); 20582305Sstevel 20592305Sstevel /* 20602305Sstevel * Initialise devinfo-related fields 20612305Sstevel */ 20622305Sstevel ssp->dip = dip; 20632305Sstevel ssp->majornum = ddi_driver_major(dip); 20642305Sstevel ssp->instance = instance; 20652305Sstevel 20662305Sstevel /* 20672305Sstevel * Set various options from .conf properties 20682305Sstevel */ 20692305Sstevel ssp->allow_echo = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2070*5107Seota DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0; 20712305Sstevel ssp->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2072*5107Seota DDI_PROP_DONTPASS, "baud-rate", 0); 20732305Sstevel ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2074*5107Seota DDI_PROP_DONTPASS, "debug", 0); 20752305Sstevel ssp->fake_cts = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 2076*5107Seota DDI_PROP_DONTPASS, "fake-cts", 0) != 0; 20772305Sstevel 20782305Sstevel /* 20792305Sstevel * Initialise current state & time 20802305Sstevel */ 20812305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_IDLE; 20822305Sstevel ssp->hw_last_pat = gethrtime(); 2083*5107Seota ssp->cycid = NULL; 20842305Sstevel 20852305Sstevel /* 20862305Sstevel * Online the hardware ... 20872305Sstevel */ 20882305Sstevel err = lombus_online(ssp); 20892305Sstevel if (err != 0) 20902305Sstevel return (lombus_unattach(ssp, instance)); 20912305Sstevel 20922305Sstevel /* 20932305Sstevel * Install soft and hard interrupt handler(s) 20942305Sstevel * Initialise mutexes and cv 20952305Sstevel * Start cyclic callbacks 20962305Sstevel * Enable interrupts 20972305Sstevel */ 20982305Sstevel err = ddi_add_softintr(dip, DDI_SOFTINT_LOW, &ssp->softid, 2099*5107Seota &ssp->lo_iblk, NULL, lombus_softint, (caddr_t)ssp); 21002305Sstevel if (err != DDI_SUCCESS) 21012305Sstevel return (lombus_unattach(ssp, instance)); 21022305Sstevel 21032305Sstevel if (ssp->sio_handle != NULL) 21042305Sstevel err = ddi_add_intr(dip, 0, &ssp->hw_iblk, NULL, 2105*5107Seota lombus_hi_intr, (caddr_t)ssp); 21062305Sstevel 21072305Sstevel mutex_init(ssp->hw_mutex, NULL, MUTEX_DRIVER, ssp->hw_iblk); 21082305Sstevel mutex_init(ssp->lo_mutex, NULL, MUTEX_DRIVER, ssp->lo_iblk); 21092305Sstevel cv_init(ssp->lo_cv, NULL, CV_DRIVER, NULL); 21102305Sstevel 2111*5107Seota /* 2112*5107Seota * Register a periodical handler. 2113*5107Seota */ 2114*5107Seota ssp->cycid = ddi_periodic_add(lombus_cyclic, ssp, LOMBUS_ONE_SEC, 2115*5107Seota DDI_IPL_1); 21162305Sstevel 21172305Sstevel /* 21182305Sstevel * Final check before enabling h/w interrupts - did 21192305Sstevel * we successfully install the h/w interrupt handler? 21202305Sstevel */ 21212305Sstevel if (err != DDI_SUCCESS) 21222305Sstevel return (lombus_unattach(ssp, instance)); 21232305Sstevel 21242305Sstevel lombus_set_irq(ssp, B_TRUE); 21252305Sstevel 21262305Sstevel /* 21272305Sstevel * All done, report success 21282305Sstevel */ 21292305Sstevel ddi_report_dev(dip); 21302305Sstevel return (DDI_SUCCESS); 21312305Sstevel } 21322305Sstevel 21332305Sstevel 21342305Sstevel static int 21352305Sstevel lombus_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 21362305Sstevel { 21372305Sstevel struct lombus_state *ssp; 21382305Sstevel int instance; 21392305Sstevel 21402305Sstevel switch (cmd) { 21412305Sstevel default: 21422305Sstevel return (DDI_FAILURE); 21432305Sstevel 21442305Sstevel case DDI_DETACH: 21452305Sstevel break; 21462305Sstevel } 21472305Sstevel 21482305Sstevel instance = ddi_get_instance(dip); 21492305Sstevel if ((ssp = lombus_getstate(dip, instance, "lombus_detach")) == NULL) 21502305Sstevel return (DDI_FAILURE); /* this "can't happen" */ 21512305Sstevel 21522305Sstevel (void) lombus_unattach(ssp, instance); 21532305Sstevel return (DDI_SUCCESS); 21542305Sstevel } 21552305Sstevel 21562305Sstevel static int 21572305Sstevel lombus_reset(dev_info_t *dip, ddi_reset_cmd_t cmd) 21582305Sstevel { 21592305Sstevel struct lombus_state *ssp; 21602305Sstevel 21612305Sstevel _NOTE(ARGUNUSED(cmd)) 21622305Sstevel 21632305Sstevel if ((ssp = lombus_getstate(dip, -1, "lombus_reset")) == NULL) 21642305Sstevel return (DDI_FAILURE); 21652305Sstevel 21662305Sstevel lombus_hw_reset(ssp); 21672305Sstevel return (DDI_SUCCESS); 21682305Sstevel } 21692305Sstevel 21702305Sstevel 21712305Sstevel /* 21722305Sstevel * System interface structures 21732305Sstevel */ 21742305Sstevel 21752305Sstevel static struct cb_ops lombus_cb_ops = 21762305Sstevel { 21772305Sstevel nodev, /* b/c open */ 21782305Sstevel nodev, /* b/c close */ 21792305Sstevel nodev, /* b strategy */ 21802305Sstevel nodev, /* b print */ 21812305Sstevel nodev, /* b dump */ 21822305Sstevel nodev, /* c read */ 21832305Sstevel nodev, /* c write */ 21842305Sstevel nodev, /* c ioctl */ 21852305Sstevel nodev, /* c devmap */ 21862305Sstevel nodev, /* c mmap */ 21872305Sstevel nodev, /* c segmap */ 21882305Sstevel nochpoll, /* c poll */ 21892305Sstevel ddi_prop_op, /* b/c prop_op */ 21902305Sstevel NULL, /* c streamtab */ 21912305Sstevel D_MP | D_NEW /* b/c flags */ 21922305Sstevel }; 21932305Sstevel 21942305Sstevel static struct bus_ops lombus_bus_ops = 21952305Sstevel { 21962305Sstevel BUSO_REV, /* revision */ 21972305Sstevel lombus_map, /* bus_map */ 21982305Sstevel 0, /* get_intrspec */ 21992305Sstevel 0, /* add_intrspec */ 22002305Sstevel 0, /* remove_intrspec */ 22012305Sstevel i_ddi_map_fault, /* map_fault */ 22022305Sstevel ddi_no_dma_map, /* dma_map */ 22032305Sstevel ddi_no_dma_allochdl, /* allocate DMA handle */ 22042305Sstevel ddi_no_dma_freehdl, /* free DMA handle */ 22052305Sstevel ddi_no_dma_bindhdl, /* bind DMA handle */ 22062305Sstevel ddi_no_dma_unbindhdl, /* unbind DMA handle */ 22072305Sstevel ddi_no_dma_flush, /* flush DMA */ 22082305Sstevel ddi_no_dma_win, /* move DMA window */ 22092305Sstevel ddi_no_dma_mctl, /* generic DMA control */ 22102305Sstevel lombus_ctlops, /* generic control */ 22112305Sstevel ddi_bus_prop_op, /* prop_op */ 22122305Sstevel ndi_busop_get_eventcookie, /* get_eventcookie */ 22132305Sstevel ndi_busop_add_eventcall, /* add_eventcall */ 22142305Sstevel ndi_busop_remove_eventcall, /* remove_eventcall */ 22152305Sstevel ndi_post_event, /* post_event */ 22162305Sstevel 0, /* interrupt control */ 22172305Sstevel 0, /* bus_config */ 22182305Sstevel 0, /* bus_unconfig */ 22192305Sstevel 0, /* bus_fm_init */ 22202305Sstevel 0, /* bus_fm_fini */ 22212305Sstevel 0, /* bus_fm_access_enter */ 22222305Sstevel 0, /* bus_fm_access_exit */ 22232305Sstevel 0, /* bus_power */ 22242305Sstevel i_ddi_intr_ops /* bus_intr_op */ 22252305Sstevel }; 22262305Sstevel 22272305Sstevel static struct dev_ops lombus_dev_ops = 22282305Sstevel { 22292305Sstevel DEVO_REV, 22302305Sstevel 0, /* refcount */ 22312305Sstevel ddi_no_info, /* getinfo */ 22322305Sstevel nulldev, /* identify */ 22332305Sstevel nulldev, /* probe */ 22342305Sstevel lombus_attach, /* attach */ 22352305Sstevel lombus_detach, /* detach */ 22362305Sstevel lombus_reset, /* reset */ 22372305Sstevel &lombus_cb_ops, /* driver operations */ 22382305Sstevel &lombus_bus_ops /* bus operations */ 22392305Sstevel }; 22402305Sstevel 22412305Sstevel static struct modldrv modldrv = 22422305Sstevel { 22432305Sstevel &mod_driverops, 22442305Sstevel "lombus driver, v%I%", 22452305Sstevel &lombus_dev_ops 22462305Sstevel }; 22472305Sstevel 22482305Sstevel static struct modlinkage modlinkage = 22492305Sstevel { 22502305Sstevel MODREV_1, 22512305Sstevel { 22522305Sstevel &modldrv, 22532305Sstevel NULL 22542305Sstevel } 22552305Sstevel }; 22562305Sstevel 22572305Sstevel 22582305Sstevel /* 22592305Sstevel * Dynamic loader interface code 22602305Sstevel */ 22612305Sstevel 22622305Sstevel int 22632305Sstevel _init(void) 22642305Sstevel { 22652305Sstevel int err; 22662305Sstevel 22672305Sstevel err = ddi_soft_state_init(&lombus_statep, 2268*5107Seota sizeof (struct lombus_state), 0); 22692305Sstevel if (err == DDI_SUCCESS) 22702305Sstevel if ((err = mod_install(&modlinkage)) != 0) { 22712305Sstevel ddi_soft_state_fini(&lombus_statep); 22722305Sstevel } 22732305Sstevel 22742305Sstevel return (err); 22752305Sstevel } 22762305Sstevel 22772305Sstevel int 22782305Sstevel _info(struct modinfo *mip) 22792305Sstevel { 22802305Sstevel return (mod_info(&modlinkage, mip)); 22812305Sstevel } 22822305Sstevel 22832305Sstevel int 22842305Sstevel _fini(void) 22852305Sstevel { 22862305Sstevel int err; 22872305Sstevel 22882305Sstevel if ((err = mod_remove(&modlinkage)) == 0) { 22892305Sstevel ddi_soft_state_fini(&lombus_statep); 22902305Sstevel lombus_major = NOMAJOR; 22912305Sstevel } 22922305Sstevel 22932305Sstevel return (err); 22942305Sstevel } 2295