pci_to_i2o.c revision a195726fa33097e56cf1c25c31feddb827e140f0
/*
* 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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* PCI-to-I2O bus nexus driver.
*
* The current implementation complies with the I2O Specification
* Version 1.5. So, it 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"
char _depends_on[] = "misc/i2o_msg";
/*
* function prototypes for bus ops routines:
*/
static int
static int
ddi_ctl_enum_t opt, void *a, void *v);
static int
struct bus_ops i2o_bus_ops = {
NULL,
NULL,
NULL,
ddi_no_dma_map, /* 2.4 DDI only - not supported */
NULL, /* (* bus_get_eventcookie)() */
NULL, /* (* bus_add_eventcall)() */
NULL, /* (* bus_remove_eventcall)() */
NULL, /* (* bus_post_event)() */
NULL, /* 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 */
i2o_intr_op /* bus_intr_op */
};
/*
* Function prototypes for dev_ops entry points.
*/
static int i2o_probe(dev_info_t *);
struct dev_ops i2o_dev_ops = {
DEVO_REV, /* devo_rev */
0, /* refcnt */
ddi_no_info, /* info */
nulldev, /* identify */
i2o_probe, /* probe */
i2o_attach, /* attach */
i2o_detach, /* detach */
nodev, /* reset */
(struct cb_ops *)0, /* driver operations */
&i2o_bus_ops /* bus operations */
};
/*
* Per IOP instance data maintained by the i2o nexus driver.
*/
typedef struct iop_nexus_instance {
int iop_state; /* state of IOP */
int iop_intr_pri; /* interrupt priority */
#ifdef I2O_DEBUG
#endif
/* Function prototypes for local functions */
#ifdef I2O_DEBUG
#endif
/*
* DMA attribute structure for I2O Spec version 1.5.
*/
static ddi_dma_attr_t i2o_dma_attr = {
DMA_ATTR_VERSION, /* version number */
(uint64_t)0, /* low DMA address range */
1, /* DMA address alignment */
1, /* DMA burstsizes */
1, /* min effective DMA size */
0xFFFF, /* s/g length */
1, /* granularity of device */
0 /* Bus specific DMA flags */
};
/* local definitions for iop_state values */
#define IOP_INIT 0 /* IOP is being initialized */
/* Default interrupt priority for IOP interrupt */
#define IOP_INTR_PRI_DEFAULT 5
#ifdef I2O_DEBUG
int i2o_nexus_debug = 0;
#else
#endif
/*
* Module linkage information for the kernel.
*/
"Nexus for I2O Spec v1.5, driver %I%",
};
/*
* 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 */
};
static struct modlinkage modlinkage = {
&modldrv,
};
static void *i2o_nexus_state;
int
_init(void)
{
int error;
sizeof (struct iop_nexus_instance), 1)) != 0)
return (error);
return (error);
}
int
_fini(void)
{
int error;
return (error);
}
int
{
}
/*
* **********************************************************************
* * bus_ops entry points *
* **********************************************************************
*/
/*
* NOTE: THIS FUNCTION IS NOT APPLICABLE FOR I2O. RETURN ERROR.
*/
/*ARGSUSED*/
static int
{
return (DDI_FAILURE);
}
static int
{
/*
* Adjust DMA attributes structure as per I2O Spec version 1.5.
*/
}
static int
ddi_ctl_enum_t opt, void *a, void *v)
{
char name[16];
int error;
switch (opt) {
case DDI_CTLOPS_INITCHILD:
return (DDI_FAILURE);
if (error != DDI_SUCCESS)
return (DDI_FAILURE);
return (DDI_SUCCESS);
case DDI_CTLOPS_UNINITCHILD:
return (DDI_SUCCESS);
case DDI_CTLOPS_REPORTDEV:
{
return (DDI_SUCCESS);
}
/*
* These functions shouldn't be called by the OSMs. Return error.
*/
case DDI_CTLOPS_DMAPMAPC:
case DDI_CTLOPS_SIDDEV:
case DDI_CTLOPS_SLAVEONLY:
case DDI_CTLOPS_AFFINITY:
case DDI_CTLOPS_REGSIZE:
case DDI_CTLOPS_NREGS:
case DDI_CTLOPS_POKE:
case DDI_CTLOPS_PEEK:
return (DDI_FAILURE);
default:
/* let the parent handle the rest */
}
}
/*
* **********************************************************************
* * dev_ops entry points *
* **********************************************************************
*/
/*
* Determine if the IOP is present.
*/
static int
{
return (DDI_PROBE_FAILURE);
if ((base_class != PCI_I2O_BASE_CLASS) ||
(sub_class != PCI_I2O_SUB_CLASS) ||
return (DDI_PROBE_FAILURE);
return (DDI_PROBE_SUCCESS);
}
/*
* attach(9E)
*/
static int
{
int nregs;
int csr;
int instance;
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
/*
* turn on Master Enable and Memory Access Enable bits.
*/
/*
* Allocate iop_nexus_instance soft state structure for this
* IOP instance.
*/
return (DDI_FAILURE);
instance);
/*
* Map the device memory (i.e IOP's shared local memory).
*
* ISSUE: Mapping the whole shared memory (4 to 16M) may be too
* much. But, to map the pages that we really need it requires
* reading the inbound FIFO to find out the range of offsets used
* for allocating inbound message frames by the IOP. It is possible
* to find the range of MFAs and then map only those pages. But,
* this will bring up the following issues:
*
* 1. IOP reset may reallocate the message frames so the
* range may change. (Note: currently i2o_msg_iop_init()
* does IOP reset so it will be a problem.)
* 2. I2O Spec doesn't restrict the IOP allocating inbound
* message frames dynamically.
* 3. Reading the MFAs should be done when no other external
* agent (e.g other IOPs) is accessing the IOP.
*
* This issue is addressed by the I2O Spec version 2.0 where
* IOP gives additional parameters which gives us the information
* we need to map only the pages that have the MFAs. For now, we
* will map the whole thing.
*/
goto cleanup;
(int)iop->shared_memsize));
goto cleanup;
}
/*
* Initialize i2o_msg_trans data structure for i2o_msg module.
*/
/* Disable IOP interrupts */
/*
* Register an interrupt handler for IOP interrupts. If the
* property 'iop_intr_pri' is set then use that otherwise
* set the priority to 5.
*/
goto cleanup;
/*
* Call i2o_msg_iop_init() to initialize the IOP.
*/
instance);
goto cleanup;
}
/* Enable IOP interrupts now */
#ifdef I2O_DEBUG
if (i2o_nexus_debug >= 2)
#endif
#ifndef I2O_BOOT_SUPPORT
/*
* Create the devinfo nodes for the I2O devices.
*/
#endif
return (DDI_SUCCESS);
/*
* free up the allocated resources and return error.
*/
if (iop->iop_base_addr != 0)
/* free up the soft state structure for this instance */
return (DDI_FAILURE);
}
/*
* detach(9E)
*/
static int
{
int instance;
instance);
return (DDI_FAILURE);
switch (cmd) {
case DDI_DETACH:
/* reset the IOP */
return (DDI_FAILURE);
/* unregister the interrupt handler */
/* unmap the shared device memory */
/* free up the soft state structure for this instance */
return (DDI_SUCCESS);
case DDI_SUSPEND: /* XXX FIX IT LATER */
case DDI_PM_SUSPEND: /* XXX FIX IT LATER */
/* fall thru */
default:
return (DDI_FAILURE);
}
}
/*
* IOP interrupt handler.
*
* Note: In the current I2O Spec (version 1.5) only Outbound PostList
* service interrupt is defined. So, this routine handles this
* interrupt.
*
* This function simply calls i2o_msg_process_reply_queue() to process
* the reply messages. It is assumed that the i2o_msg_process_reply_queue()
* will putback the processed messages into the freelist.
*/
static uint_t
{
register iop_nexus_instance_t *iop;
return (DDI_INTR_UNCLAIMED);
if (((intr_state & I2O_OUTBOUND_POSTLIST_SERVICE_INTR_MASK) == 0) ||
((intr_mask & I2O_OUTBOUND_POSTLIST_SERVICE_INTR_MASK) != 0))
/* No interrupt from this IOP */
return (DDI_INTR_UNCLAIMED);
/* Let the I2O Message module process the reply message queue */
#ifdef I2O_DEBUG
#endif
return (DDI_INTR_CLAIMED);
}
/*
* ***********************************************************************
* ** Transport functions to support the I2O Message module **
* ** **
* ** NOTE: Locking for these functions are done within the I2O **
* ** Message module. **
* ***********************************************************************
*/
/*
* Get an MFA from the Inbound FreeList FIFO.
*/
static uint_t
{
register iop_nexus_instance_t *iop;
}
/*
* Post the MFA to Inbound PostList FIFO.
*/
static int
{
register iop_nexus_instance_t *iop;
return (DDI_SUCCESS);
}
return (DDI_FAILURE); /* invalid argument(s) */
}
/*
* Get the reply MFA from the Outbound PostList FIFO.
*/
static uint_t
{
register iop_nexus_instance_t *iop;
}
/*
* Return reply MFA to the Outbound FreeList FIFO.
*/
static void
{
register iop_nexus_instance_t *iop;
}
/*
* Disable IOP hardware interrupts. Currently only bit 3 in the Interrupt
* Mask register is defined and it is for outbound postlist service
* interrupt. (See section 4.2.1.5).
*/
static void
{
register iop_nexus_instance_t *iop;
}
/*
* Enable IOP hardware interrupts. Currently only bit 3 in the Interrupt
* Mask register is defined and it is for outbound postlist service
* interrupt. (See section 4.2.1.5).
*/
static void
{
register iop_nexus_instance_t *iop;
}
#ifndef I2O_BOOT_SUPPORT
/*
* Since we don't have boot support yet, we need to create the devinfo
* nodes for the I2O devices here. No devinfo nodes are created for
* SCSI Peripheral class devices. For adapter devices, if the adapter
* is host visible (HRT has this information) then there may be a
* devinfo node else where in the devinfo tree. For each host visible
* adapter device we need to prune any other devinfo nodes for this
* adapter in the system.
*/
static void
{
int i;
/*
* Step 1
*
* Get HRT and look for any adapters that are present, assigned to
* IOP but not hidden. For each of those adapters we need to
* remove any devinfo nodes that may be present else where in
* devinfo tree.
*/
/*
* For now, we assume that all adpaters that are controlled
* by the IOP are hidden from the host. This step can be
* implemented easily in the boot system (i.e devconf on x86)
* when that phase is implemented.
*/
/* XXX DEFER IT FOR NOW XXX */
/*
* Step 2
*
* Create the devinfo nodes for each I2O class device that
* is not claimed (i.e UserTID == 0xFFF) and is not of
* SCSI peripheral type.
*/
+ sizeof (i2o_lct_entry_t)) / sizeof (i2o_lct_entry_t);
for (i = 0; i < nent; i++) {
/* If the device is already claimed then continue */
if (user_tid != 0xFFF)
continue;
switch (class) {
case I2O_CLASS_EXECUTIVE:
case I2O_CLASS_DDM:
continue;
case I2O_CLASS_ATE_PORT:
case I2O_CLASS_ATE_PERIPHERAL:
case I2O_CLASS_FLOPPY_DEVICE:
case I2O_CLASS_LAN:
case I2O_CLASS_WAN:
/* For now, ingore these types */
continue;
continue;
nodename = "disk";
compat_name = "i2o_bs";
dev_type = "block";
break;
nodename = "adapter";
compat_name = "i2o_scsi";
/*
* sub_class should indicate the type of bus.
* XXX Check this with Symbios.
*/
if (sub_class == 0x3)
dev_type = "scsi-3";
else if (sub_class == 0x2)
dev_type = "scsi-2";
else
dev_type = "scsi";
break;
default:
continue;
}
/* create the devinfo node */
"i2o_create_devinfo: ndi_devi_alloc failed");
goto fail;
}
/* create the properties */
goto fail;
dev_type) != DDI_PROP_SUCCESS)
goto fail;
goto fail;
/* now, attach the driver */
}
return;
fail:
"i2o_create_devinfo: ndi_devi_free failed");
}
}
}
#endif
#ifdef I2O_DEBUG
static ddi_dma_attr_t i2o_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 */
};
volatile int test_result;
static void
{
/* operations block info */
/* scalar parameters */
/* allocate a DMA handle */
goto cleanup;
}
sizeof (i2o_param_results_list_header_t) +
sizeof (i2o_param_read_operation_result_t) +
sizeof (*message_if) +
sizeof (i2o_param_error_info_template_t);
/* Allocate a buffer for operation block */
goto cleanup;
}
"dump_exec_params_0001: cannot bind memory"));
goto cleanup;
}
"dump_exec_params_0001: dma_bind (vaddr %p paddr %x length %x)",
(int)dma_cookie.dmac_size));
(buf + sizeof (*ops_block_head));
/* initialize the operations block header */
/* initialize operations block for group 0001 */
/* allocate the message frame */
"dump_exec_params_0001: i2o_msg_alloc failed"));
(void) ddi_dma_unbind_handle(dma_handle);
goto cleanup;
}
/* construct the UtilParamsGet message */
(sizeof (i2o_util_params_get_message_t) +
sizeof (i2o_sg_element_t)) >> 2);
test_result = 0;
/* send the message to the IOP */
/* wait for the reply */
if (test_result == 0)
(void) ddi_dma_unbind_handle(dma_handle);
/*
* **************************************************************
* Now, print all the parameters.
* **************************************************************
*/
/* group 0001h - Message Interface */
(buf + ops_block_size +
sizeof (i2o_param_results_list_header_t) +
sizeof (i2o_param_read_operation_result_t));
"?IOP Message Interface Parameters - Group 0001h:");
&message_if->InboundMax));
&message_if->InboundTarget));
&message_if->StaticCount));
&message_if->StaticLimit));
&message_if->OutboundMax));
if (dma_handle != NULL)
}
void
{
rmp = (i2o_single_reply_message_frame_t *)m;
"?Reply Message Frame (Function %x):",
}
test_result = 1;
}
#endif