summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla4xxx/ql4_isr.c
diff options
context:
space:
mode:
authorDavid Somayajulu <david.somayajulu@qlogic.com>2006-09-19 10:28:00 -0700
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>2006-10-04 13:34:04 -0500
commitafaf5a2d341d33b66b47c2716a263ce593460a08 (patch)
tree70aeac2f11cc644114eead55fa003be8288666f0 /drivers/scsi/qla4xxx/ql4_isr.c
parented542bed126caeefc6546b276e4af852d4d34f33 (diff)
downloadlwn-afaf5a2d341d33b66b47c2716a263ce593460a08.tar.gz
lwn-afaf5a2d341d33b66b47c2716a263ce593460a08.zip
[SCSI] Initial Commit of qla4xxx
open-iSCSI driver for Qlogic Corporation's iSCSI HBAs Signed-off-by: Ravi Anand <ravi.anand@qlogic.com> Signed-off-by: David Somayajulu <david.somayajulu@qlogic.com> Signed-off-by: Doug Maxey <dwm@bubba.enoyolf.org> Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/qla4xxx/ql4_isr.c')
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c797
1 files changed, 797 insertions, 0 deletions
diff --git a/drivers/scsi/qla4xxx/ql4_isr.c b/drivers/scsi/qla4xxx/ql4_isr.c
new file mode 100644
index 000000000000..b584317608d1
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_isr.c
@@ -0,0 +1,797 @@
+/*
+ * QLogic iSCSI HBA Driver
+ * Copyright (c) 2003-2006 QLogic Corporation
+ *
+ * See LICENSE.qla4xxx for copyright and licensing details.
+ */
+
+#include "ql4_def.h"
+
+/**
+ * qla2x00_process_completed_request() - Process a Fast Post response.
+ * @ha: SCSI driver HA context
+ * @index: SRB index
+ **/
+static void qla4xxx_process_completed_request(struct scsi_qla_host *ha,
+ uint32_t index)
+{
+ struct srb *srb;
+
+ srb = qla4xxx_del_from_active_array(ha, index);
+ if (srb) {
+ /* Save ISP completion status */
+ srb->cmd->result = DID_OK << 16;
+ qla4xxx_srb_compl(ha, srb);
+ } else {
+ DEBUG2(printk("scsi%ld: Invalid ISP SCSI completion handle = "
+ "%d\n", ha->host_no, index));
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+ }
+}
+
+/**
+ * qla4xxx_status_entry - processes status IOCBs
+ * @ha: Pointer to host adapter structure.
+ * @sts_entry: Pointer to status entry structure.
+ **/
+static void qla4xxx_status_entry(struct scsi_qla_host *ha,
+ struct status_entry *sts_entry)
+{
+ uint8_t scsi_status;
+ struct scsi_cmnd *cmd;
+ struct srb *srb;
+ struct ddb_entry *ddb_entry;
+ uint32_t residual;
+ uint16_t sensebytecnt;
+
+ if (sts_entry->completionStatus == SCS_COMPLETE &&
+ sts_entry->scsiStatus == 0) {
+ qla4xxx_process_completed_request(ha,
+ le32_to_cpu(sts_entry->
+ handle));
+ return;
+ }
+
+ srb = qla4xxx_del_from_active_array(ha, le32_to_cpu(sts_entry->handle));
+ if (!srb) {
+ /* FIXMEdg: Don't we need to reset ISP in this case??? */
+ DEBUG2(printk(KERN_WARNING "scsi%ld: %s: Status Entry invalid "
+ "handle 0x%x, sp=%p. This cmd may have already "
+ "been completed.\n", ha->host_no, __func__,
+ le32_to_cpu(sts_entry->handle), srb));
+ return;
+ }
+
+ cmd = srb->cmd;
+ if (cmd == NULL) {
+ DEBUG2(printk("scsi%ld: %s: Command already returned back to "
+ "OS pkt->handle=%d srb=%p srb->state:%d\n",
+ ha->host_no, __func__, sts_entry->handle,
+ srb, srb->state));
+ dev_warn(&ha->pdev->dev, "Command is NULL:"
+ " already returned to OS (srb=%p)\n", srb);
+ return;
+ }
+
+ ddb_entry = srb->ddb;
+ if (ddb_entry == NULL) {
+ cmd->result = DID_NO_CONNECT << 16;
+ goto status_entry_exit;
+ }
+
+ residual = le32_to_cpu(sts_entry->residualByteCnt);
+
+ /* Translate ISP error to a Linux SCSI error. */
+ scsi_status = sts_entry->scsiStatus;
+ switch (sts_entry->completionStatus) {
+ case SCS_COMPLETE:
+ if (scsi_status == 0) {
+ cmd->result = DID_OK << 16;
+ break;
+ }
+
+ if (sts_entry->iscsiFlags &
+ (ISCSI_FLAG_RESIDUAL_OVER|ISCSI_FLAG_RESIDUAL_UNDER))
+ cmd->resid = residual;
+
+ cmd->result = DID_OK << 16 | scsi_status;
+
+ if (scsi_status != SCSI_CHECK_CONDITION)
+ break;
+
+ /* Copy Sense Data into sense buffer. */
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ sensebytecnt = le16_to_cpu(sts_entry->senseDataByteCnt);
+ if (sensebytecnt == 0)
+ break;
+
+ memcpy(cmd->sense_buffer, sts_entry->senseData,
+ min(sensebytecnt,
+ (uint16_t) sizeof(cmd->sense_buffer)));
+
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
+ "ASC/ASCQ = %02x/%02x\n", ha->host_no,
+ cmd->device->channel, cmd->device->id,
+ cmd->device->lun, __func__,
+ sts_entry->senseData[2] & 0x0f,
+ sts_entry->senseData[12],
+ sts_entry->senseData[13]));
+
+ srb->flags |= SRB_GOT_SENSE;
+ break;
+
+ case SCS_INCOMPLETE:
+ /* Always set the status to DID_ERROR, since
+ * all conditions result in that status anyway */
+ cmd->result = DID_ERROR << 16;
+ break;
+
+ case SCS_RESET_OCCURRED:
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Device RESET occurred\n",
+ ha->host_no, cmd->device->channel,
+ cmd->device->id, cmd->device->lun, __func__));
+
+ cmd->result = DID_RESET << 16;
+ break;
+
+ case SCS_ABORTED:
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: Abort occurred\n",
+ ha->host_no, cmd->device->channel,
+ cmd->device->id, cmd->device->lun, __func__));
+
+ cmd->result = DID_RESET << 16;
+ break;
+
+ case SCS_TIMEOUT:
+ DEBUG2(printk(KERN_INFO "scsi%ld:%d:%d:%d: Timeout\n",
+ ha->host_no, cmd->device->channel,
+ cmd->device->id, cmd->device->lun));
+
+ cmd->result = DID_BUS_BUSY << 16;
+
+ /*
+ * Mark device missing so that we won't continue to send
+ * I/O to this device. We should get a ddb state change
+ * AEN soon.
+ */
+ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+ qla4xxx_mark_device_missing(ha, ddb_entry);
+ break;
+
+ case SCS_DATA_UNDERRUN:
+ case SCS_DATA_OVERRUN:
+ if (sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_OVER) {
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: " "Data overrun, "
+ "residual = 0x%x\n", ha->host_no,
+ cmd->device->channel, cmd->device->id,
+ cmd->device->lun, __func__, residual));
+
+ cmd->result = DID_ERROR << 16;
+ break;
+ }
+
+ if ((sts_entry->iscsiFlags & ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
+ /*
+ * Firmware detected a SCSI transport underrun
+ * condition
+ */
+ cmd->resid = residual;
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: UNDERRUN status "
+ "detected, xferlen = 0x%x, residual = "
+ "0x%x\n",
+ ha->host_no, cmd->device->channel,
+ cmd->device->id,
+ cmd->device->lun, __func__,
+ cmd->request_bufflen,
+ residual));
+ }
+
+ /*
+ * If there is scsi_status, it takes precedense over
+ * underflow condition.
+ */
+ if (scsi_status != 0) {
+ cmd->result = DID_OK << 16 | scsi_status;
+
+ if (scsi_status != SCSI_CHECK_CONDITION)
+ break;
+
+ /* Copy Sense Data into sense buffer. */
+ memset(cmd->sense_buffer, 0,
+ sizeof(cmd->sense_buffer));
+
+ sensebytecnt =
+ le16_to_cpu(sts_entry->senseDataByteCnt);
+ if (sensebytecnt == 0)
+ break;
+
+ memcpy(cmd->sense_buffer, sts_entry->senseData,
+ min(sensebytecnt,
+ (uint16_t) sizeof(cmd->sense_buffer)));
+
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: sense key = %x, "
+ "ASC/ASCQ = %02x/%02x\n", ha->host_no,
+ cmd->device->channel, cmd->device->id,
+ cmd->device->lun, __func__,
+ sts_entry->senseData[2] & 0x0f,
+ sts_entry->senseData[12],
+ sts_entry->senseData[13]));
+ } else {
+ /*
+ * If RISC reports underrun and target does not
+ * report it then we must have a lost frame, so
+ * tell upper layer to retry it by reporting a
+ * bus busy.
+ */
+ if ((sts_entry->iscsiFlags &
+ ISCSI_FLAG_RESIDUAL_UNDER) == 0) {
+ cmd->result = DID_BUS_BUSY << 16;
+ } else if ((cmd->request_bufflen - residual) <
+ cmd->underflow) {
+ /*
+ * Handle mid-layer underflow???
+ *
+ * For kernels less than 2.4, the driver must
+ * return an error if an underflow is detected.
+ * For kernels equal-to and above 2.4, the
+ * mid-layer will appearantly handle the
+ * underflow by detecting the residual count --
+ * unfortunately, we do not see where this is
+ * actually being done. In the interim, we
+ * will return DID_ERROR.
+ */
+ DEBUG2(printk("scsi%ld:%d:%d:%d: %s: "
+ "Mid-layer Data underrun, "
+ "xferlen = 0x%x, "
+ "residual = 0x%x\n", ha->host_no,
+ cmd->device->channel,
+ cmd->device->id,
+ cmd->device->lun, __func__,
+ cmd->request_bufflen, residual));
+
+ cmd->result = DID_ERROR << 16;
+ } else {
+ cmd->result = DID_OK << 16;
+ }
+ }
+ break;
+
+ case SCS_DEVICE_LOGGED_OUT:
+ case SCS_DEVICE_UNAVAILABLE:
+ /*
+ * Mark device missing so that we won't continue to
+ * send I/O to this device. We should get a ddb
+ * state change AEN soon.
+ */
+ if (atomic_read(&ddb_entry->state) == DDB_STATE_ONLINE)
+ qla4xxx_mark_device_missing(ha, ddb_entry);
+
+ cmd->result = DID_BUS_BUSY << 16;
+ break;
+
+ case SCS_QUEUE_FULL:
+ /*
+ * SCSI Mid-Layer handles device queue full
+ */
+ cmd->result = DID_OK << 16 | sts_entry->scsiStatus;
+ DEBUG2(printk("scsi%ld:%d:%d: %s: QUEUE FULL detected "
+ "compl=%02x, scsi=%02x, state=%02x, iFlags=%02x,"
+ " iResp=%02x\n", ha->host_no, cmd->device->id,
+ cmd->device->lun, __func__,
+ sts_entry->completionStatus,
+ sts_entry->scsiStatus, sts_entry->state_flags,
+ sts_entry->iscsiFlags,
+ sts_entry->iscsiResponse));
+ break;
+
+ default:
+ cmd->result = DID_ERROR << 16;
+ break;
+ }
+
+status_entry_exit:
+
+ /* complete the request */
+ srb->cc_stat = sts_entry->completionStatus;
+ qla4xxx_srb_compl(ha, srb);
+}
+
+/**
+ * qla4xxx_process_response_queue - process response queue completions
+ * @ha: Pointer to host adapter structure.
+ *
+ * This routine process response queue completions in interrupt context.
+ * Hardware_lock locked upon entry
+ **/
+static void qla4xxx_process_response_queue(struct scsi_qla_host * ha)
+{
+ uint32_t count = 0;
+ struct srb *srb = NULL;
+ struct status_entry *sts_entry;
+
+ /* Process all responses from response queue */
+ while ((ha->response_in =
+ (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in)) !=
+ ha->response_out) {
+ sts_entry = (struct status_entry *) ha->response_ptr;
+ count++;
+
+ /* Advance pointers for next entry */
+ if (ha->response_out == (RESPONSE_QUEUE_DEPTH - 1)) {
+ ha->response_out = 0;
+ ha->response_ptr = ha->response_ring;
+ } else {
+ ha->response_out++;
+ ha->response_ptr++;
+ }
+
+ /* process entry */
+ switch (sts_entry->hdr.entryType) {
+ case ET_STATUS:
+ /*
+ * Common status - Single completion posted in single
+ * IOSB.
+ */
+ qla4xxx_status_entry(ha, sts_entry);
+ break;
+
+ case ET_PASSTHRU_STATUS:
+ break;
+
+ case ET_STATUS_CONTINUATION:
+ /* Just throw away the status continuation entries */
+ DEBUG2(printk("scsi%ld: %s: Status Continuation entry "
+ "- ignoring\n", ha->host_no, __func__));
+ break;
+
+ case ET_COMMAND:
+ /* ISP device queue is full. Command not
+ * accepted by ISP. Queue command for
+ * later */
+
+ srb = qla4xxx_del_from_active_array(ha,
+ le32_to_cpu(sts_entry->
+ handle));
+ if (srb == NULL)
+ goto exit_prq_invalid_handle;
+
+ DEBUG2(printk("scsi%ld: %s: FW device queue full, "
+ "srb %p\n", ha->host_no, __func__, srb));
+
+ /* ETRY normally by sending it back with
+ * DID_BUS_BUSY */
+ srb->cmd->result = DID_BUS_BUSY << 16;
+ qla4xxx_srb_compl(ha, srb);
+ break;
+
+ case ET_CONTINUE:
+ /* Just throw away the continuation entries */
+ DEBUG2(printk("scsi%ld: %s: Continuation entry - "
+ "ignoring\n", ha->host_no, __func__));
+ break;
+
+ default:
+ /*
+ * Invalid entry in response queue, reset RISC
+ * firmware.
+ */
+ DEBUG2(printk("scsi%ld: %s: Invalid entry %x in "
+ "response queue \n", ha->host_no,
+ __func__,
+ sts_entry->hdr.entryType));
+ goto exit_prq_error;
+ }
+ }
+
+ /*
+ * Done with responses, update the ISP For QLA4010, this also clears
+ * the interrupt.
+ */
+ writel(ha->response_out, &ha->reg->rsp_q_out);
+ readl(&ha->reg->rsp_q_out);
+
+ return;
+
+exit_prq_invalid_handle:
+ DEBUG2(printk("scsi%ld: %s: Invalid handle(srb)=%p type=%x IOCS=%x\n",
+ ha->host_no, __func__, srb, sts_entry->hdr.entryType,
+ sts_entry->completionStatus));
+
+exit_prq_error:
+ writel(ha->response_out, &ha->reg->rsp_q_out);
+ readl(&ha->reg->rsp_q_out);
+
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+}
+
+/**
+ * qla4xxx_isr_decode_mailbox - decodes mailbox status
+ * @ha: Pointer to host adapter structure.
+ * @mailbox_status: Mailbox status.
+ *
+ * This routine decodes the mailbox status during the ISR.
+ * Hardware_lock locked upon entry. runs in interrupt context.
+ **/
+static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
+ uint32_t mbox_status)
+{
+ int i;
+
+ if ((mbox_status == MBOX_STS_BUSY) ||
+ (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) ||
+ (mbox_status >> 12 == MBOX_COMPLETION_STATUS)) {
+ ha->mbox_status[0] = mbox_status;
+
+ if (test_bit(AF_MBOX_COMMAND, &ha->flags)) {
+ /*
+ * Copy all mailbox registers to a temporary
+ * location and set mailbox command done flag
+ */
+ for (i = 1; i < ha->mbox_status_count; i++)
+ ha->mbox_status[i] =
+ readl(&ha->reg->mailbox[i]);
+
+ set_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
+ wake_up(&ha->mailbox_wait_queue);
+ }
+ } else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) {
+ /* Immediately process the AENs that don't require much work.
+ * Only queue the database_changed AENs */
+ switch (mbox_status) {
+ case MBOX_ASTS_SYSTEM_ERROR:
+ /* Log Mailbox registers */
+ if (ql4xdontresethba) {
+ DEBUG2(printk("%s:Dont Reset HBA\n",
+ __func__));
+ } else {
+ set_bit(AF_GET_CRASH_RECORD, &ha->flags);
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+ }
+ break;
+
+ case MBOX_ASTS_REQUEST_TRANSFER_ERROR:
+ case MBOX_ASTS_RESPONSE_TRANSFER_ERROR:
+ case MBOX_ASTS_NVRAM_INVALID:
+ case MBOX_ASTS_IP_ADDRESS_CHANGED:
+ case MBOX_ASTS_DHCP_LEASE_EXPIRED:
+ DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, "
+ "Reset HA\n", ha->host_no, mbox_status));
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+ break;
+
+ case MBOX_ASTS_LINK_UP:
+ DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK UP\n",
+ ha->host_no, mbox_status));
+ set_bit(AF_LINK_UP, &ha->flags);
+ break;
+
+ case MBOX_ASTS_LINK_DOWN:
+ DEBUG2(printk("scsi%ld: AEN %04x Adapter LINK DOWN\n",
+ ha->host_no, mbox_status));
+ clear_bit(AF_LINK_UP, &ha->flags);
+ break;
+
+ case MBOX_ASTS_HEARTBEAT:
+ ha->seconds_since_last_heartbeat = 0;
+ break;
+
+ case MBOX_ASTS_DHCP_LEASE_ACQUIRED:
+ DEBUG2(printk("scsi%ld: AEN %04x DHCP LEASE "
+ "ACQUIRED\n", ha->host_no, mbox_status));
+ set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags);
+ break;
+
+ case MBOX_ASTS_PROTOCOL_STATISTIC_ALARM:
+ case MBOX_ASTS_SCSI_COMMAND_PDU_REJECTED: /* Target
+ * mode
+ * only */
+ case MBOX_ASTS_UNSOLICITED_PDU_RECEIVED: /* Connection mode */
+ case MBOX_ASTS_IPSEC_SYSTEM_FATAL_ERROR:
+ case MBOX_ASTS_SUBNET_STATE_CHANGE:
+ /* No action */
+ DEBUG2(printk("scsi%ld: AEN %04x\n", ha->host_no,
+ mbox_status));
+ break;
+
+ case MBOX_ASTS_MAC_ADDRESS_CHANGED:
+ case MBOX_ASTS_DNS:
+ /* No action */
+ DEBUG2(printk(KERN_INFO "scsi%ld: AEN %04x, "
+ "mbox_sts[1]=%04x, mbox_sts[2]=%04x\n",
+ ha->host_no, mbox_status,
+ readl(&ha->reg->mailbox[1]),
+ readl(&ha->reg->mailbox[2])));
+ break;
+
+ case MBOX_ASTS_SELF_TEST_FAILED:
+ case MBOX_ASTS_LOGIN_FAILED:
+ /* No action */
+ DEBUG2(printk("scsi%ld: AEN %04x, mbox_sts[1]=%04x, "
+ "mbox_sts[2]=%04x, mbox_sts[3]=%04x\n",
+ ha->host_no, mbox_status,
+ readl(&ha->reg->mailbox[1]),
+ readl(&ha->reg->mailbox[2]),
+ readl(&ha->reg->mailbox[3])));
+ break;
+
+ case MBOX_ASTS_DATABASE_CHANGED:
+ /* Queue AEN information and process it in the DPC
+ * routine */
+ if (ha->aen_q_count > 0) {
+ /* advance pointer */
+ if (ha->aen_in == (MAX_AEN_ENTRIES - 1))
+ ha->aen_in = 0;
+ else
+ ha->aen_in++;
+
+ /* decrement available counter */
+ ha->aen_q_count--;
+
+ for (i = 1; i < MBOX_AEN_REG_COUNT; i++)
+ ha->aen_q[ha->aen_in].mbox_sts[i] =
+ readl(&ha->reg->mailbox[i]);
+
+ ha->aen_q[ha->aen_in].mbox_sts[0] = mbox_status;
+
+ /* print debug message */
+ DEBUG2(printk("scsi%ld: AEN[%d] %04x queued"
+ " mb1:0x%x mb2:0x%x mb3:0x%x mb4:0x%x\n",
+ ha->host_no, ha->aen_in,
+ mbox_status,
+ ha->aen_q[ha->aen_in].mbox_sts[1],
+ ha->aen_q[ha->aen_in].mbox_sts[2],
+ ha->aen_q[ha->aen_in].mbox_sts[3],
+ ha->aen_q[ha->aen_in]. mbox_sts[4]));
+
+ /* The DPC routine will process the aen */
+ set_bit(DPC_AEN, &ha->dpc_flags);
+ } else {
+ DEBUG2(printk("scsi%ld: %s: aen %04x, queue "
+ "overflowed! AEN LOST!!\n",
+ ha->host_no, __func__,
+ mbox_status));
+
+ DEBUG2(printk("scsi%ld: DUMP AEN QUEUE\n",
+ ha->host_no));
+
+ for (i = 0; i < MAX_AEN_ENTRIES; i++) {
+ DEBUG2(printk("AEN[%d] %04x %04x %04x "
+ "%04x\n", i,
+ ha->aen_q[i].mbox_sts[0],
+ ha->aen_q[i].mbox_sts[1],
+ ha->aen_q[i].mbox_sts[2],
+ ha->aen_q[i].mbox_sts[3]));
+ }
+ }
+ break;
+
+ default:
+ DEBUG2(printk(KERN_WARNING
+ "scsi%ld: AEN %04x UNKNOWN\n",
+ ha->host_no, mbox_status));
+ break;
+ }
+ } else {
+ DEBUG2(printk("scsi%ld: Unknown mailbox status %08X\n",
+ ha->host_no, mbox_status));
+
+ ha->mbox_status[0] = mbox_status;
+ }
+}
+
+/**
+ * qla4xxx_interrupt_service_routine - isr
+ * @ha: pointer to host adapter structure.
+ *
+ * This is the main interrupt service routine.
+ * hardware_lock locked upon entry. runs in interrupt context.
+ **/
+void qla4xxx_interrupt_service_routine(struct scsi_qla_host * ha,
+ uint32_t intr_status)
+{
+ /* Process response queue interrupt. */
+ if (intr_status & CSR_SCSI_COMPLETION_INTR)
+ qla4xxx_process_response_queue(ha);
+
+ /* Process mailbox/asynch event interrupt.*/
+ if (intr_status & CSR_SCSI_PROCESSOR_INTR) {
+ qla4xxx_isr_decode_mailbox(ha,
+ readl(&ha->reg->mailbox[0]));
+
+ /* Clear Mailbox Interrupt */
+ writel(set_rmask(CSR_SCSI_PROCESSOR_INTR),
+ &ha->reg->ctrl_status);
+ readl(&ha->reg->ctrl_status);
+ }
+}
+
+/**
+ * qla4xxx_intr_handler - hardware interrupt handler.
+ * @irq: Unused
+ * @dev_id: Pointer to host adapter structure
+ * @regs: Unused
+ **/
+irqreturn_t qla4xxx_intr_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct scsi_qla_host *ha;
+ uint32_t intr_status;
+ unsigned long flags = 0;
+ uint8_t reqs_count = 0;
+
+ ha = (struct scsi_qla_host *) dev_id;
+ if (!ha) {
+ DEBUG2(printk(KERN_INFO
+ "qla4xxx: Interrupt with NULL host ptr\n"));
+ return IRQ_NONE;
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /*
+ * Repeatedly service interrupts up to a maximum of
+ * MAX_REQS_SERVICED_PER_INTR
+ */
+ while (1) {
+ /*
+ * Read interrupt status
+ */
+ if (le32_to_cpu(ha->shadow_regs->rsp_q_in) !=
+ ha->response_out)
+ intr_status = CSR_SCSI_COMPLETION_INTR;
+ else
+ intr_status = readl(&ha->reg->ctrl_status);
+
+ if ((intr_status &
+ (CSR_SCSI_RESET_INTR|CSR_FATAL_ERROR|INTR_PENDING)) ==
+ 0) {
+ if (reqs_count == 0)
+ ha->spurious_int_count++;
+ break;
+ }
+
+ if (intr_status & CSR_FATAL_ERROR) {
+ DEBUG2(printk(KERN_INFO "scsi%ld: Fatal Error, "
+ "Status 0x%04x\n", ha->host_no,
+ readl(isp_port_error_status (ha))));
+
+ /* Issue Soft Reset to clear this error condition.
+ * This will prevent the RISC from repeatedly
+ * interrupting the driver; thus, allowing the DPC to
+ * get scheduled to continue error recovery.
+ * NOTE: Disabling RISC interrupts does not work in
+ * this case, as CSR_FATAL_ERROR overrides
+ * CSR_SCSI_INTR_ENABLE */
+ if ((readl(&ha->reg->ctrl_status) &
+ CSR_SCSI_RESET_INTR) == 0) {
+ writel(set_rmask(CSR_SOFT_RESET),
+ &ha->reg->ctrl_status);
+ readl(&ha->reg->ctrl_status);
+ }
+
+ writel(set_rmask(CSR_FATAL_ERROR),
+ &ha->reg->ctrl_status);
+ readl(&ha->reg->ctrl_status);
+
+ __qla4xxx_disable_intrs(ha);
+
+ set_bit(DPC_RESET_HA, &ha->dpc_flags);
+
+ break;
+ } else if (intr_status & CSR_SCSI_RESET_INTR) {
+ clear_bit(AF_ONLINE, &ha->flags);
+ __qla4xxx_disable_intrs(ha);
+
+ writel(set_rmask(CSR_SCSI_RESET_INTR),
+ &ha->reg->ctrl_status);
+ readl(&ha->reg->ctrl_status);
+
+ set_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
+
+ break;
+ } else if (intr_status & INTR_PENDING) {
+ qla4xxx_interrupt_service_routine(ha, intr_status);
+ ha->total_io_count++;
+ if (++reqs_count == MAX_REQS_SERVICED_PER_INTR)
+ break;
+
+ intr_status = 0;
+ }
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * qla4xxx_process_aen - processes AENs generated by firmware
+ * @ha: pointer to host adapter structure.
+ * @process_aen: type of AENs to process
+ *
+ * Processes specific types of Asynchronous Events generated by firmware.
+ * The type of AENs to process is specified by process_aen and can be
+ * PROCESS_ALL_AENS 0
+ * FLUSH_DDB_CHANGED_AENS 1
+ * RELOGIN_DDB_CHANGED_AENS 2
+ **/
+void qla4xxx_process_aen(struct scsi_qla_host * ha, uint8_t process_aen)
+{
+ uint32_t mbox_sts[MBOX_AEN_REG_COUNT];
+ struct aen *aen;
+ int i;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ while (ha->aen_out != ha->aen_in) {
+ /* Advance pointers for next entry */
+ if (ha->aen_out == (MAX_AEN_ENTRIES - 1))
+ ha->aen_out = 0;
+ else
+ ha->aen_out++;
+
+ ha->aen_q_count++;
+ aen = &ha->aen_q[ha->aen_out];
+
+ /* copy aen information to local structure */
+ for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
+ mbox_sts[i] = aen->mbox_sts[i];
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ DEBUG(printk("scsi%ld: AEN[%d] %04x, index [%d] state=%04x "
+ "mod=%x conerr=%08x \n", ha->host_no, ha->aen_out,
+ mbox_sts[0], mbox_sts[2], mbox_sts[3],
+ mbox_sts[1], mbox_sts[4]));
+
+ switch (mbox_sts[0]) {
+ case MBOX_ASTS_DATABASE_CHANGED:
+ if (process_aen == FLUSH_DDB_CHANGED_AENS) {
+ DEBUG2(printk("scsi%ld: AEN[%d] %04x, index "
+ "[%d] state=%04x FLUSHED!\n",
+ ha->host_no, ha->aen_out,
+ mbox_sts[0], mbox_sts[2],
+ mbox_sts[3]));
+ break;
+ } else if (process_aen == RELOGIN_DDB_CHANGED_AENS) {
+ /* for use during init time, we only want to
+ * relogin non-active ddbs */
+ struct ddb_entry *ddb_entry;
+
+ ddb_entry =
+ /* FIXME: name length? */
+ qla4xxx_lookup_ddb_by_fw_index(ha,
+ mbox_sts[2]);
+ if (!ddb_entry)
+ break;
+
+ ddb_entry->dev_scan_wait_to_complete_relogin =
+ 0;
+ ddb_entry->dev_scan_wait_to_start_relogin =
+ jiffies +
+ ((ddb_entry->default_time2wait +
+ 4) * HZ);
+
+ DEBUG2(printk("scsi%ld: ddb index [%d] initate"
+ " RELOGIN after %d seconds\n",
+ ha->host_no,
+ ddb_entry->fw_ddb_index,
+ ddb_entry->default_time2wait +
+ 4));
+ break;
+ }
+
+ if (mbox_sts[1] == 0) { /* Global DB change. */
+ qla4xxx_reinitialize_ddb_list(ha);
+ } else if (mbox_sts[1] == 1) { /* Specific device. */
+ qla4xxx_process_ddb_changed(ha, mbox_sts[2],
+ mbox_sts[3]);
+ }
+ break;
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
+