qlt.c revision c946faca5d4627284fb79c6b04e652b471034495
/*
* 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.
*/
#include <sys/byteorder.h>
#include <stmf_defines.h>
#include <fct_defines.h>
#include <stmf.h>
#include <portif.h>
#include <fct.h>
#include <qlt.h>
#include <qlt_dma.h>
#include <qlt_ioctl.h>
#include <stmf_ioctl.h>
int reset_only);
int reset_only);
int qlt_enable_msix = 0;
/* Array to quickly calculate next free buf index to use */
static struct cb_ops qlt_cb_ops = {
0,
};
0,
NULL,
};
#define QLT_NAME "COMSTAR QLT"
#define QLT_VERSION "1.0"
&qlt_ops,
};
static struct modlinkage modlinkage = {
};
static uint32_t qlt_loaded_counter = 0;
"-X Mode 1 133", "--Invalid--",
"-X Mode 2 66", "-X Mode 2 100",
"-X Mode 2 133", " 66" };
/* Always use 64 bit DMA. */
static ddi_dma_attr_t qlt_queue_dma_attr = {
DMA_ATTR_V0, /* dma_attr_version */
0, /* low DMA address range */
0xffffffffffffffff, /* high DMA address range */
0xffffffff, /* DMA counter register */
64, /* DMA address alignment */
0xff, /* DMA burstsizes */
1, /* min effective DMA size */
0xffffffff, /* max DMA xfer size */
0xffffffff, /* segment boundary */
1, /* s/g list length */
1, /* granularity of device */
0 /* DMA transfer flags */
};
/* qlogic logging */
int enable_extended_logging = 0;
static char qlt_provider_name[] = "qlt";
static struct stmf_port_provider *qlt_pp;
int
_init(void)
{
int ret;
if (ret == 0) {
STMF_STRUCT_PORT_PROVIDER, 0, 0);
return (EIO);
}
if (ret != 0) {
(void) stmf_deregister_port_provider(qlt_pp);
}
}
return (ret);
}
int
_fini(void)
{
int ret;
if (qlt_loaded_counter)
return (EBUSY);
if (ret == 0) {
(void) stmf_deregister_port_provider(qlt_pp);
}
return (ret);
}
int
{
}
int
{
}
static int
{
int instance;
int max_read_size;
int max_payload_size;
/* No support for suspend resume yet */
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
return (DDI_FAILURE);
}
== NULL) {
goto attach_fail_1;
}
goto attach_fail_2;
}
goto attach_fail_4;
}
goto attach_fail_4;
}
if (did == 0x2422) {
pci_bits >>= 8;
pci_bits &= 0xf;
"!qlt(%d): HBA running at PCI%sMHz (%d)",
} else {
"qlt(%d): HBA running at PCI%sMHz %s(%d)",
"(Invalid)", ((pci_bits == 0) ||
}
}
(unsigned long long)ret);
goto attach_fail_5;
}
goto attach_fail_5;
}
DDI_SUCCESS) {
goto attach_fail_6;
}
goto attach_fail_7;
}
if (ncookies != 1)
goto attach_fail_8;
/* mutex are inited in this function */
goto attach_fail_8;
"qlt%d", instance);
goto attach_fail_9;
}
/* Setup PCI cfg space registers */
if (max_read_size == 11)
if (did == 0x2422) {
if (max_read_size == 512)
val = 0;
else if (max_read_size == 1024)
val = 1;
else if (max_read_size == 2048)
val = 2;
else if (max_read_size == 4096)
val = 3;
else {
"pci-max-read-request in qlt.conf. Valid values "
"for this HBA are 512/1024/2048/4096", instance);
}
mr &= 0xfff3;
if (max_read_size == 128)
val = 0;
else if (max_read_size == 256)
val = 1;
else if (max_read_size == 512)
val = 2;
else if (max_read_size == 1024)
val = 3;
else if (max_read_size == 2048)
val = 4;
else if (max_read_size == 4096)
val = 5;
else {
"pci-max-read-request in qlt.conf. Valid values "
"for this HBA are 128/256/512/1024/2048/4096",
instance);
}
mr &= 0x8fff;
} else {
"pci-max-read-request for this device (%x)",
}
if (max_payload_size == 11)
goto over_max_payload_setting;
if (max_payload_size == 128)
val = 0;
else if (max_payload_size == 256)
val = 1;
else if (max_payload_size == 512)
val = 2;
else if (max_payload_size == 1024)
val = 3;
else {
"pcie-max-payload-size in qlt.conf. Valid values "
"for this HBA are 128/256/512/1024",
instance);
goto over_max_payload_setting;
}
mr &= 0xff1f;
} else {
"pcie-max-payload-size for this device (%x)",
}
goto attach_fail_10;
return (DDI_SUCCESS);
return (DDI_FAILURE);
}
#define FCT_I_EVENT_BRING_PORT_OFFLINE 0x83
/* ARGSUSED */
static int
{
int instance;
== NULL) {
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
return (DDI_FAILURE);
return (DDI_SUCCESS);
}
static void
{
} else {
int i;
}
}
static void
{
} else {
int i;
}
}
static void
{
int i;
}
}
}
static void
{
}
static void
{
}
static int
{
int ret;
int itype = DDI_INTR_TYPE_MSIX;
int i;
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
}
/* we need at least 2 interrupt vectors */
ret = DDI_FAILURE;
goto release_intr;
}
"requested: %d, received: %d\n",
}
if (ret != DDI_SUCCESS) {
ret = DDI_FAILURE;
goto release_intr;
}
for (i = 0; i < actual; i++) {
if (ret != DDI_SUCCESS)
goto release_mutex;
}
return (DDI_SUCCESS);
for (i = 0; i < actual; i++)
return (ret);
}
static int
{
int itype = DDI_INTR_TYPE_MSI;
int ret;
int i;
/* get the # of interrupts */
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
}
/* MSI requires only 1 interrupt. */
count = 1;
/* allocate interrupt */
ret = DDI_FAILURE;
goto free_mem;
}
"requested: %d, received:%d",
}
/*
* Get priority for first msi, assume remaining are all the same.
*/
if (ret != DDI_SUCCESS) {
ret = DDI_FAILURE;
goto release_intr;
}
/* add handler */
for (i = 0; i < actual; i++) {
if (ret != DDI_SUCCESS)
goto release_mutex;
}
return (DDI_SUCCESS);
for (i = 0; i < actual; i++)
return (ret);
}
static int
{
int count;
int actual;
int ret;
int itype = DDI_INTR_TYPE_FIXED;
/* Fixed interrupts can only have one interrupt. */
return (DDI_FAILURE);
}
ret = DDI_FAILURE;
goto free_mem;
}
if (ret != DDI_SUCCESS) {
ret = DDI_FAILURE;
goto release_intr;
}
if (ret != DDI_SUCCESS)
goto release_mutex;
return (DDI_SUCCESS);
return (ret);
}
static int
{
#if defined(__sparc)
int itypes = 0;
#endif
/*
* x86 has a bug in the ddi_intr_block_enable/disable area (6562198). So use
* MSI for sparc only for now.
*/
#if defined(__sparc)
}
return (DDI_SUCCESS);
}
if (itypes & DDI_INTR_TYPE_MSI) {
return (DDI_SUCCESS);
}
#endif
return (qlt_setup_fixed(qlt));
}
/*
* Filling the hba attributes
*/
void
struct fct_port_attrs *port_attrs)
{
int len;
"QLogic Corp.");
"%s", QLT_NAME);
"%s", QLT_VERSION);
/* Get FCode version */
(int *)&len) == DDI_PROP_SUCCESS) {
} else {
#ifdef __sparc
#define FCHBA_OPTION_ROM_ERR_TEXT "No Fcode found"
#else
#define FCHBA_OPTION_ROM_ERR_TEXT "N/A"
#endif
FCHBA_OPTION_ROM_VERSION_LEN, "%s",
}
if (qlt->qlt_25xx_chip)
}
/* ARGSUSED */
{
uint8_t *p;
switch (cmd) {
case FC_TGT_PORT_RLS:
if ((*bufsizep) < sizeof (fct_port_link_status_t)) {
ret = FCT_FAILURE;
break;
}
/* send mailbox command to get link status */
break;
}
/* GET LINK STATUS count */
if (ret != QLT_SUCCESS) {
break;
}
break;
default:
ret = FCT_FAILURE;
break;
}
return (ret);
}
{
return (FCT_FAILURE);
}
goto qlt_pstart_fail_1;
}
goto qlt_pstart_fail_2;
}
/*
* Since we keep everything in the state struct and dont allocate any
* port private area, just use that pointer to point to the
* state struct.
*/
goto qlt_pstart_fail_2_5;
}
return (QLT_SUCCESS);
(void) fct_deregister_local_port(port);
return (QLT_FAILURE);
}
{
return (QLT_FAILURE);
return (QLT_SUCCESS);
}
/*
* Called by framework to init the HBA.
* Can be called in the middle of I/O. (Why ??)
* Should make sure sane state both before and after the initialization
*/
{
int instance;
/* XXX Make sure a sane state */
return (ret);
}
/* Get resource count */
if (ret != QLT_SUCCESS)
return (ret);
/* Enable PUREX */
if (ret != QLT_SUCCESS) {
return (ret);
}
/* Pass ELS bitmap to fw */
da >>= 16;
da >>= 16;
da >>= 16;
if (ret != QLT_SUCCESS) {
elsbmp[1]);
return (ret);
}
/* Init queue pointers */
/*
* XXX support for tunables. Also should we cache icb ?
*/
return (STMF_ALLOC_FAILURE);
}
/*
* This is the 1st command adter adapter initialize which will
* use interrupts and regular mailbox interface.
*/
/* Issue mailbox to firmware */
if (ret != QLT_SUCCESS) {
}
if (ret != QLT_SUCCESS) {
(long long)ret);
}
if (ret != QLT_SUCCESS)
return (ret);
return (FCT_SUCCESS);
}
{
int retries;
goto poff_mbox_done;
}
/* Wait to grab the mailboxes */
retries++) {
if ((retries > 5) ||
goto poff_mbox_done;
}
}
qlt->qlt_intr_enabled = 0;
drv_usecwait(20);
qlt->intr_sneak_counter = 0;
return (FCT_SUCCESS);
}
static fct_status_t
{
/* Issue mailbox to firmware */
if (ret != QLT_SUCCESS) {
/* Firmware is not ready */
if (ddi_get_lbolt() < et) {
goto link_info_retry;
}
}
} else {
case 1:
break;
case 0:
break;
case 3:
break;
case 2: /*FALLTHROUGH*/
case 4:
break;
default:
}
}
if (fc_ret != QLT_SUCCESS) {
"database for F_port failed, ret = %llx", fc_ret);
} else {
uint8_t *p;
}
}
return (fc_ret);
}
static int
{
int instance;
return (EINVAL);
}
/*
* Since this is for debugging only, only allow root to issue ioctl now
*/
return (EPERM);
}
return (ENXIO);
}
/*
* It is already open for exclusive access.
* So shut the door on this caller.
*/
return (EBUSY);
}
/*
* Exclusive operation not possible
* as it is already opened
*/
return (EBUSY);
}
}
return (0);
}
/* ARGSUSED */
static int
{
int instance;
return (EINVAL);
}
return (ENXIO);
}
return (ENODEV);
}
/*
* It looks there's one hole here, maybe there could several concurrent
* shareed open session, but we never check this case.
* But it will not hurt too much, disregard it now.
*/
return (0);
}
/*
* All of these ioctls are unstable interfaces which are meant to be used
* in a controlled lab env. No formal testing will be (or needs to be) done
* for these ioctls. Specially note that running with an additional
* uploaded firmware is not supported and is provided here for test
* purposes only.
*/
/* ARGSUSED */
static int
{
int ret = 0;
#ifdef _LITTLE_ENDIAN
int i;
#endif
char info[80];
return (EPERM);
if (ret)
return (ret);
iocd->stmf_error = 0;
switch (cmd) {
case QLT_IOCTL_FETCH_FWDUMP:
break;
}
break;
}
break;
}
break;
}
break;
case QLT_IOCTL_TRIGGER_FWDUMP:
break;
}
"user triggered FWDUMP with RFLAG_RESET", (void *)qlt);
info[79] = 0;
info) != FCT_SUCCESS) {
}
break;
case QLT_IOCTL_UPLOAD_FW:
break;
}
#ifdef _LITTLE_ENDIAN
}
#endif
iocd->stmf_ibuf_size)) {
break;
}
break;
}
/* Everything looks ok, lets copy this firmware */
} else {
}
KM_SLEEP);
break;
case QLT_IOCTL_CLEAR_FW:
}
break;
case QLT_IOCTL_GET_FW_INFO:
break;
}
if (qlt->qlt_stay_offline) {
}
}
}
if (qlt->qlt_25xx_chip) {
} else {
}
break;
case QLT_IOCTL_STAY_OFFLINE:
if (!iocd->stmf_ibuf_size) {
break;
}
if (*((char *)ibuf)) {
} else {
qlt->qlt_stay_offline = 0;
}
break;
case QLT_IOCTL_MBOX:
break;
}
break;
}
if (st != QLT_SUCCESS) {
st = QLT_SUCCESS;
}
if (st != QLT_SUCCESS) {
switch (st) {
case QLT_MBOX_NOT_INITIALIZED:
break;
case QLT_MBOX_BUSY:
break;
case QLT_MBOX_TIMEOUT:
break;
case QLT_MBOX_ABORTED:
break;
}
}
break;
default:
}
if (ret == 0) {
} else if (iocd->stmf_error) {
}
if (obuf) {
}
if (ibuf) {
}
return (ret);
}
static void
{
(cmd == FCT_CMD_PORT_OFFLINE) ||
(cmd == FCT_ACK_PORT_ONLINE_COMPLETE) ||
(cmd == FCT_ACK_PORT_OFFLINE_COMPLETE));
switch (cmd) {
case FCT_CMD_PORT_ONLINE:
qlt->qlt_state_not_acked = 0;
} else {
}
}
qlt->qlt_change_state_flags = 0;
break;
case FCT_CMD_PORT_OFFLINE:
}
}
qlt->qlt_state_not_acked = 0;
} else {
}
}
break;
qlt->qlt_state_not_acked = 0;
break;
qlt->qlt_state_not_acked = 0;
(qlt->qlt_stay_offline == 0)) {
if (fct_port_initialize(port,
"qlt_ctl FCT_ACK_PORT_OFFLINE_COMPLETE "
"with RLFLAG_RESET") != FCT_SUCCESS) {
"fct_port_initialize failed, please use "
"stmfstate to start the port-%s manualy",
}
}
break;
}
}
/* ARGSUSED */
static fct_status_t
{
return (FCT_FAILURE);
}
/*
* Return a pointer to n entries in the request queue. Assumes that
* request queue lock is held. Does a very short busy wait if
* fullfill the request.
* **CALL qlt_submit_req_entries() BEFORE DROPPING THE LOCK**
*/
{
int try = 0;
while (qlt->req_available < n) {
continue;
(REQUEST_QUEUE_ENTRIES - 1));
if (qlt->req_available < n) {
if (try < 2) {
drv_usecwait(100);
try++;
continue;
} else {
"Req Q is full");
return (NULL);
}
}
break;
}
/* We dont change anything until the entries are sumitted */
}
/*
* updates the req in ptr to fw. Assumes that req lock is held.
*/
void
{
ASSERT(n >= 1);
qlt->req_ndx_to_fw += n;
qlt->req_available -= n;
}
/*
* Return a pointer to n entries in the priority request queue. Assumes that
* priority request queue lock is held. Does a very short busy wait if
* fullfill the request.
* **CALL qlt_submit_preq_entries() BEFORE DROPPING THE LOCK**
*/
{
int try = 0;
(PRIORITY_QUEUE_ENTRIES - 1));
while (req_available < n) {
continue;
(PRIORITY_QUEUE_ENTRIES - 1));
if (req_available < n) {
if (try < 2) {
drv_usecwait(100);
try++;
continue;
} else {
return (NULL);
}
}
break;
}
/* We dont change anything until the entries are sumitted */
}
/*
* updates the req in ptr to fw. Assumes that req lock is held.
*/
void
{
ASSERT(n >= 1);
qlt->preq_ndx_to_fw += n;
}
/*
* - Should not be called from Interrupt.
* - A very hardware specific function. Does not touch driver state.
* - Assumes that interrupts are disabled or not there.
* - Expects that the caller makes sure that all activity has stopped
* and its ok now to go ahead and reset the chip. Also the caller
* takes care of post reset damage control.
* - called by initialize adapter() and dump_fw(for reset only).
* - During attach() nothing much is happening and during initialize_adapter()
* the function (caller) does all the housekeeping so that this function
* can execute in peace.
* - Returns 0 on success.
*/
static fct_status_t
{
int cntr;
/* XXX: Switch off LEDs */
/* Disable Interrupts */
/* Stop DMA */
/* Wait for DMA to be stopped */
cntr = 0;
cntr++;
/* 3 sec should be more than enough */
if (cntr == 300)
return (QLT_DMA_STUCK);
}
/* Reset the Chip */
qlt->qlt_link_up = 0;
drv_usecwait(100);
/* Wait for ROM firmware to initialize (0x0000) in mailbox 0 */
cntr = 0;
cntr++;
/* 3 sec should be more than enough */
if (cntr == 300)
return (QLT_ROM_STUCK);
}
/* Disable Interrupts (Probably not needed) */
if (reset_only)
return (QLT_SUCCESS);
/* Load the two segments */
if (ret == QLT_SUCCESS) {
}
} else if (qlt->qlt_25xx_chip) {
if (ret == QLT_SUCCESS) {
}
} else {
if (ret == QLT_SUCCESS) {
}
}
if (ret != QLT_SUCCESS)
return (ret);
/* Verify Checksum */
if (ret != QLT_SUCCESS)
return (ret);
/* Execute firmware */
if (ret != QLT_SUCCESS)
return (ret);
/* Get revisions (About Firmware) */
if (ret != QLT_SUCCESS)
return (ret);
return (QLT_SUCCESS);
}
/*
* Used only from qlt_reset_chip_and_download_fw().
*/
static fct_status_t
{
uint32_t words_sent = 0;
while (words_sent < word_count) {
TOTAL_DMA_MEM_SIZE >> 2);
da >>= 16;
da >>= 16;
da >>= 16;
if (ret != QLT_SUCCESS)
return (ret);
}
return (QLT_SUCCESS);
}
/*
* Not used during normal operation. Only during driver init.
* Assumes that interrupts are disabled and mailboxes are loaded.
* Just triggers the mailbox command an waits for the completion.
* Also expects that There is nothing else going on and we will only
* get back a mailbox completion from firmware.
* ---DOES NOT CLEAR INTERRUPT---
* Used only from the code path originating from
* qlt_reset_chip_and_download_fw()
*/
static fct_status_t
{
int cntr = 0;
cntr++;
if (cntr == 100)
return (QLT_MAILBOX_STUCK);
}
if (mbox0 == 0x4000)
return (QLT_SUCCESS);
else
return (QLT_MBOX_FAILED | mbox0);
}
/* This is unexpected, dump a message */
return (QLT_UNEXPECTED_RESPONSE);
}
static mbox_cmd_t *
{
if (dma_size) {
return (NULL);
}
/* This is the most common initialization of dma ptrs */
da >>= 16;
da >>= 16;
da >>= 16;
}
return (mcp);
}
void
{
}
/*
* This can sleep. Should never be called from interrupt context.
*/
static fct_status_t
{
int retries;
int i;
char info[80];
ASSERT(0);
return (QLT_MBOX_FAILED);
}
/* See if mailboxes are still uninitialized */
return (QLT_MBOX_NOT_INITIALIZED);
}
/* Wait to grab the mailboxes */
retries++) {
if ((retries > 5) ||
return (QLT_MBOX_BUSY);
}
}
/* Make sure we always ask for mailbox 0 */
/* Load mailboxes, set state and generate RISC interrupt */
for (i = 0; i < MAX_MBOXES; i++) {
}
/* Wait for mailbox command completion */
+ drv_usectohz(MBOX_TIMEOUT)) < 0) {
info[79] = 0;
/*
* XXX Throw HBA fatal error event
*/
return (QLT_MBOX_TIMEOUT);
}
goto qlt_mbox_wait_loop;
/* Make sure its a completion */
return (QLT_MBOX_ABORTED);
}
/* MBox command completed. Clear state, retuen based on mbox 0 */
/* Mailboxes are already loaded by interrupt routine */
return (QLT_SUCCESS);
}
/*
* **SHOULD ONLY BE CALLED FROM INTERRUPT CONTEXT. DO NOT CALL ELSEWHERE**
*/
/* ARGSUSED */
static uint_t
{
int instance;
int i;
int intr_loop_count;
char info[80];
/*
* Normally we will always get this lock. If tryenter is
* failing then it means that driver is trying to do
* some cleanup and is masking the intr but some intr
* has sneaked in between. See if our device has generated
* this intr. If so then wait a bit and return claimed.
* If not then return claimed if this is the 1st instance
* of a interrupt after driver has grabbed the lock.
*/
if (risc_status & BIT_15) {
drv_usecwait(10);
return (DDI_INTR_CLAIMED);
} else if (qlt->intr_sneak_counter) {
return (DDI_INTR_CLAIMED);
} else {
return (DDI_INTR_UNCLAIMED);
}
}
if (((risc_status & BIT_15) == 0) ||
(qlt->qlt_intr_enabled == 0)) {
/*
* This might be a pure coincedence that we are operating
* in a interrupt disabled mode and another device
* sharing the interrupt line has generated an interrupt
* while an interrupt from our device might be pending. Just
* ignore it and let the code handling the interrupt
* disabled mode handle it.
*/
return (DDI_INTR_UNCLAIMED);
}
/*
* XXX take care for MSI case. disable intrs
* Its gonna be complicated becasue of the max iterations.
* as hba will have posted the intr which did not go on PCI
* but we did not service it either becasue of max iterations.
* Maybe offload the intr on a different thread.
*/
intr_loop_count = 0;
/* First check for high performance path */
if (intr_type == 0x1C) {
} else if (intr_type == 0x13) {
/* XXX what about priority queue */
} else if (intr_type == 0x1D) {
} else if (intr_type == 0x12) {
mbox6);
if (qlt->qlt_link_up) {
FCT_EVENT_LINK_RESET, 0, 0);
}
} else if (code == 0x8012) {
qlt->qlt_link_up = 0;
0, 0);
} else if (code == 0x8011) {
switch (mbox1) {
break;
break;
break;
break;
default:
}
0, 0);
} else if (code == 0x8002) {
"Got 8002, mb1=%x mb2=%x mb5=%x mb6=%x",
info[79] = 0;
}
/* Handle mailbox completion */
" when driver wasn't waiting for it %d",
} else {
for (i = 0; i < MAX_MBOXES; i++) {
(((uint32_t)1) << i)) {
}
}
}
} else {
}
if ((risc_status & BIT_15) &&
goto intr_again;
}
return (DDI_INTR_CLAIMED);
}
/* **************** NVRAM Functions ********************** */
{
/* Clear access error flag */
/* Wait for READ cycle to complete. */
break;
}
drv_usecwait(10);
}
if (timer == 0) {
return (QLT_FLASH_TIMEOUT);
return (QLT_FLASH_ACCESS_ERROR);
}
return (QLT_SUCCESS);
}
{
uint64_t empty_node_name = 0;
if (qlt->qlt_25xx_chip) {
} else {
}
/* Pause RISC. */
/* Get NVRAM data and calculate checksum. */
chksum = 0;
if (ret != QLT_SUCCESS) {
return (ret);
}
ptr++;
}
/* Release RISC Pause */
/* Sanity check NVRAM Data */
return (QLT_BAD_NVRAM_DATA);
}
/* If node name is zero, hand craft it from port name */
}
return (QLT_SUCCESS);
}
{
} else {
}
return (total_ent);
}
void
{
return;
do {
break;
}
case 0x0d: /* INOT */
break;
case 0x06: /* ATIO */
break;
default:
break;
}
(ATIO_QUEUE_ENTRIES - 1);
} while (total_ent > 0);
}
{
} else {
}
return (total_ent);
}
void
{
uint8_t c;
return;
do {
break;
}
case 0x12: /* CTIO completion */
break;
case 0x0e: /* NACK */
/* Do Nothing */
break;
case 0x29: /* CT PassThrough */
break;
case 0x33: /* Abort IO IOCB completion */
break;
case 0x51: /* PUREX */
break;
case 0x52:
break;
case 0x53: /* ELS passthrough */
if (c == 0) {
} else if (c == 3) {
} else {
}
break;
case 0x54: /* ABTS received */
break;
case 0x55: /* ABTS completion */
break;
}
(RESPONSE_QUEUE_ENTRIES - 1);
} while (total_ent > 0);
}
{
uint16_t n;
uint16_t h;
uint8_t *p;
int found = 0;
return (STMF_ALLOC_FAILURE);
}
if (ret != QLT_SUCCESS) {
return (ret);
}
found = 1;
*ret_handle = h;
if ((cmd_handle != FCT_HANDLE_NONE) &&
(cmd_handle != h)) {
"with handle %x, while the portid was "
"already using a different handle %x",
id, cmd_handle, h);
return (QLT_FAILURE);
}
break;
}
"handle %x, while the handle was already in use "
return (QLT_FAILURE);
}
p += 8;
}
if (!found) {
*ret_handle = cmd_handle;
}
return (FCT_SUCCESS);
}
/* ARGSUSED */
{
uint8_t *p;
p[0] = ELS_OP_PLOGI;
p[7] = 3;
p[8] = 0x88;
p[10] = 8;
p[13] = 0xff; p[15] = 0x1f;
p[18] = 7; p[19] = 0xd0;
p[68] = 0x80;
p[74] = 8;
p[77] = 0xff;
p[81] = 1;
return (FCT_SUCCESS);
}
/* ARGSUSED */
{
return (FCT_SUCCESS);
}
{
uint16_t h;
case 0xFFFFFC: h = 0x7FC; break;
case 0xFFFFFD: h = 0x7FD; break;
case 0xFFFFFE: h = 0x7FE; break;
case 0xFFFFFF: h = 0x7FF; break;
default:
login->cmd_rp_handle, &h);
if (ret != FCT_SUCCESS)
return (ret);
}
} else {
}
if (ret != FCT_SUCCESS)
return (ret);
if (h == FCT_HANDLE_NONE)
return (FCT_SUCCESS);
return (FCT_SUCCESS);
}
return (FCT_SUCCESS);
return (FCT_FAILURE);
}
/* invoked in single thread */
{
return (FCT_SUCCESS);
return (FCT_BUSY);
}
/* QMEM_WR32(qlt, (&req[4]), 0xffffffff); */
} else {
}
qlt->rp_dereg_status = 0;
qlt->rp_id_in_dereg = 0;
return (ret);
}
/*
* Pass received ELS up to framework.
*/
static void
{
int i, off;
char info[160];
if (iocb_flags & BIT_15) {
} else {
}
goto cmd_null;
}
GET_STRUCT_SIZE(qlt_cmd_t), 0);
cmd_null:;
"allocate space for fct_cmd", (void *)qlt);
info[159] = 0;
return;
}
}
/* Take care of fw's swapping of payload */
pldptr += 4;
off += 4;
off = 4;
pldptr += 4;
}
}
fct_post_rcvd_cmd(cmd, 0);
}
{
char info[160];
if (ioflags & FCT_IOF_FORCE_FCA_DONE) {
goto fatal_panic;
} else {
}
}
if (ioflags & FCT_IOF_FORCE_FCA_DONE) {
goto fatal_panic;
} else {
}
}
if (ioflags & FCT_IOF_FORCE_FCA_DONE) {
cmd->cmd_handle = 0;
}
} else {
ASSERT(0);
return (FCT_FAILURE);
}
"FCT_IOF_FORCE_FCA_DONE for cmd %p, ioflags-%x", (void *)cmd,
ioflags);
info[159] = 0;
return (FCT_FAILURE);
}
/* ARGSUSED */
{
flags |= 2;
} else {
flags |= 1;
}
return (FCT_BUSY);
}
return (STMF_SUCCESS);
}
/*
* We must construct proper FCP_RSP_IU now. Here we only focus on
* the handling of FCP_SNS_INFO. If there's protocol failures (FCP_RSP_INFO),
* we could have catched them before we enter here.
*/
{
int use_mode2;
int ndx;
/*
* Enter fast channel for non check condition
*/
/*
* We will use mode1
*/
scsi_status |= BIT_10;
scsi_status |= BIT_11;
}
/*
* Fillout CTIO type 7 IOCB
*/
return (FCT_BUSY);
}
/*
* Common fields
*/
/*
* Mode-specific fields
*/
/*
* Trigger FW to send SCSI status out
*/
return (STMF_SUCCESS);
}
/*
* Decide the SCSI status mode, that should be used
*/
/*
* Prepare required information per the SCSI status mode
*/
if (use_mode2) {
if (!qcmd->dbuf_rsp_iu) {
return (FCT_ALLOC_FAILURE);
}
/*
* Start to construct FCP_RSP IU
*/
/*
* FCP_RSP IU flags, byte10
*/
}
/*
* SCSI status code, byte11
*/
/*
* FCP_RESID (Overrun or underrun)
*/
/*
* FCP_SNS_LEN
*/
/*
* FCP_RSP_LEN
*/
/*
* no FCP_RSP_INFO
*/
/*
* FCP_SNS_INFO
*/
/*
* Ensure dma data consistency
*/
} else {
scsi_status |= BIT_10;
scsi_status |= BIT_11;
}
if (task->task_sense_length) {
scsi_status |= BIT_9;
}
}
/*
* Fillout CTIO type 7 IOCB
*/
if (use_mode2) {
qcmd->dbuf_rsp_iu);
}
return (FCT_BUSY);
}
/*
* Common fields
*/
if (use_mode2) {
}
/*
* Mode-specific fields
*/
if (!use_mode2) {
}
if (use_mode2) {
} else {
/*
* Data in sense buf is always big-endian, data in IOCB
* should always be little-endian, so we must do swapping.
*/
}
}
/*
* Trigger FW to send SCSI status out
*/
return (STMF_SUCCESS);
}
{
return (FCT_BUSY);
} else {
}
}
return (FCT_BUSY);
}
}
return (FCT_SUCCESS);
}
{
int i;
return (FCT_BUSY);
}
else
if (terminate) {
}
req[0x23]++;
for (i = 0; i < 12; i += 4) {
/* Take care of firmware's LE requirement */
}
return (FCT_SUCCESS);
}
static void
{
int i;
uint32_t d;
/* Just put it on the request queue */
/* XXX handle this */
return;
}
for (i = 0; i < 16; i++) {
inot += 4;
req += 4;
}
req -= 64;
req[0] = 0x0e;
}
static void
{
char info[160];
/*
* If either bidirection xfer is requested of there is extended
* CDB, atio[0x20 + 11] will be greater than or equal to 3.
*/
cdb_size = 16;
if ((b & 3) == 3) {
"received, dropping the cmd as bidirectional "
/* XXX abort the I/O */
return;
}
cdb_size += b & 0xfc;
/*
* Verify that we have enough entries. Without additional CDB
* Everything will fit nicely within the same 64 bytes. So the
* additional cdb size is essentially the # of additional bytes
* we need.
*/
" cdb (cdb size = %d bytes), however the firmware "
" did not DMAed the entire FCP_CMD IU, entry count "
/* XXX abort the I/O */
return;
}
}
if (fw_xchg_addr == 0xFFFFFFFF) {
} else {
}
/* Abort this IO */
"qlt_handle_atio: qlt-%p, can't "
"allocate space for scsi_task", (void *)qlt);
info[159] = 0;
return;
}
rportid));
return;
}
/* Dont do a 64 byte read as this is IOMMU */
q = atio+0x28;
/* XXX Handle fcp_cntl */
tm = *q++;
if (tm) {
else
}
*p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++;
*p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++;
*p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++;
*p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++;
if (cdb_size > 16) {
uint16_t i;
while (xtra) {
*p++ = *q++;
xtra--;
}
}
for (i = 0; i < 4; i++) {
cb[i] = *q++;
}
}
} else {
}
fct_post_rcvd_cmd(cmd, 0);
}
static void
{
" received when driver wasn't waiting for it",
return;
}
if (status != 0) {
"for 0x%x with status %x, subcode1 %x subcode2 %x",
else
} else {
}
}
/*
* Note that when an ELS is aborted, the regular or aborted completion
* (if any) gets posted before the abort IOCB comes back on response queue.
*/
static void
{
char info[160];
if (!CMD_HANDLE_VALID(hndl)) {
/*
* This cannot happen for unsol els completion. This can
* only happen when abort for an unsol els completes.
* This condition indicates a firmware bug.
*/
"Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p",
info[159] = 0;
return;
}
if (status == 5) {
/*
* When an unsolicited els is aborted, the abort is done
* by a ELSPT iocb with abort control. This is the aborted IOCB
* and not the abortee. We will do the cleanup when the
* IOCB which caused the abort, returns.
*/
stmf_trace(0, "--UNSOL ELS returned with status 5 --");
return;
}
/*
* Now why would this happen ???
*/
"qlt_handle_unsol_els_completion: can not "
(void *)rsp);
info[159] = 0;
return;
}
/*
* This is the same case as "if (status == 5)" above. The
* only difference is that in this case the firmware actually
* finished sending the response. So the abort attempt will
* come back with status ?. We will handle it there.
*/
stmf_trace(0, "--UNSOL ELS finished while we are trying to "
"abort it");
return;
}
}
if (status == 0) {
} else {
}
}
static void
{
char info[160];
if (!CMD_HANDLE_VALID(hndl)) {
/*
* Someone has requested to abort it, but no one is waiting for
* this completion.
*/
/*
* There could be exchange resource leakage, so
* throw HBA fatal error event now
*/
"qlt_handle_unsol_els_abort_completion: "
"Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p",
info[159] = 0;
return;
}
return;
}
/*
* Why would this happen ??
*/
"qlt_handle_unsol_els_abort_completion: can not get "
(void *)rsp);
info[159] = 0;
return;
}
}
if (status == 0) {
} else if (status == 8) {
} else {
}
}
static void
{
char info[160];
if (!CMD_HANDLE_VALID(hndl)) {
/*
* This cannot happen for sol els completion.
*/
"Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p",
info[159] = 0;
return;
}
"qlt_handle_sol_els_completion: can not "
(void *)rsp);
info[159] = 0;
return;
}
/*
* We will handle it when the ABORT IO IOCB returns.
*/
return;
}
if (status == 0) {
}
}
if (status == 0) {
} else {
}
}
static void
{
char info[160];
if (!CMD_HANDLE_VALID(hndl)) {
/*
* Solicited commands will always have a valid handle.
*/
info[159] = 0;
return;
}
"qlt_handle_ct_completion: cannot find "
(void *)rsp);
info[159] = 0;
return;
}
/*
* We will handle it when ABORT IO IOCB returns;
*/
return;
}
if (status == 0) {
}
if (status == 0) {
} else {
}
}
static void
{
uint8_t n;
char info[160];
/* XXX: Check validity of the IOCB by checking 4th byte. */
n = rsp[2];
if (!CMD_HANDLE_VALID(hndl)) {
/*
* Someone has requested to abort it, but no one is waiting for
* this completion.
*/
/*
* There could be exchange resource leakage, so
* throw HBA fatal error event now
*/
"qlt_handle_ctio_completion: hndl-"
info[159] = 0;
}
return;
}
abort_req = 1;
(void *)rsp);
} else {
abort_req = 0;
}
"qlt_handle_ctio_completion: cannot find "
(void *)rsp);
info[159] = 0;
return;
}
if (qcmd->dbuf_rsp_iu) {
}
if (abort_req) {
} else {
fc_st = FCT_SUCCESS;
}
}
} else {
} else {
}
}
/* A completion of data xfer */
if (n == 0) {
} else {
}
}
return;
}
if (!abort_req) {
/*
* This was just a pure status xfer.
*/
return;
}
}
static void
{
char info[80];
uint32_t h;
if (!CMD_HANDLE_VALID(h)) {
/*
* Solicited commands always have a valid handle.
*/
"qlt_handle_sol_abort_completion: hndl-"
info[79] = 0;
return;
}
/*
* What happened to the cmd ??
*/
"qlt_handle_sol_abort_completion: cannot "
"find cmd, hndl-%x, status-%x, rsp-%p", h, status,
(void *)rsp);
info[79] = 0;
return;
}
}
if (status == 0) {
} else if (status == 0x31) {
} else {
}
}
static void
{
char info[160];
sizeof (qlt_abts_cmd_t), 0);
"qlt_handle_rcvd_abts: qlt-%p, can't "
"allocate space for fct_cmd", (void *)qlt);
info[159] = 0;
return;
}
fct_post_rcvd_cmd(cmd, 0);
}
static void
{
char info[80];
return;
}
info[79] = 0;
}
#ifdef DEBUG
#endif
{
return (FCT_NOT_FOUND);
}
#ifdef DEBUG
if (qlt_drop_abort_counter > 0) {
return (FCT_SUCCESS);
}
#endif
}
if (flags & FCT_IOF_FORCE_FCA_DONE) {
cmd->cmd_handle = 0;
}
}
}
}
ASSERT(0);
return (FCT_FAILURE);
}
{
return (FCT_BUSY);
}
} else {
}
return (FCT_SUCCESS);
}
{
"qlt_abort_purex: fctcmd-%p, cmd_handle-%x, "
}
return (FCT_BUSY);
}
} else {
}
return (FCT_SUCCESS);
}
{
return (FCT_BUSY);
}
return (FCT_SUCCESS);
}
{
}
ASSERT(0);
return (FCT_FAILURE);
}
{
return (FCT_BUSY);
}
els->els_req_size);
return (FCT_BUSY);
}
}
return (FCT_SUCCESS);
}
{
return (FCT_BUSY);
}
ct->ct_req_size);
return (FCT_BUSY);
}
return (FCT_SUCCESS);
}
/*
* All QLT_FIRMWARE_* will mainly be handled in this function
* It can not be called in interrupt context
*
* FWDUMP's purpose is to serve ioctl, so we will use qlt_ioctl_flags
* and qlt_ioctl_lock
*/
static fct_status_t
{
int i;
int retries;
int n, size_left;
char c = ' ';
/*
* To make sure that there's no outstanding dumping task
*/
return (FCT_FAILURE);
}
/*
* To make sure not to overwrite existing dump
*/
/*
* If we have alreay one dump, but it's not triggered by user
* and the user hasn't fetched it, we shouldn't dump again.
*/
"dump, please fetech it");
return (FCT_FAILURE);
}
} else {
}
if (!qlt->qlt_fwdump_buf) {
/*
* It's the only place that we allocate buf for dumping. After
* it's allocated, we will use it until the port is detached.
*/
}
/*
* Start to dump firmware
*/
/*
* Print the ISP firmware revision number and attributes information
* Read the RISC to Host Status register
*/
"Attributes %04x\n\nR2H Status Register\n%08x",
/*
* Before pausing the RISC, make sure no mailbox can execute
*/
/*
* Wait to grab the mailboxes
*/
if (retries > 5) {
"qlt_firmware_dump: "
"can't drain out mailbox commands");
goto dump_fail;
}
}
}
/*
* Pause the RISC processor
*/
/*
* Wait for the RISC processor to pause
*/
for (i = 0; i < 200; i++) {
break;
}
drv_usecwait(1000);
}
if (i == 200) {
return (FCT_FAILURE);
}
if (!qlt->qlt_25xx_chip) {
goto over_25xx_specific_dump;
}
/*
* Capture data from 32 regsiters
*/
/*
* Disable interrupts
*/
/*
* Shadow registers
*/
addr = 0xb0000000;
for (i = 0; i < 0xb; i++) {
break;
}
if (i && ((i & 7) == 0)) {
}
addr += 0x100000;
}
if (qlt->qlt_25xx_chip) {
}
/*
* Mailbox registers
*/
for (i = 0; i < 32; i += 2) {
if ((i + 2) & 15) {
c = ' ';
} else {
c = '\n';
}
}
/*
* Transfer sequence registers
*/
/*
* Receive sequence registers
*/
if (!qlt->qlt_25xx_chip)
goto over_aseq_regs;
/*
* Auxiliary sequencer registers
*/
/*
* Command DMA registers
*/
/*
* Queues
*/
"\nRequest0 Queue DMA Channel registers\n");
"\n\nResponse0 Queue DMA Channel registers\n");
"\n\nRequest1 Queue DMA Channel registers\n");
/*
* Transmit DMA registers
*/
/*
* Receive DMA registers
*/
/*
* RISC registers
*/
/*
* Local memory controller registers
*/
if (qlt->qlt_25xx_chip) {
}
/*
* Fibre protocol module regsiters
*/
/*
* Fibre buffer registers
*/
if (qlt->qlt_25xx_chip) {
}
qlt->qlt_intr_enabled = 0;
drv_usecwait(20);
qlt->intr_sneak_counter = 0;
/*
* Memory
*/
addr = 0x20000;
endaddr = 0x22000;
words_to_read = 0;
}
QLT_SUCCESS) {
"reading risc ram - CODE RAM");
goto dump_fail;
}
if (size_left < 100000) {
"out of space - CODE RAM");
goto dump_ok;
}
addr += words_to_read;
}
addr = 0x100000;
endaddr++;
if (endaddr & 7) {
}
words_to_read = 0;
}
QLT_SUCCESS) {
"reading risc ram - EXT RAM");
goto dump_fail;
}
if (size_left < 100000) {
"out of space - EXT RAM");
goto dump_ok;
}
addr += words_to_read;
}
/*
* Label the end tag
*/
/*
* Queue dumping
*/
/*
* Lable dump reason
*/
qlt->qlt_ioctl_flags &=
return (FCT_SUCCESS);
return (FCT_FAILURE);
}
static int
int size_left)
{
int i;
int n;
char c = ' ';
for (i = 0, n = 0; i < count; i++) {
if ((i + 1) & 7) {
c = ' ';
} else {
c = '\n';
}
}
return (n);
}
static int
{
int i;
int n;
char c = ' ';
for (i = 0, n = 0; i < words; i++) {
if ((i & 7) == 0) {
addr + i);
}
if ((i + 1) & 7) {
c = ' ';
} else {
c = '\n';
}
}
return (n);
}
static int
int size_left)
{
int i;
int n;
char c = ' ';
int words;
uint16_t w;
for (i = 0, n = 0; i < words; i++) {
if ((i & 7) == 0) {
}
if ((i + 1) & 7) {
c = ' ';
} else {
c = '\n';
}
}
return (n);
}
/*
* Only called by debug dump. Interrupts are disabled and mailboxes alongwith
* mailbox ram is available.
* Copy data from RISC RAM to system memory
*/
static fct_status_t
{
/*
* System destination address
*/
da >>= 16;
da >>= 16;
da >>= 16;
/*
* Length
*/
/*
* RISC source address
*/
if (ret == QLT_SUCCESS) {
} else {
"failed 0x%llX", ret);
}
return (ret);
}