/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* The "lombus" driver provides access to the LOMlite2 virtual registers,
* so that its clients (children) need not be concerned with the details
* of the access mechanism, which in this case is implemented via a
* packet-based protocol over a serial link connected to one of the serial
* ports of the SuperIO (SIO) chip.
*
* On the other hand, this driver doesn't generally know what the virtual
* registers signify - only the clients need this information.
*/
/*
* Header files
*/
#if defined(NDI_ACC_HDL_V2)
/*
* Compiling for Solaris 9+ with access handle enhancements
*/
#else
/*
* Compatibility definitions for backport to Solaris 8
*/
#endif /* NDI_ACC_HDL_V2 */
/*
* Local definitions
*/
#define LOMBUS_INST_TO_MINOR(i) (i)
#define LOMBUS_MINOR_TO_INST(m) (m)
/*
* The following definitions are taken from the datasheet
* for the National Semiconductor PC87317 (SuperIO) chip.
*
* This chip implements UART functionality as logical device 6.
* It provides all sorts of wierd modes and extensions, but we
* have chosen to use only the 16550-compatible features
* ("non-extended mode").
*
* Hardware: serial chip register numbers
*/
/*
* Hardware: serial chip register bits
*/
/*
* rate to the number (divisor) to put in the baud rate registers
*/
/*
* Packet format ...
*/
/*
* Time periods, in nanoseconds
*
* Note that LOMBUS_ONE_SEC and some other time
*/
/*
* Local datatypes
*/
enum lombus_cmdstate {
};
/*
* This driver's soft-state structure
*/
struct lombus_state {
/*
* Configuration data, set during attach
*/
int instance;
/*
* Parameters derived from .conf properties
*/
int baud;
/*
* Hardware mutex (initialised using <hw_iblk>),
* used to prevent retriggering the softint while
* it's still fetching data out of the chip FIFO.
*/
/*
* Data protected by the hardware mutex: the watchdog-patting
* protocol data (since the dog can be patted from a high-level
* cyclic), and the interrupt-enabled flag.
*/
/*
* Flag to indicate that we've incurred a hardware fault on
* accesses to the SIO; once this is set, we fake all further
* accesses in order not to provoke additional bus errors.
*/
/*
* Serial protocol state data, protected by lo_mutex
* (which is initialised using <lo_iblk>)
*/
};
/*
* The auxiliary structure attached to each child
* (the child's parent-private-data points to this).
*/
struct lombus_child_info {
int nregs;
};
/*
* Local data
*/
static void *lombus_statep;
{
};
/*
* General utility routines ...
*/
static void
const char *fmt, ...)
{
char *p;
p = buf;
p += strlen(p);
buf);
}
}
static struct lombus_state *
{
/*
* Use the instance number from the <dip>; also,
* check that it really corresponds to this driver
*/
lombus_major = dmaj;
else if (dmaj != lombus_major) {
"%s: major number mismatch (%d vs. %d) in %s(),"
"probably due to child misconfiguration",
instance = -1;
}
}
if (instance >= 0)
"%s: devinfo mismatch (%p vs. %p) in %s(), "
"probably due to child misconfiguration",
}
}
return (ssp);
}
/*
*/
static void
{
/*
* The chip is mapped as "I/O" (e.g. with the side-effect
* bit on SPARC), therefore accesses are required to be
* in-order, with no value cacheing. However, there can
* still be write-behind buffering, so it is not guaranteed
* that a write actually reaches the chip in a given time.
*
* To force the access right through to the chip, we follow
* the write with another write (to the SCRATCH register)
* and a read (of the value just written to the SCRATCH
* register). The SCRATCH register is specifically provided
* for temporary data and has no effect on the SIO's own
* operation, making it ideal as a synchronising mechanism.
*
* If we didn't do this, it would be possible that the new
* value wouldn't reach the chip (and have the *intended*
* side-effects, such as disabling interrupts), for such a
* long time that the processor could execute a *lot* of
* instructions - including exiting the interrupt service
* routine and re-enabling interrupts. This effect was
* observed to lead to spurious (unclaimed) interrupts in
* some circumstances.
*
* This will no longer be needed once "synchronous" access
* handles are available (see PSARC/2000/269 and 2000/531).
*/
membar_sync();
}
}
static uint8_t
{
else
val = DUMMY_VALUE;
return (val);
}
static void
{
}
static boolean_t
{
}
/*
* Check for data ready.
*/
static boolean_t
{
/*
* Data is available if the RXDA bit in the LSR is nonzero
* (if reading it didn't incur a fault).
*/
}
/*
* Check for LOM ready
*/
static boolean_t
{
/*
* The LOM is ready if the CTS bit in the MSR is 1, meaning
* that the /CTS signal is being asserted (driven LOW) -
* unless we incurred a fault in trying to read the MSR!
*
* For debugging, we force the result to TRUE if the FAKE flag is set
*/
}
#if 0
/*
* Check for interrupt pending
*/
static boolean_t
{
/*
* An interrupt is pending if the IPF bit in the EIR is 0,
* assuming we didn't incur a fault in trying to ready it.
*
* Note: we expect that every time we read this register
* (which is only done from the interrupt service routine),
* we will see $11001100 (RX FIFO timeout interrupt pending).
*/
/*
* To investigate whether we're getting any abnormal interrupts
* this code checks that the status value is as expected, and that
* chip-level interrupts are supposed to be enabled at this time.
* This will cause a PANIC (on a driver compiled with DEBUG) if
* all is not as expected ...
*/
return (rslt);
}
#endif /* 0 */
/*
*/
static void
{
}
/*
*/
static void
{
val &= SIO_MCR_RTS;
val ^= SIO_MCR_RTS;
val |= SIO_MCR_STD;
}
/*
* High-level interrupt handler:
* Checks whether initialisation is complete (to avoid a race
* with mutex_init()), and whether chip interrupts are enabled.
* If not, the interrupt's not for us, so just return UNCLAIMED.
* Otherwise, disable the interrupt, trigger a softint, and return
* CLAIMED. The softint handler will then do all the real work.
*
* NOTE: the chip interrupt capability is only re-enabled once the
* receive code has run, but that can be called from a poll loop
* or cyclic callback as well as from the softint. So it's *not*
* guaranteed that there really is a chip interrupt pending here,
* 'cos the work may already have been done and the reason for the
* interrupt gone away before we get here.
*
* OTOH, if we come through here twice without the receive code
* having run in between, that's definitely wrong. In such an
* event, we would notice that chip interrupts haven't yet been
* re-enabled and return UNCLAIMED, allowing the system's jabber
* protect code (if any) to do its job.
*/
static uint_t
{
if (ssp->hw_int_enabled) {
}
}
return (claim);
}
/*
* Packet receive handler
*
* This routine should be called from the low-level softint, or the
* cyclic callback, or lombus_cmd() (for polled operation), with the
* low-level mutex already held.
*/
static void
{
"state %d; error $%x",
/*
* Check for access faults before starting the receive
* loop (we don't want to cause bus errors or suchlike
* unpleasantness in the event that the SIO has died).
*/
if (!sio_faulty(ssp)) {
/*
* Read bytes from the FIFO until they're all gone,
* or we find the 'END OF PACKET' set on one, or
* our buffer overflows (which must be an error)
*/
while (sio_data_ready(ssp)) {
if (++rcvd >= LOMBUS_BUFSIZE)
break;
if (data & LOMBUS_LAST)
break;
}
}
"rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
rcvd,
/*
* We're not expecting any data in this state, so if
* we DID receive any data, we just throw it away by
* resetting the buffer index to 0.
*/
} else if (rcvd == 0) {
/*
* No bytes received this time through (though there
* might be a partial packet sitting in the buffer).
* If it seems the LOM is taking too long to respond,
* we'll assume it's died and return an error.
*/
}
} else if (rcvd >= LOMBUS_BUFSIZE) {
/*
* Buffer overflow; discard the data & treat as an error
* (even if the last byte read did claim to terminate a
* packet, it can't be a valid one 'cos it's too long!)
*/
} else if ((data & LOMBUS_LAST) == 0) {
/*
* Packet not yet complete; leave the partial packet in
* the buffer for later ...
*/
;
/*
* Invalid "status" byte - maybe an echo of the command?
*
* As a debugging feature, we allow for this, assuming
* that if the LOM has echoed the command byte, it has
* also echoed all the parameter bytes before starting
* command processing. So, we dump out the buffer and
* then clear it, so we can go back to looking for the
* real reply.
*
* Otherwise, we just drop the data & flag an error.
*/
if (ssp->allow_echo) {
"echo $%02x $%02x $%02x $%02x "
"$%02x $%02x $%02x $%02x",
} else {
}
/*
* Wrong sequence number! Flag this as an error
*/
} else {
/*
* Finally, we know that's it's a valid reply to our
* last command. Update the ASYNC status, derive the
* reply parameter (if any), and check the ERROR bit
* to find out what the parameter means.
*
* are meaningful, but it doesn't matter; the waiting
* thread will know which one(s) it should check.
*/
if (data & LOMBUS_STATUS_ERR) {
} else {
}
}
"rcvd %d; last $%02x; state %d; error $%x; ready %d",
if (ready)
}
/*
* Low-level softint handler
*
* This routine should be triggered whenever there's a byte to be read
*/
static uint_t
{
return (DDI_INTR_CLAIMED);
}
/*
* Cyclic handler: just calls the receive routine, in case interrupts
* are not being delivered and in order to handle command timeout
*/
static void
{
}
/*
* Serial protocol
*
* This routine builds a command and sets it in progress.
*/
static uint8_t
{
uint8_t *p;
/*
* First of all, wait for the interface to be available.
*
* preempt any command in progress if the system is panicking!
*/
/*
* We have exclusive ownership, so assemble the command (backwards):
*
* [Optional] Parameter: Value to write (low 7 bits)
* [Optional] Parameter: Register number (high 7 bits)
* [Optional] Parameter: Register number (low 7 bits)
*/
switch (cmd) {
case LOMBUS_CMD_WRITE:
*p++ = val & 0x7f;
if (val >= 0x80)
/*FALLTHRU*/
case LOMBUS_CMD_READ:
if (LOMBUS_VREG_HI(vreg) != 0) {
*p++ = LOMBUS_VREG_HI(vreg);
}
*p++ = LOMBUS_VREG_LO(vreg);
/*FALLTHRU*/
case LOMBUS_CMD_NOP:
break;
}
/*
* Check and update the SIO h/w fault status before accessing
* the chip registers. If there's a (new or previous) fault,
* we'll run through the protocol but won't really touch the
* hardware and all commands will timeout. If a previously
* discovered fault has now gone away (!), then we can (try to)
* proceed with the new command (probably a probe).
*/
/*
* Wait up to LOMBUS_CTS_TIMEOUT (2 seconds) for the LOM to tell
* us that it's ready for the next command. If it doesn't, though,
* we'll send it anyway, on the basis that the CTS signal might be
* open- or short-circuited (or the LOM firmware forgot to set it,
* or the LOM just got reset, or whatever ...)
*/
start = ddi_get_lbolt();
while (!sio_lom_ready(ssp)) {
break;
}
/*
* Either the LOM is ready, or we timed out waiting for CTS.
* In either case, we're going to send the command now by
* stuffing the packet into the Tx FIFO, reversing it as we go.
* We call lombus_receive() first to ensure there isn't any
* garbage left in the Rx FIFO from an earlier command that
* timed out (or was pre-empted by a PANIC!). This also makes
* sure that SIO interrupts are enabled so we'll see the reply
* more quickly (the poll loop below will still work even if
* interrupts aren't enabled, but it will take longer).
*/
/*
* or timeout.
*/
start = ddi_get_lbolt();
}
/*
* The return value may not be meaningful but retrieve it anyway
*/
if (sio_faulty(ssp)) {
val = DUMMY_VALUE;
/*
* Some problem here ... transfer the error code from
* the per-instance state to the per-handle fault flag.
* The error code shouldn't be zero!
*/
else
}
/*
* All done now!
*/
return (val);
}
/*
* Space 0 - LOM virtual register access
* Only 8-bit accesses are supported.
*/
static uint8_t
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping originally requested.
*/
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
/*
* Derive the virtual register number and run the command
*/
}
static void
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping originally requested.
*/
/*
* Invalid access - flag a fault and return
*/
return;
}
/*
* Derive the virtual register number and run the command
*/
}
static void
{
}
static void
{
}
/*
* Space 1 - LOM watchdog pat register access
* Only 8-bit accesses are supported.
*
* Reads have no effect and return 0.
*
* Writes pat the dog by toggling the RTS line iff enough time has
* elapsed since last time we toggled it.
*
* Multi-byte reads (using ddi_rep_get8(9F)) are a fairly inefficient
* way of zeroing the destination area ;-) and still won't pat the dog.
*
* Multi-byte writes (using ddi_rep_put8(9F)) will almost certainly
* only count as a single pat, no matter how many bytes the caller
* says to write, as the inter-pat time is VERY long compared with
* the time it will take to read the memory source area.
*/
static uint8_t
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping originally requested.
*/
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
return (0);
}
static void
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping originally requested.
*/
/*
* Invalid access - flag a fault and return
*/
return;
}
}
}
static void
{
}
static void
{
}
/*
* Space 2 - LOM async event flag register access
* Only 16-bit accesses are supported.
*/
static uint16_t
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping orignally requested.
*/
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
/*
* Return the value of the asynchronous-event-pending flag
* as passed back by the LOM at the end of the last command.
*/
}
static void
{
/*
* Check the offset that the caller has added to the base address
* against the length of the mapping originally requested.
*/
/*
* Invalid access - flag a fault and return
*/
return;
}
/*
* The user can't overwrite the asynchronous-event-pending flag!
*/
}
static void
{
}
static void
{
}
/*
* All spaces - access handle fault information
* Only 32-bit accesses are supported.
*/
static uint32_t
{
/*
* Derive the offset that the caller has added to the base
* address originally returned, and use it to determine
* which meta-register is to be accessed ...
*/
switch (offset) {
case LOMBUS_FAULT_REG:
/*
* This meta-register provides a code for the most
* recent virtual register access fault, if any.
*/
return (HANDLE_FAULT(hdlp));
case LOMBUS_PROBE_REG:
/*
* Reading this meta-register clears any existing fault
* (at the virtual, not the hardware access layer), then
* runs a NOP command and returns the fault code from that.
*/
HANDLE_FAULT(hdlp) = 0;
return (HANDLE_FAULT(hdlp));
case LOMBUS_ASYNC_REG:
/*
* Obsolescent - but still supported for backwards
* compatibility. This is an alias for the newer
* LOMBUS_EVENT_REG, but doesn't require a separate
* "reg" entry and ddi_regs_map_setup() call.
*
* It returns the value of the asynchronous-event-pending
* flag as passed back by the LOM at the end of the last
* completed command.
*/
default:
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
}
static void
{
/*
* Derive the offset that the caller has added to the base
* address originally returned, and use it to determine
* which meta-register is to be accessed ...
*/
switch (offset) {
case LOMBUS_FAULT_REG:
/*
* This meta-register contains a code for the most
* recent virtual register access fault, if any.
* It can be cleared simply by writing 0 to it.
*/
return;
case LOMBUS_PROBE_REG:
/*
* Writing this meta-register clears any existing fault
* (at the virtual, not the hardware acess layer), then
* runs a NOP command. The caller can check the fault
* code later if required.
*/
HANDLE_FAULT(hdlp) = 0;
return;
default:
/*
* Invalid access - flag a fault
*/
return;
}
}
static void
{
}
static void
{
}
/*
* Finally, some dummy functions for all unsupported access
*/
static uint8_t
{
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static uint16_t
{
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static uint64_t
{
/*
* Invalid access - flag a fault and return a dummy value
*/
return (DUMMY_VALUE);
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static void
{
/*
* Invalid access - flag a fault
*/
}
static int
{
return (HANDLE_FAULT(hdlp) != 0);
}
/*
* Hardware setup - put the SIO chip in the required operational
* state, with all our favourite parameters programmed correctly.
* This routine leaves all SIO interrupts disabled.
*/
static void
{
/*
* Disable interrupts, soft reset Tx and Rx circuitry,
*/
/*
* Select the proper baud rate; if the value is invalid
* (presumably 0, i.e. not specified, but also if the
* "baud" property is set to some silly value), we assume
* the default.
*/
else
/*
* According to the datasheet, it is forbidden for the divisor
* register to be zero. So when loading the register in two
* steps, we have to make sure that the temporary value formed
* between loads is nonzero. However, we can't rely on either
* half already having a nonzero value, as the datasheet also
* says that these registers are indeterminate after a reset!
* So, we explicitly set the low byte to a non-zero value first;
* then we can safely load the high byte, and then the correct
* value for the low byte, without the result ever being zero.
*/
/*
* Program the remaining device registers as required
*/
}
/*
* Higher-level setup & teardown
*/
static void
{
}
static int
{
caddr_t p;
int nregs;
int err;
nregs = 0;
switch (nregs) {
default:
case 1:
/*
* regset 0 represents the SIO operating registers
*/
lombus_dev_acc_attr, &h);
"regmap 0 status %d addr $%p", err, p);
if (err != DDI_SUCCESS)
return (EIO);
ssp->sio_handle = h;
break;
case 0:
/*
* If no registers are defined, succeed vacuously;
* commands will be accepted, but we fake the accesses.
*/
break;
}
/*
* Now that the registers are mapped, we can initialise the SIO h/w
*/
return (0);
}
/*
* Nexus routines
*/
#if defined(NDI_ACC_HDL_V2)
};
};
};
static int
{
switch (op) {
default:
return (DDI_ME_UNIMPLEMENTED);
case DDI_MO_MAP_LOCKED:
switch (space) {
default:
return (DDI_ME_REGSPEC_RANGE);
case LOMBUS_VREG_SPACE:
break;
case LOMBUS_PAT_SPACE:
break;
case LOMBUS_EVENT_SPACE:
break;
}
return (DDI_SUCCESS);
case DDI_MO_UNMAP:
return (DDI_SUCCESS);
}
}
#else
static int
{
switch (op) {
default:
return (DDI_ME_UNIMPLEMENTED);
case DDI_MO_MAP_LOCKED:
switch (space) {
default:
return (DDI_ME_REGSPEC_RANGE);
case LOMBUS_VREG_SPACE:
break;
case LOMBUS_PAT_SPACE:
break;
case LOMBUS_EVENT_SPACE:
break;
}
return (DDI_SUCCESS);
case DDI_MO_UNMAP:
return (DDI_SUCCESS);
}
}
#endif /* NDI_ACC_HDL_V2 */
static int
{
return (DDI_FAILURE); /* this "can't happen" */
/*
* Validate mapping request ...
*/
return (DDI_ME_UNSUPPORTED);
return (DDI_ME_UNSUPPORTED);
return (DDI_ME_UNIMPLEMENTED);
return (DDI_ME_INVAL);
return (DDI_ME_INVAL);
return (DDI_ME_RNUMBER_RANGE);
return (DDI_ME_INVAL);
if (len == 0)
if (len < 0)
return (DDI_ME_INVAL);
return (DDI_ME_INVAL);
}
static int
{
int *regs;
int limit;
int err;
int i;
return (DDI_FAILURE); /* this "can't happen" */
switch (op) {
default:
break;
case DDI_CTLOPS_INITCHILD:
/*
* First, look up and validate the "reg" property.
*
* It must be a non-empty integer array containing a set
* of triples. Once we've verified that, we can treat it
* as an array of type lombus_regspec_t[], which defines
* the meaning of the elements of each triple:
* + the first element of each triple must be a valid space
* + the second and third elements (base, size) of each
* triple must define a valid subrange of that space
* If it passes all the tests, we save it away for future
* reference in the child's parent-private-data field.
*/
if (err != DDI_PROP_SUCCESS)
return (DDI_FAILURE);
switch (rsp[i].lombus_space) {
default:
limit = 0;
err = 1;
break;
case LOMBUS_VREG_SPACE:
break;
case LOMBUS_PAT_SPACE:
break;
case LOMBUS_EVENT_SPACE:
break;
}
if (rsp[i].lombus_size == 0)
}
if (err) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_CTLOPS_REGSIZE:
return (DDI_FAILURE);
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_CTLOPS_NREGS:
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
}
/*
* Clean up on detach or failure of attach
*/
static int
{
}
}
return (DDI_FAILURE);
}
/*
* Autoconfiguration routines
*/
static int
{
int instance;
int err;
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_ATTACH:
break;
}
/*
* Allocate the soft-state structure
*/
return (DDI_FAILURE);
/*
* Initialise devinfo-related fields
*/
/*
* Set various options from .conf properties
*/
DDI_PROP_DONTPASS, "allow-lom-echo", 0) != 0;
DDI_PROP_DONTPASS, "baud-rate", 0);
DDI_PROP_DONTPASS, "debug", 0);
DDI_PROP_DONTPASS, "fake-cts", 0) != 0;
/*
* Initialise current state & time
*/
/*
* Online the hardware ...
*/
if (err != 0)
/*
* Install soft and hard interrupt handler(s)
* Initialise mutexes and cv
* Start cyclic callbacks
* Enable interrupts
*/
if (err != DDI_SUCCESS)
/*
* Register a periodical handler.
*/
/*
* Final check before enabling h/w interrupts - did
* we successfully install the h/w interrupt handler?
*/
if (err != DDI_SUCCESS)
/*
* All done, report success
*/
return (DDI_SUCCESS);
}
static int
{
int instance;
switch (cmd) {
default:
return (DDI_FAILURE);
case DDI_DETACH:
break;
}
return (DDI_FAILURE); /* this "can't happen" */
return (DDI_SUCCESS);
}
static int
{
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
/*
* System interface structures
*/
{
nodev, /* b/c open */
nodev, /* b/c close */
nodev, /* b strategy */
nodev, /* b print */
nodev, /* b dump */
nodev, /* c read */
nodev, /* c write */
nodev, /* c ioctl */
nodev, /* c devmap */
nodev, /* c mmap */
nodev, /* c segmap */
nochpoll, /* c poll */
ddi_prop_op, /* b/c prop_op */
NULL, /* c streamtab */
};
{
BUSO_REV, /* revision */
lombus_map, /* bus_map */
0, /* get_intrspec */
0, /* add_intrspec */
0, /* remove_intrspec */
i_ddi_map_fault, /* map_fault */
ddi_no_dma_map, /* dma_map */
ddi_no_dma_allochdl, /* allocate DMA handle */
ddi_no_dma_freehdl, /* free DMA handle */
ddi_no_dma_bindhdl, /* bind DMA handle */
ddi_no_dma_unbindhdl, /* unbind DMA handle */
ddi_no_dma_flush, /* flush DMA */
ddi_no_dma_win, /* move DMA window */
ddi_no_dma_mctl, /* generic DMA control */
lombus_ctlops, /* generic control */
ddi_bus_prop_op, /* prop_op */
ndi_busop_get_eventcookie, /* get_eventcookie */
ndi_busop_add_eventcall, /* add_eventcall */
ndi_busop_remove_eventcall, /* remove_eventcall */
ndi_post_event, /* post_event */
0, /* interrupt control */
0, /* bus_config */
0, /* bus_unconfig */
0, /* bus_fm_init */
0, /* bus_fm_fini */
0, /* bus_fm_access_enter */
0, /* bus_fm_access_exit */
0, /* bus_power */
i_ddi_intr_ops /* bus_intr_op */
};
{
0, /* refcount */
ddi_no_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
lombus_attach, /* attach */
lombus_detach, /* detach */
lombus_reset, /* reset */
&lombus_cb_ops, /* driver operations */
&lombus_bus_ops, /* bus operations */
NULL, /* power */
ddi_quiesce_not_supported, /* devo_quiesce */
};
{
"lombus driver",
};
{
{
&modldrv,
}
};
/*
* Dynamic loader interface code
*/
int
_init(void)
{
int err;
sizeof (struct lombus_state), 0);
if (err == DDI_SUCCESS)
}
return (err);
}
int
{
}
int
_fini(void)
{
int err;
}
return (err);
}