i2o_msg.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* I2O Message module, which implements OSM interfaces to provides
* transport functionality for the OSMs. It depends on the I2O nexus
* driver for bus specific transport mechanisms.
*
* Note: The current implementation assumes only 32bit virtual
* addresses and 32bit context fields in I2O messages.
*/
#include <sys/ddidmareq.h>
#include <sys/ddi_impldefs.h>
#include <sys/bustypes.h>
#include <sys/archsystm.h>
#include "i2o_impl.h"
#ifndef I2O_BOOT_SUPPORT
char _depends_on[] = "misc/busra";
#endif
/*
* ************************************************************************
* *** Implementation specific data structures/definitions. ***
* ************************************************************************
*/
/*
* Implementation of i2o_iop_handle_t data structure.
*
* dip devinfo node pointer of the I2O device
* tid IOP assigned TID for this device.
* iop pointer to iop_instance_t data structure.
*/
typedef struct i2o_iop_impl_hdl {
struct iop_instance *iop;
/*
* Implementation of i2o_msg_handle_t data structure.
*
* next pointer to the next handle (used when the requests
* are queued up)
* dma_handle DMA handle associated with this message buffer
* msgp pointer to the message frame.
* acc_hdl DDI access handle for this message frame.
*/
typedef struct i2o_msg_impl_hdl {
struct i2o_msg_impl_hdl *next;
void *msgp;
/*
* Per IOP instance data structure maintained by the I2O Message
* module.
*
* Locks used:
* iop_ib_mutex Used to serialize access to the inbound message
* queue and to protect send_queue_* fields.
*
* iop_ob_mutex Used to serialize access to the outbound message
* queue.
*/
typedef struct iop_instance {
struct iop_instance *next;
volatile int iop_flags;
/* IOP Status Block structure */
struct {
} status;
/* Logical Configuration Table (LCT) */
struct {
} lct;
/* Hardware Resource Table (HRT) */
struct {
} hrt;
/* outbound message queue */
struct {
} ob_msg_queue;
struct {
} hw_config;
/* System Table Entry for this IOP */
struct {
} systab;
/* OSM registration book keeping */
struct {
} osm_registry;
/*
* i2o_send_msg() queue information. The fields send_queue_head
* and send_queue_tail are protected under iop_ib_mutex.
*/
/* common transaction context structure */
typedef struct tcontext {
int status;
int done_flag;
} tcontext_t;
/* definitions for iop_flags values */
/*
* DMA attribute structure for I2O Spec version 1.5.
*
* (Note: Specifies sg list length to 1 to get contiguous memory)
*/
static ddi_dma_attr_t dma_attr_contig = {
DMA_ATTR_VERSION, /* version number */
(uint64_t)0, /* low DMA address range */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0x1, /* s/g length */
1, /* granularity of device */
0 /* Bus specific DMA flags */
};
/*
* Device attribute structure for I2O version 1.5.
*
* I2O data structures (whether it is in IOP's memory or host memory)
* are in Little Endian format.
*/
static ddi_device_acc_attr_t i2o_dev_acc_attr = {
DDI_STRUCTURE_LE_ACC, /* devacc_attr_endian_flags for LE access */
DDI_STRICTORDER_ACC /* devacc_attr_dataorder */
};
/* Function prototypes for local functions */
#ifdef I2O_DEBUG
/* function prototypes for debug functions */
#endif
/*
* Local Data definitions.
*
* niop
* number of active IOPs initialized to OP state.
*
* next_iop_id
* Counter to assign unique ID (IOP_ID) to the next IOP that
* gets initialized by the i2o nexus.
*
* ioplist
* pointer to the linked list of IOP data structures (i.e iop_instance
* structures) that are initilized.
*
* iop_reset_time_delay_in_ticks
* This is the time delay to get a valid mfa from the inbound freelist
* after doing ExecIopReset. This delay really depends on the platform
* specific hardware. Here we are using 2 seconds and this seems to
* work fine for the Madrona platform.
*/
static iop_instance_t *ioplist;
int iop_reset_time_delay_in_ticks = 200;
/*
* Debug flag definitions.
*/
#ifdef I2O_DEBUG
int i2o_debug = I2O_DEBUG_LCT;
#else
#endif
#define SUCCESS 1
#define FAILURE 0
/*
* Module linkage information for the kernel.
*/
extern struct mod_ops mod_miscops;
"I2O Message Module version 1.5",
};
static struct modlinkage modlinkage = {
&modlmisc,
};
int
_init(void)
{
int error;
return (error);
}
int
_fini(void)
{
int error;
return (error);
}
int
{
}
/*
* Utility macros to initialize message structures.
*/
/* initialize standard message header */
{ \
}
/* initialize standard SGL Simple Element structure */
{ \
}
/*
* ************************************************************************
* Tunable parameters/properties.
* ob_msg_framesize_default
* Default frame size for Outbound Message queue. The minimum
* size is 64 bytes. It should be multiple of 4.
* ob_msg_queue_length_default
* Default size (i.e #of MFAs) in the Outbound Queue. The
* minimum is 16.
* ************************************************************************
*/
int ob_msg_framesize_default = 128;
int ob_msg_queue_length_default = 32;
/*
* ************************************************************************
* ************************************************************************
*/
static i2o_single_reply_message_frame_t *
{
delay(1);
if (--ticks == 0) {
(CE_CONT, "iop_poll_reply_msg: timed out"));
return (NULL); /* time out - possible hang? */
}
}
return ((i2o_single_reply_message_frame_t *)
}
void *
{
/*
* If we don't have a valid frame then wait for a while and
* try again. This may be necessary at the beginning if
* the IOP is in the INITIALIZATION state.
*/
delay(100);
}
}
/*
* ************************************************************************
* ********* Private interfaces used by the I2O Nexus driver. ***********
* ************************************************************************
*/
/*
* i2o_msg_iop_init()
*
* Called from the attach(9E) function in the i2o nexus driver.
* Initializes the IOP to the OPERATIONAL state. Returns an access
* handle (i.e i2o_iop_handle_t) for successful initialization,
* otherwise it returns NULL.
*
* Assumption(s):
* 1. The IOP interrupts are not enabled when this function is
* called. The caller (i.e attach(9E)) enables the IOP interrupts
* upon the successful return from this function.
* 2. It is assumed that the I2O nexus driver will create the
* devinfo nodes for the I2O devices if necessary. i.e the
* caller of this function will create the devinfo tree
* already created it.
*/
{
int i;
int reset_done = 0; /* only one IOP_RESET operation */
int init_time;
/*
* Allocate an iop instance data.
*/
niop++;
/*
* **************************************************************
* Step 1: Get the IOP status block by sending ExecStatusGet
* message.
*
* NOTE: Normally we expect IOP to be in 'RESET' state or 'OP'
* state. Any other state is really doubtful!
* **************************************************************
*/
/*
* We give 5 minutes for IOP to reset before complaining to user;
* the time for IOP to come to RESET state after it receives the
* EXEC_IOP_RESET really depends on the I2O hardware configured
* under the IOP.
*/
goto cleanup;
/* Check for I2O Version; we only support version 1.5 */
goto cleanup;
(CE_CONT, "i2o_msg_iop_init: Initial IOP state %x",
case I2O_IOP_STATE_RESET:
break;
if (init_time <= 0)
"IOP is still in I2O_IOP_STATE_INITIALIZING state!!");
else
init_time -= 100;
delay(100);
goto try_again;
/* reset the IOP and wait for a while for the IOP to reset */
goto cleanup;
reset_done = 1;
goto try_again;
case I2O_IOP_STATE_HOLD:
case I2O_IOP_STATE_READY:
case I2O_IOP_STATE_FAILED:
case I2O_IOP_STATE_FAULTED:
/* reset the IOP and try again */
reset_done = 1;
goto try_again;
}
default:
goto cleanup;
}
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_STATUS)
#endif
/*
* **************************************************************
* Step 2: Initialize the Outbound message queue.
* **************************************************************
*/
goto cleanup;
/*
* **************************************************************
* Step 3: Get the Hardware Resource Table (HRT).
* **************************************************************
*/
goto cleanup;
#if !defined(I2O_BOOT_SUPPORT)
/*
* **************************************************************
* configure the hidden adapters. The IOP status buffer
* has the required information.
*
* allocated by the host? Currently the IRTOS always reports
* the CurrentPrivateMemSize as zero.
* **************************************************************
*/
if (priv_mem_size > (uint32_t)0) {
/* need to allocate PCI memory space */
goto cleanup;
}
if (priv_io_size > (uint32_t)0) {
/* need to allocate PCI i/o space */
goto cleanup;
}
#endif
/*
* **************************************************************
* Step 5: Create the System Table entry for this IOP and send
* ExecSysTabSet to all the IOPs. It enables the IOP
* to OPERATIONAL state.
* **************************************************************
*/
goto cleanup;
/*
* **************************************************************
* Step 6: Read LCT by sending ExecLctNotify message.
* **************************************************************
*/
goto cleanup;
/*
* **************************************************************
* Step 7: Set event notification request for the events that
* we are interested.
* **************************************************************
*/
goto cleanup;
} else {
}
/* find the max TIDs allocated by the IOP */
sizeof (i2o_lct_t) + sizeof (i2o_lct_entry_t)) /
sizeof (i2o_lct_entry_t);
for (i = 0; i < lct_entries; i++) {
}
/* allocate the IOP handle table */
kmem_zalloc(sizeof (i2o_iop_impl_hdl_t) *
/*
* initialize the IOP handle (i2o_iop_handle_t) for the nexus to use and
* return the handle.
*/
return ((i2o_iop_handle_t *)hdl);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
}
}
}
}
--niop;
return (NULL);
}
/*
* i2o_msg_iop_unint()
*
* Called from the detach(9E) function in the i2o nexus driver.
* It would uninitialize IOP by sending ExecIopReset to bring the
* IOP to RESET state. And then it will free up any resources/data-structures
* allocated for this IOP instance.
*
* Assumption(s):
* 1. It is assumed that all the I2O devices are quiesced before calling
* this function. Which means all OSMs have already done the
* i2o_msg_osm_unregister() for the devices they claimed.
*/
int
{
i2o_iop_handle_t h = *handlep;
iop_instance_t *p;
/*
* First, disable the IOP hardware interrupts.
*/
/*
* *********************************************************
* if there are multiple IOPs then we need to send
* ExecPathQuiesce message to other IOPs before
* resetting this IOP.
* *********************************************************
*/
if (p == iop)
continue;
/*
* Send ExecPathQuiesce message to this IOP.
*/
"i2o_msg_iop_uninit: trans_msg_alloc failed"));
goto cleanup;
}
/* initialize the transcation context structure */
/* construct the ExecPathQuiesce message */
sizeof (i2o_exec_path_quiesce_message_t),
p->nexus_trans->acc_handle);
/* send the message to the IOP and wait for the reply */
(void) iop_msg_send(p, (void *)mp);
}
}
/*
* *********************************************************
* Send an ExecSysQuiesce message to the IOP.
* *********************************************************
*/
/* allocate a message frame from Inbound queue */
"i2o_msg_iop_uninit: trans_msg_alloc failed"));
goto cleanup;
}
/* construct the ExecSysQuiesce message */
sizeof (i2o_exec_sys_quiesce_message_t),
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/*
* Since interrupts are disabled, we poll for the reply message
* for ExecSysQuiesce request. Could we expect any other reply
* messages for previous activity on this IOP (like some
* event notification)? For now, we can safely ignore any other
* reply messages.
*/
for (;;) {
goto cleanup;
/* ignore reply messages other than for ExecSysQuiesce */
continue;
}
break; /* successful */
/* message failed */
goto cleanup;
}
/* free up the reply message buffer */
/*
* *********************************************************
* Now, send an ExecIopReset message to the IOP.
* *********************************************************
*/
goto cleanup;
/*
* *********************************************************
* Free up the system resources.
* *********************************************************
*/
}
}
}
}
}
}
/*
* If i2o_msg_send_proc() is running then wait for it to exit.
*/
/* wake up the i2o_msg_send_proc() thread */
/* wait until the i2o_msg_send_proc() stops running */
delay(1);
}
} else {
iop_instance_t *p, *prev;
if (p == iop) {
break;
}
prev = p;
}
}
--niop; /* number of active IOPs */
/* free up the IOP handle table */
return (DDI_SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
if (dma_handle != NULL)
return (DDI_FAILURE);
}
/*
* Send ExecStatusGet message to get IOP status. It allocates the
* buffer resources if they are not already allocated.
*
* Returns SUCCESS if it succeeds in getting the IOP status.
*/
static int
{
i2o_exec_status_get_reply_t *s = 0;
/* allocate a DMA handle */
"i2o_get_iop_status: ddi_dma_alloc_handle failed"));
goto cleanup;
}
}
/* allocate the buffer for the IOP status block */
sizeof (i2o_exec_status_get_reply_t), &i2o_dev_acc_attr,
"i2o_get_iop_status: ddi_dma_mem_alloc failed"));
goto cleanup;
}
}
"i2o_get_iop_status: cannot bind memory"));
goto cleanup;
}
"i2o_get_iop_status: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* allocate a message frame from Inbound queue */
"i2o_get_iop_status: trans_msg_alloc failed"));
goto cleanup;
}
/* construct the ExecStatusGet message */
sizeof (i2o_exec_status_get_message_t),
&msgp->ReplyBufferAddressHigh, 0);
sizeof (i2o_exec_status_get_reply_t));
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/*
* Poll on the status block field 'SyncByte' because there is
* no reply to ExecStatusGet message. The IOP writes '0xFF'
* to 'SyncByte' field when it finished writing to the status
* block structure.
*/
delay(1);
/* sync DMA memory */
}
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
return (FAILURE);
}
/*
* Allocate message frames for the Outbound queue and send ExecOutboundInit
* message to the IOP.
*
* Description:
* Since we don't know how to determine how many frames to be
* allocated, we either depend on the user specified property
* (i.e ob-msg-queue-length) or use the current IOP's default.
* Allocate the message queue as one physically contiguous chunk.
* Send ExecOutboundInit message with the list of MFAs.
*/
static int
{
int nframes, max_nframes;
int frame_size;
int count;
/* allocate a DMA handle */
"i2o_init_outbound_queue: ddi_dma_alloc_handle failed"));
goto cleanup;
}
}
DDI_PROP_DONTPASS, "ob-msg-queue-length",
DDI_PROP_DONTPASS, "ob-msg-framesize",
/* allocate the buffer for the message frames */
"i2o_init_outbound_queue: ddi_dma_mem_alloc failed"));
goto cleanup;
}
}
"i2o_init_outbound_queue: cannot bind memory"));
goto cleanup;
}
"i2o_init_outbound_queue: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* allocate a message frame from Inbound queue */
"i2o_init_outbound_queue: trans_msg_alloc failed"));
goto cleanup;
}
/*
* Construct the ExecOutboundInit message. Use the base address
* of the outbound message queue buffer for the status word structure
* instead of allocating a new word.
*/
sizeof (i2o_exec_outbound_init_message_t),
stat->InitStatus = 0;
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/*
* Poll on the status word for completion.
*/
while ((stat->InitStatus == 0) ||
delay(2);
/* sync DMA memory */
0, 1, DDI_DMA_SYNC_FORCPU);
}
"i2o_init_outbound_queue: FAILED (InitStatus %x)",
stat->InitStatus));
goto cleanup;
}
/*
* Now, write the MFAs to the Outbound FIFO.
*/
}
(CE_CONT, "i2o_init_outbound_queue: SUCCEEDED"));
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
return (FAILURE);
}
/*
* Get HRT by sending ExecHrtGet message to the IOP.
* It is assumed that Outbound queue is already initialized
* so that the IOP can send a reply to the ExecHrtGet message.
* Also it is assumed that the IOP interrupts are disabled.
*/
static int
{
/* allocate a DMA handle if necessary */
"i2o_get_hrt: ddi_dma_alloc_handle failed"));
goto cleanup;
}
}
/*
* Allocate a temporary buffer to get the HRT size information
* (i.e only header part of the HRT).
*/
sizeof (i2o_hrt_t), &i2o_dev_acc_attr,
"i2o_get_hrt: ddi_dma_mem_alloc failed"));
goto cleanup;
}
"i2o_get_hrt: cannot bind memory"));
goto cleanup;
}
"i2o_get_hrt: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* allocate a message frame from Inbound queue */
"i2o_get_hrt: trans_msg_alloc failed"));
goto cleanup;
}
/*
* Construct the ExecHrtGet message.
*/
sizeof (i2o_exec_hrt_get_message_t), I2O_EXEC_HRT_GET);
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/* since interrupts are disabled, we poll for the reply message */
if (rmp)
goto cleanup;
}
/* free up the reply message buffer */
(sizeof (i2o_hrt_entry_t) *
/*
* NOTE: Some old versions of RTOS is not setting the EntryLength
* correctly. Also, I am not sure if all implementations of RTOS
* set this field. So, the following ASSERTION is disabled.
*
* ASSERT((buf->EntryLength * 4) == sizeof (i2o_hrt_entry_t));
*/
/* free up the temporary buffer */
/*
* Now, allocate the correct size buffer for HRT and send another
* ExecHrtGet message.
*/
/* free up any old buffer */
}
/* allocate a new buffer for HRT */
"i2o_get_hrt: ddi_dma_mem_alloc failed"));
goto cleanup;
}
}
"i2o_get_hrt: cannot bind memory"));
goto cleanup;
}
"i2o_get_hrt: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* allocate a message frame from Inbound queue */
"i2o_get_hrt: trans_msg_alloc failed"));
goto cleanup;
}
/*
* Construct the ExecHrtGet message (again!).
*/
sizeof (i2o_exec_hrt_get_message_t), I2O_EXEC_HRT_GET);
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/* since interrupts are disabled, we poll for the reply message */
if (rmp)
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
goto cleanup;
}
/* free up the reply message buffer */
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_HRT)
#endif
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
return (FAILURE);
}
/*
* Create the system table entry for the IOP and update all IOPs with
* the latest System Table. It sends ExecSysTabSet message to all
* IOPs. If necessary it sends the ExecSysEnable to the new IOP that
* is being initialized.
*
* Note: It is assumed that this routine is called from the IOP init
* routine.
*/
static int
{
int i;
iop_instance_t *p;
/*
* *********************************************************
* Create SysTab entry for this IOP.
* *********************************************************
*/
"i2o_create_systab: ddi_dma_alloc_handle failed"));
goto cleanup;
}
/* allocate the buffer for systab entry */
"i2o_create_systab: ddi_dma_mem_alloc failed"));
goto cleanup;
}
/*
* initialize the systab entry with the information from the iop
* status buffer.
*/
/*
* *********************************************************
* Create a System Table for sending ExecSysTabSet message
* to all IOP(s).
* *********************************************************
*/
/* allocate the buffer for systab header and for the systab entries */
systab_size = sizeof (i2o_set_systab_header_t) +
(niop * sizeof (i2o_iop_entry_t));
"i2o_create_systab: ddi_dma_alloc_handle failed"));
goto cleanup;
}
&acc_hdl) != DDI_SUCCESS) {
"i2o_create_systab: ddi_dma_mem_alloc failed"));
goto cleanup;
}
/* fill in the systab header and systab entries */
p = p->next;
}
"i2o_create_systab: cannot bind memory"));
goto cleanup;
}
"i2o_create_systab: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* Now, send ExecSysTabSet message to each of the IOPs */
/* allocate a message frame from Inbound queue */
"i2o_create_systab: trans_msg_alloc failed"));
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
/*
* Construct the ExecSysTabSet message.
*
* Note: The implied assumption is that the message frame has
* enough room for 3 SGL elements.
*/
sizeof (i2o_exec_sys_tab_set_message_t) +
msgp->HostUnitID = 0;
if (p != iop) {
/*
* For other IOPs we don't poll the reply, so
* setup the InitiatorContext/TransactionContext.
*/
/* initialize the transcation context structure */
}
/* First buffer is for systab itself */
/*
* Second buffer is for Private Memory Space allocation.
*
* Note: The spec is not clear if this buffer can be NULL
* for the IOP which was already initialized to OP state
* and there is no change to its configuration. Here, we
* will set it to NULL assuming that it is ignored.
*/
if (p == iop) {
} else {
I2O_SGL_FLAGS_END_OF_BUFFER, 0, 0);
}
/*
* Third buffer is for Private IO Space allocation.
*
* Note: The spec is not clear if this buffer can be NULL
* for the IOP which was already initialized to OP state
* and there is no change to its configuration. Here, we
* will set it to NULL assuming that it is ignored.
*/
if (p == iop) {
} else {
I2O_SGL_FLAGS_END_OF_BUFFER, 0, 0);
}
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
(void) iop_msg_send(p, (void *)msgp);
/* wait for the reply message */
if (p == iop) {
/*
* For this IOP, interrupts are disabled. So, we poll
* for the reply message.
*/
rmp = iop_poll_reply_msg(p);
if (rmp)
iop_msg_free(p, rmp);
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
dump_reply_message(p, rmp);
#endif
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
/* paranoia? */
/* free up the reply message buffer */
iop_msg_free(p, rmp);
} else {
/* For other IOPs, wait for the reply message */
/* check the status for SUCCESS */
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
}
/*
* For the new IOP, send the ExecSysEnable message.
*/
if (p == iop) {
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
}
}
(void) ddi_dma_unbind_handle(dma_handle);
"i2o_create_systab: invalid IOP state"));
goto cleanup;
}
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
if (dma_handle != NULL)
return (FAILURE);
}
/*
* Send the ExecSysEnable message to the IOP if it is in the
* READY state. It assumes that the IOP interrupts are disabled.
*/
static int
{
/*
* Get the current state of IOP.
*/
goto cleanup;
/* allocate a message frame from Inbound queue */
"i2o_create_systab: trans_msg_alloc failed"));
goto cleanup;
}
/* Construct the ExecSysEanble message. */
sizeof (i2o_exec_sys_enable_message_t), I2O_EXEC_SYS_ENABLE);
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/* since interrupts are disabled, we poll for the reply message */
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
if (rmp)
goto cleanup;
}
/* paranoia? */
/* get the IOP state now; it should be in OPERATIONAL state */
goto cleanup;
}
return (SUCCESS);
return (FAILURE);
}
/*
* Send an ExecIopReset message. This function is called from the
* i2o_msg_iop_init() with interrupts disabled.
*/
static int
{
/* allocate a DMA handle */
"i2o_send_exec_iop_reset: ddi_dma_alloc_handle failed"));
goto cleanup;
}
/*
* Allocate a temporary buffer for the status word structure.
*/
sizeof (i2o_exec_iop_reset_status_t), &i2o_dev_acc_attr,
"i2o_send_exec_iop_reset: ddi_dma_mem_alloc failed"));
goto cleanup;
}
"i2o_send_exec_iop_reset: cannot bind memory"));
goto cleanup;
}
"i2o_send_exec_iop_reset: dma_bind (vaddr %p paddr %x length %x)",
/* allocate a message frame from Inbound queue */
"i2o_send_exec_iop_reset: trans_msg_alloc failed"));
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
/* construct the ExecIopReset message */
sizeof (i2o_exec_iop_reset_message_t),
&rmsgp->StatusWordHighAddress, 0);
buf->ResetStatus = 0;
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/* poll on the status word for IN_PROGRESS state */
goto cleanup;
if (--mseconds < 0) {
(CE_CONT, "iop_reset: timed out"));
goto cleanup;
}
/* sync DMA memory */
}
(void) ddi_dma_unbind_handle(dma_handle);
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
if (dma_handle != NULL)
return (FAILURE);
}
static int
{
/* allocate a DMA handle if necessary */
"i2o_get_lct: ddi_dma_alloc_handle failed"));
goto cleanup;
}
}
/*
* Allocate the buffer for LCT and send ExecLctNotify message.
*/
/* free up any old buffer */
}
/* allocate a new buffer for LCT */
"i2o_get_lct: ddi_dma_mem_alloc failed"));
goto cleanup;
}
}
"i2o_get_lct: cannot bind memory"));
goto cleanup;
}
"i2o_get_lct: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
/* allocate a message frame from Inbound queue */
"i2o_get_lct: trans_msg_alloc failed"));
goto cleanup;
}
/*
* Construct the ExecLctNotify message.
*/
sizeof (i2o_exec_lct_notify_message_t), I2O_EXEC_LCT_NOTIFY);
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
/* since interrupts are disabled, we poll for the reply message */
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
if (rmp)
goto cleanup;
}
/* paranoia? */
/* free up the reply message buffer */
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_LCT)
#endif
return (SUCCESS);
/*
* Error return; free up the allocated resources and return NULL.
*/
}
}
return (FAILURE);
}
#define EXEC_CLASS_EVENT_MASK \
/*
* Register Executive Class events to get notified by the IOP when
* any of these events occur.
*
* Assumpition: IOP interrupts are disabled.
*/
static int
{
/* allocate a message frame from Inbound queue */
"i2o_iop_event_register: trans_msg_alloc failed"));
return (FAILURE);
}
/*
* Construct the UtilEventRegister message. There is no reply
* to this message until one of the specified events occurs.
*/
sizeof (i2o_util_event_register_message_t),
/* Fow now, specify only the Executive Class events and */
/* Config Dialog request event */
#ifdef I2O_DEBUG
if (i2o_debug & I2O_DEBUG_MSG)
#endif
/* send the message to the IOP */
return (SUCCESS);
}
/*
* called by the I2O nexus driver from i2o_create_devinfo().
*/
void
{
if (lctp)
if (acc_handlep)
}
/*
* ************************************************************************
* *** Reply Message Processing functions ***
* ************************************************************************
*/
/*
* Call back function to process the Executive Class event notification
* messages.
*
* For now, we process only the MODIFY_LCT event to update the local
* copy of the LCT. For other events we simply print the event information
* and ignore it. XXX FIX IT WHEN NEEDED XXX
*/
static void
{
int i, n;
msgp = (i2o_util_event_register_reply_t *)p;
iop = (iop_instance_t *)
switch (event_indicator) {
"^Received ADAPTER_FAULT event from the IOP %x",
break;
"^Received CONNECTION_FAIL event from the IOP %x",
break;
"^Received DDM_AVAILABILITY event from the IOP %x",
break;
"^Received HARDWARE_FAIL event from the IOP %x",
break;
/*
* LCT entry is modified. Need to update the local
* copy of the LCT.
*/
/*
* locate the entry in the local copy of the LCT that
* matches the TID that has changed. And update the entry.
*/
sizeof (i2o_lct_entry_t);
for (i = 0; i < n; i++) {
break;
}
ASSERT(i < n);
/* copy the modified entry */
#ifdef I2O_DEBUG
#endif
break;
"^Received NEW_LCT_ENTRY event from the IOP %x",
break;
case I2O_EVENT_IND_POWER_FAIL:
break;
"^Received RESOURCE_LIMITS event from the IOP %x",
break;
case I2O_EVENT_IND_XCT_CHANGE:
break;
"^Received RESET_IMMINENT event from the IOP %x",
break;
"^Received RESET_PENDING event from the IOP %x",
break;
"^Received CONFIGURATION_FLAG event from the IOP %x",
break;
}
}
/*
* Common reply message processing function: It simply copies the status
* code and sets the done_flag in the tcontext structure.
*/
static void
{
tcontext_t *tp;
}
/*
* ************************************************************************
* *** Implementation of OSM interfaces (PSARC: 1997/173) ***
* ************************************************************************
*/
/*
* Register the OSM for the specified I2O device.
*
* Implementation: The simplest implementation is to use the TID of
* the I2O device as the key to avoid multiple registrations. Since
* the max number of TIDs allocated is not big (< 32) we can simply
* maintain an array and use TID as the index. From the dip we need
* to find the IOP that this device belongs to. Currently, we do
* this by looking into the ioplist that matches with the devinfo
* node pointer of the parent for this device.
*/
int
{
/* get the TID for this device */
return (DDI_FAILURE);
/*
* Find the IOP that matches the parent of this device.
*/
break;
}
return (DDI_FAILURE);
/* verify that the device is not already registerd. */
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
void
{
}
/*
* Allocate a message frame for sending an I2O request message.
*
* Description: We allocate a system memory buffer so that the caller
* can take his own time to prepare the message. When the caller
* calls i2o_msg_send() then we allocate the real message frame
* from the inbound queue and free up the system memory buffer
* after copying the data.
*/
int
{
/*
* Allocate the message frame buffer from the system memory.
*
* Note:
* For now, the allocation is done by directly calling
* the DDI framework. Later this should be fixed to allocate
* it from a local pool for performance reasons.
*/
return (DDI_FAILURE);
/* size of the buffer includes the i2o_msg_impl_hdl_t structure */
return (DDI_FAILURE);
}
/* set the message size field in the message header */
*acc_handlep = acc_hdl;
return (DDI_SUCCESS);
}
/*
* Send the I2O message to the IOP.
*
* Description: We need to do the following.
* 1. If the inbound message queue freelist is empty (i.e no
* valid mfa available) then queue up this request for
* the i2o_msg_send_process() thread and return DDI_SUCCESS.
* 2. We have a valid mfa. Copy the data from the system memory
* message buffer into the real message frame and send the
* message.
* 3. Free up the system memory message buffer.
*
* Note:
* For now, we return the system memory buffer back to the DDI
* framework. Fix this later to put the buffer into the local pool
* for better performance.
*/
int
{
/* get a valid mfa from the inbound message freelist FIFO */
/*
* No valid MFA available. Queue up the request.
*/
} else {
}
/* wakeup the i2o_msg_send_proc() */
else {
/*
* Create the i2o_msg_send_proc() thread and
* run it.
*/
}
iop->send_queue_count++;
return (DDI_SUCCESS);
}
real_msgp = (i2o_message_frame_t *)
/*
* Copy the message to the real message frame.
*/
/* send the message to the IOP */
/*
* Now, free up the system resources allocated for this message
*/
return (DDI_SUCCESS);
}
/*
* Copy the LCT contents to the specified buffer.
*/
int
{
copy_size = 0;
else {
}
if (real_sizep)
*real_sizep = copy_size;
return (DDI_SUCCESS);
}
/*
* Process the reply message queue. This routine is called at the time of
* IOP hw interrupt. Current implementation is a simple while loop which
* reads the outbound postlist FIFO and if the MFA is a valid MFA
* (i.e MFA != 0xFFFFFFFF) then it calls the callback function in the
* InitiatorContext field of the message.
*/
void
{
void (* initiator_context)();
/*
* WORKAROUND for i960 chip bug. It seems that sometimes the i960
* is dropping the write request when IxWorks writes the MFA to
* the FIFO before generating the interrupt. As a workaround, IxWorks
* writes 0xFFFFFFFF first and then correct MFA. Two successive
* reads to the postlist FIFO should confirm if the FIFO is
* really empty. In the absence of reading the FIFO second time
* results in another call to IOP interrupt service routine.
* For better performance we do the second read of FIFO here.
*/
/* read the FIFO again */
}
initiator_context = (void (*)())
/*
* Check for NULL Initiator Context field. If it is NULL
* (should not happen) then ignore the message.
*/
if (initiator_context == NULL) {
" (Function: 0x%x Transaction Context: 0x%x"
" TID: 0x%x) - reply msg ignored",
} else {
/*
* We need to do the DMA sync for the message frame
* because the reply message buffers are allocated
* from the system memory and the IOP does DMA to
* write to this memory.
*/
/*
* Call the callback function of the OSM to process
* the message.
*/
(* initiator_context)((void *)rmp,
}
/* Now, putback the MFA into the outbound freelist FIFO */
/* get the next MFA from the FIFO */
}
}
/*
* Process the I2O request message queue. The request messages are
* queued up for this IOP because there was no free mfa available
* from the inbound freelist. Currently, there is no mechanism
* where IOP can inform the host when the freelist is not empty.
* So, we just have to poll on the inbound message queue fifo until
* we find a valid frame. But, starvation on the inbound MFAs should
* not happen with the current implementations of IRTOS where the
* IRTOS copies the message into local buffer and puts the MFA back
* on the freelist immediately. So, this code may never get executed
* in practice!
*
*/
static void
{
for (;;) {
while (q != NULL) {
/*
* get a valid mfa from the inbound message
* freelist FIFO
*/
/*
* No valid MFA available. Wait for a while
* and try again.
*/
delay(1);
continue; /* try again */
}
real_msgp = (i2o_message_frame_t *)
/* Copy the message to the real message frame */
/* send the message to the IOP */
/* free up the associated system resources */
dma_handle = q->dma_handle;
q = q->next;
}
/* if the IOP is being uninitialized then exit */
thread_exit();
"i2o_msg_send_proc: exit..."));
}
/* otherwise, wait for the wakeup call from i2o_msg_send() */
}
}
/*
* ************************************************************************
* *** Functions used for Debugging only ***
* ************************************************************************
*/
#ifdef I2O_DEBUG
static void
{
int i;
for (i = 0; i < sizeof (i2o_exec_status_get_reply_t); i += 4)
}
static void
{
int i, n;
uint32_t *p;
"?Hardware Resource Table (IOP_ID %x HRTVersion %x #ent %x):\n",
for (i = 0; i < n; i++, entp++) {
continue;
p[2], p[3]);
case I2O_PCI_BUS:
break;
default:
break;
}
}
}
static void
{
int i, j, n;
sizeof (i2o_lct_entry_t);
for (i = 0; i < n; i++) {
for (j = 0; j < 8; j++)
entp++;
}
}
static void
{
}
static void
{
return;
}
#endif
static uint_t
{
#ifdef lint
#endif
if (*base != 0)
req.ra_boundbase = 0;
NDI_RA_TYPE_MEM, 0) == NDI_FAILURE) {
*base = 0;
return (0);
} else {
return (*base);
}
}
static void
{
#ifdef lint
#endif
NDI_RA_TYPE_MEM, 0);
}
static uint_t
{
#ifdef lint
#endif
if (*base != 0)
req.ra_boundbase = 0;
NDI_RA_TYPE_IO, 0) == NDI_FAILURE) {
*base = 0;
return (0);
} else {
return (*base);
}
}
static void
{
#ifdef lint
#endif
NDI_RA_TYPE_IO, 0);
}