/*
* 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 "bscbus" 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 Xbus (similar to ebus) parallel link to the
* H8 host interface registers.
*
* On the other hand, this driver doesn't generally know what the virtual
* registers signify - only the clients need this information.
*/
#if defined(__sparc)
#endif
#if defined(NDI_ACC_HDL_V2)
/*
* Compiling for Solaris 10+ with access handle enhancements
*/
#else
/*
* Compatibility definitions for backport to Solaris 8/9
*/
#endif /* NDI_ACC_HDL_V2 */
/*
* Local definitions
*/
#define BSCBUS_INST_TO_MINOR(i) (i)
#define BSCBUS_MINOR_TO_INST(m) (m)
#ifdef DEBUG
#define BSCBUS_LOGSTATUS
#endif /* DEBUG */
#ifdef BSCBUS_LOGSTATUS
/*
* BSC command logging routines.
* Record the data passing to and from the BSC
*/
typedef enum {
typedef struct {
int bcl_seq;
#endif /* BSCBUS_LOGSTATUS */
/*
* The following definitions are taken from the Hardware Manual for
* the Hitachi H8S/2148 in conjunction with the hardware specification
* for the Stiletto blade.
*
* Each instance of the host interface has 3 registers on the H8:
* IDRn - Input Data Register - write-only for Solaris.
* writes to this can be done via two
* addresses - control and data.
* The H8 can determine which address was
* written by examining the C/D bit in
* the status register.
* ODRn - Output Data Register - read-only for Solaris.
* A read has the side effect of acknowledging
* interrupts.
* STRn - Status Register - read-only for Solaris.
*
*
*
* In terms of host access to this the Input and Output data registers are
* mapped at the same address.
*/
#define H8_IDRD 0
#define H8_ODR 0
/* 0=data, 1=command */
/*
* Packet format ...
*/
/*
* Time periods, in nanoseconds
*
* Note that LOMBUS_ONE_SEC and some other time
*/
/*
* Local datatypes
*/
enum bscbus_cmdstate {
};
struct bscbus_channel_state {
/* Changes to these are protected by the instance ch_mutex mutex */
unsigned int chno;
/*
* Flag to indicate that we've incurred a hardware fault on
* accesses to the H8; once this is set, we fake all further
* accesses in order not to provoke additional bus errors.
*/
/*
* Data protected by the dog_mutex: the watchdog-patting
* protocol data (since the dog can be patted from a high-level
* cyclic), and the interrupt-enabled flag.
*/
unsigned int pat_retry_count;
unsigned int pat_fail_count;
/*
* Serial protocol state data, protected by lo_mutex
* (which is initialised using <lo_iblk>)
*/
int unclaimed_count;
};
/*
* This driver's soft-state structure
*/
struct bscbus_state {
/*
* Configuration data, set during attach
*/
int instance;
/*
* Parameters derived from .conf properties
*/
/*
* Flag to indicate that we are using per channel
* mapping of the register sets and interrupts.
* reg set 0 is chan 0
* reg set 1 is chan 1 ...
*
* Interrupts are specified in that order but later
* channels may not have interrupts.
*/
/*
* channel state data, protected by ch_mutex
*/
#ifdef BSCBUS_LOGSTATUS
/*
* Command logging buffer for recording transactions with the
* BSC. This is useful for debugging failed transactions and other
* such funnies.
*/
#endif /* BSCBUS_LOGSTATUS */
};
/*
* The auxiliary structure attached to each child
* (the child's parent-private-data points to this).
*/
struct bscbus_child_info {
int nregs;
};
#ifdef BSCBUS_LOGSTATUS
#else /* BSCBUS_LOGSTATUS */
#endif /* BSCBUS_LOGSTATUS */
/*
* Local data
*/
static void *bscbus_statep;
};
/*
* General utility routines ...
*/
#ifdef DEBUG
static void
const char *fmt, ...)
{
char *p;
p = buf;
p += strlen(p);
}
}
#else /* DEBUG */
#define bscbus_trace
#endif /* DEBUG */
static struct bscbus_state *
{
/*
* Use the instance number from the <dip>; also,
* check that it really corresponds to this driver
*/
bscbus_major = dmaj;
else if (dmaj != bscbus_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
{
}
}
static uint8_t
{
else
val = DUMMY_VALUE;
return (val);
}
static void
{
}
static boolean_t
{
}
/*
* Write data into h8 registers
*/
static void
{
while (status & H8_STR_IBF) {
/*
* Previous attempts to contact BSC have failed.
* Do not bother waiting for it to eat previous
* data.
* Pat anyway just in case the BSC is really alive
* and the IBF bit is lying.
*/
"retry count exceeded");
return;
}
if (--doglimit == 0) {
/* The BSC is not responding - give up */
csp->pat_fail_count++;
csp->pat_retry_count++;
/* Pat anyway just in case the BSC is really alive */
"poll limit exceeded");
return;
}
}
csp->pat_retry_count = 0;
}
/*
* State diagrams for how bscbus_process works.
* BSCBUS_CMDSTATE_IDLE No transaction in progress
* BSCBUS_CMDSTATE_BUSY Setting up command
* BSCBUS_CMDSTATE_CLEARING Clearing firmware busy status
* BSCBUS_CMDSTATE_SENDING Waiting to send data to f/w
* BSCBUS_CMDSTATE_PENDING Waiting for ack from f/w
* BSCBUS_CMDSTATE_WAITING Waiting for status from f/w
* BSCBUS_CMDSTATE_ERROR Command failed with error
*
* +----------+
* | |
* | (0/1) | abnormal
* +----------+ state
* | \ detected
* | \------>------+ +----<---+
* bsc | | | |
* is | V V |
* ready| +----------+ |
* | | | ^
* | | CLEARING | |
* | | (2) | |
* | +----------+ |
* | cleared / | \ | more to clear
* | / | \-->--+
* | +-------<-------/ V
* | | |
* V V |timeout
* +----------+ timeout |
* | |------>---------+--------+
* | SENDING | |
* | (3) |------<-------+ |
* +----------+ | V
* sent| \ send ^ack |
* last| \ next |received |
* | \ +----------+ |
* | \ | | |
* | \------>| PENDING |-->-+
* | | (4) | |
* | +----------+ |timeout
* | +---<----+ |
* | | | |
* V V | |
* +----------+ | |
* | | | |
* | WAITING | ^ |
* | (5) | | |
* +----------+ | |
* | | |more | |
* | V |required| |
* done| | +--->----+ |
* | +--->--------------+ +---<---+
* V V V
* +----------+ +----------+
* | | | |
* | READY | | ERROR |
* | (7) | | (6) |
* +----------+ +----------+
* | |
* V V
* | |
* +------>---+---<------+
* |
* |
* Back to
* Idle
*/
static void
{
/*
* When we get here we actually expect H8_STR_IBF to
* be clear but we check just in case of problems.
*/
if (!(status & H8_STR_IBF)) {
"state %d; val $%x",
if (!BSCBUS_TX_PENDING(csp)) {
/* No more pending - move to waiting state */
"moving to waiting");
/* Extend deadline because time has moved on */
} else {
/* Wait for ack of this byte */
"moving to pending");
}
}
}
static void
{
/*
* We only enter this state if H8_STR_BUSY was set when
* we started the transaction. We just ignore all received
* data until we see OBF set AND BUSY cleared.
* It is not good enough to see BUSY clear on its own
*/
/* Throw away any data received up until now */
"busy cleared");
/*
* Send the next byte immediately.
* At this stage we should clear the OBF flag because that
* data has been used. IBF is still valid so do not clear that.
*/
status &= ~(H8_STR_OBF);
} else {
if (status & H8_STR_OBF) {
}
}
}
static void
{
/* We are waiting for an acknowledgement of a byte */
if (status & H8_STR_OBF) {
"moving to sending");
/*
* Send the next byte immediately.
* At this stage we should clear the OBF flag because that
* data has been used. IBF is still valid so do not clear that.
*/
status &= ~(H8_STR_OBF);
}
}
static boolean_t
{
if (status & H8_STR_OBF) {
if (++rcvd < BSCBUS_BUFSIZE)
"rcvd %d: $%02x $%02x $%02x $%02x $%02x $%02x $%02x $%02x",
rcvd,
}
if (rcvd == 0) {
/*
* No bytes received this time through (though there
* might be a partial packet sitting in the buffer).
*/
/* EMPTY */
;
} else if (rcvd >= BSCBUS_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 & BSCBUS_LAST) == 0) {
/*
* Packet not yet complete; leave the partial packet in
* the buffer for later ...
*/
/* Invalid "status" byte - maybe an echo of the command? */
/* 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 & BSCBUS_STATUS_ERR) {
} else {
}
}
return (ready);
}
/*
* Packet receive handler
*
* This routine should be called from the low-level softint,
* or bscbus_cmd() (for polled operation), with the
* low-level mutex already held.
*/
static void
{
"state %d; error $%x",
}
case BSCBUS_CMDSTATE_CLEARING:
break;
case BSCBUS_CMDSTATE_SENDING:
break;
case BSCBUS_CMDSTATE_PENDING:
break;
case BSCBUS_CMDSTATE_WAITING:
break;
default:
/* Nothing to do */
break;
}
/*
* Check for timeouts - but only if the command has not yet
* completed (ready is true when command completes in this
* call to bscbus_process OR cmdstate is READY or ERROR if
* this is a spurious call to bscbus_process i.e. a spurious
* interrupt)
*/
if (!ready &&
"timeout previous state %d; error $%x",
/* Move onto sending because busy might be stuck */
/* Extend timeout relative to original start time */
}
}
"last $%02x; state %d; error $%x; ready %d",
}
if (ready)
}
static uint_t
{
/*
* Read the registers to ensure that the interrupt is cleared.
* Status must be read first because reading data changes the
* status.
* We always read the data because that clears the interrupt down.
* This is horrible hardware semantics but we have to do it!
*/
if (!(status & H8_STR_OBF)) {
csp->unclaimed_count++;
} else {
}
if (status & H8_STR_TOKENPROTOCOL) {
if (csp->interrupt_failed) {
}
}
return (DDI_INTR_CLAIMED);
}
void
{
/*
* This routine is only called if we timeout in userland
* waiting for an interrupt. This generally means that we have
* lost interrupt capabilities or that something has gone
* wrong. In this case we are allowed to access the hardware
* and read the data register if necessary.
* If interrupts return then recovery actions should mend us!
*/
/* Should look for data to receive */
if (status & H8_STR_OBF) {
/* There is data available */
}
}
/*
* Serial protocol
*
* This routine builds a command and sets it in progress.
*/
static uint8_t
{
/*
* 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 BSCBUS_CMD_WRITE:
if (val >= 0x80)
/*FALLTHRU*/
case BSCBUS_CMD_READ:
if (BSCBUS_VREG_HI(vreg) != 0) {
}
/*FALLTHRU*/
case BSCBUS_CMD_NOP:
break;
}
/*
* Check and update the H8 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).
*/
/*
* Prepare for the command (to be processed by the interrupt
* or timeout.
*/
start = ddi_get_lbolt();
if (status & H8_STR_BUSY) {
/*
* Must ensure that the busy state has cleared before
* sending the command
*/
"h8 reporting status (%x) busy - clearing", status);
} else {
/* It is clear to send the command immediately */
"sending first byte of command, status %x", status);
}
(csp->interrupt_failed ?
if (!csp->interrupt_failed) {
BSCBUS_CMD_POLLNOINTS / 1000);
}
}
}
/*
* The return value may not be meaningful but retrieve it anyway
*/
if (bscbus_faulty(csp)) {
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.
*
* 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 BSC 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 - ensure that there are no pending transactions and
* hence no pending interrupts. We do this be ensuring that the BSC is
* not reporting a busy condition and that it does not have any data
* pending in its output buffer.
* This is important because if we have pending interrupts at attach
* time Solaris will hang due to bugs in ddi_get_iblock_cookie.
*/
static void
{
/* No-one using this instance - no need to reset hardware */
return;
}
if (status & H8_STR_BUSY) {
/*
* Give the h8 time to complete a reply.
* In practice we should never worry about this
* because whenever we get here it will have been
* long enough for the h8 to complete a reply
*/
"h8 reporting status (%x) busy - waiting", status);
if (ddi_in_panic()) {
} else {
}
}
/* Reply should be completed by now. Try to clear busy status */
for (timeout = BSCBUS_HWRESET_TIMEOUT;
(timeout > 0);
timeout -= BSCBUS_HWRESET_POLL) {
if (status & H8_STR_OBF) {
if (!(status & H8_STR_BUSY)) {
/* We are done */
break;
}
}
if (ddi_in_panic()) {
} else {
}
}
if (timeout <= 0) {
"clearing busy status");
}
}
/*
* We read ODR just in case there is a pending interrupt with
* no data. This is potentially dangerous because we could get
* out of sync due to race conditions BUT at this point the
* channel should be idle so it is safe.
*/
}
/*
* Higher-level setup & teardown
*/
static void
{
}
static int
{
caddr_t p;
int nregs;
int err;
nregs = 0;
switch (nregs) {
case 1:
/*
* regset 0 represents the H8 interface registers
*/
bscbus_dev_acc_attr, &h);
if (err != DDI_SUCCESS)
return (EIO);
break;
case 0:
/*
* If no registers are defined, succeed vacuously;
* commands will be accepted, but we fake the accesses.
*/
break;
default:
/*
* Remember that we are using the new register scheme.
* reg set 0 is chan 0
* reg set 1 is chan 1 ...
* Interrupts are specified in that order but later
* channels may not have interrupts.
* We map the regs later on a per channel basis.
*/
break;
}
return (0);
}
static int
{
int err;
"claim channel for channel %d, count %d",
/* No-one is using this channel - initialise it */
"initialise channel %d, count %d",
csp->pat_retry_count = 0;
csp->pat_fail_count = 0;
/* Map appropriate register set for this channel */
caddr_t p;
&p, 0, 0, bscbus_dev_acc_attr, &h);
if (err != DDI_SUCCESS) {
goto failed1;
}
"mapped chno=%d ch_handle=%d ch_regs=%p",
} else {
/*
* if using the old reg property scheme use the
* common mapping.
*/
}
/* Ensure no interrupts pending prior to getting iblk cookie */
/*
* we don't want lo_mutex to be initialised
* with an iblock cookie if we are the wdog,
* because we don't use interrupts.
*/
MUTEX_DRIVER, NULL);
csp->unclaimed_count = 0;
} else {
int ninterrupts;
/*
* check that there is an interrupt for this
* this channel. If we fail to setup interrupts we
* must unmap the registers and fail.
*/
if (err != DDI_SUCCESS) {
ninterrupts = 0;
}
"no interrupt available for "
goto failed2;
}
"bscbus interrupts are high "
"level - channel not usable.");
goto failed2;
} else {
if (err != DDI_SUCCESS) {
goto failed2;
}
csp->unclaimed_count = 0;
if (err != DDI_SUCCESS) {
goto failed2;
}
}
}
/*
* The channel is now live and may
* receive interrupts
*/
"request conflicts with previous mapping. old %x, new %x.",
goto failed1;
}
return (1);
/* unmap regs for failed channel */
}
return (0);
}
static void
{
/* No-one is now using this channel - shutdown channel */
"shutdown channel %d, count %d",
}
/* unmap registers if using the new register scheme */
}
}
"release channel %d, count %d",
}
/*
* Nexus routines
*/
#if defined(NDI_ACC_HDL_V2)
};
};
};
static int
{
switch (op) {
default:
return (DDI_ME_UNIMPLEMENTED);
case DDI_MO_MAP_LOCKED:
if (bscbus_claim_channel(csp,
(space == LOMBUS_PAT_SPACE)) == 0) {
return (DDI_ME_GENERIC);
}
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:
if (bscbus_claim_channel(csp,
(space == LOMBUS_PAT_SPACE)) == 0) {
return (DDI_ME_GENERIC);
}
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);
return (bscbus_map_handle(
}
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);
default:
limit = 0;
err = 1;
"child(%p): unknown reg space %d",
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);
}
}
/*
* This nexus does not support passing interrupts to leaf drivers, so
* all the intrspec-related operations just fail as cleanly as possible.
*/
/*ARGSUSED*/
static int
{
#if defined(__sparc)
#else
return (DDI_FAILURE);
#endif
}
/*
* Clean up on detach or failure of attach
*/
static int
{
int chno;
}
}
#ifdef BSCBUS_LOGSTATUS
if (ssp->cmd_log_size != 0) {
}
#endif /* BSCBUS_LOGSTATUS */
return (DDI_FAILURE);
}
/*
* Autoconfiguration routines
*/
static int
{
int chno;
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, "debug", 0);
#ifdef BSCBUS_LOGSTATUS
if (ssp->cmd_log_size != 0) {
ssp->cmd_log_idx = 0;
sizeof (bsc_cmd_log_t), KM_SLEEP);
}
#endif /* BSCBUS_LOGSTATUS */
/*
* Online the hardware ...
*/
if (err != 0)
/*
* Initialise state
* The hardware/interrupts are setup at map time to
* avoid claiming hardware that OBP is using
*/
}
/*
* 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
{
int chno;
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 */
bscbus_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 */
bscbus_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 */
bscbus_intr_op /* bus_intr_op */
};
{
0, /* refcount */
ddi_no_info, /* getinfo */
nulldev, /* identify */
nulldev, /* probe */
bscbus_attach, /* attach */
bscbus_detach, /* detach */
bscbus_reset, /* reset */
&bscbus_cb_ops, /* driver operations */
&bscbus_bus_ops, /* bus operations */
NULL, /* power */
ddi_quiesce_not_needed, /* quiesce */
};
{
"bscbus driver",
};
{
{
&modldrv,
}
};
/*
* Dynamic loader interface code
*/
int
_init(void)
{
int err;
sizeof (struct bscbus_state), 0);
if (err == DDI_SUCCESS)
}
return (err);
}
int
{
}
int
_fini(void)
{
int err;
}
return (err);
}
#ifdef BSCBUS_LOGSTATUS
{
int idx;
return;
return;
if (ssp->cmd_log_size == 0)
return;
return;
}
#endif /* BSCBUS_LOGSTATUS */