12305Sstevel /*
22305Sstevel * CDDL HEADER START
32305Sstevel *
42305Sstevel * The contents of this file are subject to the terms of the
55107Seota * Common Development and Distribution License (the "License").
65107Seota * 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 /*
2211066Srafael.vanoni@sun.com * Copyright 2009 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
362305Sstevel /*
372305Sstevel * Header files
382305Sstevel */
392305Sstevel
402305Sstevel #include <sys/types.h>
412305Sstevel #include <sys/conf.h>
422305Sstevel #include <sys/debug.h>
432305Sstevel #include <sys/errno.h>
442305Sstevel #include <sys/file.h>
452305Sstevel #include <sys/intr.h>
462305Sstevel #include <sys/kmem.h>
472305Sstevel #include <sys/membar.h>
482305Sstevel #include <sys/modctl.h>
492305Sstevel #include <sys/note.h>
502305Sstevel #include <sys/open.h>
512305Sstevel #include <sys/poll.h>
522305Sstevel #include <sys/spl.h>
532305Sstevel #include <sys/stat.h>
542305Sstevel #include <sys/strlog.h>
552305Sstevel
562305Sstevel #include <sys/ddi.h>
572305Sstevel #include <sys/sunddi.h>
582305Sstevel #include <sys/sunndi.h>
592305Sstevel
602305Sstevel #include <sys/lombus.h>
612305Sstevel
622305Sstevel
632305Sstevel #if defined(NDI_ACC_HDL_V2)
642305Sstevel
652305Sstevel /*
662305Sstevel * Compiling for Solaris 9+ with access handle enhancements
672305Sstevel */
682305Sstevel #define HANDLE_TYPE ndi_acc_handle_t
692305Sstevel #define HANDLE_ADDR(hdlp) (hdlp->ah_addr)
702305Sstevel #define HANDLE_FAULT(hdlp) (hdlp->ah_fault)
712305Sstevel #define HANDLE_MAPLEN(hdlp) (hdlp->ah_len)
722305Sstevel #define HANDLE_PRIVATE(hdlp) (hdlp->ah_bus_private)
732305Sstevel
742305Sstevel #else
752305Sstevel
762305Sstevel /*
772305Sstevel * Compatibility definitions for backport to Solaris 8
782305Sstevel */
792305Sstevel #define HANDLE_TYPE ddi_acc_impl_t
802305Sstevel #define HANDLE_ADDR(hdlp) (hdlp->ahi_common.ah_addr)
812305Sstevel #define HANDLE_FAULT(hdlp) (hdlp->ahi_fault)
822305Sstevel #define HANDLE_MAPLEN(hdlp) (hdlp->ahi_common.ah_len)
832305Sstevel #define HANDLE_PRIVATE(hdlp) (hdlp->ahi_common.ah_bus_private)
842305Sstevel
852305Sstevel #define ddi_driver_major(dip) ddi_name_to_major(ddi_binding_name(dip))
862305Sstevel
872305Sstevel #endif /* NDI_ACC_HDL_V2 */
882305Sstevel
892305Sstevel
902305Sstevel /*
912305Sstevel * Local definitions
922305Sstevel */
932305Sstevel #define MYNAME "lombus"
942305Sstevel #define NOMAJOR (~(major_t)0)
952305Sstevel #define DUMMY_VALUE (~(int8_t)0)
962305Sstevel
972305Sstevel #define LOMBUS_INST_TO_MINOR(i) (i)
982305Sstevel #define LOMBUS_MINOR_TO_INST(m) (m)
992305Sstevel
1002305Sstevel #define LOMBUS_DUMMY_ADDRESS ((caddr_t)0x0CADD1ED)
1012305Sstevel #define ADDR_TO_OFFSET(a, hdlp) ((caddr_t)(a) - HANDLE_ADDR(hdlp))
1022305Sstevel #define ADDR_TO_VREG(a) ((caddr_t)(a) - LOMBUS_DUMMY_ADDRESS)
1032305Sstevel #define VREG_TO_ADDR(v) (LOMBUS_DUMMY_ADDRESS + (v))
1042305Sstevel
1052305Sstevel
1062305Sstevel /*
1072305Sstevel * The following definitions are taken from the datasheet
1082305Sstevel * for the National Semiconductor PC87317 (SuperIO) chip.
1092305Sstevel *
1102305Sstevel * This chip implements UART functionality as logical device 6.
1112305Sstevel * It provides all sorts of wierd modes and extensions, but we
1122305Sstevel * have chosen to use only the 16550-compatible features
1132305Sstevel * ("non-extended mode").
1142305Sstevel *
1152305Sstevel * Hardware: serial chip register numbers
1162305Sstevel */
1172305Sstevel #define SIO_RXD 0 /* read */
1182305Sstevel #define SIO_TXD 0 /* write */
1192305Sstevel #define SIO_IER 1
1202305Sstevel #define SIO_EIR 2 /* read */
1212305Sstevel #define SIO_FCR 2 /* write */
1222305Sstevel #define SIO_LCR 3
1232305Sstevel #define SIO_BSR 3 /* wierd */
1242305Sstevel #define SIO_MCR 4
1252305Sstevel #define SIO_LSR 5
1262305Sstevel #define SIO_MSR 6
1272305Sstevel #define SIO_SCR 7
1282305Sstevel
1292305Sstevel #define SIO_LBGDL 0 /* bank 1 */
1302305Sstevel #define SIO_LBGDH 1 /* bank 1 */
1312305Sstevel
1322305Sstevel /*
1332305Sstevel * Hardware: serial chip register bits
1342305Sstevel */
1352305Sstevel #define SIO_IER_RXHDL_IE 0x01
1362305Sstevel #define SIO_IER_STD 0x00
1372305Sstevel
1382305Sstevel #define SIO_EIR_IPF 0x01
1392305Sstevel #define SIO_EIR_IPR0 0x02
1402305Sstevel #define SIO_EIR_IPR1 0x04
1412305Sstevel #define SIO_EIR_RXFT 0x08
1422305Sstevel #define SIO_EIR_FEN0 0x40
1432305Sstevel #define SIO_EIR_FEN1 0x80
1442305Sstevel
1452305Sstevel #define SIO_FCR_FIFO_EN 0x01
1462305Sstevel #define SIO_FCR_RXSR 0x02
1472305Sstevel #define SIO_FCR_TXSR 0x04
1482305Sstevel #define SIO_FCR_RXFTH0 0x40
1492305Sstevel #define SIO_FCR_RXFTH1 0x80
1502305Sstevel #define SIO_FCR_STD (SIO_FCR_RXFTH0|SIO_FCR_FIFO_EN)
1512305Sstevel
1522305Sstevel #define SIO_LCR_WLS0 0x01
1532305Sstevel #define SIO_LCR_WLS1 0x02
1542305Sstevel #define SIO_LCR_STB 0x04
1552305Sstevel #define SIO_LCR_PEN 0x08
1562305Sstevel #define SIO_LCR_EPS 0x10
1572305Sstevel #define SIO_LCR_STKP 0x20
1582305Sstevel #define SIO_LCR_SBRK 0x40
1592305Sstevel #define SIO_LCR_BKSE 0x80
1602305Sstevel #define SIO_LCR_8BIT (SIO_LCR_WLS0|SIO_LCR_WLS1)
1612305Sstevel #define SIO_LCR_EPAR (SIO_LCR_PEN|SIO_LCR_EPS)
1622305Sstevel #define SIO_LCR_STD (SIO_LCR_8BIT|SIO_LCR_EPAR)
1632305Sstevel
1642305Sstevel #define SIO_BSR_BANK0 (SIO_LCR_STD)
1652305Sstevel #define SIO_BSR_BANK1 (SIO_LCR_BKSE|SIO_LCR_STD)
1662305Sstevel
1672305Sstevel #define SIO_MCR_DTR 0x01
1682305Sstevel #define SIO_MCR_RTS 0x02
1692305Sstevel #define SIO_MCR_ISEN 0x08
1702305Sstevel #define SIO_MCR_STD (SIO_MCR_ISEN)
1712305Sstevel
1722305Sstevel #define SIO_LSR_RXDA 0x01
1732305Sstevel #define SIO_LSR_OE 0x02
1742305Sstevel #define SIO_LSR_PE 0x04
1752305Sstevel #define SIO_LSR_FE 0x08
1762305Sstevel #define SIO_LSR_BRKE 0x10
1772305Sstevel #define SIO_LSR_TXRDY 0x20
1782305Sstevel #define SIO_LSR_TXEMP 0x40
1792305Sstevel #define SIO_LSR_ER_INF 0x80
1802305Sstevel
1812305Sstevel #define SIO_MSR_DCTS 0x01
1822305Sstevel #define SIO_MSR_DDSR 0x02
1832305Sstevel #define SIO_MSR_TERI 0x04
1842305Sstevel #define SIO_MSR_DDCD 0x08
1852305Sstevel #define SIO_MSR_CTS 0x10
1862305Sstevel #define SIO_MSR_DSR 0x20
1872305Sstevel #define SIO_MSR_RI 0x40
1882305Sstevel #define SIO_MSR_DCD 0x80
1892305Sstevel
1902305Sstevel /*
1912305Sstevel * Min/max/default baud rates, and a macro to convert from a baud
1922305Sstevel * rate to the number (divisor) to put in the baud rate registers
1932305Sstevel */
1942305Sstevel #define SIO_BAUD_MIN 50
1952305Sstevel #define SIO_BAUD_MAX 115200
1962305Sstevel #define SIO_BAUD_DEFAULT 38400
1972305Sstevel #define SIO_BAUD_TO_DIVISOR(b) (115200 / (b))
1982305Sstevel
1992305Sstevel
2002305Sstevel /*
2012305Sstevel * Packet format ...
2022305Sstevel */
2032305Sstevel #define LOMBUS_MASK 0xc0 /* Byte-type bits */
2042305Sstevel #define LOMBUS_PARAM 0x00 /* Parameter byte: 0b0xxxxxxx */
2052305Sstevel #define LOMBUS_LAST 0x80 /* Last byte of packet */
2062305Sstevel #define LOMBUS_CMD 0x80 /* Command byte: 0b10###XWV */
2072305Sstevel #define LOMBUS_STATUS 0xc0 /* Status byte: 0b11###AEV */
2082305Sstevel
2092305Sstevel #define LOMBUS_SEQ 0x38 /* Sequence number bits */
2102305Sstevel #define LOMBUS_SEQ_LSB 0x08 /* Sequence number LSB */
2112305Sstevel #define LOMBUS_CMD_XADDR 0x04 /* Extended (2-byte) addressing */
2122305Sstevel #define LOMBUS_CMD_WRITE 0x02 /* Write command */
2132305Sstevel #define LOMBUS_CMD_WMSB 0x01 /* Set MSB on Write */
2142305Sstevel #define LOMBUS_CMD_READ 0x01 /* Read command */
2152305Sstevel #define LOMBUS_CMD_NOP 0x00 /* NOP command */
2162305Sstevel
2172305Sstevel #define LOMBUS_STATUS_ASYNC 0x04 /* Asynchronous event pending */
2182305Sstevel #define LOMBUS_STATUS_ERR 0x02 /* Error in command processing */
2192305Sstevel #define LOMBUS_STATUS_MSB 0x01 /* MSB of Value read */
2202305Sstevel
2212305Sstevel #define LOMBUS_VREG_LO(x) ((x) & ((1 << 7) - 1))
2222305Sstevel #define LOMBUS_VREG_HI(x) ((x) >> 7)
2232305Sstevel
2242305Sstevel #define LOMBUS_BUFSIZE 8
2252305Sstevel
2262305Sstevel
2272305Sstevel /*
2282305Sstevel * Time periods, in nanoseconds
2292305Sstevel *
2302305Sstevel * Note that LOMBUS_ONE_SEC and some other time
2312305Sstevel * periods are defined in <sys/lombus.h>
2322305Sstevel */
2332305Sstevel #define LOMBUS_CMD_POLL (LOMBUS_ONE_SEC/20)
2342305Sstevel #define LOMBUS_CTS_POLL (LOMBUS_ONE_SEC/20)
2352305Sstevel #define LOMBUS_CTS_TIMEOUT (LOMBUS_ONE_SEC*2)
2362305Sstevel
2372305Sstevel
2382305Sstevel /*
2392305Sstevel * Local datatypes
2402305Sstevel */
2412305Sstevel enum lombus_cmdstate {
2422305Sstevel LOMBUS_CMDSTATE_IDLE,
2432305Sstevel LOMBUS_CMDSTATE_BUSY,
2442305Sstevel LOMBUS_CMDSTATE_WAITING,
2452305Sstevel LOMBUS_CMDSTATE_READY,
2462305Sstevel LOMBUS_CMDSTATE_ERROR
2472305Sstevel };
2482305Sstevel
2492305Sstevel
2502305Sstevel /*
2512305Sstevel * This driver's soft-state structure
2522305Sstevel */
2532305Sstevel
2542305Sstevel struct lombus_state {
2552305Sstevel /*
2562305Sstevel * Configuration data, set during attach
2572305Sstevel */
2582305Sstevel dev_info_t *dip;
2592305Sstevel major_t majornum;
2602305Sstevel int instance;
2612305Sstevel
2622305Sstevel ddi_acc_handle_t sio_handle;
2632305Sstevel uint8_t *sio_regs;
2642305Sstevel ddi_softintr_t softid;
2655107Seota ddi_periodic_t cycid; /* periodical callback */
2662305Sstevel
2672305Sstevel /*
2682305Sstevel * Parameters derived from .conf properties
2692305Sstevel */
2702305Sstevel boolean_t allow_echo;
2712305Sstevel int baud;
2722305Sstevel uint32_t debug;
2732305Sstevel boolean_t fake_cts;
2742305Sstevel
2752305Sstevel /*
2762305Sstevel * Hardware mutex (initialised using <hw_iblk>),
2772305Sstevel * used to prevent retriggering the softint while
2782305Sstevel * it's still fetching data out of the chip FIFO.
2792305Sstevel */
2802305Sstevel kmutex_t hw_mutex[1];
2812305Sstevel ddi_iblock_cookie_t hw_iblk;
2822305Sstevel
2832305Sstevel /*
2842305Sstevel * Data protected by the hardware mutex: the watchdog-patting
2852305Sstevel * protocol data (since the dog can be patted from a high-level
2862305Sstevel * cyclic), and the interrupt-enabled flag.
2872305Sstevel */
2882305Sstevel hrtime_t hw_last_pat;
2892305Sstevel boolean_t hw_int_enabled;
2902305Sstevel
2912305Sstevel /*
2922305Sstevel * Flag to indicate that we've incurred a hardware fault on
2932305Sstevel * accesses to the SIO; once this is set, we fake all further
2942305Sstevel * accesses in order not to provoke additional bus errors.
2952305Sstevel */
2962305Sstevel boolean_t sio_fault;
2972305Sstevel
2982305Sstevel /*
2992305Sstevel * Serial protocol state data, protected by lo_mutex
3002305Sstevel * (which is initialised using <lo_iblk>)
3012305Sstevel */
3022305Sstevel kmutex_t lo_mutex[1];
3032305Sstevel ddi_iblock_cookie_t lo_iblk;
3042305Sstevel kcondvar_t lo_cv[1];
3052305Sstevel
3062305Sstevel volatile enum lombus_cmdstate cmdstate;
3072305Sstevel clock_t deadline;
3082305Sstevel uint8_t cmdbuf[LOMBUS_BUFSIZE];
3092305Sstevel uint8_t reply[LOMBUS_BUFSIZE];
3102305Sstevel uint8_t async;
3112305Sstevel uint8_t index;
3122305Sstevel uint8_t result;
3132305Sstevel uint8_t sequence;
3142305Sstevel uint32_t error;
3152305Sstevel };
3162305Sstevel
3172305Sstevel /*
3182305Sstevel * The auxiliary structure attached to each child
3192305Sstevel * (the child's parent-private-data points to this).
3202305Sstevel */
3212305Sstevel struct lombus_child_info {
3222305Sstevel lombus_regspec_t *rsp;
3232305Sstevel int nregs;
3242305Sstevel };
3252305Sstevel
3262305Sstevel
3272305Sstevel /*
3282305Sstevel * Local data
3292305Sstevel */
3302305Sstevel
3312305Sstevel static void *lombus_statep;
3322305Sstevel
3332305Sstevel static major_t lombus_major = NOMAJOR;
3342305Sstevel
3352305Sstevel static ddi_device_acc_attr_t lombus_dev_acc_attr[1] =
3362305Sstevel {
3372305Sstevel DDI_DEVICE_ATTR_V0,
3382305Sstevel DDI_STRUCTURE_LE_ACC,
3392305Sstevel DDI_STRICTORDER_ACC
3402305Sstevel };
3412305Sstevel
3422305Sstevel
3432305Sstevel /*
3442305Sstevel * General utility routines ...
3452305Sstevel */
3462305Sstevel
3472305Sstevel static void
lombus_trace(struct lombus_state * ssp,char code,const char * caller,const char * fmt,...)3482305Sstevel lombus_trace(struct lombus_state *ssp, char code, const char *caller,
3492305Sstevel const char *fmt, ...)
3502305Sstevel {
3512305Sstevel char buf[256];
3522305Sstevel char *p;
3532305Sstevel va_list va;
3542305Sstevel
3552305Sstevel if (ssp->debug & (1 << (code-'@'))) {
3562305Sstevel p = buf;
357*11311SSurya.Prakki@Sun.COM (void) snprintf(p, sizeof (buf) - (p - buf),
3585107Seota "%s/%s: ", MYNAME, caller);
3592305Sstevel p += strlen(p);
3602305Sstevel
3612305Sstevel va_start(va, fmt);
362*11311SSurya.Prakki@Sun.COM (void) vsnprintf(p, sizeof (buf) - (p - buf), fmt, va);
3632305Sstevel va_end(va);
3642305Sstevel
3652305Sstevel buf[sizeof (buf) - 1] = '\0';
366*11311SSurya.Prakki@Sun.COM (void) strlog(ssp->majornum, ssp->instance, code, SL_TRACE,
367*11311SSurya.Prakki@Sun.COM buf);
3682305Sstevel }
3692305Sstevel }
3702305Sstevel
3712305Sstevel static struct lombus_state *
lombus_getstate(dev_info_t * dip,int instance,const char * caller)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
sio_put_reg(struct lombus_state * ssp,uint_t reg,uint8_t val)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
sio_get_reg(struct lombus_state * ssp,uint_t reg)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
sio_check_fault_status(struct lombus_state * ssp)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
sio_faulty(struct lombus_state * ssp)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
sio_data_ready(struct lombus_state * ssp)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
sio_lom_ready(struct lombus_state * ssp)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",
5235107Seota 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",
5505107Seota 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
lombus_set_irq(struct lombus_state * ssp,boolean_t newstate)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
lombus_toggle_rts(struct lombus_state * ssp)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
lombus_hi_intr(caddr_t arg)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;
6235107Seota 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
lombus_receive(struct lombus_state * ssp)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",
6525107Seota "state %d; error $%x",
6535107Seota 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",
6815107Seota "rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
6825107Seota rcvd,
6835107Seota ssp->reply[0], ssp->reply[1],
6845107Seota ssp->reply[2], ssp->reply[3],
6855107Seota ssp->reply[4], ssp->reply[5],
6865107Seota 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",
7395107Seota "echo $%02x $%02x $%02x $%02x "
7405107Seota "$%02x $%02x $%02x $%02x",
7415107Seota ssp->reply[0], ssp->reply[1],
7425107Seota ssp->reply[2], ssp->reply[3],
7435107Seota ssp->reply[4], ssp->reply[5],
7445107Seota 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",
7825107Seota "rcvd %d; last $%02x; state %d; error $%x; ready %d",
7835107Seota 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
lombus_softint(caddr_t arg)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
lombus_cyclic(void * arg)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
lombus_cmd(HANDLE_TYPE * hdlp,ptrdiff_t vreg,uint_t val,uint_t cmd)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 uint8_t *p;
8322305Sstevel
8332305Sstevel /*
8342305Sstevel * First of all, wait for the interface to be available.
8352305Sstevel *
8362305Sstevel * NOTE: we blow through all the mutex/cv/state checking and
8372305Sstevel * preempt any command in progress if the system is panicking!
8382305Sstevel */
8392305Sstevel ssp = HANDLE_PRIVATE(hdlp);
8402305Sstevel mutex_enter(ssp->lo_mutex);
8412305Sstevel while (ssp->cmdstate != LOMBUS_CMDSTATE_IDLE && !panicstr)
8422305Sstevel cv_wait(ssp->lo_cv, ssp->lo_mutex);
8432305Sstevel
8442305Sstevel ssp->cmdstate = LOMBUS_CMDSTATE_BUSY;
8452305Sstevel ssp->sequence = (ssp->sequence + LOMBUS_SEQ_LSB) & LOMBUS_SEQ;
8462305Sstevel
8472305Sstevel /*
8482305Sstevel * We have exclusive ownership, so assemble the command (backwards):
8492305Sstevel *
8502305Sstevel * [byte 0] Command: modified by XADDR and/or WMSB bits
8512305Sstevel * [Optional] Parameter: Value to write (low 7 bits)
8522305Sstevel * [Optional] Parameter: Register number (high 7 bits)
8532305Sstevel * [Optional] Parameter: Register number (low 7 bits)
8542305Sstevel */
8552305Sstevel p = &ssp->cmdbuf[0];
8562305Sstevel *p++ = LOMBUS_CMD | ssp->sequence | cmd;
8572305Sstevel switch (cmd) {
8582305Sstevel case LOMBUS_CMD_WRITE:
8592305Sstevel *p++ = val & 0x7f;
8602305Sstevel if (val >= 0x80)
8612305Sstevel ssp->cmdbuf[0] |= LOMBUS_CMD_WMSB;
8622305Sstevel /*FALLTHRU*/
8632305Sstevel case LOMBUS_CMD_READ:
8642305Sstevel if (LOMBUS_VREG_HI(vreg) != 0) {
8652305Sstevel *p++ = LOMBUS_VREG_HI(vreg);
8662305Sstevel ssp->cmdbuf[0] |= LOMBUS_CMD_XADDR;
8672305Sstevel }
8682305Sstevel *p++ = LOMBUS_VREG_LO(vreg);
8692305Sstevel /*FALLTHRU*/
8702305Sstevel case LOMBUS_CMD_NOP:
8712305Sstevel break;
8722305Sstevel }
8732305Sstevel
8742305Sstevel /*
8752305Sstevel * Check and update the SIO h/w fault status before accessing
8762305Sstevel * the chip registers. If there's a (new or previous) fault,
8772305Sstevel * we'll run through the protocol but won't really touch the
8782305Sstevel * hardware and all commands will timeout. If a previously
8792305Sstevel * discovered fault has now gone away (!), then we can (try to)
8802305Sstevel * proceed with the new command (probably a probe).
8812305Sstevel */
8822305Sstevel sio_check_fault_status(ssp);
8832305Sstevel
8842305Sstevel /*
8852305Sstevel * Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
8862305Sstevel * us that it's ready for the next command. If it doesn't, though,
8872305Sstevel * we'll send it anyway, on the basis that the CTS signal might be
8882305Sstevel * open- or short-circuited (or the LOM firmware forgot to set it,
8892305Sstevel * or the LOM just got reset, or whatever ...)
8902305Sstevel */
8912305Sstevel start = ddi_get_lbolt();
8922305Sstevel ssp->deadline = start + drv_usectohz(LOMBUS_CTS_TIMEOUT/1000);
8932305Sstevel while (!sio_lom_ready(ssp)) {
89411066Srafael.vanoni@sun.com if (ddi_get_lbolt() > ssp->deadline)
8952305Sstevel break;
89611066Srafael.vanoni@sun.com
897*11311SSurya.Prakki@Sun.COM (void) cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
89811066Srafael.vanoni@sun.com drv_usectohz(LOMBUS_CTS_POLL/1000), TR_CLOCK_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) {
93011066Srafael.vanoni@sun.com if (cv_reltimedwait(ssp->lo_cv, ssp->lo_mutex,
93111066Srafael.vanoni@sun.com drv_usectohz(LOMBUS_CMD_POLL/1000), TR_CLOCK_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
lombus_vreg_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_vreg_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_vreg_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_vreg_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_pat_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_pat_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_pat_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_pat_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_event_get16(HANDLE_TYPE * hdlp,uint16_t * addr)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
lombus_event_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)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
lombus_event_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_event_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_meta_get32(HANDLE_TYPE * hdlp,uint32_t * addr)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;
1246*11311SSurya.Prakki@Sun.COM (void) 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
lombus_meta_put32(HANDLE_TYPE * hdlp,uint32_t * addr,uint32_t val)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;
1301*11311SSurya.Prakki@Sun.COM (void) 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
lombus_meta_rep_get32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
lombus_meta_rep_put32(HANDLE_TYPE * hdlp,uint32_t * host_addr,uint32_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get8(HANDLE_TYPE * hdlp,uint8_t * addr)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
lombus_no_put8(HANDLE_TYPE * hdlp,uint8_t * addr,uint8_t val)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
lombus_no_rep_get8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put8(HANDLE_TYPE * hdlp,uint8_t * host_addr,uint8_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get16(HANDLE_TYPE * hdlp,uint16_t * addr)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
lombus_no_put16(HANDLE_TYPE * hdlp,uint16_t * addr,uint16_t val)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
lombus_no_rep_get16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put16(HANDLE_TYPE * hdlp,uint16_t * host_addr,uint16_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_get64(HANDLE_TYPE * hdlp,uint64_t * addr)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
lombus_no_put64(HANDLE_TYPE * hdlp,uint64_t * addr,uint64_t val)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
lombus_no_rep_get64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
lombus_no_rep_put64(HANDLE_TYPE * hdlp,uint64_t * host_addr,uint64_t * dev_addr,size_t repcount,uint_t flags)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
lombus_acc_fault_check(HANDLE_TYPE * hdlp)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
lombus_hw_reset(struct lombus_state * ssp)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
lombus_offline(struct lombus_state * ssp)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
lombus_online(struct lombus_state * ssp)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
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)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
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)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
lombus_map(dev_info_t * dip,dev_info_t * rdip,ddi_map_req_t * mp,off_t off,off_t len,caddr_t * addrp)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,
18675107Seota rsp->lombus_space, VREG_TO_ADDR(rsp->lombus_base+off), len,
18685107Seota mp->map_handlep, addrp));
18692305Sstevel }
18702305Sstevel
18712305Sstevel static int
lombus_ctlops(dev_info_t * dip,dev_info_t * rdip,ddi_ctl_enum_t op,void * arg,void * result)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,
19105107Seota 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),
19615107Seota "%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",
19805107Seota ddi_node_name(rdip), ddi_get_name_addr(rdip),
19815107Seota 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
lombus_unattach(struct lombus_state * ssp,int instance)20082305Sstevel lombus_unattach(struct lombus_state *ssp, int instance)
20092305Sstevel {
20102305Sstevel if (ssp != NULL) {
20112305Sstevel lombus_hw_reset(ssp);
20125107Seota if (ssp->cycid != NULL) {
20135107Seota ddi_periodic_delete(ssp->cycid);
20145107Seota 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
lombus_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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,
20705107Seota DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0;
20712305Sstevel ssp->baud = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20725107Seota DDI_PROP_DONTPASS, "baud-rate", 0);
20732305Sstevel ssp->debug = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20745107Seota DDI_PROP_DONTPASS, "debug", 0);
20752305Sstevel ssp->fake_cts = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
20765107Seota 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();
20835107Seota 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,
20995107Seota &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,
21055107Seota 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
21115107Seota /*
21125107Seota * Register a periodical handler.
21135107Seota */
21145107Seota ssp->cycid = ddi_periodic_add(lombus_cyclic, ssp, LOMBUS_ONE_SEC,
21155107Seota 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
lombus_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
lombus_reset(dev_info_t * dip,ddi_reset_cmd_t cmd)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 */
22387656SSherry.Moore@Sun.COM &lombus_bus_ops, /* bus operations */
22397656SSherry.Moore@Sun.COM NULL, /* power */
22407656SSherry.Moore@Sun.COM ddi_quiesce_not_supported, /* devo_quiesce */
22412305Sstevel };
22422305Sstevel
22432305Sstevel static struct modldrv modldrv =
22442305Sstevel {
22452305Sstevel &mod_driverops,
22467656SSherry.Moore@Sun.COM "lombus driver",
22472305Sstevel &lombus_dev_ops
22482305Sstevel };
22492305Sstevel
22502305Sstevel static struct modlinkage modlinkage =
22512305Sstevel {
22522305Sstevel MODREV_1,
22532305Sstevel {
22542305Sstevel &modldrv,
22552305Sstevel NULL
22562305Sstevel }
22572305Sstevel };
22582305Sstevel
22592305Sstevel
22602305Sstevel /*
22612305Sstevel * Dynamic loader interface code
22622305Sstevel */
22632305Sstevel
22642305Sstevel int
_init(void)22652305Sstevel _init(void)
22662305Sstevel {
22672305Sstevel int err;
22682305Sstevel
22692305Sstevel err = ddi_soft_state_init(&lombus_statep,
22705107Seota sizeof (struct lombus_state), 0);
22712305Sstevel if (err == DDI_SUCCESS)
22722305Sstevel if ((err = mod_install(&modlinkage)) != 0) {
22732305Sstevel ddi_soft_state_fini(&lombus_statep);
22742305Sstevel }
22752305Sstevel
22762305Sstevel return (err);
22772305Sstevel }
22782305Sstevel
22792305Sstevel int
_info(struct modinfo * mip)22802305Sstevel _info(struct modinfo *mip)
22812305Sstevel {
22822305Sstevel return (mod_info(&modlinkage, mip));
22832305Sstevel }
22842305Sstevel
22852305Sstevel int
_fini(void)22862305Sstevel _fini(void)
22872305Sstevel {
22882305Sstevel int err;
22892305Sstevel
22902305Sstevel if ((err = mod_remove(&modlinkage)) == 0) {
22912305Sstevel ddi_soft_state_fini(&lombus_statep);
22922305Sstevel lombus_major = NOMAJOR;
22932305Sstevel }
22942305Sstevel
22952305Sstevel return (err);
22962305Sstevel }
2297